From df9a254393dc2bcbfaa8f50a1e8fd635dd2d6729 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Mon, 1 Jun 2026 14:56:27 -0700 Subject: [PATCH] Heal stale generated Orca CLI launchers Detect generated Orca Unix launcher files as stale CLI registrations and replace them during install, while keeping arbitrary regular files protected as conflicts. This removes the stale /usr/local/bin/orca blocker found during memory profiling. --- docs/renderer-memory-profile-2026-06-01.md | 13 ++++ src/main/cli/cli-installer.test.ts | 80 ++++++++++++++++++++++ src/main/cli/cli-installer.ts | 46 +++++++++++++ 3 files changed, 139 insertions(+) diff --git a/docs/renderer-memory-profile-2026-06-01.md b/docs/renderer-memory-profile-2026-06-01.md index 61d8d1333..f8e321a88 100644 --- a/docs/renderer-memory-profile-2026-06-01.md +++ b/docs/renderer-memory-profile-2026-06-01.md @@ -76,3 +76,16 @@ during the live profile. The fixes are covered by unit and e2e tests, but the next packaged build should be re-profiled under the same active Codex TUI load to confirm the Resource Usage display and terminal previews match the expected lower-churn behavior. + +## Follow-up: CLI Profiling Blocker + +Continuing the profile after this change confirmed the public +`/usr/local/bin/orca` command was still broken because it was a regular +generated launcher file pointing at a removed development build. The CLI +installer previously self-healed stale symlinks, but treated regular files as +conflicts. That meant Settings could not replace an Orca-owned stale launcher, +forcing profiling to use the packaged CLI fallback. + +The follow-up fix teaches the installer to recognize only generated Orca Unix +launcher files as stale and replaceable. Arbitrary regular files at the command +path remain conflicts. diff --git a/src/main/cli/cli-installer.test.ts b/src/main/cli/cli-installer.test.ts index 9f642eeda..001f88164 100644 --- a/src/main/cli/cli-installer.test.ts +++ b/src/main/cli/cli-installer.test.ts @@ -436,6 +436,86 @@ describe('CliInstaller', () => { } ) + // Why: old dev/package experiments wrote a generated Orca launcher file + // directly into /usr/local/bin/orca. That broke profiling because Settings + // treated the regular file as a hard conflict and would not self-heal it. + it.skipIf(process.platform === 'win32')( + 'replaces stale generated Unix launcher files', + async () => { + const fixture = await makeFixture() + const commandDir = join(fixture.root, 'bin') + const installPath = join(commandDir, 'orca') + const resourcesPath = join(fixture.root, 'Current.app', 'Contents', 'Resources') + const launcherPath = join(resourcesPath, 'bin', 'orca') + const oldCliPath = join(fixture.root, 'OldWorktree', 'out', 'cli', 'index.js') + await mkdir(commandDir, { recursive: true }) + await mkdir(join(resourcesPath, 'bin'), { recursive: true }) + await writeFile(launcherPath, '#!/usr/bin/env bash\n', 'utf8') + await writeFile( + installPath, + [ + '#!/usr/bin/env bash', + 'set -euo pipefail', + "ELECTRON='/tmp/Old.app/Contents/MacOS/Electron'", + `CLI='${oldCliPath}'`, + 'export ORCA_NODE_OPTIONS="${NODE_OPTIONS-}"', + 'export ORCA_NODE_REPL_EXTERNAL_MODULE="${NODE_REPL_EXTERNAL_MODULE-}"', + 'unset NODE_OPTIONS', + 'unset NODE_REPL_EXTERNAL_MODULE', + 'ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" "$@"', + '' + ].join('\n'), + 'utf8' + ) + + const installer = new CliInstaller({ + platform: 'darwin', + isPackaged: true, + resourcesPath, + commandPathOverride: installPath, + processPathEnv: commandDir + }) + + await expect(installer.getStatus()).resolves.toMatchObject({ + state: 'stale', + currentTarget: oldCliPath + }) + await expect(installer.install()).resolves.toMatchObject({ state: 'installed' }) + await expect(readlink(installPath)).resolves.toBe(launcherPath) + } + ) + + it.skipIf(process.platform === 'win32')( + 'keeps arbitrary regular files at the command path as conflicts', + async () => { + const fixture = await makeFixture() + const commandDir = join(fixture.root, 'bin') + const installPath = join(commandDir, 'orca') + const resourcesPath = await createPackagedMacLauncher(fixture.root) + await mkdir(commandDir, { recursive: true }) + await writeFile( + installPath, + '#!/usr/bin/env bash\nELECTRON_RUN_AS_NODE=1 /tmp/not-orca "$@"\n', + 'utf8' + ) + + const installer = new CliInstaller({ + platform: 'darwin', + isPackaged: true, + resourcesPath, + commandPathOverride: installPath, + processPathEnv: commandDir + }) + + await expect(installer.getStatus()).resolves.toMatchObject({ + state: 'conflict', + currentTarget: null + }) + await expect(installer.install()).rejects.toThrow('Refusing to replace non-Orca command') + await expect(readFile(installPath, 'utf8')).resolves.toContain('/tmp/not-orca') + } + ) + // Why: a dev build can temporarily own the public command on developer // machines; packaged Orca should treat that as stale, not a hard conflict. it.skipIf(process.platform === 'win32')( diff --git a/src/main/cli/cli-installer.ts b/src/main/cli/cli-installer.ts index 043566db1..e7dae5f38 100644 --- a/src/main/cli/cli-installer.ts +++ b/src/main/cli/cli-installer.ts @@ -476,6 +476,22 @@ export class CliInstaller { try { const stats = await lstat(commandPath) if (!stats.isSymbolicLink()) { + if (stats.isFile()) { + const currentContent = await readFile(commandPath, 'utf8') + const managedTarget = extractManagedUnixLauncherTarget(currentContent) + if (managedTarget) { + return this.buildStatus({ + commandPath, + launcherPath, + installMethod: 'symlink', + supported: true, + state: 'stale', + currentTarget: managedTarget, + detail: `${commandPath} contains an older Orca launcher.` + }) + } + } + return this.buildStatus({ commandPath, launcherPath, @@ -841,6 +857,36 @@ set "ORCA_LAUNCHER=${escapeWindowsBatchValue(launcherPath)}" ` } +function extractManagedUnixLauncherTarget(content: string): string | null { + if ( + !content.includes('ELECTRON_RUN_AS_NODE=1') || + !content.includes('ORCA_NODE_OPTIONS') || + !content.includes('NODE_REPL_EXTERNAL_MODULE') + ) { + return null + } + + const cliPath = extractShellAssignment(content, 'CLI') + if (!cliPath) { + return null + } + + // Why: older dev installs wrote a generated shell launcher directly to + // /usr/local/bin/orca. Treat only Orca's compiled CLI entrypoints as managed; + // arbitrary user scripts that happen to launch Electron must stay conflicts. + return /(?:^|[/\\])(?:out|app\.asar\.unpacked[/\\]out)[/\\]cli[/\\]index\.js$/.test(cliPath) + ? cliPath + : null +} + +function extractShellAssignment(content: string, name: string): string | null { + const match = new RegExp(`^${name}=('([^']*)'|"([^"]*)"|([^\\n]+))$`, 'm').exec(content) + if (!match) { + return null + } + return (match[2] ?? match[3] ?? match[4] ?? '').trim() +} + function splitPathEntries(platform: NodeJS.Platform, value: string | null): string[] { if (!value) { return []