mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-07 16:09:43 +02:00
feat(codex): add Codex (OpenAI CLI) run-mode foundation
Add Codex as a first-class session mode alongside Claude/Shell/OpenCode. - codex-cli-resolver: locate the `codex` binary and augment PATH (mirrors the OpenCode resolver) - SessionMode 'codex' + CodexConfig (model, resumeSessionId, dangerouslyBypass, renderMode); persisted in SessionState and threaded through CreateSession/ RespawnPane options - schema validation: CodexConfigSchema, CODEX_ env-var prefix allowlist, mode enums on create/quick-start, codexDangerouslyBypassApprovals setting - tmux launch: buildCodexCommand, setenv for OPENAI_API_KEY/CODEX_* (keeps secrets out of ps), truecolor COLORTERM, codex PATH resolution - session + routes: availability check (clear install hint), config passthrough, tmux-required guard; Codex skips Claude-only parsers (Ralph/respawn/token) - run-mode UI: "Run CX" selector option + dedicated Codex CLI settings tab with the bypass-approvals toggle; GET /api/codex/status Scope: foundation only. Codex terminal redraw handling and xterm snapshot/replay are intentionally excluded and tracked separately. Verification: tsc --noEmit, eslint, prettier --check, check:frontend-syntax all clean; full test:ci suite green (2712 passed, 0 failed); server boot smoke OK. Co-Authored-By: Saqeb Akhter <saqeb.akhter@gmail.com>
This commit is contained in:
co-authored by
Saqeb Akhter
parent
e1f611b8fb
commit
70378315da
@@ -282,6 +282,17 @@ export function registerSessionRoutes(
|
||||
}
|
||||
}
|
||||
|
||||
// Check Codex availability if requested
|
||||
if (body.mode === 'codex') {
|
||||
const { isCodexAvailable } = await import('../../utils/codex-cli-resolver.js');
|
||||
if (!isCodexAvailable()) {
|
||||
return createErrorResponse(
|
||||
ApiErrorCode.OPERATION_FAILED,
|
||||
'Codex CLI not found. Install with: npm install -g @openai/codex'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-validate resumeSessionId: check that the conversation file actually exists
|
||||
// in Claude's projects directory. If not, skip resume to avoid confusing
|
||||
// "No conversation found" errors from Claude CLI.
|
||||
@@ -318,9 +329,11 @@ export function registerSessionRoutes(
|
||||
const model =
|
||||
mode === 'opencode'
|
||||
? body.openCodeConfig?.model
|
||||
: mode !== 'shell'
|
||||
? modelConfig?.defaultModel || undefined
|
||||
: undefined;
|
||||
: mode === 'codex'
|
||||
? body.codexConfig?.model
|
||||
: mode !== 'shell'
|
||||
? modelConfig?.defaultModel || undefined
|
||||
: undefined;
|
||||
const claudeModeConfig = await ctx.getClaudeModeConfig();
|
||||
const session = new Session({
|
||||
workingDir,
|
||||
@@ -333,6 +346,7 @@ export function registerSessionRoutes(
|
||||
claudeMode: claudeModeConfig.claudeMode,
|
||||
allowedTools: claudeModeConfig.allowedTools,
|
||||
openCodeConfig: mode === 'opencode' ? body.openCodeConfig : undefined,
|
||||
codexConfig: mode === 'codex' ? body.codexConfig : undefined,
|
||||
resumeSessionId: validatedResumeId,
|
||||
envOverrides: body.envOverrides,
|
||||
effort: body.effort,
|
||||
@@ -1112,6 +1126,7 @@ export function registerSessionRoutes(
|
||||
caseName = 'testcase',
|
||||
mode = 'claude',
|
||||
openCodeConfig,
|
||||
codexConfig,
|
||||
envOverrides,
|
||||
effort,
|
||||
} = parseBody(QuickStartSchema, req.body);
|
||||
@@ -1127,6 +1142,17 @@ export function registerSessionRoutes(
|
||||
}
|
||||
}
|
||||
|
||||
// Check Codex availability if requested
|
||||
if (mode === 'codex') {
|
||||
const { isCodexAvailable } = await import('../../utils/codex-cli-resolver.js');
|
||||
if (!isCodexAvailable()) {
|
||||
return createErrorResponse(
|
||||
ApiErrorCode.OPERATION_FAILED,
|
||||
'Codex CLI not found. Install with: npm install -g @openai/codex'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve case path: check linked-cases registry first, then fall back to CASES_DIR.
|
||||
// This mirrors the behaviour of resolveCasePath() in case-routes so that linked
|
||||
// external project directories are honoured by quick-start just like regular case routes.
|
||||
@@ -1179,9 +1205,11 @@ export function registerSessionRoutes(
|
||||
const qsModel =
|
||||
mode === 'opencode'
|
||||
? openCodeConfig?.model
|
||||
: mode !== 'shell'
|
||||
? qsModelConfig?.defaultModel || undefined
|
||||
: undefined;
|
||||
: mode === 'codex'
|
||||
? codexConfig?.model
|
||||
: mode !== 'shell'
|
||||
? qsModelConfig?.defaultModel || undefined
|
||||
: undefined;
|
||||
const qsClaudeModeConfig = await ctx.getClaudeModeConfig();
|
||||
const session = new Session({
|
||||
workingDir: casePath,
|
||||
@@ -1193,6 +1221,7 @@ export function registerSessionRoutes(
|
||||
claudeMode: qsClaudeModeConfig.claudeMode,
|
||||
allowedTools: qsClaudeModeConfig.allowedTools,
|
||||
openCodeConfig: mode === 'opencode' ? openCodeConfig : undefined,
|
||||
codexConfig: mode === 'codex' ? codexConfig : undefined,
|
||||
envOverrides,
|
||||
effort,
|
||||
});
|
||||
|
||||
@@ -341,6 +341,14 @@ export function registerSystemRoutes(
|
||||
};
|
||||
});
|
||||
|
||||
app.get('/api/codex/status', async () => {
|
||||
const { isCodexAvailable, resolveCodexDir } = await import('../../utils/codex-cli-resolver.js');
|
||||
return {
|
||||
available: isCodexAvailable(),
|
||||
path: resolveCodexDir(),
|
||||
};
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// State & Lifecycle (cleanup, lifecycle log, stats)
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
Reference in New Issue
Block a user