codex-flows/apps/cli/src/args.ts
matamune 0d3c2e2ab6
All checks were successful
ci / check (push) Successful in 30s
Merge Code Mode flow support into main
2026-05-13 03:17:06 +00:00

199 lines
4.5 KiB
TypeScript

import { isAppServerAction, type AppServerAction } from "./actions.ts";
export type ParsedArgs =
| { type: "help" }
| { type: "actions" }
| { type: "extract-code-mode"; outputDir: string | undefined }
| { type: "extract-code-mode-tool-input"; outputDir: string | undefined }
| {
type: "run-code-mode";
file: string;
cwd: string | undefined;
codexCommand: string | undefined;
url: string;
timeoutMs: number;
pretty: boolean;
}
| {
type: "call";
action: AppServerAction;
paramsText: string | undefined;
url: string;
timeoutMs: number;
pretty: boolean;
};
export const DEFAULT_WS_URL = "ws://127.0.0.1:3585";
const defaultTimeoutMs = 90_000;
export function parseArgs(argv: string[], env: NodeJS.ProcessEnv): ParsedArgs {
const positionals: string[] = [];
let url = env.CODEX_WORKSPACE_APP_SERVER_WS_URL ?? DEFAULT_WS_URL;
let timeoutMs = defaultTimeoutMs;
let pretty = true;
let cwd: string | undefined;
let outputDir: string | undefined;
let codexCommand = env.CODEX_APP_SERVER_CODEX_COMMAND;
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
if (!arg) {
continue;
}
if (arg === "-h" || arg === "--help") {
return { type: "help" };
}
if (arg === "--url" || arg === "--ws-url") {
const value = argv[index + 1];
if (!value) {
throw new Error(`${arg} requires a WebSocket URL`);
}
url = value;
index += 1;
continue;
}
if (arg.startsWith("--url=")) {
url = arg.slice("--url=".length);
continue;
}
if (arg.startsWith("--ws-url=")) {
url = arg.slice("--ws-url=".length);
continue;
}
if (arg === "--timeout-ms") {
const value = argv[index + 1];
if (!value) {
throw new Error("--timeout-ms requires a number");
}
timeoutMs = parseTimeout(value);
index += 1;
continue;
}
if (arg.startsWith("--timeout-ms=")) {
timeoutMs = parseTimeout(arg.slice("--timeout-ms=".length));
continue;
}
if (arg === "--compact") {
pretty = false;
continue;
}
if (arg === "--pretty") {
pretty = true;
continue;
}
if (arg === "--cwd") {
const value = argv[index + 1];
if (!value) {
throw new Error("--cwd requires a path");
}
cwd = value;
index += 1;
continue;
}
if (arg.startsWith("--cwd=")) {
cwd = arg.slice("--cwd=".length);
continue;
}
if (arg === "--output-dir") {
const value = argv[index + 1];
if (!value) {
throw new Error("--output-dir requires a path");
}
outputDir = value;
index += 1;
continue;
}
if (arg.startsWith("--output-dir=")) {
outputDir = arg.slice("--output-dir=".length);
continue;
}
if (arg === "--codex-command") {
const value = argv[index + 1];
if (!value) {
throw new Error("--codex-command requires a command path");
}
codexCommand = value;
index += 1;
continue;
}
if (arg.startsWith("--codex-command=")) {
codexCommand = arg.slice("--codex-command=".length);
continue;
}
if (arg === "--") {
positionals.push(...argv.slice(index + 1));
break;
}
if (arg.startsWith("-")) {
throw new Error(`Unknown option: ${arg}`);
}
positionals.push(arg);
}
const command = positionals[0];
if (!command) {
return { type: "help" };
}
if (command === "help") {
return { type: "help" };
}
if (command === "actions") {
return { type: "actions" };
}
if (command === "extract-code-mode") {
return {
type: "extract-code-mode",
outputDir,
};
}
if (command === "extract-code-mode-tool-input") {
return {
type: "extract-code-mode-tool-input",
outputDir,
};
}
if (command === "run-code-mode") {
const file = firstPositional(positionals.slice(1));
if (!file) {
throw new Error("run-code-mode requires a candidate file");
}
return {
type: "run-code-mode",
file,
cwd,
codexCommand,
url,
timeoutMs,
pretty,
};
}
const action = command === "call" ? positionals[1] : command;
const paramsParts = command === "call" ? positionals.slice(2) : positionals.slice(1);
if (!action) {
throw new Error("call requires an action name");
}
if (!isAppServerAction(action)) {
throw new Error(`Unknown action: ${action}`);
}
return {
type: "call",
action,
paramsText: paramsParts.length > 0 ? paramsParts.join(" ") : undefined,
url,
timeoutMs,
pretty,
};
}
function firstPositional(values: string[]) {
return values.find((value) => !value.startsWith("--"));
}
function parseTimeout(value: string) {
const parsed = Number(value);
if (!Number.isInteger(parsed) || parsed <= 0) {
throw new Error("--timeout-ms must be a positive integer");
}
return parsed;
}