@youdie006/prodex 0.5.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",
@@ -1192,222 +1169,6 @@ function defaultIo() {
1192
1169
  stderr: (line) => console.error(line)
1193
1170
  };
1194
1171
  }
1195
- function printHelp(stdout) {
1196
- stdout(`prodex v${CLI_VERSION}
1197
-
1198
- Commands:
1199
- prodex --version
1200
- prodex init [--cwd /absolute/path/to/repo]
1201
- prodex doctor [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1202
- 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"] [--clear-model|--clear-pro-mode|--clear-effort|--clear-project] [--interactive]
1203
- prodex start [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1204
- prodex status [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js] [--show-token] [--url-only] [--unsafe-show-non-expiring-token]
1205
- prodex tunnel url [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js] --public-url https://... [--show-token] [--url-only]
1206
- prodex release status [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1207
- prodex release pack [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js] --pack-destination /absolute/path [--keep-workdir]
1208
- prodex onboard [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1209
- prodex project prompt [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1210
- prodex claude prompt [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1211
- prodex claude config [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1212
- prodex pro ask [--dry-run] [--cwd /absolute/path/to/repo] [--file path] "prompt" # dry-run preview
1213
- 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
1214
- prodex pro browser help [--source-cli /absolute/path/to/dist/cli.js]
1215
- prodex pro browser check [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo] [--port 9333] [--timeout-ms 1500]
1216
- prodex pro browser smoke [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo] [--port 9333] [--timeout-ms 90000]
1217
- prodex pro browser models [--source-cli /absolute/path/to/dist/cli.js] [--port 9333] [--timeout-ms 15000] # read-only list of model menu options
1218
- 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" | --project-new "name"] "prompt" # explicit visible-browser send
1219
- prodex pro latest [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo]
1220
- prodex pro list [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo]
1221
- prodex pro show <task-id|latest> [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo]
1222
- prodex tasks create [--cwd /absolute/path/to/repo] --title "Title" --prompt "Prompt"
1223
- prodex tasks list [--status new|claimed|done|blocked] [--cwd /absolute/path/to/repo]
1224
- prodex tasks show <task-id|latest> [--cwd /absolute/path/to/repo]
1225
- prodex tasks claim <task-id> [--cwd /absolute/path/to/repo] [--by codex]
1226
- prodex tasks complete <task-id> [--cwd /absolute/path/to/repo] --summary "Summary" [--command "npm test"] [--artifact .bridge/artifacts/results/name.md=text]
1227
- prodex tasks block <task-id> [--cwd /absolute/path/to/repo] --summary "Summary" [--code code] [--next-step "Next step"] [--retryable]
1228
- prodex results show <task-id|latest> [--cwd /absolute/path/to/repo]
1229
- prodex results artifact <task-id|latest> [artifact-path] [--cwd /absolute/path/to/repo]
1230
- prodex results reseal <task-id|latest> --confirm-current-result [--cwd /absolute/path/to/repo]
1231
- prodex receipts list [--kind kind] [--task-id task-id] [--cwd /absolute/path/to/repo]
1232
- prodex receipts show <receipt-id|latest> [--cwd /absolute/path/to/repo]
1233
- prodex receipts rotate-key [--cwd /absolute/path/to/repo]
1234
- prodex sessions list [--status preview|running|done|blocked] [--cwd /absolute/path/to/repo]
1235
- prodex sessions show <session-id|latest> [--cwd /absolute/path/to/repo]
1236
- prodex mcp [--cwd /absolute/path/to/repo]`);
1237
- }
1238
- function printInitHelp(stdout) {
1239
- stdout(`prodex init
1240
-
1241
- Commands:
1242
- prodex init [--cwd /absolute/path/to/repo]
1243
-
1244
- Initialize the local .bridge receipt ledger and bridge .gitignore entries.`);
1245
- }
1246
- function printSetupHelp(stdout) {
1247
- stdout(`prodex setup
1248
-
1249
- Commands:
1250
- 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"] [--clear-model|--clear-pro-mode|--clear-effort|--clear-project] [--interactive]
1251
-
1252
- Save a loopback-only HTTP MCP profile in .bridge/config.local.json. Use --token-ttl-hours before tunnels or ChatGPT Project use.
1253
-
1254
- Optional visible-browser send defaults (applied by \`pro browser ask\` when the matching per-ask flag is omitted):
1255
- --model Composer model to pick by its exact menu label (verified: Pro)
1256
- --pro-mode Pro sub-mode: 기본 (standard) or 확장 (extended)
1257
- --effort Reasoning effort: 즉시 / 중간 / 높음 / 매우 높음 (English aliases: instant/medium/high/max); picking one deselects Pro
1258
- --project Sidebar project to enter before sending
1259
- Clear a saved default with --clear-model / --clear-pro-mode / --clear-effort / --clear-project.
1260
- --pro-mode and --effort are different model axes and cannot be combined. View saved defaults with \`prodex status\`.`);
1261
- }
1262
- function printStartHelp(stdout) {
1263
- stdout(`prodex start
1264
-
1265
- Commands:
1266
- prodex start [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1267
-
1268
- Start the local loopback HTTP MCP server from the saved setup profile.`);
1269
- }
1270
- function printStatusHelp(stdout) {
1271
- stdout(`prodex status
1272
-
1273
- Commands:
1274
- prodex status [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js] [--show-token] [--url-only] [--unsafe-show-non-expiring-token]
1275
-
1276
- Show the saved local MCP URL with tokens redacted by default.`);
1277
- }
1278
- function printTunnelHelp(stdout) {
1279
- stdout(`prodex tunnel
1280
-
1281
- Commands:
1282
- prodex tunnel url [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js] --public-url https://... [--show-token] [--url-only]
1283
-
1284
- Format a public tunnel MCP URL from an existing local setup. This command does not create a tunnel.`);
1285
- }
1286
- function printTunnelUrlHelp(stdout) {
1287
- stdout(`prodex tunnel url
1288
-
1289
- Commands:
1290
- prodex tunnel url [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js] --public-url https://... [--show-token] [--url-only]
1291
-
1292
- This command does not create a tunnel. It only formats your supplied public URL with the saved short-lived MCP token.`);
1293
- }
1294
- function printDoctorHelp(stdout) {
1295
- stdout(`prodex doctor
1296
-
1297
- Commands:
1298
- prodex doctor [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1299
-
1300
- Run local bridge, MCP, write/apply/stage, and HTTP MCP smoke checks without opening ChatGPT.`);
1301
- }
1302
- function printOnboardHelp(stdout) {
1303
- stdout(`prodex onboard
1304
-
1305
- Commands:
1306
- prodex onboard [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1307
-
1308
- Print a local-first setup guide for Codex, ChatGPT Projects, Claude, and visible-browser Pro consults.`);
1309
- }
1310
- function printMcpHelp(stdout) {
1311
- stdout(`prodex mcp
1312
-
1313
- Commands:
1314
- prodex mcp [--cwd /absolute/path/to/repo]
1315
-
1316
- Run the stdio MCP server for local clients such as Claude. This does not reveal HTTP MCP URL tokens.`);
1317
- }
1318
- function printReleaseHelp(stdout) {
1319
- stdout(`prodex release
1320
-
1321
- Commands:
1322
- prodex release status [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1323
- prodex release pack [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js] --pack-destination /absolute/path [--keep-workdir]
1324
-
1325
- Release commands are local checks and package preparation helpers; they do not publish or push.`);
1326
- }
1327
- function printProHelp(stdout) {
1328
- stdout(`prodex pro
1329
-
1330
- Commands:
1331
- prodex pro ask [--dry-run] [--cwd /absolute/path/to/repo] [--file path] "prompt"
1332
- prodex pro browser help [--source-cli /absolute/path/to/dist/cli.js]
1333
- prodex pro browser login [--cwd /absolute/path/to/repo] [--dry-run] [--source-cli /absolute/path/to/dist/cli.js] [--launch-timeout-ms 5000]
1334
- prodex pro browser check [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo]
1335
- prodex pro browser smoke [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo]
1336
- prodex pro browser models [--source-cli /absolute/path/to/dist/cli.js]
1337
- 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" | --project-new "name"] "prompt"
1338
- prodex pro latest [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo]
1339
- prodex pro list [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo]
1340
- prodex pro show <task-id|latest> [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo]
1341
-
1342
- Use \`prodex pro ask\` for dry-run/manual previews.
1343
- Use \`prodex pro browser ask\` only when you want an explicit visible-browser send.
1344
- Model/project selection (visible-browser send):
1345
- --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.
1346
- --pro-mode 기본 | 확장 Pro sub-mode (only when the model is Pro); 확장 raises the default timeout to 300000 ms
1347
- --effort 즉시|중간|높음|매우 높음 Reasoning effort (aliases: instant/medium/high/max); picking one deselects Pro
1348
- --project "name" Enter an existing sidebar project first (cannot combine with --target-url)
1349
- Labels match the Korean ChatGPT UI; run \`prodex pro browser models\` to list what your account shows.
1350
- Persist defaults with \`prodex setup --model/--pro-mode/--effort/--project\`; clear them with setup --clear-model/--clear-pro-mode/--clear-effort/--clear-project.
1351
- (Creating a new project from the CLI is planned; for now create it in ChatGPT and pass --project.)`);
1352
- }
1353
- function printProjectHelp(stdout) {
1354
- stdout(`prodex project
1355
-
1356
- Commands:
1357
- prodex project prompt [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1358
-
1359
- Print a ChatGPT Project MCP verification prompt. The prompt asks for read/task handoff verification only.`);
1360
- }
1361
- function printClaudeHelp(stdout) {
1362
- stdout(`prodex claude
1363
-
1364
- Commands:
1365
- prodex claude prompt [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1366
- prodex claude config [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
1367
-
1368
- Print Claude MCP setup and verification helpers. These commands do not start MCP or reveal HTTP tokens.`);
1369
- }
1370
- function printTasksHelp(stdout) {
1371
- stdout(`prodex tasks
1372
-
1373
- Commands:
1374
- prodex tasks create [--cwd /absolute/path/to/repo] --title "Title" --prompt "Prompt"
1375
- prodex tasks list [--status new|claimed|done|blocked] [--cwd /absolute/path/to/repo]
1376
- prodex tasks show <task-id|latest> [--cwd /absolute/path/to/repo]
1377
- prodex tasks claim <task-id> [--cwd /absolute/path/to/repo] [--by codex]
1378
- prodex tasks complete <task-id> [--cwd /absolute/path/to/repo] --summary "Summary" [--command "npm test"] [--artifact .bridge/artifacts/results/name.md=text]
1379
- prodex tasks block <task-id> [--cwd /absolute/path/to/repo] --summary "Summary" [--code code] [--next-step "Next step"] [--retryable]`);
1380
- }
1381
- function printResultsHelp(stdout) {
1382
- stdout(`prodex results
1383
-
1384
- Commands:
1385
- prodex results show <task-id|latest> [--cwd /absolute/path/to/repo]
1386
- prodex results artifact <task-id|latest> [artifact-path] [--cwd /absolute/path/to/repo]
1387
- prodex results reseal <task-id|latest> --confirm-current-result [--cwd /absolute/path/to/repo]`);
1388
- }
1389
- function printReceiptsHelp(stdout) {
1390
- stdout(`prodex receipts
1391
-
1392
- Commands:
1393
- prodex receipts list [--kind kind] [--task-id task-id] [--cwd /absolute/path/to/repo]
1394
- prodex receipts show <receipt-id|latest> [--cwd /absolute/path/to/repo]
1395
- prodex receipts rotate-key [--cwd /absolute/path/to/repo]
1396
-
1397
- rotate-key generates a new signing key for receipt integrity seals and keeps the
1398
- previous keys in .bridge/receipt-key.local so receipts signed before the
1399
- rotation still verify.`);
1400
- }
1401
- function printSessionsHelp(stdout) {
1402
- stdout(`prodex sessions
1403
-
1404
- Commands:
1405
- prodex sessions list [--status preview|running|done|blocked] [--cwd /absolute/path/to/repo]
1406
- prodex sessions show <session-id|latest> [--cwd /absolute/path/to/repo]`);
1407
- }
1408
- function isHelpSubcommand(value) {
1409
- return value === "help" || value === "--help" || value === "-h";
1410
- }
1411
1172
  function isHelpArgs(args) {
1412
1173
  return args.length > 0 && isHelpSubcommand(args[0]);
1413
1174
  }
@@ -1427,84 +1188,6 @@ function printProBrowserHelpIfRequested(args, command, io, options) {
1427
1188
  printProBrowserHelp(io.stdout, resolveOptionalFileFlag(io.cwd, args, "--source-cli"));
1428
1189
  return true;
1429
1190
  }
1430
- function findHelpFlagIndexBeforePromptDelimiter(args) {
1431
- const delimiterIndex = args.indexOf("--");
1432
- const limit = delimiterIndex === -1 ? args.length : delimiterIndex;
1433
- return args.findIndex((arg, index) => index < limit && isHelpSubcommand(arg));
1434
- }
1435
- function assertHelpRequestArgs(args, command, options) {
1436
- const delimiterIndex = args.indexOf("--");
1437
- const commandArgs = delimiterIndex === -1 ? args : args.slice(0, delimiterIndex);
1438
- const valueFlagSet = new Set(options.valueFlags ?? []);
1439
- const booleanFlagSet = new Set(options.booleanFlags ?? []);
1440
- const maxPositionals = options.maxPositionals ?? 0;
1441
- let positionals = 0;
1442
- for (let index = 0; index < commandArgs.length; index += 1) {
1443
- const arg = commandArgs[index];
1444
- if (isHelpSubcommand(arg))
1445
- continue;
1446
- if (valueFlagSet.has(arg)) {
1447
- const next = commandArgs[index + 1];
1448
- if (next && !isHelpSubcommand(next)) {
1449
- readFlagValue(commandArgs, index, arg);
1450
- index += 1;
1451
- }
1452
- continue;
1453
- }
1454
- if (booleanFlagSet.has(arg))
1455
- continue;
1456
- if (arg.startsWith("-")) {
1457
- throw unknownOptionError(arg, command, [...valueFlagSet, ...booleanFlagSet]);
1458
- }
1459
- if (positionals >= maxPositionals) {
1460
- throw new Error(`Unexpected argument for ${command}: ${arg}`);
1461
- }
1462
- positionals += 1;
1463
- }
1464
- }
1465
- function unknownSubcommandError(command, subcommand, expected) {
1466
- const suggestion = closestSuggestion(subcommand, expected);
1467
- const suggestionText = suggestion ? ` Did you mean \`prodex ${command} ${suggestion}\`?` : "";
1468
- return new Error(`Unknown ${command} subcommand: ${subcommand}.${suggestionText} Expected one of: ${expected.join(", ")}. Run \`prodex ${command} --help\`.`);
1469
- }
1470
- function unknownTopLevelCommandError(command) {
1471
- const suggestion = closestSuggestion(command, TOP_LEVEL_COMMANDS);
1472
- const suggestionText = suggestion ? ` Did you mean \`prodex ${suggestion}\`?` : "";
1473
- return new Error(`Unknown command: ${command}.${suggestionText} Run \`prodex help\`.`);
1474
- }
1475
- function unknownOptionError(option, command, candidates) {
1476
- const suggestion = closestSuggestion(option, candidates);
1477
- const suggestionText = suggestion ? `. Did you mean \`${suggestion}\`?` : "";
1478
- const context = command ? ` for ${command}` : "";
1479
- return new Error(`Unknown option${context}: ${option}${suggestionText}`);
1480
- }
1481
- function closestSuggestion(value, candidates) {
1482
- let best;
1483
- for (const candidate of candidates) {
1484
- const distance = editDistance(value, candidate);
1485
- const prefixMatch = isUsefulPrefixSuggestion(value, candidate);
1486
- if (!best || (prefixMatch && !best.prefixMatch) || (prefixMatch === best.prefixMatch && distance < best.distance)) {
1487
- best = { command: candidate, distance, prefixMatch };
1488
- }
1489
- }
1490
- return best && (best.prefixMatch || best.distance <= 2) ? best.command : undefined;
1491
- }
1492
- function isUsefulPrefixSuggestion(value, candidate) {
1493
- return value.length >= 5 && candidate.startsWith(value);
1494
- }
1495
- function editDistance(left, right) {
1496
- const previous = Array.from({ length: right.length + 1 }, (_, index) => index);
1497
- const current = Array.from({ length: right.length + 1 }, () => 0);
1498
- for (let leftIndex = 1; leftIndex <= left.length; leftIndex += 1) {
1499
- current[0] = leftIndex;
1500
- for (let rightIndex = 1; rightIndex <= right.length; rightIndex += 1) {
1501
- const substitutionCost = left[leftIndex - 1] === right[rightIndex - 1] ? 0 : 1;
1502
- current[rightIndex] = Math.min(previous[rightIndex] + 1, current[rightIndex - 1] + 1, previous[rightIndex - 1] + substitutionCost);
1503
- }
1504
- previous.splice(0, previous.length, ...current);
1505
- }
1506
- return previous[right.length];
1507
- }
1508
1191
  function legacyChatGptNamespaceError(subcommand) {
1509
1192
  const prefix = subcommand ? `Unknown legacy chatgpt subcommand: ${subcommand}.` : "The legacy `chatgpt` namespace is hidden.";
1510
1193
  return new Error(`${prefix} Use \`prodex pro browser help\` for visible-browser commands.`);
@@ -1682,21 +1365,12 @@ function formatClaudeConfig(cwd, sourceCli) {
1682
1365
  }
1683
1366
  }, null, 2);
1684
1367
  }
1685
- function shellQuote(value) {
1686
- return /^[A-Za-z0-9_./:@=-]+$/.test(value) ? value : `'${value.replaceAll("'", "'\\''")}'`;
1687
- }
1688
- function formatCliCommand(sourceCli) {
1689
- return sourceCli ? `node ${shellQuote(sourceCli)}` : "prodex";
1690
- }
1691
1368
  function formatInitCommand(sourceCli, options = {}) {
1692
1369
  return [`${formatCliCommand(sourceCli)} init`, options.cwd ? `--cwd ${shellQuote(options.cwd)}` : undefined].filter(Boolean).join(" ");
1693
1370
  }
1694
1371
  function formatSetupCommand(sourceCli, options = {}) {
1695
1372
  return [`${formatCliCommand(sourceCli)} setup`, options.cwd ? `--cwd ${shellQuote(options.cwd)}` : undefined].filter(Boolean).join(" ");
1696
1373
  }
1697
- function formatSourceCliOption(sourceCli) {
1698
- return sourceCli ? ` --source-cli ${shellQuote(sourceCli)}` : "";
1699
- }
1700
1374
  function formatBrowserLoginCommand(sourceCli, options = {}) {
1701
1375
  return formatCommandInCwd(formatBrowserLoginCommandBody(sourceCli, options), options.cwd);
1702
1376
  }
@@ -2765,46 +2439,6 @@ function printBrowserLoginGuide(stdout, input) {
2765
2439
  stdout("The dedicated profile path above will be reused by the real login command.");
2766
2440
  }
2767
2441
  }
2768
- function printProBrowserHelp(stdout, sourceCli) {
2769
- const cli = formatCliCommand(sourceCli);
2770
- const sourceCliOption = formatSourceCliOption(sourceCli);
2771
- const loginUsage = sourceCli
2772
- ? `${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]`
2773
- : "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]";
2774
- const checkUsage = sourceCli
2775
- ? `${cli} pro browser check${sourceCliOption} [--cwd /absolute/path/to/repo] [--port 9333] [--timeout-ms 1500]`
2776
- : "prodex pro browser check [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo] [--port 9333] [--timeout-ms 1500]";
2777
- const smokeUsage = sourceCli
2778
- ? `${cli} pro browser smoke${sourceCliOption} [--cwd /absolute/path/to/repo] [--port 9333] [--timeout-ms 90000]`
2779
- : "prodex pro browser smoke [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo] [--port 9333] [--timeout-ms 90000]";
2780
- const selectionUsage = '[--model Pro] [--pro-mode 기본|확장] [--effort 즉시|중간|높음|"매우 높음"] [--project "name" | --project-new "name"]';
2781
- const askUsage = sourceCli
2782
- ? `${cli} pro browser ask${sourceCliOption} [--cwd /absolute/path/to/repo] [--port 9333] [--timeout-ms 90000] [--target-url url --confirm-target] [--file path] ${selectionUsage} "prompt"`
2783
- : `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"`;
2784
- const modelsUsage = sourceCli
2785
- ? `${cli} pro browser models${sourceCliOption} [--port 9333] [--timeout-ms 15000]`
2786
- : "prodex pro browser models [--source-cli /absolute/path/to/dist/cli.js] [--port 9333] [--timeout-ms 15000]";
2787
- stdout(`${cli} pro browser
2788
-
2789
- Commands:
2790
- ${loginUsage}
2791
- ${checkUsage}
2792
- ${smokeUsage}
2793
- ${modelsUsage}
2794
- ${askUsage}
2795
-
2796
- Visible-browser sends require a manual browser session and stop on login, captcha, Cloudflare, permission, rate-limit, or usage-limit blockers.
2797
- Model/project selection (ask):
2798
- --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.
2799
- --pro-mode Pro sub-mode: 기본 (standard) or 확장 (extended), used when the model is Pro. 확장 raises the default --timeout-ms to 300000.
2800
- --effort Reasoning effort: 즉시 / 중간 / 높음 / 매우 높음 (aliases: instant/medium/high/max). Picking an effort switches the composer to the standard reasoning model, deselecting Pro.
2801
- --project Enter an existing sidebar project before sending. Cannot be combined with --target-url.
2802
- --pro-mode and --effort cannot be combined. These labels match the Korean ChatGPT UI; set your ChatGPT display language to Korean for selection flags.
2803
- Run \`${cli} pro browser models${sourceCliOption}\` to list the labels your account currently shows.
2804
- Persist defaults with \`${cli} setup${sourceCliOption}\`; per-ask flags override them.
2805
- Use \`${cli} pro ask\` for dry-run/manual previews.
2806
- \`${cli} pro browser ask${sourceCliOption}\` always attempts an explicit visible-browser send.`);
2807
- }
2808
2442
  async function assertBrowserLaunchStayedAlive(opened, timeoutMs) {
2809
2443
  const outcome = await waitForBrowserLaunchReady(opened, timeoutMs);
2810
2444
  if (outcome.reachable)
@@ -3342,167 +2976,19 @@ function parseTunnelPublicUrl(publicUrl) {
3342
2976
  function isLoopbackHost(hostname) {
3343
2977
  return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
3344
2978
  }
3345
- function readFlag(args, flag) {
3346
- const index = args.indexOf(flag);
3347
- if (index === -1)
3348
- return undefined;
3349
- return readFlagValue(args, index, flag);
3350
- }
3351
- function readNumberFlag(args, flag) {
3352
- const raw = readFlag(args, flag);
3353
- if (raw === undefined)
3354
- return undefined;
3355
- const value = Number(raw);
3356
- if (!Number.isFinite(value))
3357
- throw new Error(`${flag} requires a finite number`);
3358
- return value;
3359
- }
3360
- function readPositiveNumberFlag(args, flag) {
3361
- const value = readNumberFlag(args, flag);
3362
- if (value === undefined)
3363
- return undefined;
3364
- if (value <= 0)
3365
- throw new Error(`${flag} must be greater than 0`);
3366
- return value;
3367
- }
3368
- function readPortFlag(args, flag) {
3369
- const value = readNumberFlag(args, flag);
3370
- if (value === undefined)
3371
- return undefined;
3372
- if (!Number.isInteger(value) || value < 1 || value > 65535) {
3373
- throw new Error(`${flag} must be an integer from 1 to 65535`);
3374
- }
3375
- return value;
3376
- }
3377
2979
  function readChatGptBrowserUrlFlag(args) {
3378
2980
  return normalizeChatGptTargetUrl(readFlag(args, "--url") ?? "https://chatgpt.com/");
3379
2981
  }
3380
- function readRepeatedFlag(args, flag) {
3381
- const values = [];
3382
- for (let index = 0; index < args.length; index += 1) {
3383
- if (args[index] === flag) {
3384
- values.push(readFlagValue(args, index, flag));
3385
- index += 1;
3386
- }
3387
- }
3388
- return values;
3389
- }
3390
- function resolveCwdFlag(defaultCwd, args) {
3391
- const cwd = readFlag(args, "--cwd");
3392
- if (!cwd)
3393
- return defaultCwd;
3394
- return resolveExistingDirectoryFlag(defaultCwd, cwd, "--cwd");
3395
- }
3396
2982
  function resolveOptionalPathFlag(defaultCwd, args, flag) {
3397
2983
  const value = readFlag(args, flag);
3398
2984
  return value ? resolveExistingPathFlag(defaultCwd, value, flag) : undefined;
3399
2985
  }
3400
- function resolveOptionalFileFlag(defaultCwd, args, flag) {
3401
- const value = readFlag(args, flag);
3402
- return value ? resolveExistingFileFlag(defaultCwd, value, flag) : undefined;
3403
- }
3404
- function resolveExistingPathFlag(defaultCwd, value, flag) {
3405
- const resolved = path.resolve(defaultCwd, value);
3406
- try {
3407
- return realpathSync(resolved);
3408
- }
3409
- catch {
3410
- throw new Error(`${flag} does not exist or is not accessible: ${resolved}`);
3411
- }
3412
- }
3413
- function resolveExistingFileFlag(defaultCwd, value, flag) {
3414
- const resolved = resolveExistingPathFlag(defaultCwd, value, flag);
3415
- if (!statSync(resolved).isFile()) {
3416
- throw new Error(`${flag} must be a file: ${resolved}`);
3417
- }
3418
- return resolved;
3419
- }
3420
- function resolveExistingDirectoryFlag(defaultCwd, value, flag) {
3421
- const resolved = resolveExistingPathFlag(defaultCwd, value, flag);
3422
- if (!statSync(resolved).isDirectory()) {
3423
- throw new Error(`${flag} must be a directory: ${resolved}`);
3424
- }
3425
- return resolved;
3426
- }
3427
- function assertOnlyOptions(args, command, valueFlags, booleanFlags = []) {
3428
- const valueFlagSet = new Set(valueFlags);
3429
- const booleanFlagSet = new Set(booleanFlags);
3430
- for (let index = 0; index < args.length; index += 1) {
3431
- const arg = args[index];
3432
- if (valueFlagSet.has(arg)) {
3433
- readFlagValue(args, index, arg);
3434
- index += 1;
3435
- continue;
3436
- }
3437
- if (booleanFlagSet.has(arg))
3438
- continue;
3439
- if (arg.startsWith("-")) {
3440
- throw unknownOptionError(arg, command, [...valueFlagSet, ...booleanFlagSet]);
3441
- }
3442
- throw new Error(`Unexpected argument for ${command}: ${arg}`);
3443
- }
3444
- }
3445
- function readPositionalsWithOptions(args, command, maxPositionals, valueFlags, booleanFlags = []) {
3446
- const valueFlagSet = new Set(valueFlags);
3447
- const booleanFlagSet = new Set(booleanFlags);
3448
- const positionals = [];
3449
- for (let index = 0; index < args.length; index += 1) {
3450
- const arg = args[index];
3451
- if (valueFlagSet.has(arg)) {
3452
- readFlagValue(args, index, arg);
3453
- index += 1;
3454
- continue;
3455
- }
3456
- if (booleanFlagSet.has(arg))
3457
- continue;
3458
- if (arg.startsWith("-")) {
3459
- throw unknownOptionError(arg, command, [...valueFlagSet, ...booleanFlagSet]);
3460
- }
3461
- if (positionals.length >= maxPositionals) {
3462
- throw new Error(`Unexpected argument for ${command}: ${arg}`);
3463
- }
3464
- positionals.push(arg);
3465
- }
3466
- return positionals;
3467
- }
3468
- function assertNoExtraArgs(args, command, maxPositionals) {
3469
- for (const arg of args.slice(maxPositionals)) {
3470
- if (arg.startsWith("-")) {
3471
- throw new Error(`Unknown option for ${command}: ${arg}`);
3472
- }
3473
- throw new Error(`Unexpected argument for ${command}: ${arg}`);
3474
- }
3475
- }
3476
2986
  function readRequiredLeadingArgument(args, command, placeholder) {
3477
2987
  const value = args[0];
3478
2988
  if (!value || value.startsWith("-"))
3479
2989
  throw new Error(`${command} requires ${placeholder}`);
3480
2990
  return value;
3481
2991
  }
3482
- const ASK_PRO_BOOLEAN_FLAGS = new Set(["--dry-run", "--send", "--confirm-target"]);
3483
- const ASK_PRO_SELECTION_VALUE_FLAGS = ["--project", "--project-new", "--model", "--pro-mode", "--effort"];
3484
- const ASK_PRO_VALUE_FLAGS = new Set([
3485
- "--cwd",
3486
- "--file",
3487
- "--port",
3488
- "--timeout-ms",
3489
- "--target-url",
3490
- "--source-cli",
3491
- ...ASK_PRO_SELECTION_VALUE_FLAGS
3492
- ]);
3493
- const ASK_PRO_PREVIEW_VALUE_FLAGS = new Set([
3494
- "--cwd",
3495
- "--file",
3496
- "--port",
3497
- "--timeout-ms",
3498
- "--target-url",
3499
- ...ASK_PRO_SELECTION_VALUE_FLAGS
3500
- ]);
3501
- // Setup persists defaults for a subset of the per-ask selection flags. A new
3502
- // project is created per-ask, never as a standing default, so --project-new is
3503
- // intentionally excluded here.
3504
- const ASK_PRO_SELECTION_DEFAULT_FLAGS = ["--model", "--pro-mode", "--effort", "--project"];
3505
- const ASK_PRO_SELECTION_CLEAR_FLAGS = ["--clear-model", "--clear-pro-mode", "--clear-effort", "--clear-project"];
3506
2992
  function parseBrowserDefaultFlags(args) {
3507
2993
  const model = readFlag(args, "--model");
3508
2994
  const proModeRaw = readFlag(args, "--pro-mode");
@@ -3608,76 +3094,6 @@ function formatBrowserDefaults(defaults) {
3608
3094
  parts.push(`project=${defaults.project}`);
3609
3095
  return parts.length > 0 ? parts.join(", ") : "(none)";
3610
3096
  }
3611
- function parseAskProArgs(args, valueFlags = ASK_PRO_VALUE_FLAGS) {
3612
- const delimiterIndex = args.indexOf("--");
3613
- const optionArgs = delimiterIndex === -1 ? args : args.slice(0, delimiterIndex);
3614
- const promptTail = delimiterIndex === -1 ? [] : args.slice(delimiterIndex + 1);
3615
- const positionalPromptParts = [];
3616
- for (let index = 0; index < optionArgs.length; index += 1) {
3617
- const arg = optionArgs[index];
3618
- if (!arg.startsWith("--")) {
3619
- if (arg.startsWith("-"))
3620
- throw unknownOptionError(arg, undefined, [...valueFlags, ...ASK_PRO_BOOLEAN_FLAGS]);
3621
- positionalPromptParts.push(arg);
3622
- continue;
3623
- }
3624
- if (ASK_PRO_BOOLEAN_FLAGS.has(arg))
3625
- continue;
3626
- if (valueFlags.has(arg)) {
3627
- readFlagValue(optionArgs, index, arg);
3628
- index += 1;
3629
- continue;
3630
- }
3631
- throw unknownOptionError(arg, undefined, [...valueFlags, ...ASK_PRO_BOOLEAN_FLAGS]);
3632
- }
3633
- return { optionArgs, promptParts: [...positionalPromptParts, ...promptTail] };
3634
- }
3635
- function askProOptionArgs(args) {
3636
- const delimiterIndex = args.indexOf("--");
3637
- return delimiterIndex === -1 ? args : args.slice(0, delimiterIndex);
3638
- }
3639
- function hasAskProMode(args) {
3640
- const optionArgs = askProOptionArgs(args);
3641
- return optionArgs.includes("--send") || optionArgs.includes("--dry-run");
3642
- }
3643
- function hasAskProSendMode(args) {
3644
- return askProOptionArgs(args).includes("--send");
3645
- }
3646
- function hasAskProDryRunMode(args) {
3647
- return askProOptionArgs(args).includes("--dry-run");
3648
- }
3649
- function readFlagValue(args, index, flag) {
3650
- const value = args[index + 1];
3651
- if (!value || value.startsWith("--"))
3652
- throw new Error(`${flag} requires a value`);
3653
- return value;
3654
- }
3655
- function readSessionStatusFlag(args) {
3656
- const value = readFlag(args, "--status");
3657
- if (value === undefined)
3658
- return undefined;
3659
- if (value === "preview" || value === "running" || value === "done" || value === "blocked")
3660
- return value;
3661
- throw new Error("--status must be one of preview, running, done, blocked");
3662
- }
3663
- const TASK_STATUSES = TaskStatusSchema.options;
3664
- function readTaskStatusFlag(args) {
3665
- const value = readFlag(args, "--status");
3666
- if (value === undefined)
3667
- return undefined;
3668
- if (TaskStatusSchema.safeParse(value).success)
3669
- return value;
3670
- throw new Error(`--status must be one of ${TASK_STATUSES.join(", ")}`);
3671
- }
3672
- const RECEIPT_KINDS = ReceiptKindSchema.options;
3673
- function readReceiptKindFlag(args) {
3674
- const value = readFlag(args, "--kind");
3675
- if (value === undefined)
3676
- return undefined;
3677
- if (ReceiptKindSchema.safeParse(value).success)
3678
- return value;
3679
- throw new Error(`--kind must be one of ${RECEIPT_KINDS.join(", ")}`);
3680
- }
3681
3097
  function formatTokenExpiryLine(config) {
3682
3098
  const tokenStatus = getTokenExpiryStatus(config);
3683
3099
  if (tokenStatus.status === "valid")