@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/README.md +12 -3
- package/dist/chatgpt-browser.d.ts +6 -3
- package/dist/chatgpt-browser.js +131 -19
- package/dist/chatgpt-browser.js.map +1 -1
- package/dist/cli-args.d.ts +52 -0
- package/dist/cli-args.js +332 -0
- package/dist/cli-args.js.map +1 -0
- package/dist/cli-help.d.ts +20 -0
- package/dist/cli-help.js +259 -0
- package/dist/cli-help.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +88 -592
- package/dist/cli.js.map +1 -1
- package/dist/store.d.ts +5 -1
- package/dist/store.js +48 -13
- package/dist/store.js.map +1 -1
- package/package.json +1 -1
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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")
|