Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/adapters/cli/codex-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ export function createCodexAppAdapter(pathOverride?: string): CliAdapter {
let cachedCodexBin: string | undefined;
return {
id: 'codex-app',
authPaths: ['~/.codex/auth.json'],
// Whole ~/.codex kept REAL (see codex.ts): its SQLite state/log DBs can't get
// fcntl locks on the sandbox home overlay, so codex hangs ~57s then exits 1.
authPaths: ['~/.codex'],
resolvedBin: process.execPath,

// resolvedBin is node-running-the-runner; the REAL codex is spawned later for
Expand Down
8 changes: 7 additions & 1 deletion src/adapters/cli/codex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,13 @@ export function createCodexAdapter(pathOverride?: string): CliAdapter {
let cachedBin: string | undefined;
return {
id: 'codex',
authPaths: ['~/.codex/auth.json'],
// Whole ~/.codex kept REAL, not just auth.json: codex opens SQLite state/log
// DBs there (state_*.sqlite / logs_*.sqlite). Under the file sandbox the home
// is an overlayfs merge, and overlayfs (kernel + fuse) doesn't support the
// POSIX fcntl locks SQLite needs — the connection pool blocks ~57s then codex
// exits 1 ("pool timed out"). Binding the dir real gives working locks and
// keeps login/history persistent (same rationale as auth.json).
authPaths: ['~/.codex'],
get resolvedBin(): string { return (cachedBin ??= resolveCommand(rawBin)); },

buildArgs({ sessionId, resume, resumeSessionId, workingDir, model, disableCliBypass }) {
Expand Down
7 changes: 5 additions & 2 deletions test/cli-adapters.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,9 +308,12 @@ describe('codex buildArgs', () => {
expect(args.join('\n')).not.toContain('BOTMUX_TURN_ID');
});

it('keeps Codex home untouched', () => {
it('keeps the whole ~/.codex real in the sandbox (SQLite needs fcntl locks the home overlay lacks)', () => {
expect(adapter.buildSpawnEnv).toBeUndefined();
expect(adapter.authPaths).toEqual(['~/.codex/auth.json']);
// Not just auth.json: codex's state_*.sqlite / logs_*.sqlite live under
// ~/.codex and time out (~57s → exit 1) if the dir is on the overlayfs home,
// which doesn't support POSIX byte-range locks. Bind the whole dir real.
expect(adapter.authPaths).toEqual(['~/.codex']);
// skillsDir resolves under CODEX_HOME (default ~/.codex) so it tracks where
// Codex actually scans skills when CODEX_HOME is overridden.
expect(adapter.skillsDir).toBe(join(codexHome(), 'skills'));
Expand Down