Repository Public
1/**2 * Codex CLI launch arguments: what the runtime accepts for an agent session of the `codex`3 * profile. Verified against `codex --help` / `codex resume --help` of codex-cli 0.166.0.4 *5 * The server only ever sends one of: `[]`, `['resume', '--last']`, `['resume', <uuid>]`.6 * Everything else is refused here (defence in depth):7 * - approval / sandbox bypass flags, by name, with a dedicated error code;8 * - any configuration override (`-c`, `--config`, `--enable`, `--disable`, `--profile`), which9 * could switch the approval policy or the sandbox off just as well;10 * - any other shape — in particular a free positional argument, which Codex would take as a11 * PROMPT and run immediately.12 */13const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;1415/** Long options that are refused whatever their value. */16const FORBIDDEN_LONG = new Set([17 '--dangerously-bypass-approvals-and-sandbox',18 '--yolo', // hidden alias of the flag above (accepted by 0.166)19 '--dangerously-bypass-hook-trust',20 '--approve-for-me', // approvals answered by an automatic reviewer instead of the user21 '--not-so-yolo', // hidden alias of --approve-for-me (accepted by 0.166)22 '--full-auto', // older CLIs: workspace-write sandbox without approval prompts23 '--config',24 '--enable',25 '--disable',26 '--profile',27 '--remote',28 '--remote-auth-token-env',29]);30/** Short options refused in every form (`-c x=y`, `-cx=y`, `-p name`, `-pname`). */31const FORBIDDEN_SHORT = new Set(['c', 'p']);3233/** Options refused only with a value that removes a safeguard. */34const DANGEROUS_VALUE: Record<string, RegExp> = {35 '--sandbox': /^danger-full-access$/i,36 '--ask-for-approval': /^never$/i,37};38const SHORT_TO_LONG: Record<string, string> = { s: '--sandbox', a: '--ask-for-approval' };3940/**41 * Returns the offending flag when `args` contains an approval/sandbox bypass or a configuration42 * override, else null.43 */44export function findForbiddenCodexArg(args: readonly string[]): string | null {45 for (let i = 0; i < args.length; i++) {46 const a = (args[i] ?? '').trim();47 if (a === '--') break; // everything after is positional48 if (a.startsWith('--')) {49 const eq = a.indexOf('=');50 const flag = eq < 0 ? a : a.slice(0, eq);51 if (FORBIDDEN_LONG.has(flag)) return flag;52 const danger = DANGEROUS_VALUE[flag];53 if (danger) {54 const value = (eq < 0 ? (args[i + 1] ?? '') : a.slice(eq + 1)).trim();55 if (danger.test(value)) return `${flag} ${value}`;56 }57 // Anything spelling a bypass that this list does not know yet.58 if (/bypass|yolo/i.test(flag)) return flag;59 continue;60 }61 if (a.startsWith('-') && a.length > 1) {62 const letter = a[1] as string;63 if (FORBIDDEN_SHORT.has(letter)) return `-${letter}`;64 const long = SHORT_TO_LONG[letter];65 if (long) {66 const attached = a.slice(2).replace(/^=/, '');67 const value = (attached !== '' ? attached : (args[i + 1] ?? '')).trim();68 if ((DANGEROUS_VALUE[long] as RegExp).test(value)) return `${long} ${value}`;69 }70 }71 }72 return null;73}7475/**76 * The only argument lists a Codex agent session may be started with. Returns the reason when77 * `args` is anything else, else null.78 */79export function findInvalidCodexArgs(args: readonly string[]): string | null {80 if (args.length === 0) return null;81 if (args[0] !== 'resume') return 'only "resume <session id>" or "resume --last" may be passed to codex';82 if (args.length !== 2) return 'codex resume takes exactly one of: a session id, --last';83 const target = args[1] ?? '';84 if (target === '--last') return null;85 if (!UUID_RE.test(target)) return 'the value after "resume" must be a session id (UUID) or --last';86 return null;87}8889/** argv tail for a Codex agent session (the server's builder; the runtime re-validates). */90export function buildCodexArgs(o: { resumeConversationId?: string | null; continueLast?: boolean }): string[] {91 if (o.resumeConversationId) return ['resume', o.resumeConversationId];92 if (o.continueLast) return ['resume', '--last'];93 return [];94}95