SPB Git

Repository Public

codex-args.ts 4.0 KB 95 lines typescript

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
Files main
Menu

Theme