feat: remove legacy OpenCode runtime
Cut product flows over to Coding/Pi and retain only the migration-owned v1 boundary. Promote supported native optional packages because electron-builder omitted pnpm transitive optional closure from the packaged ASAR.
This commit is contained in:
@@ -55,6 +55,7 @@ const PERSISTED_USER_DATA_FILES = new Set([
|
||||
'gateway-prelaunch-maintenance-cache.json',
|
||||
'niancode-device-identity.json',
|
||||
'niancode-providers.json',
|
||||
// Legacy installed-user store filename; it must never enter an artifact.
|
||||
'opencode-projects.json',
|
||||
'settings.json',
|
||||
]);
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { createRequire } from 'node:module';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const root = path.resolve(__dirname, '..');
|
||||
const require = createRequire(import.meta.url);
|
||||
const outputDir = path.join(root, 'build', 'opencode-ai');
|
||||
const rootPackageJson = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
|
||||
|
||||
const targetArgs = process.argv.slice(2);
|
||||
for (const arg of targetArgs) {
|
||||
if (!/^--(?:platform|arch)=\S+$/.test(arg)) {
|
||||
throw new Error(`Unknown bundle target option: ${arg}`);
|
||||
}
|
||||
}
|
||||
|
||||
function targetOption(name, fallback) {
|
||||
const prefix = `--${name}=`;
|
||||
const option = targetArgs.find((arg) => arg.startsWith(prefix));
|
||||
return option ? option.slice(prefix.length) : fallback;
|
||||
}
|
||||
|
||||
const targetPlatform = targetOption('platform', process.platform);
|
||||
const targetArch = targetOption('arch', process.arch);
|
||||
|
||||
function fail(message) {
|
||||
console.error(`[bundle-opencode] ${message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (targetPlatform !== process.platform) {
|
||||
fail(`Cross-OS OpenCode staging is not supported: host ${process.platform}, target ${targetPlatform}.`);
|
||||
}
|
||||
|
||||
function resolvePackageRoot(packageName) {
|
||||
try {
|
||||
return path.dirname(require.resolve(`${packageName}/package.json`, {
|
||||
paths: [root],
|
||||
}));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const packageRoot = resolvePackageRoot('opencode-ai');
|
||||
if (!packageRoot) {
|
||||
fail('Missing dependency "opencode-ai". Run npm install or pnpm install before packaging.');
|
||||
}
|
||||
|
||||
const packageJsonPath = path.join(packageRoot, 'package.json');
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||
const declaredVersion = rootPackageJson.devDependencies?.['opencode-ai']
|
||||
?? rootPackageJson.dependencies?.['opencode-ai'];
|
||||
if (declaredVersion !== packageJson.version) {
|
||||
fail(
|
||||
`The opencode-ai dependency must be pinned exactly to the bundled version. Declared ${declaredVersion ?? 'missing'}, installed ${packageJson.version ?? 'unknown'}.`,
|
||||
);
|
||||
}
|
||||
const packageRequire = createRequire(packageJsonPath);
|
||||
const bin = packageJson.bin;
|
||||
const hasOpencodeBin = typeof bin === 'string'
|
||||
|| (bin && typeof bin === 'object' && typeof bin.opencode === 'string');
|
||||
|
||||
if (!hasOpencodeBin) {
|
||||
fail('Installed "opencode-ai" package does not expose an "opencode" binary.');
|
||||
}
|
||||
|
||||
fs.rmSync(outputDir, { recursive: true, force: true });
|
||||
fs.mkdirSync(path.dirname(outputDir), { recursive: true });
|
||||
fs.cpSync(packageRoot, outputDir, {
|
||||
recursive: true,
|
||||
dereference: true,
|
||||
filter: (src) => {
|
||||
if (src.includes(`${path.sep}.git${path.sep}`)) return false;
|
||||
if (path.relative(packageRoot, src).split(path.sep)[0] === 'node_modules') return false;
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
const platformMap = {
|
||||
darwin: 'darwin',
|
||||
linux: 'linux',
|
||||
win32: 'windows',
|
||||
};
|
||||
const archMap = {
|
||||
x64: 'x64',
|
||||
arm64: 'arm64',
|
||||
arm: 'arm',
|
||||
};
|
||||
|
||||
function getNativePackageCandidates() {
|
||||
const platform = platformMap[targetPlatform] ?? targetPlatform;
|
||||
const arch = archMap[targetArch] ?? targetArch;
|
||||
const base = `opencode-${platform}-${arch}`;
|
||||
|
||||
if (arch === 'x64') {
|
||||
return [`${base}-baseline`, base];
|
||||
}
|
||||
|
||||
return [base];
|
||||
}
|
||||
|
||||
function bundleNativeLauncher() {
|
||||
const binaryName = targetPlatform === 'win32' ? 'opencode.exe' : 'opencode';
|
||||
const outputBinary = path.join(outputDir, 'bin', binaryName);
|
||||
|
||||
for (const packageName of getNativePackageCandidates()) {
|
||||
let dependencyPackageJsonPath;
|
||||
try {
|
||||
dependencyPackageJsonPath = packageRequire.resolve(`${packageName}/package.json`);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
||||
const dependencyRoot = path.dirname(dependencyPackageJsonPath);
|
||||
const nativeBinary = path.join(dependencyRoot, 'bin', binaryName);
|
||||
if (!fs.existsSync(nativeBinary)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fs.copyFileSync(nativeBinary, outputBinary);
|
||||
fs.chmodSync(outputBinary, 0o755);
|
||||
return { packageName, outputBinary };
|
||||
}
|
||||
|
||||
fail(
|
||||
`Missing native opencode binary for ${targetPlatform}/${targetArch}. Tried ${getNativePackageCandidates().join(', ')}.`,
|
||||
);
|
||||
}
|
||||
|
||||
const nativeLauncher = bundleNativeLauncher();
|
||||
const verificationEnv = {
|
||||
...process.env,
|
||||
PATH: process.platform === 'win32'
|
||||
? [process.env.SystemRoot && path.join(process.env.SystemRoot, 'System32'), process.env.SystemRoot]
|
||||
.filter(Boolean)
|
||||
.join(path.delimiter)
|
||||
: '/usr/bin:/bin',
|
||||
};
|
||||
delete verificationEnv.NODE_PATH;
|
||||
delete verificationEnv.BUN_INSTALL;
|
||||
const versionProbe = spawnSync(nativeLauncher.outputBinary, ['--version'], {
|
||||
cwd: outputDir,
|
||||
encoding: 'utf8',
|
||||
env: verificationEnv,
|
||||
});
|
||||
if (versionProbe.error || versionProbe.status !== 0) {
|
||||
fail(
|
||||
`Bundled native launcher failed its self-contained version probe: ${versionProbe.error?.message ?? versionProbe.stderr ?? versionProbe.stdout}`,
|
||||
);
|
||||
}
|
||||
const reportedVersion = `${versionProbe.stdout ?? ''}${versionProbe.stderr ?? ''}`.trim();
|
||||
if (reportedVersion !== packageJson.version) {
|
||||
fail(`Bundled native launcher reported ${reportedVersion || 'no version'}, expected ${packageJson.version}.`);
|
||||
}
|
||||
console.log(
|
||||
`[bundle-opencode] bundled and verified self-contained opencode-ai@${packageJson.version} launcher ${nativeLauncher.packageName} -> ${path.relative(root, outputDir)}`,
|
||||
);
|
||||
@@ -1,202 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
import { spawn } from 'node:child_process';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { dirname, join, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { createServer } from 'node:net';
|
||||
|
||||
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const enabled = process.env.NIANCODE_OPENCODE_REAL_SMOKE === '1'
|
||||
|| process.env.OPENCODE_REAL_SMOKE === '1';
|
||||
|
||||
function skip(reason) {
|
||||
console.log(`[opencode-real-smoke] skipped: ${reason}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
skip('set NIANCODE_OPENCODE_REAL_SMOKE=1 to launch a real opencode runtime');
|
||||
}
|
||||
|
||||
function resolveOpencodeBin() {
|
||||
const explicit = process.env.NIANCODE_OPENCODE_BIN || process.env.OPENCODE_BIN;
|
||||
if (explicit) return explicit;
|
||||
|
||||
const binName = process.platform === 'win32' ? 'opencode.cmd' : 'opencode';
|
||||
const localBin = join(repoRoot, 'node_modules', '.bin', binName);
|
||||
if (existsSync(localBin)) return localBin;
|
||||
return binName;
|
||||
}
|
||||
|
||||
async function resolveConfigContent() {
|
||||
if (process.env.OPENCODE_CONFIG_CONTENT) return process.env.OPENCODE_CONFIG_CONTENT;
|
||||
if (process.env.OPENCODE_REAL_SMOKE_CONFIG) return process.env.OPENCODE_REAL_SMOKE_CONFIG;
|
||||
if (process.env.OPENCODE_REAL_SMOKE_CONFIG_FILE) {
|
||||
return await readFile(resolve(process.env.OPENCODE_REAL_SMOKE_CONFIG_FILE), 'utf8');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function getFreePort() {
|
||||
return await new Promise((resolvePort, reject) => {
|
||||
const server = createServer();
|
||||
server.once('error', reject);
|
||||
server.listen(0, '127.0.0.1', () => {
|
||||
const address = server.address();
|
||||
const port = typeof address === 'object' && address ? address.port : null;
|
||||
server.close(() => {
|
||||
if (typeof port === 'number') {
|
||||
resolvePort(port);
|
||||
return;
|
||||
}
|
||||
reject(new Error('Unable to reserve a local smoke-test port'));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function waitForListening(proc, timeoutMs) {
|
||||
return awaitableProcessOutput(proc, timeoutMs, (text) => {
|
||||
const match = text.match(/opencode server listening\s+on\s+(https?:\/\/[^\s]+)/);
|
||||
return match?.[1] ?? null;
|
||||
});
|
||||
}
|
||||
|
||||
function awaitableProcessOutput(proc, timeoutMs, resolveFromText) {
|
||||
return new Promise((resolveUrl, reject) => {
|
||||
let settled = false;
|
||||
let stderr = '';
|
||||
const finish = (callback) => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
clearTimeout(timer);
|
||||
callback();
|
||||
};
|
||||
const timer = setTimeout(() => {
|
||||
finish(() => reject(new Error(`Timed out waiting for opencode server after ${timeoutMs}ms${stderr ? `\n${stderr}` : ''}`)));
|
||||
}, timeoutMs);
|
||||
|
||||
proc.stdout?.on('data', (chunk) => {
|
||||
const text = chunk.toString();
|
||||
process.stdout.write(text);
|
||||
const result = resolveFromText(text);
|
||||
if (result) finish(() => resolveUrl(result));
|
||||
});
|
||||
proc.stderr?.on('data', (chunk) => {
|
||||
const text = chunk.toString();
|
||||
stderr += text;
|
||||
process.stderr.write(text);
|
||||
});
|
||||
proc.on('error', (error) => finish(() => reject(error)));
|
||||
proc.on('exit', (code) => {
|
||||
finish(() => reject(new Error(`opencode exited before it became ready: ${code ?? 'unknown'}${stderr ? `\n${stderr}` : ''}`)));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function sessionIdOf(session) {
|
||||
if (!session || typeof session !== 'object') return null;
|
||||
const value = session.id ?? session.sessionID ?? session.sessionId;
|
||||
return typeof value === 'string' && value.trim() ? value : null;
|
||||
}
|
||||
|
||||
async function requestJson(url, init = {}) {
|
||||
const response = await fetch(url, {
|
||||
...init,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(init.headers ?? {}),
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
const body = await response.text().catch(() => '');
|
||||
throw new Error(`opencode request failed ${response.status} ${response.statusText}${body ? `\n${body}` : ''}`);
|
||||
}
|
||||
if (response.status === 204) return null;
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async function createProjectDir() {
|
||||
if (process.env.OPENCODE_REAL_SMOKE_PROJECT) {
|
||||
return {
|
||||
path: resolve(process.env.OPENCODE_REAL_SMOKE_PROJECT),
|
||||
cleanup: async () => {},
|
||||
};
|
||||
}
|
||||
|
||||
const path = await mkdtemp(join(tmpdir(), 'niancode-opencode-smoke-'));
|
||||
await writeFile(join(path, 'README.md'), '# Makelore opencode real-runtime smoke\n');
|
||||
return {
|
||||
path,
|
||||
cleanup: async () => {
|
||||
await rm(path, { recursive: true, force: true });
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const configContent = await resolveConfigContent();
|
||||
if (!configContent && process.env.OPENCODE_REAL_SMOKE_ALLOW_LOCAL_CONFIG !== '1') {
|
||||
skip('provide OPENCODE_CONFIG_CONTENT, OPENCODE_REAL_SMOKE_CONFIG, or OPENCODE_REAL_SMOKE_CONFIG_FILE');
|
||||
}
|
||||
|
||||
JSON.parse(configContent ?? '{}');
|
||||
|
||||
const binPath = resolveOpencodeBin();
|
||||
const port = Number(process.env.OPENCODE_REAL_SMOKE_PORT) || await getFreePort();
|
||||
const startupTimeoutMs = Number(process.env.OPENCODE_REAL_SMOKE_STARTUP_TIMEOUT_MS) || 20_000;
|
||||
const apiTimeoutMs = Number(process.env.OPENCODE_REAL_SMOKE_API_TIMEOUT_MS) || 60_000;
|
||||
const project = await createProjectDir();
|
||||
const proc = spawn(binPath, ['serve', '--hostname=127.0.0.1', `--port=${port}`], {
|
||||
env: {
|
||||
...process.env,
|
||||
...(configContent ? { OPENCODE_CONFIG_CONTENT: configContent } : {}),
|
||||
},
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
windowsHide: true,
|
||||
shell: process.platform === 'win32' && /\.cmd$/i.test(binPath),
|
||||
});
|
||||
|
||||
try {
|
||||
const baseUrl = await waitForListening(proc, startupTimeoutMs);
|
||||
const directoryQuery = `directory=${encodeURIComponent(project.path)}`;
|
||||
const scopedHeaders = { 'x-opencode-directory': encodeURIComponent(project.path) };
|
||||
|
||||
const sessions = await requestJson(`${baseUrl}/session?${directoryQuery}`, {
|
||||
signal: AbortSignal.timeout(apiTimeoutMs),
|
||||
headers: scopedHeaders,
|
||||
});
|
||||
if (!Array.isArray(sessions)) {
|
||||
throw new Error('Expected /session to return an array');
|
||||
}
|
||||
|
||||
const session = await requestJson(`${baseUrl}/session?${directoryQuery}`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({}),
|
||||
signal: AbortSignal.timeout(apiTimeoutMs),
|
||||
headers: scopedHeaders,
|
||||
});
|
||||
const sessionId = sessionIdOf(session);
|
||||
if (!sessionId) {
|
||||
throw new Error('Created opencode session did not include an id');
|
||||
}
|
||||
|
||||
if (process.env.OPENCODE_REAL_SMOKE_PROMPT) {
|
||||
await requestJson(`${baseUrl}/session/${encodeURIComponent(sessionId)}/message?${directoryQuery}`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
parts: [
|
||||
{ type: 'text', text: process.env.OPENCODE_REAL_SMOKE_PROMPT },
|
||||
],
|
||||
}),
|
||||
signal: AbortSignal.timeout(apiTimeoutMs),
|
||||
headers: scopedHeaders,
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`[opencode-real-smoke] ok: started ${baseUrl}, listed ${sessions.length} session(s), created ${sessionId}`);
|
||||
} finally {
|
||||
proc.kill();
|
||||
await project.cleanup();
|
||||
}
|
||||
@@ -1,13 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { getPythonTarget, PYTHON_VERSION } from './bundled-python-manifest.mjs';
|
||||
|
||||
const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
|
||||
const PROJECT_ROOT = path.resolve(SCRIPT_DIR, '..');
|
||||
|
||||
function readOption(name) {
|
||||
const prefix = `--${name}=`;
|
||||
const inline = process.argv.find((value) => value.startsWith(prefix));
|
||||
@@ -35,10 +30,8 @@ if (!targetId || !root) {
|
||||
try {
|
||||
const target = getPythonTarget(targetId);
|
||||
const executable = path.resolve(root, target.executable);
|
||||
const meowaCli = path.join(PROJECT_ROOT, '.opencode', 'skills', 'game-assets', 'meowart_api.py');
|
||||
runProbe(executable, 'version', ['--version']);
|
||||
runProbe(executable, 'stdlib', ['-c', "import json, pathlib, urllib.request; print('stdlib-ok')"]);
|
||||
runProbe(executable, 'meowa-help', [meowaCli, '--help']);
|
||||
process.stdout.write(`[python] ${targetId} verified for Python ${PYTHON_VERSION}\n`);
|
||||
} catch (error) {
|
||||
process.stderr.write(`[python] ${targetId} verification failed: ${error instanceof Error ? error.message : String(error)}\n`);
|
||||
|
||||
@@ -146,9 +146,6 @@ const npmPackagePath = join(npmRuntimeDir, 'package.json');
|
||||
const npmCliPath = join(npmRuntimeDir, 'bin', 'npm-cli.js');
|
||||
const appAsarPath = join(resourcesDir, 'app.asar');
|
||||
const appAsarUnpackedPath = join(resourcesDir, 'app.asar.unpacked');
|
||||
const opencodeRuntimeDir = join(resourcesDir, 'opencode-ai');
|
||||
const opencodePackagePath = join(opencodeRuntimeDir, 'package.json');
|
||||
const opencodeExecutable = join(opencodeRuntimeDir, 'bin', 'opencode.exe');
|
||||
const pythonRuntimeDir = join(resourcesDir, 'python');
|
||||
const pythonExecutable = join(pythonRuntimeDir, 'python.exe');
|
||||
const toolsBinDir = join(resourcesDir, 'bin');
|
||||
@@ -159,8 +156,6 @@ for (const filePath of [
|
||||
appAsarPath,
|
||||
npmPackagePath,
|
||||
npmCliPath,
|
||||
opencodePackagePath,
|
||||
opencodeExecutable,
|
||||
pythonExecutable,
|
||||
uvExecutable,
|
||||
]) {
|
||||
@@ -170,31 +165,20 @@ for (const filePath of [
|
||||
const PE_MACHINE_AMD64 = 0x8664;
|
||||
for (const [label, executable] of [
|
||||
['Makelore executable', appExecutable],
|
||||
['OpenCode executable', opencodeExecutable],
|
||||
['Python executable', pythonExecutable],
|
||||
['uv executable', uvExecutable],
|
||||
]) {
|
||||
assertPeMachine(executable, PE_MACHINE_AMD64, label);
|
||||
}
|
||||
|
||||
const declaredOpencodeVersion = packageJson.devDependencies?.['opencode-ai']
|
||||
?? packageJson.dependencies?.['opencode-ai'];
|
||||
const packagedOpencode = JSON.parse(readFileSync(opencodePackagePath, 'utf8'));
|
||||
assertEqual(packagedOpencode.version, declaredOpencodeVersion, 'packaged OpenCode version');
|
||||
|
||||
const systemRoot = process.env.SystemRoot ?? process.env.WINDIR ?? 'C:\\Windows';
|
||||
const installLocalProbeEnv = {
|
||||
...process.env,
|
||||
PATH: [join(systemRoot, 'System32'), systemRoot].join(';'),
|
||||
};
|
||||
for (const key of ['NODE_PATH', 'BUN_INSTALL', 'OPENCODE_BIN', 'OPENCODE_PATH', 'NIANCODE_PYTHON_PATH', 'NIANCODE_UV_PATH']) {
|
||||
for (const key of ['NODE_PATH', 'BUN_INSTALL', 'NIANCODE_PYTHON_PATH', 'NIANCODE_UV_PATH']) {
|
||||
delete installLocalProbeEnv[key];
|
||||
}
|
||||
const opencodeVersion = run(opencodeExecutable, ['--version'], {
|
||||
cwd: opencodeRuntimeDir,
|
||||
env: installLocalProbeEnv,
|
||||
});
|
||||
assertEqual(opencodeVersion, declaredOpencodeVersion, 'OpenCode executable version');
|
||||
const uvVersion = run(uvExecutable, ['--version'], {
|
||||
cwd: toolsBinDir,
|
||||
env: installLocalProbeEnv,
|
||||
@@ -232,8 +216,6 @@ const appRequire = createRequire(join(dirname(process.execPath), 'resources', 'a
|
||||
const packagedPackage = appRequire('./package.json');
|
||||
const msgpackResolved = appRequire.resolve('msgpackr');
|
||||
const canvasResolved = appRequire.resolve('@napi-rs/canvas');
|
||||
const playwrightPackageResolved = appRequire.resolve('@playwright/mcp/package.json');
|
||||
const playwrightCliResolved = join(dirname(playwrightPackageResolved), 'cli.js');
|
||||
const { pack, unpack } = appRequire('msgpackr');
|
||||
const { createCanvas } = appRequire('@napi-rs/canvas');
|
||||
const packedValue = unpack(pack({ ok: true, text: '\u5362\u6b22' }));
|
||||
@@ -271,8 +253,6 @@ console.log('NIANCODE_ARTIFACT_PROBE=' + JSON.stringify({
|
||||
},
|
||||
msgpackResolved,
|
||||
canvasResolved,
|
||||
playwrightPackageResolved,
|
||||
playwrightCliResolved,
|
||||
nativeModules,
|
||||
packedValue,
|
||||
canvas: [canvas.width, canvas.height],
|
||||
@@ -316,12 +296,6 @@ if (!isPathInside(appAsarPath, probe.msgpackResolved)) {
|
||||
if (!isPathInside(appAsarPath, probe.canvasResolved)) {
|
||||
throw new Error(`@napi-rs/canvas was not resolved from app.asar: ${probe.canvasResolved}`);
|
||||
}
|
||||
if (!isPathInside(appAsarPath, probe.playwrightPackageResolved)) {
|
||||
throw new Error(`@playwright/mcp was not resolved from app.asar: ${probe.playwrightPackageResolved}`);
|
||||
}
|
||||
if (!isPathInside(appAsarPath, probe.playwrightCliResolved)) {
|
||||
throw new Error(`@playwright/mcp CLI was not resolved from app.asar: ${probe.playwrightCliResolved}`);
|
||||
}
|
||||
const packagedNpm = JSON.parse(readFileSync(npmPackagePath, 'utf8'));
|
||||
assertEqual(packagedNpm.version, packageJson.dependencies.npm, 'packaged npm version');
|
||||
const npmVersion = run(appExecutable, [npmCliPath, '--version'], {
|
||||
@@ -329,10 +303,6 @@ const npmVersion = run(appExecutable, [npmCliPath, '--version'], {
|
||||
env: { ...installLocalProbeEnv, ELECTRON_RUN_AS_NODE: '1' },
|
||||
});
|
||||
assertEqual(npmVersion, packageJson.dependencies.npm, 'packaged npm CLI version');
|
||||
run(appExecutable, [probe.playwrightCliResolved, '--help'], {
|
||||
cwd: dirname(appExecutable),
|
||||
env: { ...installLocalProbeEnv, ELECTRON_RUN_AS_NODE: '1' },
|
||||
});
|
||||
const msgpackNative = probe.nativeModules.find(({ modulePath, physicalPath, exists }) => (
|
||||
exists
|
||||
&& isPathInside(appAsarPath, modulePath)
|
||||
@@ -372,15 +342,6 @@ const evidence = {
|
||||
installerSha256,
|
||||
runtime: { electron: probe.electron, node: probe.node },
|
||||
installLocalRuntimes: {
|
||||
opencode: {
|
||||
executable: opencodeExecutable,
|
||||
version: opencodeVersion,
|
||||
sha256: await sha256(opencodeExecutable),
|
||||
},
|
||||
playwrightMcp: {
|
||||
packageJson: probe.playwrightPackageResolved,
|
||||
cli: probe.playwrightCliResolved,
|
||||
},
|
||||
python: pythonProbe,
|
||||
uv: { executable: uvExecutable, version: uvVersion },
|
||||
npm: { packageJson: npmPackagePath, cli: npmCliPath, version: npmVersion },
|
||||
|
||||
Reference in New Issue
Block a user