mirror of
https://github.com/stablyai/orca.git
synced 2026-10-08 00:02:38 +00:00
* fix(startup): stop the PATH seed pinning nvm to its newest install patchPackagedProcessPath prepends the newest nvm version dir to process.env.PATH, then hydrateShellPath probes the login shell with that same env. nvm's startup `use` honors whatever node is already on PATH instead of the user's `default` alias, so the probe returns a PATH pinned to the newest install and every terminal pane inherits it. A user whose newest nvm node is a bare install then loses every global CLI (codex, claude, gemini, vercel...) inside Orca while they still resolve in Ghostty/Terminal, which start from the bare GUI PATH and fall through to `default`. Probe with the PATH the process launched with. Windows already gets this through WindowsShellPathOwnership. * test(startup): pin the platform in the probe-env test shellProbeEnv short-circuits on win32, so the POSIX-only assertion failed for anyone running the suite on Windows. Matches the convention in hydrate-shell-path.windows.test.ts. Also narrows the win32 exemption comment: WindowsShellPathOwnership snapshots its baseline after the seeds land, so it does not unwind them. The exemption holds because Windows keys PATH as `Path` and no Windows seed pins a node version. * fix(startup): give win32 the same probe insulation, keyed by Path The previous commit exempted win32 on the stated grounds that no Windows seed pins a node version. That is wrong: getVersionManagerDirectories calls getNvmVersionDirectories on every platform, so a Git Bash user whose nvm uses the POSIX ~/.nvm/versions/node layout gets the newest version dir seeded on Windows too, and the -ilc Git Bash probe inherits it. Record the PATH key alongside the value and overwrite that entry in place, so Windows never carries both `Path` and `PATH` — which was the only real reason to skip win32. * fix(startup): snapshot the launch PATH at module load, log probe failures Three loose ends from the review, folded in rather than deferred. The probe's clean PATH was handed over by an explicit recordLaunchPath call from the seeding site, so the invariant lived across three files and a refactor that moved the seed call would silently re-pin nvm. Snapshot PATH during module init instead: that runs while the import graph is evaluated, strictly before any statement in main's body, so it cannot observe the seeds and there is no call ordering left to break. All seven importers are static, so no lazy import can defeat it. A test asserts the probe ignores later process.env mutation, and fails if the live read is reintroduced. A failed startup probe leaves the seeded newest-nvm dir in front and said nothing, so the population whose rc files blow the 5s budget hit the original symptom with no diagnosable trace. Log the failureReason. The probe is an interactive login shell, so rc files that exec into a multiplexer or start a heavy prompt can outrun that budget with no way to opt out. Set ORCA_SHELL_PATH_PROBE=1 so they can take a fast path. * fix(startup): drop the other-cased PATH key from the Windows probe env Caught by running the suite on a real Windows machine, not a mocked platform. The spread of process.env is a plain, case-sensitive object, while Windows resolves env names case-insensitively. Writing the captured `Path` back onto it left the seeded value still live under `PATH`, so the probe shell could read either one — the exact duplicate-key hazard the win32 branch was supposed to prevent. Drop any other-cased variant of the key before writing. * fix(startup): preserve launch PATH across app restarts
62 lines
1.9 KiB
TypeScript
62 lines
1.9 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
const { appRelaunchMock, recordDurableCrashBreadcrumbMock } = vi.hoisted(() => ({
|
|
appRelaunchMock: vi.fn(),
|
|
recordDurableCrashBreadcrumbMock: vi.fn()
|
|
}))
|
|
|
|
vi.mock('electron', () => ({ app: { relaunch: appRelaunchMock } }))
|
|
vi.mock('./crash-reporting/durable-crash-breadcrumb', () => ({
|
|
recordDurableCrashBreadcrumb: recordDurableCrashBreadcrumbMock
|
|
}))
|
|
|
|
import { relaunchApp } from './app-relaunch'
|
|
import { _resetHydrateShellPathCache, _setLaunchPathForTests } from './startup/hydrate-shell-path'
|
|
|
|
beforeEach(() => {
|
|
appRelaunchMock.mockReset()
|
|
recordDurableCrashBreadcrumbMock.mockReset()
|
|
})
|
|
|
|
const originalPath = process.env.PATH
|
|
|
|
afterEach(() => {
|
|
_resetHydrateShellPathCache()
|
|
if (originalPath === undefined) {
|
|
delete process.env.PATH
|
|
} else {
|
|
process.env.PATH = originalPath
|
|
}
|
|
})
|
|
|
|
describe('relaunchApp', () => {
|
|
it('durably records the reason before scheduling the replacement process', () => {
|
|
relaunchApp('gpu-fallback', { processReason: 'crashed', exitCode: 5 })
|
|
|
|
expect(recordDurableCrashBreadcrumbMock).toHaveBeenCalledOnce()
|
|
expect(recordDurableCrashBreadcrumbMock).toHaveBeenCalledWith('app_relaunch_requested', {
|
|
processReason: 'crashed',
|
|
exitCode: 5,
|
|
reason: 'gpu-fallback'
|
|
})
|
|
expect(appRelaunchMock).toHaveBeenCalledOnce()
|
|
expect(recordDurableCrashBreadcrumbMock.mock.invocationCallOrder[0]).toBeLessThan(
|
|
appRelaunchMock.mock.invocationCallOrder[0]
|
|
)
|
|
})
|
|
|
|
it('does not carry Orca PATH seeds into the replacement process', () => {
|
|
process.env.PATH = '/seeded/newest-nvm/bin:/usr/bin'
|
|
_setLaunchPathForTests('/usr/bin')
|
|
let inheritedPath: string | undefined
|
|
appRelaunchMock.mockImplementation(() => {
|
|
inheritedPath = process.env.PATH
|
|
})
|
|
|
|
relaunchApp('renderer-request')
|
|
|
|
expect(inheritedPath).toBe('/usr/bin')
|
|
expect(process.env.PATH).toBe('/seeded/newest-nvm/bin:/usr/bin')
|
|
})
|
|
})
|