mirror of
https://github.com/stablyai/orca.git
synced 2026-10-23 08:03:39 +00:00
* fix(codex): start short-lived Codex app-servers on Windows without cmd.exe Orca's short-lived Codex app-server launches (history index passes, the state-DB recovery claimant, hook trust grants, and the rate-limit and model catalog probes) wrapped npm's codex.cmd in `cmd.exe /d /c` before handing it to spawnProcess. That hid the shim from spawnProcess's resolver, so every one of these launches went through cmd.exe instead of straight to node. Hand the bare CLI path to the spawn chokepoint like the chat session already does. The rate-limit probe and the recovery claimant also move off direct node:child_process imports, shrinking that allowlist by two. * chore(codex): drop stale cmd.exe premises from the probe kill and CLI pairing comments Short-lived Codex app-servers now reach spawnProcess as the bare shim, so the direct child is node for a resolved npm shim and cmd.exe only as the fallback. * test(child-process): delist the rate-limit probe from the hidden-console ratchet The probe now spawns through spawnProcess, which always hides the console.
77 lines
2.7 KiB
TypeScript
77 lines
2.7 KiB
TypeScript
import { spawnProcess } from '../../shared/child-process/run-process'
|
|
import type { ChildProcessHandle, ProcessSpec } from '../../shared/child-process/process-spec'
|
|
import { admitProcessTreeKill } from '../../shared/child-process/process-tree-kill-gate'
|
|
|
|
/** Spawn seam for tests; production always goes through the hardened spawnProcess wrapper. */
|
|
export type CodexAppServerSpawn = (
|
|
program: string,
|
|
args: string[],
|
|
options: Record<string, unknown>
|
|
) => ChildProcessHandle
|
|
|
|
export const spawnCodexAppServerProcess: CodexAppServerSpawn = (program, args, options) =>
|
|
spawnProcess({ program, args, ...options } as ProcessSpec)
|
|
|
|
export function killCodexAppServerProcessTree(
|
|
child: Pick<ChildProcessHandle, 'pid' | 'kill'>,
|
|
options: { platform?: NodeJS.Platform; spawnImpl?: CodexAppServerSpawn } = {}
|
|
): void {
|
|
const platform = options.platform ?? process.platform
|
|
const spawnImpl = options.spawnImpl ?? spawnCodexAppServerProcess
|
|
if (platform === 'win32' && child.pid) {
|
|
if (
|
|
!admitProcessTreeKill({
|
|
pid: child.pid,
|
|
site: 'codex-app-server-session-deadline',
|
|
scope: 'win-taskkill-tree'
|
|
})
|
|
) {
|
|
// Refusal blocks the tree walk, not the termination: the root kill is
|
|
// handle-addressed, so it cannot reach the recycled pid we refused.
|
|
child.kill('SIGKILL')
|
|
return
|
|
}
|
|
try {
|
|
// Why: npm-installed Codex runs behind a launcher (node, or cmd.exe for an unresolved shim);
|
|
// killing only that wrapper leaves the app-server child alive after a timeout.
|
|
const killer = spawnImpl('taskkill', ['/pid', String(child.pid), '/t', '/f'], {
|
|
stdio: 'ignore',
|
|
windowsHide: true
|
|
})
|
|
let fellBack = false
|
|
const killDirectChild = (): void => {
|
|
if (!fellBack) {
|
|
fellBack = true
|
|
child.kill('SIGKILL')
|
|
}
|
|
}
|
|
killer.on('error', killDirectChild)
|
|
killer.on('exit', (code) => {
|
|
if (code !== 0) {
|
|
killDirectChild()
|
|
}
|
|
})
|
|
killer.unref()
|
|
return
|
|
} catch {
|
|
// Fall through to the direct-child best effort when taskkill cannot start.
|
|
}
|
|
}
|
|
if (child.pid) {
|
|
try {
|
|
// npm/package-manager launchers insert a shim child on POSIX. Reap its
|
|
// direct descendants before signalling the wrapper itself.
|
|
const descendants = spawnImpl('pkill', ['-KILL', '-P', String(child.pid)], {
|
|
stdio: 'ignore'
|
|
})
|
|
// A missing pkill surfaces as an async 'error' event, and an unhandled one
|
|
// takes down the main process.
|
|
descendants.on('error', () => undefined)
|
|
descendants.unref()
|
|
} catch {
|
|
// The direct kill below remains the fallback when pkill is unavailable.
|
|
}
|
|
}
|
|
child.kill('SIGKILL')
|
|
}
|