@youdie006/prodex 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,8 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { execFile } from "node:child_process";
3
- import { realpathSync, statSync } from "node:fs";
3
+ import { realpathSync } from "node:fs";
4
4
  import { lstat, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
5
- import { createRequire } from "node:module";
6
5
  import { tmpdir } from "node:os";
7
6
  import path from "node:path";
8
7
  import { fileURLToPath } from "node:url";
@@ -17,36 +16,14 @@ import { startHttpMcpServer } from "./http-mcp.js";
17
16
  import { createMcpToolHandlers } from "./mcp-tools.js";
18
17
  import { runMcpServer } from "./mcp.js";
19
18
  import { readVerifiedUtf8File, writeVerifiedUtf8File } from "./safe-file.js";
20
- import { ReceiptKindSchema, TaskStatusSchema } from "./schema.js";
21
19
  import { BridgeStore, MAX_FETCHABLE_RESULT_ARTIFACT_BYTES } from "./store.js";
20
+ import { ASK_PRO_BOOLEAN_FLAGS, ASK_PRO_PREVIEW_VALUE_FLAGS, ASK_PRO_SELECTION_CLEAR_FLAGS, ASK_PRO_SELECTION_DEFAULT_FLAGS, ASK_PRO_VALUE_FLAGS, assertHelpRequestArgs, assertNoExtraArgs, assertOnlyOptions, findHelpFlagIndexBeforePromptDelimiter, formatCliCommand, formatSourceCliOption, hasAskProDryRunMode, hasAskProMode, hasAskProSendMode, isHelpSubcommand, parseAskProArgs, readFlag, readPortFlag, readPositionalsWithOptions, readPositiveNumberFlag, readReceiptKindFlag, readRepeatedFlag, readSessionStatusFlag, readTaskStatusFlag, resolveCwdFlag, resolveExistingPathFlag, resolveOptionalFileFlag, shellQuote, unknownSubcommandError, unknownTopLevelCommandError } from "./cli-args.js";
21
+ import { CLI_VERSION, printClaudeHelp, printDoctorHelp, printHelp, printInitHelp, printMcpHelp, printOnboardHelp, printProBrowserHelp, printProHelp, printProjectHelp, printReceiptsHelp, printReleaseHelp, printResultsHelp, printSessionsHelp, printSetupHelp, printStartHelp, printStatusHelp, printTasksHelp, printTunnelHelp, printTunnelUrlHelp } from "./cli-help.js";
22
22
  const execFileAsync = promisify(execFile);
23
- const requirePackageJson = createRequire(import.meta.url);
24
- const packageJson = requirePackageJson("../package.json");
25
23
  const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
26
- const CLI_VERSION = packageJson.version ?? "0.0.0";
27
24
  const RESERVED_PACKAGE_NAMES = new Set(["node_modules", "favicon.ico"]);
28
25
  const PRO_BROWSER_SMOKE_TOKEN = "PRODEX_PRO_SMOKE_OK";
29
26
  const TOKEN_BEARING_MCP_URL_AUTHORITY_WARNING = "Token-bearing MCP URL authorizes all enabled bridge tools, including repo_read_file, repo_search, repo_write_file_dry_run, repo_write_file_apply, and repo_stage_reviewed_paths. Paste it only into your own trusted private MCP client.";
30
- const TOP_LEVEL_COMMANDS = [
31
- "help",
32
- "version",
33
- "init",
34
- "setup",
35
- "start",
36
- "status",
37
- "tunnel",
38
- "doctor",
39
- "onboard",
40
- "project",
41
- "claude",
42
- "tasks",
43
- "results",
44
- "receipts",
45
- "sessions",
46
- "pro",
47
- "release",
48
- "mcp"
49
- ];
50
27
  const DOCTOR_REQUIRED_MCP_TOOLS = [
51
28
  "bridge_create_task",
52
29
  "bridge_list_tasks",
@@ -93,12 +70,21 @@ export async function runCli(args, io = defaultIo()) {
93
70
  }
94
71
  if (command === "setup") {
95
72
  const setupValueFlags = ["--cwd", "--host", "--port", "--token", "--token-ttl-hours", ...ASK_PRO_SELECTION_DEFAULT_FLAGS];
96
- const setupBooleanFlags = [...ASK_PRO_SELECTION_CLEAR_FLAGS];
73
+ const setupBooleanFlags = [...ASK_PRO_SELECTION_CLEAR_FLAGS, "--interactive"];
97
74
  if (printHelpIfRequested(rest, "setup", io.stdout, printSetupHelp, { valueFlags: setupValueFlags, booleanFlags: setupBooleanFlags }))
98
75
  return 0;
99
76
  assertOnlyOptions(rest, "setup", setupValueFlags, setupBooleanFlags);
100
77
  const targetCwd = resolveCwdFlag(io.cwd, rest);
101
- const browserDefaults = parseBrowserDefaultFlags(rest);
78
+ const interactive = rest.includes("--interactive");
79
+ if (interactive) {
80
+ const conflicting = [...ASK_PRO_SELECTION_DEFAULT_FLAGS, ...ASK_PRO_SELECTION_CLEAR_FLAGS].filter((flag) => rest.includes(flag));
81
+ if (conflicting.length > 0) {
82
+ throw new Error(`setup --interactive collects the browser defaults itself; drop ${conflicting.join(", ")} or run without --interactive.`);
83
+ }
84
+ }
85
+ const browserDefaults = interactive
86
+ ? await runBrowserDefaultsWizard(resolvePromptUser(io), io.stdout)
87
+ : parseBrowserDefaultFlags(rest);
102
88
  const config = await writeLocalConfig(targetCwd, {
103
89
  host: readFlag(rest, "--host") ?? "127.0.0.1",
104
90
  port: readPortFlag(rest, "--port") ?? 8787,
@@ -671,7 +657,18 @@ export async function runCli(args, io = defaultIo()) {
671
657
  io.stdout(JSON.stringify(receipt, null, 2));
672
658
  return 0;
673
659
  }
674
- throw unknownSubcommandError("receipts", subcommand, ["list", "show"]);
660
+ if (subcommand === "rotate-key") {
661
+ if (printHelpIfRequested(receiptArgs, "receipts rotate-key", io.stdout, printReceiptsHelp, { valueFlags: ["--cwd"] }))
662
+ return 0;
663
+ assertOnlyOptions(receiptArgs, "receipts rotate-key", ["--cwd"]);
664
+ const targetStore = new BridgeStore(resolveCwdFlag(io.cwd, receiptArgs));
665
+ await targetStore.ensure();
666
+ const rotated = await targetStore.rotateReceiptIntegrityKey();
667
+ io.stdout(`Rotated the local receipt integrity key: ${rotated.keys} key(s) in .bridge/receipt-key.local.`);
668
+ io.stdout("New receipts are signed with the new key; receipts signed before the rotation still verify via the retained legacy keys.");
669
+ return 0;
670
+ }
671
+ throw unknownSubcommandError("receipts", subcommand, ["list", "show", "rotate-key"]);
675
672
  }
676
673
  if (command === "sessions") {
677
674
  const [subcommand, ...sessionArgs] = rest;
@@ -940,13 +937,8 @@ export async function runCli(args, io = defaultIo()) {
940
937
  if (explicitProject !== undefined && explicitProjectNew !== undefined) {
941
938
  throw new Error("ask-pro cannot combine --project and --project-new; pick an existing project or create one.");
942
939
  }
943
- if (explicitProjectNew !== undefined) {
944
- // Creating a project mutates the real ChatGPT account and the modal
945
- // automation is unverified, so this stays gated until it is confirmed live.
946
- throw new Error('--project-new is not supported yet; create the project in ChatGPT, then use --project "name".');
947
- }
948
- if (normalizedTargetUrl && explicitProject !== undefined) {
949
- throw new Error("ask-pro cannot combine --target-url with --project: --target-url pins the confirmed tab while --project navigates the sidebar away from it. Open the project thread in the browser and pass its URL as --target-url instead.");
940
+ if (normalizedTargetUrl && (explicitProject !== undefined || explicitProjectNew !== undefined)) {
941
+ throw new Error("ask-pro cannot combine --target-url with --project/--project-new: --target-url pins the confirmed tab while the project step navigates the sidebar away from it. Open the project thread in the browser and pass its URL as --target-url instead.");
950
942
  }
951
943
  const explicitModel = readFlag(parsedAskPro.optionArgs, "--model");
952
944
  const explicitProModeRaw = readFlag(parsedAskPro.optionArgs, "--pro-mode");
@@ -961,12 +953,14 @@ export async function runCli(args, io = defaultIo()) {
961
953
  // pinning --target-url suppresses a default project (it would navigate away
962
954
  // from the confirmed tab).
963
955
  const selectionModel = explicitModel ?? browserDefaults?.model;
964
- const selectionProject = explicitProject ?? (normalizedTargetUrl ? undefined : browserDefaults?.project);
956
+ const selectionProjectNew = explicitProjectNew;
957
+ const selectionProject = explicitProject ?? (normalizedTargetUrl || selectionProjectNew !== undefined ? undefined : browserDefaults?.project);
965
958
  const reasoningAxisChosen = explicitProMode !== undefined || explicitEffort !== undefined;
966
959
  const selectionProMode = explicitProMode ?? (reasoningAxisChosen ? undefined : browserDefaults?.pro_mode);
967
960
  const selectionEffort = explicitEffort ?? (reasoningAxisChosen ? undefined : browserDefaults?.effort);
968
961
  const selectionMetadata = {
969
962
  ...(selectionProject ? { project: selectionProject } : {}),
963
+ ...(selectionProjectNew ? { project_new: selectionProjectNew } : {}),
970
964
  ...(selectionModel ? { model: selectionModel } : {}),
971
965
  ...(selectionProMode ? { pro_mode: selectionProMode } : {}),
972
966
  ...(selectionEffort ? { effort: selectionEffort } : {})
@@ -1038,6 +1032,7 @@ export async function runCli(args, io = defaultIo()) {
1038
1032
  targetUrl: normalizedTargetUrl,
1039
1033
  timeoutMs: browserTimeoutMs,
1040
1034
  project: selectionProject,
1035
+ projectNew: selectionProjectNew,
1041
1036
  model: selectionModel,
1042
1037
  proMode: selectionProMode,
1043
1038
  effort: selectionEffort
@@ -1174,216 +1169,6 @@ function defaultIo() {
1174
1169
  stderr: (line) => console.error(line)
1175
1170
  };
1176
1171
  }
1177
- function printHelp(stdout) {
1178
- stdout(`prodex v${CLI_VERSION}
1179
-
1180
- Commands:
1181
- prodex --version
1182
- prodex init [--cwd /absolute/path/to/repo]
1183
- prodex doctor [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1184
- prodex setup [--cwd /absolute/path/to/repo] [--host 127.0.0.1] [--port 8787] [--token-ttl-hours <hours>] [--model Pro] [--pro-mode 기본|확장] [--effort 즉시|중간|높음|"매우 높음"] [--project "name"]
1185
- prodex start [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1186
- prodex status [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js] [--show-token] [--url-only] [--unsafe-show-non-expiring-token]
1187
- prodex tunnel url [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js] --public-url https://... [--show-token] [--url-only]
1188
- prodex release status [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1189
- prodex release pack [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js] --pack-destination /absolute/path [--keep-workdir]
1190
- prodex onboard [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1191
- prodex project prompt [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1192
- prodex claude prompt [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1193
- prodex claude config [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1194
- prodex pro ask [--dry-run] [--cwd /absolute/path/to/repo] [--file path] "prompt" # dry-run preview
1195
- prodex pro browser login [--cwd /absolute/path/to/repo] [--dry-run] [--source-cli /absolute/path/to/dist/cli.js] [--profile-dir path] [--port 9333] [--url https://chatgpt.com/...] [--launch-timeout-ms 5000] # preview/open visible browser login
1196
- prodex pro browser help [--source-cli /absolute/path/to/dist/cli.js]
1197
- prodex pro browser check [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo] [--port 9333] [--timeout-ms 1500]
1198
- prodex pro browser smoke [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo] [--port 9333] [--timeout-ms 90000]
1199
- prodex pro browser models [--source-cli /absolute/path/to/dist/cli.js] [--port 9333] [--timeout-ms 15000] # read-only list of model menu options
1200
- prodex pro browser ask [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo] [--port 9333] [--timeout-ms 90000] [--target-url url --confirm-target] [--file path] [--model Pro] [--pro-mode 기본|확장] [--effort 즉시|중간|높음|"매우 높음"] [--project "name"] "prompt" # explicit visible-browser send
1201
- prodex pro latest [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo]
1202
- prodex pro list [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo]
1203
- prodex pro show <task-id|latest> [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo]
1204
- prodex tasks create [--cwd /absolute/path/to/repo] --title "Title" --prompt "Prompt"
1205
- prodex tasks list [--status new|claimed|done|blocked] [--cwd /absolute/path/to/repo]
1206
- prodex tasks show <task-id|latest> [--cwd /absolute/path/to/repo]
1207
- prodex tasks claim <task-id> [--cwd /absolute/path/to/repo] [--by codex]
1208
- prodex tasks complete <task-id> [--cwd /absolute/path/to/repo] --summary "Summary" [--command "npm test"] [--artifact .bridge/artifacts/results/name.md=text]
1209
- prodex tasks block <task-id> [--cwd /absolute/path/to/repo] --summary "Summary" [--code code] [--next-step "Next step"] [--retryable]
1210
- prodex results show <task-id|latest> [--cwd /absolute/path/to/repo]
1211
- prodex results artifact <task-id|latest> [artifact-path] [--cwd /absolute/path/to/repo]
1212
- prodex results reseal <task-id|latest> --confirm-current-result [--cwd /absolute/path/to/repo]
1213
- prodex receipts list [--kind kind] [--task-id task-id] [--cwd /absolute/path/to/repo]
1214
- prodex receipts show <receipt-id|latest> [--cwd /absolute/path/to/repo]
1215
- prodex sessions list [--status preview|running|done|blocked] [--cwd /absolute/path/to/repo]
1216
- prodex sessions show <session-id|latest> [--cwd /absolute/path/to/repo]
1217
- prodex mcp [--cwd /absolute/path/to/repo]`);
1218
- }
1219
- function printInitHelp(stdout) {
1220
- stdout(`prodex init
1221
-
1222
- Commands:
1223
- prodex init [--cwd /absolute/path/to/repo]
1224
-
1225
- Initialize the local .bridge receipt ledger and bridge .gitignore entries.`);
1226
- }
1227
- function printSetupHelp(stdout) {
1228
- stdout(`prodex setup
1229
-
1230
- Commands:
1231
- prodex setup [--cwd /absolute/path/to/repo] [--host 127.0.0.1] [--port 8787] [--token-ttl-hours <hours>] [--model Pro] [--pro-mode 기본|확장] [--effort 즉시|중간|높음|"매우 높음"] [--project "name"]
1232
-
1233
- Save a loopback-only HTTP MCP profile in .bridge/config.local.json. Use --token-ttl-hours before tunnels or ChatGPT Project use.
1234
-
1235
- Optional visible-browser send defaults (applied by \`pro browser ask\` when the matching per-ask flag is omitted):
1236
- --model Composer model to pick by its exact menu label (verified: Pro)
1237
- --pro-mode Pro sub-mode: 기본 (standard) or 확장 (extended)
1238
- --effort Reasoning effort: 즉시 / 중간 / 높음 / 매우 높음 (English aliases: instant/medium/high/max); picking one deselects Pro
1239
- --project Sidebar project to enter before sending
1240
- Clear a saved default with --clear-model / --clear-pro-mode / --clear-effort / --clear-project.
1241
- --pro-mode and --effort are different model axes and cannot be combined. View saved defaults with \`prodex status\`.`);
1242
- }
1243
- function printStartHelp(stdout) {
1244
- stdout(`prodex start
1245
-
1246
- Commands:
1247
- prodex start [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1248
-
1249
- Start the local loopback HTTP MCP server from the saved setup profile.`);
1250
- }
1251
- function printStatusHelp(stdout) {
1252
- stdout(`prodex status
1253
-
1254
- Commands:
1255
- prodex status [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js] [--show-token] [--url-only] [--unsafe-show-non-expiring-token]
1256
-
1257
- Show the saved local MCP URL with tokens redacted by default.`);
1258
- }
1259
- function printTunnelHelp(stdout) {
1260
- stdout(`prodex tunnel
1261
-
1262
- Commands:
1263
- prodex tunnel url [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js] --public-url https://... [--show-token] [--url-only]
1264
-
1265
- Format a public tunnel MCP URL from an existing local setup. This command does not create a tunnel.`);
1266
- }
1267
- function printTunnelUrlHelp(stdout) {
1268
- stdout(`prodex tunnel url
1269
-
1270
- Commands:
1271
- prodex tunnel url [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js] --public-url https://... [--show-token] [--url-only]
1272
-
1273
- This command does not create a tunnel. It only formats your supplied public URL with the saved short-lived MCP token.`);
1274
- }
1275
- function printDoctorHelp(stdout) {
1276
- stdout(`prodex doctor
1277
-
1278
- Commands:
1279
- prodex doctor [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1280
-
1281
- Run local bridge, MCP, write/apply/stage, and HTTP MCP smoke checks without opening ChatGPT.`);
1282
- }
1283
- function printOnboardHelp(stdout) {
1284
- stdout(`prodex onboard
1285
-
1286
- Commands:
1287
- prodex onboard [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1288
-
1289
- Print a local-first setup guide for Codex, ChatGPT Projects, Claude, and visible-browser Pro consults.`);
1290
- }
1291
- function printMcpHelp(stdout) {
1292
- stdout(`prodex mcp
1293
-
1294
- Commands:
1295
- prodex mcp [--cwd /absolute/path/to/repo]
1296
-
1297
- Run the stdio MCP server for local clients such as Claude. This does not reveal HTTP MCP URL tokens.`);
1298
- }
1299
- function printReleaseHelp(stdout) {
1300
- stdout(`prodex release
1301
-
1302
- Commands:
1303
- prodex release status [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1304
- prodex release pack [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js] --pack-destination /absolute/path [--keep-workdir]
1305
-
1306
- Release commands are local checks and package preparation helpers; they do not publish or push.`);
1307
- }
1308
- function printProHelp(stdout) {
1309
- stdout(`prodex pro
1310
-
1311
- Commands:
1312
- prodex pro ask [--dry-run] [--cwd /absolute/path/to/repo] [--file path] "prompt"
1313
- prodex pro browser help [--source-cli /absolute/path/to/dist/cli.js]
1314
- prodex pro browser login [--cwd /absolute/path/to/repo] [--dry-run] [--source-cli /absolute/path/to/dist/cli.js] [--launch-timeout-ms 5000]
1315
- prodex pro browser check [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo]
1316
- prodex pro browser smoke [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo]
1317
- prodex pro browser models [--source-cli /absolute/path/to/dist/cli.js]
1318
- prodex pro browser ask [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo] [--target-url url --confirm-target] [--file path] [--model Pro] [--pro-mode 기본|확장] [--effort 즉시|중간|높음|"매우 높음"] [--project "name"] "prompt"
1319
- prodex pro latest [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo]
1320
- prodex pro list [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo]
1321
- prodex pro show <task-id|latest> [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo]
1322
-
1323
- Use \`prodex pro ask\` for dry-run/manual previews.
1324
- Use \`prodex pro browser ask\` only when you want an explicit visible-browser send.
1325
- Model/project selection (visible-browser send):
1326
- --model "label" Pick the composer model by its exact menu label (verified: Pro). Submenu models (e.g. GPT-5.5 variants) are rejected for now.
1327
- --pro-mode 기본 | 확장 Pro sub-mode (only when the model is Pro); 확장 raises the default timeout to 300000 ms
1328
- --effort 즉시|중간|높음|매우 높음 Reasoning effort (aliases: instant/medium/high/max); picking one deselects Pro
1329
- --project "name" Enter an existing sidebar project first (cannot combine with --target-url)
1330
- Labels match the Korean ChatGPT UI; run \`prodex pro browser models\` to list what your account shows.
1331
- Persist defaults with \`prodex setup --model/--pro-mode/--effort/--project\`; clear them with setup --clear-model/--clear-pro-mode/--clear-effort/--clear-project.
1332
- (Creating a new project from the CLI is planned; for now create it in ChatGPT and pass --project.)`);
1333
- }
1334
- function printProjectHelp(stdout) {
1335
- stdout(`prodex project
1336
-
1337
- Commands:
1338
- prodex project prompt [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1339
-
1340
- Print a ChatGPT Project MCP verification prompt. The prompt asks for read/task handoff verification only.`);
1341
- }
1342
- function printClaudeHelp(stdout) {
1343
- stdout(`prodex claude
1344
-
1345
- Commands:
1346
- prodex claude prompt [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1347
- prodex claude config [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1348
-
1349
- Print Claude MCP setup and verification helpers. These commands do not start MCP or reveal HTTP tokens.`);
1350
- }
1351
- function printTasksHelp(stdout) {
1352
- stdout(`prodex tasks
1353
-
1354
- Commands:
1355
- prodex tasks create [--cwd /absolute/path/to/repo] --title "Title" --prompt "Prompt"
1356
- prodex tasks list [--status new|claimed|done|blocked] [--cwd /absolute/path/to/repo]
1357
- prodex tasks show <task-id|latest> [--cwd /absolute/path/to/repo]
1358
- prodex tasks claim <task-id> [--cwd /absolute/path/to/repo] [--by codex]
1359
- prodex tasks complete <task-id> [--cwd /absolute/path/to/repo] --summary "Summary" [--command "npm test"] [--artifact .bridge/artifacts/results/name.md=text]
1360
- prodex tasks block <task-id> [--cwd /absolute/path/to/repo] --summary "Summary" [--code code] [--next-step "Next step"] [--retryable]`);
1361
- }
1362
- function printResultsHelp(stdout) {
1363
- stdout(`prodex results
1364
-
1365
- Commands:
1366
- prodex results show <task-id|latest> [--cwd /absolute/path/to/repo]
1367
- prodex results artifact <task-id|latest> [artifact-path] [--cwd /absolute/path/to/repo]
1368
- prodex results reseal <task-id|latest> --confirm-current-result [--cwd /absolute/path/to/repo]`);
1369
- }
1370
- function printReceiptsHelp(stdout) {
1371
- stdout(`prodex receipts
1372
-
1373
- Commands:
1374
- prodex receipts list [--kind kind] [--task-id task-id] [--cwd /absolute/path/to/repo]
1375
- prodex receipts show <receipt-id|latest> [--cwd /absolute/path/to/repo]`);
1376
- }
1377
- function printSessionsHelp(stdout) {
1378
- stdout(`prodex sessions
1379
-
1380
- Commands:
1381
- prodex sessions list [--status preview|running|done|blocked] [--cwd /absolute/path/to/repo]
1382
- prodex sessions show <session-id|latest> [--cwd /absolute/path/to/repo]`);
1383
- }
1384
- function isHelpSubcommand(value) {
1385
- return value === "help" || value === "--help" || value === "-h";
1386
- }
1387
1172
  function isHelpArgs(args) {
1388
1173
  return args.length > 0 && isHelpSubcommand(args[0]);
1389
1174
  }
@@ -1403,84 +1188,6 @@ function printProBrowserHelpIfRequested(args, command, io, options) {
1403
1188
  printProBrowserHelp(io.stdout, resolveOptionalFileFlag(io.cwd, args, "--source-cli"));
1404
1189
  return true;
1405
1190
  }
1406
- function findHelpFlagIndexBeforePromptDelimiter(args) {
1407
- const delimiterIndex = args.indexOf("--");
1408
- const limit = delimiterIndex === -1 ? args.length : delimiterIndex;
1409
- return args.findIndex((arg, index) => index < limit && isHelpSubcommand(arg));
1410
- }
1411
- function assertHelpRequestArgs(args, command, options) {
1412
- const delimiterIndex = args.indexOf("--");
1413
- const commandArgs = delimiterIndex === -1 ? args : args.slice(0, delimiterIndex);
1414
- const valueFlagSet = new Set(options.valueFlags ?? []);
1415
- const booleanFlagSet = new Set(options.booleanFlags ?? []);
1416
- const maxPositionals = options.maxPositionals ?? 0;
1417
- let positionals = 0;
1418
- for (let index = 0; index < commandArgs.length; index += 1) {
1419
- const arg = commandArgs[index];
1420
- if (isHelpSubcommand(arg))
1421
- continue;
1422
- if (valueFlagSet.has(arg)) {
1423
- const next = commandArgs[index + 1];
1424
- if (next && !isHelpSubcommand(next)) {
1425
- readFlagValue(commandArgs, index, arg);
1426
- index += 1;
1427
- }
1428
- continue;
1429
- }
1430
- if (booleanFlagSet.has(arg))
1431
- continue;
1432
- if (arg.startsWith("-")) {
1433
- throw unknownOptionError(arg, command, [...valueFlagSet, ...booleanFlagSet]);
1434
- }
1435
- if (positionals >= maxPositionals) {
1436
- throw new Error(`Unexpected argument for ${command}: ${arg}`);
1437
- }
1438
- positionals += 1;
1439
- }
1440
- }
1441
- function unknownSubcommandError(command, subcommand, expected) {
1442
- const suggestion = closestSuggestion(subcommand, expected);
1443
- const suggestionText = suggestion ? ` Did you mean \`prodex ${command} ${suggestion}\`?` : "";
1444
- return new Error(`Unknown ${command} subcommand: ${subcommand}.${suggestionText} Expected one of: ${expected.join(", ")}. Run \`prodex ${command} --help\`.`);
1445
- }
1446
- function unknownTopLevelCommandError(command) {
1447
- const suggestion = closestSuggestion(command, TOP_LEVEL_COMMANDS);
1448
- const suggestionText = suggestion ? ` Did you mean \`prodex ${suggestion}\`?` : "";
1449
- return new Error(`Unknown command: ${command}.${suggestionText} Run \`prodex help\`.`);
1450
- }
1451
- function unknownOptionError(option, command, candidates) {
1452
- const suggestion = closestSuggestion(option, candidates);
1453
- const suggestionText = suggestion ? `. Did you mean \`${suggestion}\`?` : "";
1454
- const context = command ? ` for ${command}` : "";
1455
- return new Error(`Unknown option${context}: ${option}${suggestionText}`);
1456
- }
1457
- function closestSuggestion(value, candidates) {
1458
- let best;
1459
- for (const candidate of candidates) {
1460
- const distance = editDistance(value, candidate);
1461
- const prefixMatch = isUsefulPrefixSuggestion(value, candidate);
1462
- if (!best || (prefixMatch && !best.prefixMatch) || (prefixMatch === best.prefixMatch && distance < best.distance)) {
1463
- best = { command: candidate, distance, prefixMatch };
1464
- }
1465
- }
1466
- return best && (best.prefixMatch || best.distance <= 2) ? best.command : undefined;
1467
- }
1468
- function isUsefulPrefixSuggestion(value, candidate) {
1469
- return value.length >= 5 && candidate.startsWith(value);
1470
- }
1471
- function editDistance(left, right) {
1472
- const previous = Array.from({ length: right.length + 1 }, (_, index) => index);
1473
- const current = Array.from({ length: right.length + 1 }, () => 0);
1474
- for (let leftIndex = 1; leftIndex <= left.length; leftIndex += 1) {
1475
- current[0] = leftIndex;
1476
- for (let rightIndex = 1; rightIndex <= right.length; rightIndex += 1) {
1477
- const substitutionCost = left[leftIndex - 1] === right[rightIndex - 1] ? 0 : 1;
1478
- current[rightIndex] = Math.min(previous[rightIndex] + 1, current[rightIndex - 1] + 1, previous[rightIndex - 1] + substitutionCost);
1479
- }
1480
- previous.splice(0, previous.length, ...current);
1481
- }
1482
- return previous[right.length];
1483
- }
1484
1191
  function legacyChatGptNamespaceError(subcommand) {
1485
1192
  const prefix = subcommand ? `Unknown legacy chatgpt subcommand: ${subcommand}.` : "The legacy `chatgpt` namespace is hidden.";
1486
1193
  return new Error(`${prefix} Use \`prodex pro browser help\` for visible-browser commands.`);
@@ -1658,21 +1365,12 @@ function formatClaudeConfig(cwd, sourceCli) {
1658
1365
  }
1659
1366
  }, null, 2);
1660
1367
  }
1661
- function shellQuote(value) {
1662
- return /^[A-Za-z0-9_./:@=-]+$/.test(value) ? value : `'${value.replaceAll("'", "'\\''")}'`;
1663
- }
1664
- function formatCliCommand(sourceCli) {
1665
- return sourceCli ? `node ${shellQuote(sourceCli)}` : "prodex";
1666
- }
1667
1368
  function formatInitCommand(sourceCli, options = {}) {
1668
1369
  return [`${formatCliCommand(sourceCli)} init`, options.cwd ? `--cwd ${shellQuote(options.cwd)}` : undefined].filter(Boolean).join(" ");
1669
1370
  }
1670
1371
  function formatSetupCommand(sourceCli, options = {}) {
1671
1372
  return [`${formatCliCommand(sourceCli)} setup`, options.cwd ? `--cwd ${shellQuote(options.cwd)}` : undefined].filter(Boolean).join(" ");
1672
1373
  }
1673
- function formatSourceCliOption(sourceCli) {
1674
- return sourceCli ? ` --source-cli ${shellQuote(sourceCli)}` : "";
1675
- }
1676
1374
  function formatBrowserLoginCommand(sourceCli, options = {}) {
1677
1375
  return formatCommandInCwd(formatBrowserLoginCommandBody(sourceCli, options), options.cwd);
1678
1376
  }
@@ -2741,46 +2439,6 @@ function printBrowserLoginGuide(stdout, input) {
2741
2439
  stdout("The dedicated profile path above will be reused by the real login command.");
2742
2440
  }
2743
2441
  }
2744
- function printProBrowserHelp(stdout, sourceCli) {
2745
- const cli = formatCliCommand(sourceCli);
2746
- const sourceCliOption = formatSourceCliOption(sourceCli);
2747
- const loginUsage = sourceCli
2748
- ? `${cli} pro browser login${sourceCliOption} [--cwd /absolute/path/to/repo] [--dry-run] [--profile-dir path] [--port 9333] [--url https://chatgpt.com/...] [--launch-timeout-ms 5000]`
2749
- : "prodex pro browser login [--cwd /absolute/path/to/repo] [--dry-run] [--source-cli /absolute/path/to/dist/cli.js] [--profile-dir path] [--port 9333] [--url https://chatgpt.com/...] [--launch-timeout-ms 5000]";
2750
- const checkUsage = sourceCli
2751
- ? `${cli} pro browser check${sourceCliOption} [--cwd /absolute/path/to/repo] [--port 9333] [--timeout-ms 1500]`
2752
- : "prodex pro browser check [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo] [--port 9333] [--timeout-ms 1500]";
2753
- const smokeUsage = sourceCli
2754
- ? `${cli} pro browser smoke${sourceCliOption} [--cwd /absolute/path/to/repo] [--port 9333] [--timeout-ms 90000]`
2755
- : "prodex pro browser smoke [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo] [--port 9333] [--timeout-ms 90000]";
2756
- const selectionUsage = '[--model Pro] [--pro-mode 기본|확장] [--effort 즉시|중간|높음|"매우 높음"] [--project "name"]';
2757
- const askUsage = sourceCli
2758
- ? `${cli} pro browser ask${sourceCliOption} [--cwd /absolute/path/to/repo] [--port 9333] [--timeout-ms 90000] [--target-url url --confirm-target] [--file path] ${selectionUsage} "prompt"`
2759
- : `prodex pro browser ask [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo] [--port 9333] [--timeout-ms 90000] [--target-url url --confirm-target] [--file path] ${selectionUsage} "prompt"`;
2760
- const modelsUsage = sourceCli
2761
- ? `${cli} pro browser models${sourceCliOption} [--port 9333] [--timeout-ms 15000]`
2762
- : "prodex pro browser models [--source-cli /absolute/path/to/dist/cli.js] [--port 9333] [--timeout-ms 15000]";
2763
- stdout(`${cli} pro browser
2764
-
2765
- Commands:
2766
- ${loginUsage}
2767
- ${checkUsage}
2768
- ${smokeUsage}
2769
- ${modelsUsage}
2770
- ${askUsage}
2771
-
2772
- Visible-browser sends require a manual browser session and stop on login, captcha, Cloudflare, permission, rate-limit, or usage-limit blockers.
2773
- Model/project selection (ask):
2774
- --model Composer model to pick by its exact menu label (verified: Pro). Models whose menu entry opens a submenu of variants are rejected with a clear error for now.
2775
- --pro-mode Pro sub-mode: 기본 (standard) or 확장 (extended), used when the model is Pro. 확장 raises the default --timeout-ms to 300000.
2776
- --effort Reasoning effort: 즉시 / 중간 / 높음 / 매우 높음 (aliases: instant/medium/high/max). Picking an effort switches the composer to the standard reasoning model, deselecting Pro.
2777
- --project Enter an existing sidebar project before sending. Cannot be combined with --target-url.
2778
- --pro-mode and --effort cannot be combined. These labels match the Korean ChatGPT UI; set your ChatGPT display language to Korean for selection flags.
2779
- Run \`${cli} pro browser models${sourceCliOption}\` to list the labels your account currently shows.
2780
- Persist defaults with \`${cli} setup${sourceCliOption}\`; per-ask flags override them.
2781
- Use \`${cli} pro ask\` for dry-run/manual previews.
2782
- \`${cli} pro browser ask${sourceCliOption}\` always attempts an explicit visible-browser send.`);
2783
- }
2784
2442
  async function assertBrowserLaunchStayedAlive(opened, timeoutMs) {
2785
2443
  const outcome = await waitForBrowserLaunchReady(opened, timeoutMs);
2786
2444
  if (outcome.reachable)
@@ -3318,167 +2976,19 @@ function parseTunnelPublicUrl(publicUrl) {
3318
2976
  function isLoopbackHost(hostname) {
3319
2977
  return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
3320
2978
  }
3321
- function readFlag(args, flag) {
3322
- const index = args.indexOf(flag);
3323
- if (index === -1)
3324
- return undefined;
3325
- return readFlagValue(args, index, flag);
3326
- }
3327
- function readNumberFlag(args, flag) {
3328
- const raw = readFlag(args, flag);
3329
- if (raw === undefined)
3330
- return undefined;
3331
- const value = Number(raw);
3332
- if (!Number.isFinite(value))
3333
- throw new Error(`${flag} requires a finite number`);
3334
- return value;
3335
- }
3336
- function readPositiveNumberFlag(args, flag) {
3337
- const value = readNumberFlag(args, flag);
3338
- if (value === undefined)
3339
- return undefined;
3340
- if (value <= 0)
3341
- throw new Error(`${flag} must be greater than 0`);
3342
- return value;
3343
- }
3344
- function readPortFlag(args, flag) {
3345
- const value = readNumberFlag(args, flag);
3346
- if (value === undefined)
3347
- return undefined;
3348
- if (!Number.isInteger(value) || value < 1 || value > 65535) {
3349
- throw new Error(`${flag} must be an integer from 1 to 65535`);
3350
- }
3351
- return value;
3352
- }
3353
2979
  function readChatGptBrowserUrlFlag(args) {
3354
2980
  return normalizeChatGptTargetUrl(readFlag(args, "--url") ?? "https://chatgpt.com/");
3355
2981
  }
3356
- function readRepeatedFlag(args, flag) {
3357
- const values = [];
3358
- for (let index = 0; index < args.length; index += 1) {
3359
- if (args[index] === flag) {
3360
- values.push(readFlagValue(args, index, flag));
3361
- index += 1;
3362
- }
3363
- }
3364
- return values;
3365
- }
3366
- function resolveCwdFlag(defaultCwd, args) {
3367
- const cwd = readFlag(args, "--cwd");
3368
- if (!cwd)
3369
- return defaultCwd;
3370
- return resolveExistingDirectoryFlag(defaultCwd, cwd, "--cwd");
3371
- }
3372
2982
  function resolveOptionalPathFlag(defaultCwd, args, flag) {
3373
2983
  const value = readFlag(args, flag);
3374
2984
  return value ? resolveExistingPathFlag(defaultCwd, value, flag) : undefined;
3375
2985
  }
3376
- function resolveOptionalFileFlag(defaultCwd, args, flag) {
3377
- const value = readFlag(args, flag);
3378
- return value ? resolveExistingFileFlag(defaultCwd, value, flag) : undefined;
3379
- }
3380
- function resolveExistingPathFlag(defaultCwd, value, flag) {
3381
- const resolved = path.resolve(defaultCwd, value);
3382
- try {
3383
- return realpathSync(resolved);
3384
- }
3385
- catch {
3386
- throw new Error(`${flag} does not exist or is not accessible: ${resolved}`);
3387
- }
3388
- }
3389
- function resolveExistingFileFlag(defaultCwd, value, flag) {
3390
- const resolved = resolveExistingPathFlag(defaultCwd, value, flag);
3391
- if (!statSync(resolved).isFile()) {
3392
- throw new Error(`${flag} must be a file: ${resolved}`);
3393
- }
3394
- return resolved;
3395
- }
3396
- function resolveExistingDirectoryFlag(defaultCwd, value, flag) {
3397
- const resolved = resolveExistingPathFlag(defaultCwd, value, flag);
3398
- if (!statSync(resolved).isDirectory()) {
3399
- throw new Error(`${flag} must be a directory: ${resolved}`);
3400
- }
3401
- return resolved;
3402
- }
3403
- function assertOnlyOptions(args, command, valueFlags, booleanFlags = []) {
3404
- const valueFlagSet = new Set(valueFlags);
3405
- const booleanFlagSet = new Set(booleanFlags);
3406
- for (let index = 0; index < args.length; index += 1) {
3407
- const arg = args[index];
3408
- if (valueFlagSet.has(arg)) {
3409
- readFlagValue(args, index, arg);
3410
- index += 1;
3411
- continue;
3412
- }
3413
- if (booleanFlagSet.has(arg))
3414
- continue;
3415
- if (arg.startsWith("-")) {
3416
- throw unknownOptionError(arg, command, [...valueFlagSet, ...booleanFlagSet]);
3417
- }
3418
- throw new Error(`Unexpected argument for ${command}: ${arg}`);
3419
- }
3420
- }
3421
- function readPositionalsWithOptions(args, command, maxPositionals, valueFlags, booleanFlags = []) {
3422
- const valueFlagSet = new Set(valueFlags);
3423
- const booleanFlagSet = new Set(booleanFlags);
3424
- const positionals = [];
3425
- for (let index = 0; index < args.length; index += 1) {
3426
- const arg = args[index];
3427
- if (valueFlagSet.has(arg)) {
3428
- readFlagValue(args, index, arg);
3429
- index += 1;
3430
- continue;
3431
- }
3432
- if (booleanFlagSet.has(arg))
3433
- continue;
3434
- if (arg.startsWith("-")) {
3435
- throw unknownOptionError(arg, command, [...valueFlagSet, ...booleanFlagSet]);
3436
- }
3437
- if (positionals.length >= maxPositionals) {
3438
- throw new Error(`Unexpected argument for ${command}: ${arg}`);
3439
- }
3440
- positionals.push(arg);
3441
- }
3442
- return positionals;
3443
- }
3444
- function assertNoExtraArgs(args, command, maxPositionals) {
3445
- for (const arg of args.slice(maxPositionals)) {
3446
- if (arg.startsWith("-")) {
3447
- throw new Error(`Unknown option for ${command}: ${arg}`);
3448
- }
3449
- throw new Error(`Unexpected argument for ${command}: ${arg}`);
3450
- }
3451
- }
3452
2986
  function readRequiredLeadingArgument(args, command, placeholder) {
3453
2987
  const value = args[0];
3454
2988
  if (!value || value.startsWith("-"))
3455
2989
  throw new Error(`${command} requires ${placeholder}`);
3456
2990
  return value;
3457
2991
  }
3458
- const ASK_PRO_BOOLEAN_FLAGS = new Set(["--dry-run", "--send", "--confirm-target"]);
3459
- const ASK_PRO_SELECTION_VALUE_FLAGS = ["--project", "--project-new", "--model", "--pro-mode", "--effort"];
3460
- const ASK_PRO_VALUE_FLAGS = new Set([
3461
- "--cwd",
3462
- "--file",
3463
- "--port",
3464
- "--timeout-ms",
3465
- "--target-url",
3466
- "--source-cli",
3467
- ...ASK_PRO_SELECTION_VALUE_FLAGS
3468
- ]);
3469
- const ASK_PRO_PREVIEW_VALUE_FLAGS = new Set([
3470
- "--cwd",
3471
- "--file",
3472
- "--port",
3473
- "--timeout-ms",
3474
- "--target-url",
3475
- ...ASK_PRO_SELECTION_VALUE_FLAGS
3476
- ]);
3477
- // Setup persists defaults for a subset of the per-ask selection flags. A new
3478
- // project is created per-ask, never as a standing default, so --project-new is
3479
- // intentionally excluded here.
3480
- const ASK_PRO_SELECTION_DEFAULT_FLAGS = ["--model", "--pro-mode", "--effort", "--project"];
3481
- const ASK_PRO_SELECTION_CLEAR_FLAGS = ["--clear-model", "--clear-pro-mode", "--clear-effort", "--clear-project"];
3482
2992
  function parseBrowserDefaultFlags(args) {
3483
2993
  const model = readFlag(args, "--model");
3484
2994
  const proModeRaw = readFlag(args, "--pro-mode");
@@ -3516,6 +3026,62 @@ function parseBrowserDefaultFlags(args) {
3516
3026
  ...(clearProject ? { project: undefined } : {})
3517
3027
  };
3518
3028
  }
3029
+ function resolvePromptUser(io) {
3030
+ if (io.promptUser)
3031
+ return io.promptUser;
3032
+ if (!process.stdin.isTTY) {
3033
+ throw new Error("setup --interactive needs a terminal (TTY). Use the --model/--pro-mode/--effort/--project flags instead.");
3034
+ }
3035
+ return async (question) => {
3036
+ const readline = await import("node:readline/promises");
3037
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
3038
+ try {
3039
+ return (await rl.question(question)).trim();
3040
+ }
3041
+ finally {
3042
+ rl.close();
3043
+ }
3044
+ };
3045
+ }
3046
+ // Ask up to `attempts` times; empty input means skip (returns undefined).
3047
+ async function askChoice(prompt, question, choices, attempts = 3) {
3048
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
3049
+ const raw = (await prompt(question)).trim();
3050
+ if (raw === "")
3051
+ return undefined;
3052
+ const index = Number.parseInt(raw, 10);
3053
+ if (Number.isInteger(index) && index >= 1 && index <= choices.length)
3054
+ return choices[index - 1];
3055
+ }
3056
+ throw new Error(`No valid choice after ${attempts} attempts; run setup again or use flags.`);
3057
+ }
3058
+ async function runBrowserDefaultsWizard(prompt, stdout) {
3059
+ stdout("Browser send defaults (press Enter to skip a question; labels match the Korean ChatGPT UI):");
3060
+ const model = await askChoice(prompt, "Default model — 1) Pro [Enter=skip]: ", ["Pro"]);
3061
+ let proMode;
3062
+ let effort;
3063
+ if (model === "Pro") {
3064
+ proMode = (await askChoice(prompt, "Pro sub-mode — 1) 기본 2) 확장 [Enter=skip]: ", ["기본", "확장"]));
3065
+ }
3066
+ else {
3067
+ effort = (await askChoice(prompt, "Reasoning effort — 1) 즉시 2) 중간 3) 높음 4) 매우 높음 [Enter=skip]: ", [
3068
+ "즉시",
3069
+ "중간",
3070
+ "높음",
3071
+ "매우 높음"
3072
+ ]));
3073
+ }
3074
+ const projectRaw = (await prompt('Default project name (existing sidebar project) [Enter=skip]: ')).trim();
3075
+ const project = projectRaw === "" ? undefined : projectRaw;
3076
+ if (model === undefined && proMode === undefined && effort === undefined && project === undefined)
3077
+ return undefined;
3078
+ return {
3079
+ ...(model !== undefined ? { model } : {}),
3080
+ ...(proMode !== undefined ? { proMode } : {}),
3081
+ ...(effort !== undefined ? { effort } : {}),
3082
+ ...(project !== undefined ? { project } : {})
3083
+ };
3084
+ }
3519
3085
  function formatBrowserDefaults(defaults) {
3520
3086
  const parts = [];
3521
3087
  if (defaults.model)
@@ -3528,76 +3094,6 @@ function formatBrowserDefaults(defaults) {
3528
3094
  parts.push(`project=${defaults.project}`);
3529
3095
  return parts.length > 0 ? parts.join(", ") : "(none)";
3530
3096
  }
3531
- function parseAskProArgs(args, valueFlags = ASK_PRO_VALUE_FLAGS) {
3532
- const delimiterIndex = args.indexOf("--");
3533
- const optionArgs = delimiterIndex === -1 ? args : args.slice(0, delimiterIndex);
3534
- const promptTail = delimiterIndex === -1 ? [] : args.slice(delimiterIndex + 1);
3535
- const positionalPromptParts = [];
3536
- for (let index = 0; index < optionArgs.length; index += 1) {
3537
- const arg = optionArgs[index];
3538
- if (!arg.startsWith("--")) {
3539
- if (arg.startsWith("-"))
3540
- throw unknownOptionError(arg, undefined, [...valueFlags, ...ASK_PRO_BOOLEAN_FLAGS]);
3541
- positionalPromptParts.push(arg);
3542
- continue;
3543
- }
3544
- if (ASK_PRO_BOOLEAN_FLAGS.has(arg))
3545
- continue;
3546
- if (valueFlags.has(arg)) {
3547
- readFlagValue(optionArgs, index, arg);
3548
- index += 1;
3549
- continue;
3550
- }
3551
- throw unknownOptionError(arg, undefined, [...valueFlags, ...ASK_PRO_BOOLEAN_FLAGS]);
3552
- }
3553
- return { optionArgs, promptParts: [...positionalPromptParts, ...promptTail] };
3554
- }
3555
- function askProOptionArgs(args) {
3556
- const delimiterIndex = args.indexOf("--");
3557
- return delimiterIndex === -1 ? args : args.slice(0, delimiterIndex);
3558
- }
3559
- function hasAskProMode(args) {
3560
- const optionArgs = askProOptionArgs(args);
3561
- return optionArgs.includes("--send") || optionArgs.includes("--dry-run");
3562
- }
3563
- function hasAskProSendMode(args) {
3564
- return askProOptionArgs(args).includes("--send");
3565
- }
3566
- function hasAskProDryRunMode(args) {
3567
- return askProOptionArgs(args).includes("--dry-run");
3568
- }
3569
- function readFlagValue(args, index, flag) {
3570
- const value = args[index + 1];
3571
- if (!value || value.startsWith("--"))
3572
- throw new Error(`${flag} requires a value`);
3573
- return value;
3574
- }
3575
- function readSessionStatusFlag(args) {
3576
- const value = readFlag(args, "--status");
3577
- if (value === undefined)
3578
- return undefined;
3579
- if (value === "preview" || value === "running" || value === "done" || value === "blocked")
3580
- return value;
3581
- throw new Error("--status must be one of preview, running, done, blocked");
3582
- }
3583
- const TASK_STATUSES = TaskStatusSchema.options;
3584
- function readTaskStatusFlag(args) {
3585
- const value = readFlag(args, "--status");
3586
- if (value === undefined)
3587
- return undefined;
3588
- if (TaskStatusSchema.safeParse(value).success)
3589
- return value;
3590
- throw new Error(`--status must be one of ${TASK_STATUSES.join(", ")}`);
3591
- }
3592
- const RECEIPT_KINDS = ReceiptKindSchema.options;
3593
- function readReceiptKindFlag(args) {
3594
- const value = readFlag(args, "--kind");
3595
- if (value === undefined)
3596
- return undefined;
3597
- if (ReceiptKindSchema.safeParse(value).success)
3598
- return value;
3599
- throw new Error(`--kind must be one of ${RECEIPT_KINDS.join(", ")}`);
3600
- }
3601
3097
  function formatTokenExpiryLine(config) {
3602
3098
  const tokenStatus = getTokenExpiryStatus(config);
3603
3099
  if (tokenStatus.status === "valid")