feat(vscode): codex 命令统一组装,模型/思考级别可在设置面板配置

新增 cc/codexCommand.ts 作为 codex 调用的唯一出口(buildCodexArgs /
buildCodexCommandString),把 exec-review / resume / interactive 三种形态
的 flag 拼装收敛到一处;模型与思考级别作为自由文本项写入全局设置并贯穿
webview 消息通道,空值则不传 -c,沿用 codex config.toml 默认。
This commit is contained in:
2026-07-13 06:43:58 +08:00
parent aff99c57f3
commit 5188c23765
11 changed files with 225 additions and 12 deletions
+81
View File
@@ -0,0 +1,81 @@
/**
* Single source of truth for assembling `codex` invocations.
*
* The review flow shells codex three different ways — headless
* `codex exec review` (spawned as an arg array), `codex resume <id>` and the
* interactive `codex '<prompt>'` TUI (both sent to a terminal). Building the
* flags in one place keeps `--dangerously-bypass-approvals-and-sandbox` and the
* optional model / reasoning-effort `-c` overrides from drifting apart.
*
* Model and reasoning effort are free-form: an empty value omits the override
* so codex falls back to its own `config.toml` default.
*/
export interface CodexCommandOpts {
/** 'exec-review' = `codex exec review ...`'resume' = `codex resume <id>`'interactive' = `codex '<prompt>'` */
mode: 'exec-review' | 'resume' | 'interactive'
/** resume 模式必填:codex thread id */
sessionId?: string
/** interactive / exec-review 模式的 promptpositional)。调用方保证 interactive 模式下不含单引号。 */
prompt?: string
/** 空 = 不传 `-c model=`,用 codex 默认 */
model?: string
/** 空 = 不传 `-c model_reasoning_effort=`,用 codex 默认。合法值 minimal/low/medium/high/xhigh */
reasoningEffort?: string
/** exec-review 模式:追加 --json */
json?: boolean
}
const BYPASS_FLAG = '--dangerously-bypass-approvals-and-sandbox'
/**
* `-c key=value` overrides for model / reasoning effort. Blank values are
* dropped so codex keeps its config.toml default.
*/
function configOverrides(opts: CodexCommandOpts): string[] {
const parts: string[] = []
const model = opts.model?.trim()
if (model)
parts.push('-c', `model=${model}`)
const effort = opts.reasoningEffort?.trim()
if (effort)
parts.push('-c', `model_reasoning_effort=${effort}`)
return parts
}
/**
* Shared assembly for both output shapes. `quotePrompt` wraps the positional
* prompt in single quotes for the shell-string form; the spawn-array form
* passes it raw (no shell, so no quoting).
*/
function buildParts(opts: CodexCommandOpts, quotePrompt: boolean): string[] {
const overrides = configOverrides(opts)
const prompt = opts.prompt
? (quotePrompt ? `'${opts.prompt}'` : opts.prompt)
: undefined
switch (opts.mode) {
case 'exec-review':
return [
'exec',
'review',
...overrides,
BYPASS_FLAG,
...(opts.json ? ['--json'] : []),
...(prompt !== undefined ? [prompt] : []),
]
case 'resume':
return ['resume', ...overrides, BYPASS_FLAG, ...(opts.sessionId ? [opts.sessionId] : [])]
case 'interactive':
return [...overrides, BYPASS_FLAG, ...(prompt !== undefined ? [prompt] : [])]
}
}
/** Argument array (without the leading `codex`) for `spawn('codex', args)`. */
export function buildCodexArgs(opts: CodexCommandOpts): string[] {
return buildParts(opts, false)
}
/** Full shell command string (with `codex` prefix) for `terminal.sendText`. */
export function buildCodexCommandString(opts: CodexCommandOpts): string {
return ['codex', ...buildParts(opts, true)].join(' ')
}
+12 -9
View File
@@ -11,11 +11,16 @@
import { spawn } from 'node:child_process'
import { logger } from '../logging/logger'
import { buildCodexArgs } from './codexCommand'
export interface RunReviewOpts {
workspaceRoot: string
/** Already-substituted prompt — placeholders must be resolved by the caller. */
prompt: string
/** 空 = 不传 `-c model=`,用 codex config.toml 默认。 */
model?: string
/** 空 = 不传 `-c model_reasoning_effort=`,用 codex 默认。 */
reasoningEffort?: string
/** Spawn timeout in milliseconds. Defaults to 5 minutes. */
timeoutMs?: number
/**
@@ -34,15 +39,13 @@ const DEFAULT_TIMEOUT_MS = 300_000
*/
export async function runReview(opts: RunReviewOpts): Promise<void> {
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS
const args: string[] = [
'exec',
'-c',
'model_reasoning_effort=xhigh',
'review',
'--dangerously-bypass-approvals-and-sandbox',
'--json',
opts.prompt,
]
const args = buildCodexArgs({
mode: 'exec-review',
prompt: opts.prompt,
model: opts.model,
reasoningEffort: opts.reasoningEffort,
json: true,
})
await new Promise<void>((resolve) => {
let child: ReturnType<typeof spawn>