agent-ecosystem/scripts/dev-with-runtime.mjs

90 lines
2.9 KiB
JavaScript

#!/usr/bin/env node
import fs from 'node:fs';
import path from 'node:path';
import { spawnSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
function runOrExit(cmd, args, options = {}) {
const result = spawnSync(cmd, args, {
stdio: 'inherit',
...options,
});
if (result.error) {
console.error(`Failed to run ${cmd}: ${result.error.message}`);
process.exit(1);
}
if (result.status !== 0) {
process.exit(result.status ?? 1);
}
}
function readPackageManagerCommand(repoRoot) {
const packageJsonPath = path.join(repoRoot, 'package.json');
const rawPackageJson = fs.readFileSync(packageJsonPath, 'utf8');
const packageJson = JSON.parse(rawPackageJson);
const rawPackageManager = packageJson.packageManager;
if (typeof rawPackageManager !== 'string' || rawPackageManager.trim().length === 0) {
return 'pnpm';
}
const [packageManagerName] = rawPackageManager.trim().split('@', 1);
if (!packageManagerName) {
return 'pnpm';
}
return packageManagerName;
}
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
const uiRepoRoot = path.resolve(scriptDir, '..');
// Keep the dev runtime target explicit. This workspace can contain multiple
// sibling repos with the same package name, so auto-discovery is ambiguous and
// can silently point the UI at the wrong runtime after branch switches.
const runtimeRepoRoot = process.env.CLAUDE_DEV_RUNTIME_ROOT?.trim();
if (!runtimeRepoRoot) {
console.error(
'CLAUDE_DEV_RUNTIME_ROOT is required for pnpm dev. ' +
'Point it at the runtime repo root you want the UI to use in dev.'
);
process.exit(1);
}
const runtimePackageJsonPath = path.join(runtimeRepoRoot, 'package.json');
if (!fs.existsSync(runtimePackageJsonPath)) {
console.error(`CLAUDE_DEV_RUNTIME_ROOT does not look like a repo root: ${runtimeRepoRoot}`);
process.exit(1);
}
const runtimePackageManager = readPackageManagerCommand(runtimeRepoRoot);
if (process.argv.includes('--print-runtime-path')) {
process.stdout.write(`${runtimeRepoRoot}\n`);
process.exit(0);
}
// Respect the runtime repo's own package manager. The UI repo uses pnpm, but
// the runtime may legitimately be a Bun workspace, and forcing pnpm there can
// fail before the build even starts.
runOrExit(runtimePackageManager, ['run', 'build:dev'], { cwd: runtimeRepoRoot });
const runtimeCliPath = path.join(runtimeRepoRoot, 'cli-dev');
const uiEnv = {
...process.env,
// Dev-only free-code runtime override. Keep it separate from the generic
// CLAUDE_CLI_PATH override so switching the app into Claude CLI mode still
// resolves the real official binary instead of this local cli-dev shim.
CLAUDE_FREE_CODE_CLI_PATH: runtimeCliPath,
};
// If the parent shell exported a stale generic override, do not let it leak
// into the Electron main process. Claude mode must resolve the real binary.
delete uiEnv.CLAUDE_CLI_PATH;
runOrExit('pnpm', ['run', 'dev:ui'], {
cwd: uiRepoRoot,
env: uiEnv,
});