mirror of
https://github.com/stablyai/orca.git
synced 2026-10-05 16:02:22 +00:00
fix(agent-hooks): keep Orca's own CODEX_HOME out of the host probe
Orca exports CODEX_HOME/ORCA_CODEX_HOME for its managed Codex accounts. The probe inherited them, so it reported Orca's own account home as if the host user had redirected there — which broke the Node 18 managed-hook smoke and would have installed hooks into the managed account home on any client that runs the relay in-process. The child now gets the installer's HOME pinned and both variables stripped; a redirect the user's profile or launcher sets is unaffected, since the login shell re-exports it.
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { chmod, mkdtemp, rm, writeFile } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { probeCodexHomeViaAppServer } from './codex-app-server-home-probe'
|
||||
import {
|
||||
buildCodexProbeEnvironment,
|
||||
probeCodexHomeViaAppServer
|
||||
} from './codex-app-server-home-probe'
|
||||
|
||||
// The probe speaks to a real child over pipes; `/bin/sh` is the shell it uses
|
||||
// on every host that reaches this code (Windows remotes never install hooks).
|
||||
@@ -122,3 +125,46 @@ sleep 30
|
||||
expect(Date.now() - startedAt).toBeLessThan(5_000)
|
||||
})
|
||||
})
|
||||
|
||||
describe('buildCodexProbeEnvironment', () => {
|
||||
afterEach(() => {
|
||||
vi.unstubAllEnvs()
|
||||
})
|
||||
|
||||
it('drops the CODEX_HOME Orca injected for its own managed accounts', () => {
|
||||
// Orca exports these for its managed Codex accounts. Reading one back would
|
||||
// report Orca's own answer as if it were the host user's configuration.
|
||||
vi.stubEnv('CODEX_HOME', '/orca/codex-accounts/abc/home')
|
||||
vi.stubEnv('ORCA_CODEX_HOME', '/orca/codex-accounts/abc/home')
|
||||
|
||||
const env = buildCodexProbeEnvironment('/home/dev')
|
||||
|
||||
expect(env.CODEX_HOME).toBeUndefined()
|
||||
expect(env.ORCA_CODEX_HOME).toBeUndefined()
|
||||
})
|
||||
|
||||
it('pins HOME to the home the installer resolved', () => {
|
||||
expect(buildCodexProbeEnvironment('/home/dev').HOME).toBe('/home/dev')
|
||||
})
|
||||
|
||||
onPosix('asks Codex under the installer s home, not Orca s injected one', async () => {
|
||||
vi.stubEnv('CODEX_HOME', '/orca/codex-accounts/abc/home')
|
||||
const fake = await withFakeCodex(
|
||||
`#!/bin/sh
|
||||
read -r _line
|
||||
home="\${CODEX_HOME:-$HOME/.codex}"
|
||||
printf '{"id":1,"result":{"codexHome":"%s"}}\\n' "$home"
|
||||
sleep 5
|
||||
`
|
||||
)
|
||||
|
||||
await expect(
|
||||
probeCodexHomeViaAppServer({
|
||||
loginShell: '/bin/sh',
|
||||
loginShellFlag: '-c',
|
||||
env: { ...buildCodexProbeEnvironment('/home/dev'), PATH: fake.PATH },
|
||||
timeoutMs: 10_000
|
||||
})
|
||||
).resolves.toBe('/home/dev/.codex')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -20,6 +20,23 @@ const PROBE_TIMEOUT_MS = 12_000
|
||||
const MAX_STDOUT_BYTES = 64 * 1024
|
||||
const INITIALIZE_ID = 1
|
||||
|
||||
/**
|
||||
* The environment the probe hands the child.
|
||||
*
|
||||
* `HOME` is pinned to the home the installer resolved, so parent and child
|
||||
* agree on which account is being configured. `CODEX_HOME`/`ORCA_CODEX_HOME`
|
||||
* are dropped because Orca injects them for its own managed accounts — reading
|
||||
* one back would report Orca's own answer as if it were the host user's. A
|
||||
* value the user's profile or launcher wrapper sets is unaffected: the login
|
||||
* shell re-exports it, which is the whole point of the probe.
|
||||
*/
|
||||
export function buildCodexProbeEnvironment(home: string): NodeJS.ProcessEnv {
|
||||
const env: NodeJS.ProcessEnv = { ...process.env, HOME: home }
|
||||
delete env.CODEX_HOME
|
||||
delete env.ORCA_CODEX_HOME
|
||||
return env
|
||||
}
|
||||
|
||||
/**
|
||||
* `codex app-server` exits without answering when stdin reaches EOF, so the
|
||||
* request is written and the pipe is left open until the reply lands.
|
||||
|
||||
@@ -3,7 +3,10 @@ import { basename } from 'node:path'
|
||||
import { homedir, userInfo } from 'node:os'
|
||||
import { promisify } from 'node:util'
|
||||
import { installRemoteManagedAgentHooks } from './remote-managed-hook-installers'
|
||||
import { probeCodexHomeViaAppServer } from './codex-app-server-home-probe'
|
||||
import {
|
||||
buildCodexProbeEnvironment,
|
||||
probeCodexHomeViaAppServer
|
||||
} from './codex-app-server-home-probe'
|
||||
import type { AgentHookTarget } from '../../shared/agent-hook-types'
|
||||
import { createManagedHookLocalFilesystem } from './managed-hook-local-filesystem'
|
||||
import { withManagedHookInstallLock } from './managed-hook-install-lock'
|
||||
@@ -109,6 +112,7 @@ export async function resolveRelayRedirectedCodexHome(
|
||||
const reported = await probe({
|
||||
loginShell: shell,
|
||||
loginShellFlag: flag,
|
||||
env: buildCodexProbeEnvironment(home),
|
||||
...(signal ? { signal } : {})
|
||||
})
|
||||
const codexHome = reported === null ? null : normalizePosixAgentHome(reported.trim())
|
||||
|
||||
Reference in New Issue
Block a user