fix(codex): disable plugins in short-lived probes (#21617)

This commit is contained in:
Neil
2026-09-19 01:31:08 -07:00
committed by GitHub
parent ea02d90704
commit 14cddfeda7
4 changed files with 36 additions and 12 deletions
@@ -1,10 +1,19 @@
// The config override replaces legacy values before Codex validates config.toml.
export const CODEX_DISABLE_PLUGINS_ARGS = ['-c', 'features.plugins=false'] as const
const CODEX_READ_ONLY_APP_SERVER_TAIL = ['-s', 'read-only', '-a', 'never', 'app-server'] as const
export const CODEX_READ_ONLY_APP_SERVER_ARGS = [
'-c',
'approval_policy=never',
'-s',
'read-only',
'-a',
'never',
'app-server'
...CODEX_READ_ONLY_APP_SERVER_TAIL
] as const
// Rate-limit probes are short-lived; plugin startup can launch marketplace
// clones that outlive the probe teardown. Other read-only app-servers retain
// normal plugin behavior.
export const CODEX_RATE_LIMIT_APP_SERVER_ARGS = [
'-c',
'approval_policy=never',
...CODEX_DISABLE_PLUGINS_ARGS,
...CODEX_READ_ONLY_APP_SERVER_TAIL
] as const
@@ -41,6 +41,8 @@ const STUB_CODEX_SOURCE = `
const expectedArgs = [
'-c',
'approval_policy=never',
'-c',
'features.plugins=false',
'-s',
'read-only',
'-a',
+4 -3
View File
@@ -52,7 +52,7 @@ import { fetchCodexRateLimits } from './codex-fetcher'
import { probeCodexAuthPresence } from './codex-auth-presence'
import { getActiveHiddenRateLimitPtyCount } from './hidden-pty-cleanup'
import { getCmdExePath } from '../win32-utils'
import { CODEX_READ_ONLY_APP_SERVER_ARGS } from '../codex-cli/codex-read-only-app-server-args'
import { CODEX_RATE_LIMIT_APP_SERVER_ARGS } from '../codex-cli/codex-read-only-app-server-args'
function makeDisposable() {
return { dispose: vi.fn() }
@@ -724,7 +724,7 @@ describe('fetchCodexRateLimits', () => {
"export CODEX_HOME='\\''/home/alice/.local/share/orca/account/home'\\''"
)
expect(shellCommand).toContain(
"exec codex '\\''-c'\\'' '\\''approval_policy=never'\\'' '\\''-s'\\'' '\\''read-only'\\'' '\\''-a'\\'' '\\''never'\\'' '\\''app-server'\\'' <&3 >&4 3<&- 4>&-"
"exec codex '\\''-c'\\'' '\\''approval_policy=never'\\'' '\\''-c'\\'' '\\''features.plugins=false'\\'' '\\''-s'\\'' '\\''read-only'\\'' '\\''-a'\\'' '\\''never'\\'' '\\''app-server'\\'' <&3 >&4 3<&- 4>&-"
)
expect(shellCommand.match(/<&3 >&4 3<&- 4>&-/g)).toHaveLength(3)
expect(shellCommand.match(/exec codex [^\n]+<&3 >&4 3<&- 4>&-/g)).toHaveLength(3)
@@ -788,7 +788,7 @@ describe('fetchCodexRateLimits', () => {
const [spawnFile, spawnArgs, spawnOptions] = childSpawnMock.mock.calls[0]
expect(spawnFile).toBe(getCmdExePath())
expect(spawnArgs).toEqual(['/d', '/c', codexCommand, ...CODEX_READ_ONLY_APP_SERVER_ARGS])
expect(spawnArgs).toEqual(['/d', '/c', codexCommand, ...CODEX_RATE_LIMIT_APP_SERVER_ARGS])
expect(spawnOptions).toEqual(
expect.objectContaining({
env: expect.objectContaining({ CODEX_HOME: 'C:\\Users\\alice\\.codex' })
@@ -848,6 +848,7 @@ describe('fetchCodexRateLimits', () => {
"export CODEX_HOME='\\''/home/alice/.local/share/orca/account/home'\\''"
)
expect(shellCommand).toContain('exec codex ')
expect(shellCommand).toContain('features.plugins=false')
expect(shellCommand).not.toContain('_orca_codex')
expect(shellCommand).not.toContain('wsl-codex-path')
expect(spawnOptions).toEqual(
+16 -4
View File
@@ -3,7 +3,10 @@ import { spawn } from 'node:child_process'
import { isCodexAuthError } from '../../shared/codex-auth-errors'
import { buildWslExecArgs, buildWslLoginShellCommand } from '../../shared/wsl-login-shell-command'
import { parseWslUncPath } from '../../shared/wsl-paths'
import { CODEX_READ_ONLY_APP_SERVER_ARGS } from '../codex-cli/codex-read-only-app-server-args'
import {
CODEX_DISABLE_PLUGINS_ARGS,
CODEX_RATE_LIMIT_APP_SERVER_ARGS
} from '../codex-cli/codex-read-only-app-server-args'
import { resolveCodexCommand } from '../codex-cli/command'
// Why: import from the shared module, not the codex-cli re-export, so a test that
// mocks '../codex-cli/command' does not have to restate this pure helper.
@@ -44,6 +47,11 @@ const WSL_RPC_TIMEOUT_MS = 25_000
const RPC_INIT_TIMEOUT_MS = 30_000
const WSL_RPC_INIT_TIMEOUT_MS = 40_000
// Keep the PTY fallback aligned with the RPC probe: rate-limit collection does
// not need marketplace/plugin startup, and those background clones can outlive
// the short-lived probe process.
const CODEX_RATE_LIMIT_PLUGIN_ARGS = CODEX_DISABLE_PLUGINS_ARGS
export type FetchCodexRateLimitsOptions = CodexRateLimitFetchOptions
function buildWslCodexCommand(
@@ -96,7 +104,7 @@ async function fetchViaRpc(options?: CodexRateLimitFetchOptions): Promise<Provid
if (options?.signal?.aborted) {
return abortedCodexRateLimitResult()
}
const codexArgs = [...CODEX_READ_ONLY_APP_SERVER_ARGS]
const codexArgs = [...CODEX_RATE_LIMIT_APP_SERVER_ARGS]
const wslCodex = options?.codexHomePath
? buildWslCodexCommand(options.codexHomePath, codexArgs, true)
: null
@@ -126,13 +134,17 @@ async function fetchViaRpc(options?: CodexRateLimitFetchOptions): Promise<Provid
function resolvePtyCommand(options?: CodexRateLimitFetchOptions) {
const wslCodex = options?.codexHomePath
? buildWslCodexCommand(options.codexHomePath, [], false)
? buildWslCodexCommand(options.codexHomePath, [...CODEX_RATE_LIMIT_PLUGIN_ARGS], false)
: null
const codexCommand = wslCodex ? 'codex' : resolveCodexCommand()
const isWin32 = process.platform === 'win32'
return {
command: wslCodex ? wslCodex.command : isWin32 ? getCmdExePath() : codexCommand,
args: wslCodex ? wslCodex.args : isWin32 ? ['/d', '/c', codexCommand] : [],
args: wslCodex
? wslCodex.args
: isWin32
? ['/d', '/c', codexCommand, ...CODEX_RATE_LIMIT_PLUGIN_ARGS]
: [...CODEX_RATE_LIMIT_PLUGIN_ARGS],
cwd: resolveHiddenRateLimitPtyCwd(),
env: withCliRuntimeOnPath(codexCommand, {
...(wslCodex ? processEnvWithoutCodexHome() : process.env),