@youdie006/prodex 0.16.1 → 0.16.3

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.
Files changed (64) hide show
  1. package/dist/banner.js +0 -1
  2. package/dist/bundle.js +0 -1
  3. package/dist/chatgpt-browser.js +0 -1
  4. package/dist/cli-args.js +0 -1
  5. package/dist/cli-help.js +5 -5
  6. package/dist/cli-ledger.js +0 -1
  7. package/dist/cli-pro.js +24 -13
  8. package/dist/cli-server.js +0 -1
  9. package/dist/cli-shared.js +0 -1
  10. package/dist/cli.js +0 -1
  11. package/dist/config.js +0 -1
  12. package/dist/http-mcp.js +6 -2
  13. package/dist/index.js +0 -1
  14. package/dist/mcp-tools.js +0 -1
  15. package/dist/mcp.js +15 -2
  16. package/dist/registry.js +0 -1
  17. package/dist/repo-write.js +0 -1
  18. package/dist/repo.js +0 -1
  19. package/dist/safe-file.js +0 -1
  20. package/dist/schema.js +0 -1
  21. package/dist/store.js +24 -2
  22. package/package.json +1 -1
  23. package/dist/banner.d.ts +0 -5
  24. package/dist/banner.js.map +0 -1
  25. package/dist/bundle.d.ts +0 -15
  26. package/dist/bundle.js.map +0 -1
  27. package/dist/chatgpt-browser.d.ts +0 -252
  28. package/dist/chatgpt-browser.js.map +0 -1
  29. package/dist/cli-args.d.ts +0 -54
  30. package/dist/cli-args.js.map +0 -1
  31. package/dist/cli-help.d.ts +0 -20
  32. package/dist/cli-help.js.map +0 -1
  33. package/dist/cli-ledger.d.ts +0 -22
  34. package/dist/cli-ledger.js.map +0 -1
  35. package/dist/cli-pro.d.ts +0 -157
  36. package/dist/cli-pro.js.map +0 -1
  37. package/dist/cli-server.d.ts +0 -42
  38. package/dist/cli-server.js.map +0 -1
  39. package/dist/cli-shared.d.ts +0 -68
  40. package/dist/cli-shared.js.map +0 -1
  41. package/dist/cli.d.ts +0 -14
  42. package/dist/cli.js.map +0 -1
  43. package/dist/config.d.ts +0 -110
  44. package/dist/config.js.map +0 -1
  45. package/dist/http-mcp.d.ts +0 -20
  46. package/dist/http-mcp.js.map +0 -1
  47. package/dist/index.d.ts +0 -8
  48. package/dist/index.js.map +0 -1
  49. package/dist/mcp-tools.d.ts +0 -395
  50. package/dist/mcp-tools.js.map +0 -1
  51. package/dist/mcp.d.ts +0 -57
  52. package/dist/mcp.js.map +0 -1
  53. package/dist/registry.d.ts +0 -8
  54. package/dist/registry.js.map +0 -1
  55. package/dist/repo-write.d.ts +0 -47
  56. package/dist/repo-write.js.map +0 -1
  57. package/dist/repo.d.ts +0 -28
  58. package/dist/repo.js.map +0 -1
  59. package/dist/safe-file.d.ts +0 -25
  60. package/dist/safe-file.js.map +0 -1
  61. package/dist/schema.d.ts +0 -402
  62. package/dist/schema.js.map +0 -1
  63. package/dist/store.d.ts +0 -164
  64. package/dist/store.js.map +0 -1
package/dist/banner.js CHANGED
@@ -44,4 +44,3 @@ export function shouldColorize(env = process.env, isTty = process.stdout.isTTY)
44
44
  return env.FORCE_COLOR !== "0";
45
45
  return Boolean(isTty);
46
46
  }
47
- //# sourceMappingURL=banner.js.map
package/dist/bundle.js CHANGED
@@ -27,4 +27,3 @@ export async function buildDryRunBundle(root, input) {
27
27
  created_at: nowIso()
28
28
  };
29
29
  }
30
- //# sourceMappingURL=bundle.js.map
@@ -1873,4 +1873,3 @@ function hasChromeLikeVersion(command) {
1873
1873
  function sleep(ms) {
1874
1874
  return new Promise((resolve) => setTimeout(resolve, ms));
1875
1875
  }
1876
- //# sourceMappingURL=chatgpt-browser.js.map
package/dist/cli-args.js CHANGED
@@ -357,4 +357,3 @@ export function printHelpIfRequested(args, command, stdout, printHelp, options =
357
357
  printHelp(stdout);
358
358
  return true;
359
359
  }
360
- //# sourceMappingURL=cli-args.js.map
package/dist/cli-help.js CHANGED
@@ -31,12 +31,12 @@ Ask / consult commands:
31
31
 
32
32
  Bridge ledger (durable tasks/results/receipts/sessions under .bridge/):
33
33
  prodex init [--cwd /absolute/path/to/repo]
34
- prodex tasks create [--cwd /absolute/path/to/repo] --title "Title" --prompt "Prompt"
34
+ prodex tasks create [--cwd /absolute/path/to/repo] --title "Title" --prompt "Prompt" [--repo-id id] [--file path]
35
35
  prodex tasks list [--status new|claimed|done|blocked] [--cwd /absolute/path/to/repo] [--json]
36
36
  prodex tasks show <task-id|latest> [--cwd /absolute/path/to/repo]
37
37
  prodex tasks claim <task-id> [--cwd /absolute/path/to/repo] [--by codex]
38
38
  prodex tasks complete <task-id> [--cwd /absolute/path/to/repo] --summary "Summary" [--command "npm test"] [--artifact .bridge/artifacts/results/name.md=text]
39
- prodex tasks block <task-id> [--cwd /absolute/path/to/repo] --summary "Summary" [--code code] [--next-step "Next step"] [--retryable]
39
+ prodex tasks block <task-id> [--cwd /absolute/path/to/repo] --summary "Summary" [--code code] [--next-step "Next step"] [--retryable] [--command "npm test"]
40
40
  prodex results show <task-id|latest> [--cwd /absolute/path/to/repo]
41
41
  prodex results artifact <task-id|latest> [artifact-path] [--cwd /absolute/path/to/repo]
42
42
  prodex results reseal <task-id|latest> --confirm-current-result [--cwd /absolute/path/to/repo]
@@ -45,6 +45,7 @@ Bridge ledger (durable tasks/results/receipts/sessions under .bridge/):
45
45
  prodex receipts rotate-key [--cwd /absolute/path/to/repo]
46
46
  prodex sessions list [--status preview|running|done|blocked] [--cwd /absolute/path/to/repo] [--json]
47
47
  prodex sessions show <session-id|latest> [--cwd /absolute/path/to/repo]
48
+ prodex sessions cancel <session-id|latest> [--cwd /absolute/path/to/repo]
48
49
 
49
50
  Agent / MCP integration:
50
51
  prodex mcp [--cwd /absolute/path/to/repo]
@@ -200,12 +201,12 @@ export function printTasksHelp(stdout) {
200
201
  stdout(`prodex tasks
201
202
 
202
203
  Commands:
203
- prodex tasks create [--cwd /absolute/path/to/repo] --title "Title" --prompt "Prompt"
204
+ prodex tasks create [--cwd /absolute/path/to/repo] --title "Title" --prompt "Prompt" [--repo-id id] [--file path]
204
205
  prodex tasks list [--status new|claimed|done|blocked] [--cwd /absolute/path/to/repo] [--json]
205
206
  prodex tasks show <task-id|latest> [--cwd /absolute/path/to/repo]
206
207
  prodex tasks claim <task-id> [--cwd /absolute/path/to/repo] [--by codex]
207
208
  prodex tasks complete <task-id> [--cwd /absolute/path/to/repo] --summary "Summary" [--command "npm test"] [--artifact .bridge/artifacts/results/name.md=text]
208
- prodex tasks block <task-id> [--cwd /absolute/path/to/repo] --summary "Summary" [--code code] [--next-step "Next step"] [--retryable]`);
209
+ prodex tasks block <task-id> [--cwd /absolute/path/to/repo] --summary "Summary" [--code code] [--next-step "Next step"] [--retryable] [--command "npm test"]`);
209
210
  }
210
211
  export function printResultsHelp(stdout) {
211
212
  stdout(`prodex results
@@ -275,4 +276,3 @@ Persist defaults with \`${cli} setup${sourceCliOption}\`; per-ask flags override
275
276
  Use \`${cli} pro ask\` for dry-run/manual previews.
276
277
  \`${cli} pro browser ask${sourceCliOption}\` always attempts an explicit visible-browser send.`);
277
278
  }
278
- //# sourceMappingURL=cli-help.js.map
@@ -378,4 +378,3 @@ export async function listResultsForInspection(store) {
378
378
  export async function listRawResultsForInspection(store) {
379
379
  return store.listResultsReadOnly();
380
380
  }
381
- //# sourceMappingURL=cli-ledger.js.map
package/dist/cli-pro.js CHANGED
@@ -215,16 +215,22 @@ export async function runProCommand(rest, io, runCliFn) {
215
215
  });
216
216
  return 0;
217
217
  }
218
- const opened = openChatGptBrowser({
219
- port,
220
- profileDir,
221
- url: loginUrl
222
- });
223
- await assertBrowserLaunchStayedAlive(opened, launchTimeoutMs);
218
+ // If the dedicated Chrome is already reachable on this port, do NOT spawn
219
+ // again: Chrome's singleton would just open ANOTHER window (the recurring
220
+ // "extra windows" problem, which then blocks sends as
221
+ // ambiguous_chatgpt_tabs). Reuse the running instance instead.
222
+ const alreadyRunning = (await getChatGptBrowserStatus({ port })).reachable;
223
+ const opened = alreadyRunning
224
+ ? { profileDir: profileDir ?? defaultChatGptProfileDir(), port }
225
+ : openChatGptBrowser({ port, profileDir, url: loginUrl });
226
+ if (!alreadyRunning) {
227
+ await assertBrowserLaunchStayedAlive(opened, launchTimeoutMs);
228
+ }
224
229
  // Remember this launch so ask auto-recovery reuses the same profile.
225
230
  await recordBrowserLoginLaunch({ profile_dir: opened.profileDir, port: opened.port });
226
231
  printBrowserLoginGuide(io.stdout, {
227
- opened: true,
232
+ opened: !alreadyRunning,
233
+ reused: alreadyRunning,
228
234
  loginUrl,
229
235
  profileDir: opened.profileDir,
230
236
  port: opened.port,
@@ -481,8 +487,9 @@ export async function runAskProCommand(rest, io) {
481
487
  throw new Error("--json applies to the visible-browser send output; the dry-run preview does not support it.");
482
488
  }
483
489
  const prompt = parsedAskPro.promptParts.join(" ").trim();
484
- if (!prompt)
485
- throw new Error("ask-pro requires a prompt");
490
+ if (!prompt) {
491
+ throw new Error('ask-pro requires a prompt. Example: prodex ask "Explain this stack trace" (or pipe input: git diff | prodex ask --stdin "review this diff").');
492
+ }
486
493
  let promptText = prompt;
487
494
  if (parsedAskPro.optionArgs.includes("--stdin")) {
488
495
  // Unix ergonomics: `git diff | prodex ask --stdin "review this"`.
@@ -1090,6 +1097,7 @@ export async function listConsultListEntries(store, options = { readOnly: true }
1090
1097
  return entries;
1091
1098
  }
1092
1099
  export function printBrowserLoginGuide(stdout, input) {
1100
+ const windowAvailable = input.opened || input.reused === true;
1093
1101
  const loginCommand = formatBrowserLoginCommand(input.sourceCli, input.commandOptions);
1094
1102
  const runtimeCommandOptions = {
1095
1103
  ...(input.commandOptions?.cwd ? { cwd: input.commandOptions.cwd } : {}),
@@ -1098,10 +1106,14 @@ export function printBrowserLoginGuide(stdout, input) {
1098
1106
  const checkCommand = formatBrowserCheckCommand(input.sourceCli, runtimeCommandOptions);
1099
1107
  const smokeCommand = formatBrowserSmokeCommand(input.sourceCli, runtimeCommandOptions);
1100
1108
  stdout("ChatGPT Pro browser login");
1101
- stdout(input.opened ? "Opened the dedicated Chrome window for ChatGPT." : "Dry run: no browser was opened.");
1109
+ stdout(input.reused
1110
+ ? "Chrome is already running for ChatGPT - reusing it (no new window opened)."
1111
+ : input.opened
1112
+ ? "Opened the dedicated Chrome window for ChatGPT."
1113
+ : "Dry run: no browser was opened.");
1102
1114
  stdout("");
1103
1115
  stdout("Steps:");
1104
- if (input.opened) {
1116
+ if (windowAvailable) {
1105
1117
  stdout(`1. Log in manually at ${input.loginUrl} in the dedicated Chrome window.`);
1106
1118
  stdout("2. If ChatGPT asks for captcha, Cloudflare/human verification, permission, or account verification, complete it in the browser.");
1107
1119
  stdout("3. For usage limit, message limit, model limit, or rate limit, wait for the reset or choose an available model in the browser.");
@@ -1123,7 +1135,7 @@ export function printBrowserLoginGuide(stdout, input) {
1123
1135
  stdout("");
1124
1136
  stdout(`Profile: ${input.profileDir}`);
1125
1137
  stdout(`Debug: http://127.0.0.1:${input.port}`);
1126
- if (input.opened) {
1138
+ if (windowAvailable) {
1127
1139
  stdout("You can close this Chrome window after check/smoke or when you are done. The dedicated profile is reused next time.");
1128
1140
  }
1129
1141
  else {
@@ -1390,4 +1402,3 @@ export function orphanConsultResultError(taskId) {
1390
1402
  export function sleep(ms) {
1391
1403
  return new Promise((resolve) => setTimeout(resolve, ms));
1392
1404
  }
1393
- //# sourceMappingURL=cli-pro.js.map
@@ -396,4 +396,3 @@ export async function assertGitignoreTargetSafe(filePath) {
396
396
  throw error;
397
397
  }
398
398
  }
399
- //# sourceMappingURL=cli-server.js.map
@@ -204,4 +204,3 @@ export function computeSendPacingWaitMs(lastSendAtMs, nowMs, intervalMs) {
204
204
  return 0; // clock skew / bad marker: do not wait
205
205
  return elapsed >= intervalMs ? 0 : intervalMs - elapsed;
206
206
  }
207
- //# sourceMappingURL=cli-shared.js.map
package/dist/cli.js CHANGED
@@ -1341,4 +1341,3 @@ if (isDirectCliInvocation()) {
1341
1341
  process.exitCode = 1;
1342
1342
  });
1343
1343
  }
1344
- //# sourceMappingURL=cli.js.map
package/dist/config.js CHANGED
@@ -316,4 +316,3 @@ async function assertDirectoryHandle(handle, label) {
316
316
  throw new Error(`${label} must be a real directory`);
317
317
  }
318
318
  }
319
- //# sourceMappingURL=config.js.map
package/dist/http-mcp.js CHANGED
@@ -23,7 +23,12 @@ export async function startHttpMcpServer(options) {
23
23
  return;
24
24
  }
25
25
  if (!isAuthorized(req, requestUrl, token, options.tokenExpiresAt)) {
26
- writeJson(res, 401, { error: "unauthorized" });
26
+ // Same 401 for missing/wrong/expired (no oracle), but tell the caller
27
+ // HOW to authorize without leaking anything token-specific.
28
+ writeJson(res, 401, {
29
+ error: "unauthorized",
30
+ hint: "Provide a valid token via `?prodex_token=<token>` or `Authorization: Bearer <token>`. If your token expired, regenerate the profile with `prodex setup` and re-read the URL from `prodex status`."
31
+ });
27
32
  return;
28
33
  }
29
34
  if (req.method === "POST") {
@@ -233,4 +238,3 @@ function writeJson(res, status, value) {
233
238
  res.writeHead(status, { "content-type": "application/json" });
234
239
  res.end(`${JSON.stringify(value)}\n`);
235
240
  }
236
- //# sourceMappingURL=http-mcp.js.map
package/dist/index.js CHANGED
@@ -6,4 +6,3 @@ export * from "./chatgpt-browser.js";
6
6
  export * from "./config.js";
7
7
  export * from "./http-mcp.js";
8
8
  export * from "./mcp-tools.js";
9
- //# sourceMappingURL=index.js.map
package/dist/mcp-tools.js CHANGED
@@ -199,4 +199,3 @@ function assertMcpFiles(files) {
199
199
  assertMcpTextField(file.path, `files[${index}].path`, MAX_MCP_SHORT_TEXT_BYTES);
200
200
  }
201
201
  }
202
- //# sourceMappingURL=mcp-tools.js.map
package/dist/mcp.js CHANGED
@@ -279,9 +279,22 @@ export class LimitedStdioServerTransport {
279
279
  }
280
280
  const line = this.buffer.toString("utf8", 0, newlineIndex).replace(/\r$/, "");
281
281
  this.buffer = this.buffer.subarray(newlineIndex + 1);
282
- const message = JSONRPCMessageSchema.parse(JSON.parse(line));
282
+ if (line.trim().length === 0)
283
+ continue;
284
+ let message;
285
+ try {
286
+ message = JSONRPCMessageSchema.parse(JSON.parse(line));
287
+ }
288
+ catch (error) {
289
+ // A single malformed frame is recoverable: report it and keep
290
+ // processing the rest of the buffer instead of tearing down the whole
291
+ // session (which would also drop any valid pipelined messages after
292
+ // it). This matches the SDK's StdioServerTransport. Only the oversize
293
+ // guards above stay fatal.
294
+ this.onerror?.(error instanceof Error ? error : new Error(String(error)));
295
+ continue;
296
+ }
283
297
  this.onmessage?.(message);
284
298
  }
285
299
  }
286
300
  }
287
- //# sourceMappingURL=mcp.js.map
package/dist/registry.js CHANGED
@@ -102,4 +102,3 @@ async function registerBridgeRootInner(root) {
102
102
  // Advisory registry - never let it fail a bridge operation.
103
103
  }
104
104
  }
105
- //# sourceMappingURL=registry.js.map
@@ -424,4 +424,3 @@ function buildSimpleDiff(repoPath, before, after) {
424
424
  ...after.replace(/\r\n/g, "\n").split("\n").map((line) => `+${line}`)
425
425
  ].join("\n");
426
426
  }
427
- //# sourceMappingURL=repo-write.js.map
package/dist/repo.js CHANGED
@@ -484,4 +484,3 @@ export async function assertResolvedRepoPathAllowed(root, resolved, repoPath) {
484
484
  function isErrorCode(error, code) {
485
485
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
486
486
  }
487
- //# sourceMappingURL=repo.js.map
package/dist/safe-file.js CHANGED
@@ -292,4 +292,3 @@ function procFdPath(fd) {
292
292
  function isMissingFileError(error) {
293
293
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
294
294
  }
295
- //# sourceMappingURL=safe-file.js.map
package/dist/schema.js CHANGED
@@ -106,4 +106,3 @@ export function makeBridgeId(prefix, title, date = new Date()) {
106
106
  const stamp = date.toISOString().replace(/[-:]/g, "").replace(/\..+$/, "").replace("T", "_");
107
107
  return `${prefix}_${stamp}_${slugify(title)}`;
108
108
  }
109
- //# sourceMappingURL=schema.js.map
package/dist/store.js CHANGED
@@ -273,8 +273,26 @@ export class BridgeStore {
273
273
  }
274
274
  async listFinalizedResultsReadOnly() {
275
275
  const results = await this.listResultsReadOnly();
276
+ if (results.length === 0)
277
+ return results;
278
+ if (!(await this.hasReadyStorageDirReadOnly("receipts"))) {
279
+ throw untrustedResultError(this.root, results[0].task_id, "has no trusted task_completed receipt");
280
+ }
281
+ // Read receipts ONCE and index them by task_id instead of re-reading and
282
+ // re-parsing the whole receipts directory for every result, which was
283
+ // O(results x receipts) on the completion/list hot path.
284
+ const completionReceiptsByTask = new Map();
285
+ for (const receipt of await this.readAll("receipts", parseReceiptRecord, { cleanupTempHardLinks: false })) {
286
+ if (receipt.kind !== "task_completed" || !receipt.task_id)
287
+ continue;
288
+ const existing = completionReceiptsByTask.get(receipt.task_id);
289
+ if (existing)
290
+ existing.push(receipt);
291
+ else
292
+ completionReceiptsByTask.set(receipt.task_id, [receipt]);
293
+ }
276
294
  for (const result of results) {
277
- await this.assertTrustedTaskCompletionReceiptReadOnly(result.task_id, result);
295
+ await this.assertReceiptsTrustCompletion(result.task_id, result, completionReceiptsByTask.get(result.task_id) ?? []);
278
296
  }
279
297
  return results;
280
298
  }
@@ -505,6 +523,11 @@ export class BridgeStore {
505
523
  throw untrustedResultError(this.root, taskId, "has no trusted task_completed receipt");
506
524
  }
507
525
  const receipts = (await this.readAll("receipts", parseReceiptRecord, { cleanupTempHardLinks: false })).filter((receipt) => receipt.kind === "task_completed" && receipt.task_id === taskId);
526
+ await this.assertReceiptsTrustCompletion(taskId, result, receipts);
527
+ }
528
+ // Given the already-loaded task_completed receipts for one task, accept if any
529
+ // one is locally trusted and its result digest matches; otherwise throw.
530
+ async assertReceiptsTrustCompletion(taskId, result, receipts) {
508
531
  if (receipts.length === 0) {
509
532
  throw untrustedResultError(this.root, taskId, "has no trusted task_completed receipt");
510
533
  }
@@ -1542,4 +1565,3 @@ function redactReceiptForDisplay(receipt, integrityStatus) {
1542
1565
  }
1543
1566
  return ReceiptSchema.parse({ ...receipt, metadata });
1544
1567
  }
1545
- //# sourceMappingURL=store.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@youdie006/prodex",
3
- "version": "0.16.1",
3
+ "version": "0.16.3",
4
4
  "description": "Local receipt bus for coordinating Codex execution with ChatGPT Pro/Projects consultation.",
5
5
  "author": "youdie006",
6
6
  "license": "MIT",
package/dist/banner.d.ts DELETED
@@ -1,5 +0,0 @@
1
- export interface BannerOptions {
2
- color?: boolean;
3
- }
4
- export declare function renderBanner(options?: BannerOptions): string;
5
- export declare function shouldColorize(env?: NodeJS.ProcessEnv, isTty?: boolean): boolean;
@@ -1 +0,0 @@
1
- {"version":3,"file":"banner.js","sourceRoot":"","sources":["../src/banner.ts"],"names":[],"mappings":"AAKA,kFAAkF;AAClF,8EAA8E;AAC9E,oCAAoC;AACpC,MAAM,QAAQ,GAAG;IACf,2BAA2B;IAC3B,2BAA2B;IAC3B,2BAA2B;IAC3B,2BAA2B;IAC3B,2BAA2B;IAC3B,2BAA2B;CAC5B,CAAC;AAEF,MAAM,QAAQ,GAAG;IACf,0BAA0B;IAC1B,0BAA0B;IAC1B,0BAA0B;IAC1B,0BAA0B;IAC1B,0BAA0B;IAC1B,2BAA2B;CAC5B,CAAC;AAEF,MAAM,GAAG,GAAG,GAAG,CAAC;AAChB,MAAM,GAAG,GAAG,GAAG,GAAG,kBAAkB,CAAC,CAAC,gBAAgB;AACtD,MAAM,GAAG,GAAG,GAAG,GAAG,oBAAoB,CAAC,CAAC,mDAAmD;AAC3F,MAAM,GAAG,GAAG,GAAG,GAAG,oBAAoB,CAAC,CAAC,UAAU;AAClD,MAAM,KAAK,GAAG,GAAG,GAAG,KAAK,CAAC;AAE1B,MAAM,OAAO,GAAG,gEAAgE,CAAC;AAEjF,MAAM,UAAU,YAAY,CAAC,UAAyB,EAAE;IACtD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC;IACpC,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,GAAG,EAAE,CAAC,CAAC;IACzE,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACd,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,GAAG,OAAO,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC;IACjE,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACzB,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,cAAc,CAAC,MAAyB,OAAO,CAAC,GAAG,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK;IAC/F,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,IAAI,GAAG,CAAC,QAAQ,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IACpE,uHAAuH;IACvH,IAAI,GAAG,CAAC,WAAW,KAAK,SAAS,IAAI,GAAG,CAAC,WAAW,KAAK,EAAE;QAAE,OAAO,GAAG,CAAC,WAAW,KAAK,GAAG,CAAC;IAC5F,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC;AACxB,CAAC"}
package/dist/bundle.d.ts DELETED
@@ -1,15 +0,0 @@
1
- import { type BridgeFile } from "./schema.js";
2
- export interface DryRunBundleInput {
3
- prompt: string;
4
- files: string[];
5
- }
6
- export interface DryRunBundle {
7
- schema_version: 1;
8
- id: string;
9
- mode: "manual_copy";
10
- prompt: string;
11
- files: BridgeFile[];
12
- text: string;
13
- created_at: string;
14
- }
15
- export declare function buildDryRunBundle(root: string, input: DryRunBundleInput): Promise<DryRunBundle>;
@@ -1 +0,0 @@
1
- {"version":3,"file":"bundle.js","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,cAAc,EAAmB,MAAM,aAAa,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAiBzC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAY,EAAE,KAAwB;IAC5E,MAAM,QAAQ,GAAa;QACzB,0BAA0B;QAC1B,EAAE;QACF,qCAAqC;QACrC,EAAE;QACF,WAAW;QACX,EAAE;QACF,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE;KACpB,CAAC;IACF,MAAM,KAAK,GAAiB,EAAE,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC/B,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC;QAClE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QAC/F,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,YAAY,IAAI,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO;QACL,cAAc,EAAE,cAAc;QAC9B,EAAE,EAAE,YAAY,CAAC,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACvE,IAAI,EAAE,aAAa;QACnB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,KAAK;QACL,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;QACzB,UAAU,EAAE,MAAM,EAAE;KACrB,CAAC;AACJ,CAAC"}
@@ -1,252 +0,0 @@
1
- export interface ChatGptBrowserOptions {
2
- port?: number;
3
- profileDir?: string;
4
- url?: string;
5
- }
6
- export interface ChatGptBrowserLaunch {
7
- command: string;
8
- args: string[];
9
- profileDir: string;
10
- port: number;
11
- waitForEarlyExit: (timeoutMs?: number) => Promise<ChatGptBrowserEarlyExit | undefined>;
12
- }
13
- export interface ChatGptBrowserEarlyExit {
14
- code?: number | null;
15
- signal?: NodeJS.Signals | null;
16
- error?: string;
17
- }
18
- export interface ChatGptBrowserStatus {
19
- reachable: boolean;
20
- loggedInLikely: boolean;
21
- hasComposer: boolean;
22
- visibilityState?: string;
23
- url?: string;
24
- title?: string;
25
- modelHints: string[];
26
- blocker?: {
27
- code: string;
28
- message: string;
29
- retryable: boolean;
30
- next_step?: string;
31
- };
32
- }
33
- export declare class ChatGptBrowserBlockerError extends Error {
34
- readonly blocker: NonNullable<ChatGptBrowserStatus["blocker"]>;
35
- constructor(blocker: NonNullable<ChatGptBrowserStatus["blocker"]>);
36
- }
37
- export type ChatGptReasoningEffort = "즉시" | "중간" | "높음" | "매우 높음";
38
- export type ChatGptProMode = "기본" | "확장";
39
- /** Normalize a CLI reasoning-effort value onto the exact ChatGPT menu label. */
40
- export declare function parseReasoningEffort(raw: string): ChatGptReasoningEffort;
41
- /** Normalize a CLI Pro sub-mode value onto the exact ChatGPT menu label. */
42
- export declare function parseProMode(raw: string): ChatGptProMode;
43
- export declare const DEFAULT_CDP_PORT = 9333;
44
- /**
45
- * Stability policy for the answer poll loop. Returns true once the observed
46
- * (answer, generating) stream should be accepted as final:
47
- * - normal answers: two consecutive identical, non-generating polls;
48
- * - answers whose tail looks like the streaming caret: six additional
49
- * confirmations (~6s at the 1s poll cadence), so a lingering caret gets
50
- * dropped by ChatGPT's finalization re-render first, while an answer that
51
- * genuinely ends with an underscore is still accepted after the extra wait.
52
- */
53
- export declare function createChatGptAnswerStabilityTracker(): (answer: string, generating: boolean) => boolean;
54
- /**
55
- * Resolve the Chrome DevTools port: explicit flag > PRODEX_CDP_PORT env >
56
- * default 9333. The env var exists so a non-default port picked at login does
57
- * not have to be repeated as --port on every later command.
58
- */
59
- export declare function resolveCdpPort(explicit?: number): number;
60
- export type SendChatGptProgressPhase = "connecting" | "tab_ready" | "selecting" | "sent" | "waiting" | "answered";
61
- export interface SendChatGptProgressEvent {
62
- phase: SendChatGptProgressPhase;
63
- elapsedMs: number;
64
- detail?: string;
65
- }
66
- export interface SendChatGptPromptOptions {
67
- port?: number;
68
- prompt: string;
69
- targetUrl?: string;
70
- timeoutMs?: number;
71
- /** Switch into this sidebar project (by visible name) before sending. */
72
- project?: string;
73
- /** Create a new project with this name before sending. */
74
- projectNew?: string;
75
- /** Model to select in the composer picker, e.g. "Pro" or "GPT-5.5". */
76
- model?: string;
77
- /** Pro sub-mode, used when model is Pro. */
78
- proMode?: ChatGptProMode;
79
- /** Reasoning effort, used for non-Pro models. */
80
- effort?: ChatGptReasoningEffort;
81
- /**
82
- * Navigate the tab to a fresh chat (chatgpt.com root) before sending. Long
83
- * accumulated threads eventually break prompt-acceptance detection
84
- * (measured live), so agent loops and debates should send each consult
85
- * into a fresh chat. Incompatible with targetUrl.
86
- */
87
- newChat?: boolean;
88
- /** Progress callback so long sends can report phase + elapsed instead of staying silent. */
89
- onProgress?: (event: SendChatGptProgressEvent) => void;
90
- }
91
- export interface SendChatGptPromptResult {
92
- url: string;
93
- title: string;
94
- answer: string;
95
- modelHints: string[];
96
- warnings: string[];
97
- }
98
- interface ChatGptAnswerState {
99
- title: string;
100
- url: string;
101
- answer: string;
102
- modelHints: string[];
103
- generating: boolean;
104
- assistantMessageCount: number;
105
- userMessageCount: number;
106
- textSample: string;
107
- blockerTextSample: string;
108
- blockerScanTextSample?: string;
109
- visibleButtonLabels: string[];
110
- }
111
- export interface ChatGptPageTextState {
112
- textSample: string;
113
- blockerTextSample?: string;
114
- blockerScanTextSample?: string;
115
- visibleButtonLabels: string[];
116
- }
117
- export declare const CHATGPT_RUNTIME_BLOCKER_TEXT_EXCLUDED_ANCESTORS = "[data-message-author-role],script,style,noscript,[aria-hidden=\"true\"],div[role=\"textbox\"],textarea,[contenteditable=\"true\"]";
118
- export declare const CHATGPT_BLOCKER_SCAN_EXCLUDED_ANCESTORS = "[data-message-author-role],script,style,noscript,[aria-hidden=\"true\"],div[role=\"textbox\"],textarea,[contenteditable=\"true\"],nav,aside,[role=\"navigation\"]";
119
- export declare const CHATGPT_COMPOSER_CANDIDATE_EXCLUDED_ANCESTORS = "[data-message-author-role],script,style,noscript,[aria-hidden=\"true\"]";
120
- export declare const PRODEX_ACTIVE_COMPOSER_ATTRIBUTE = "data-prodex-active-composer";
121
- export interface DevtoolsPage {
122
- id?: string;
123
- type: string;
124
- url: string;
125
- title: string;
126
- webSocketDebuggerUrl: string;
127
- }
128
- export declare const CHATGPT_STREAMING_SELECTOR = "[data-testid=\"stop-button\"],[data-message-author-role=\"assistant\"][aria-busy=\"true\"],[data-message-author-role=\"assistant\"] [aria-busy=\"true\"]";
129
- export declare function defaultChatGptProfileDir(): string;
130
- export interface BrowserLoginLaunchRecord {
131
- profile_dir: string;
132
- port: number;
133
- }
134
- /**
135
- * Remember how the last login launched the browser (profile dir + port) so
136
- * auto-recovery relaunches the SAME profile instead of the default one - a
137
- * custom-profile user must never be silently sent to a different account.
138
- * Best-effort: failures never break the login.
139
- */
140
- export declare function recordBrowserLoginLaunch(record: BrowserLoginLaunchRecord): Promise<void>;
141
- export declare function readLastBrowserLoginLaunch(): Promise<Partial<BrowserLoginLaunchRecord> | undefined>;
142
- export declare function buildChromeLaunchArgs(options: Required<ChatGptBrowserOptions>): string[];
143
- export declare function inferLoggedInLikely(text: string, visibleButtonLabels?: string[]): boolean;
144
- export declare function isUsableChatGptAnswer(answer: string): boolean;
145
- export declare function hasFreshChatGptAnswer(previousAssistantMessageCount: number, state: Pick<ChatGptAnswerState, "answer" | "assistantMessageCount" | "generating">): boolean;
146
- export interface AcceptanceTimeoutContext {
147
- timeoutMs: number;
148
- composerStillHasText: boolean;
149
- submitButtonFound: boolean;
150
- }
151
- export declare function acceptanceTimeoutError(ctx: AcceptanceTimeoutContext): Error;
152
- export declare function hasPartialChatGptAnswer(previousAssistantMessageCount: number, state: Pick<ChatGptAnswerState, "answer" | "assistantMessageCount">): boolean;
153
- /**
154
- * True when the tab is on a fresh, empty ChatGPT chat: the root URL (not a
155
- * /c/<id> conversation) with zero messages. Used after a --new-chat navigation
156
- * to confirm the page actually reached the fresh chat before the answer-count
157
- * baseline is captured - a lingering old thread (slow navigation) has a /c/ URL
158
- * or non-zero counts and would poison the baseline.
159
- */
160
- export declare function isFreshChatGptPage(state: {
161
- url: string;
162
- assistantMessageCount: number;
163
- userMessageCount: number;
164
- }): boolean;
165
- export declare function hasChatGptPromptAcceptance(previous: Pick<ChatGptAnswerState, "assistantMessageCount" | "userMessageCount">, state: Pick<ChatGptAnswerState, "assistantMessageCount" | "generating" | "userMessageCount">): boolean;
166
- export declare function chatGptBusyBlocker(generating: boolean): ChatGptBrowserStatus["blocker"] | undefined;
167
- export declare function isLikelyChatGptSubmitButton(label: string, dataTestId: string | null): boolean;
168
- export declare function isLikelyChatGptGeneratingControl(label: string): boolean;
169
- export declare function detectChatGptBlocker(text: string, visibleButtonLabels?: string[]): ChatGptBrowserStatus["blocker"] | undefined;
170
- export declare function detectChatGptPageBlocker(state: ChatGptPageTextState): ChatGptBrowserStatus["blocker"] | undefined;
171
- export declare function inferChatGptPageLoggedInLikely(state: ChatGptPageTextState): boolean;
172
- export declare function chatGptBlockerErrorFromAnswerState(state: {
173
- textSample: string;
174
- blockerTextSample?: string;
175
- blockerScanTextSample?: string;
176
- visibleButtonLabels: string[];
177
- }): string | undefined;
178
- export declare function chatGptBlockerFromAnswerState(state: {
179
- textSample: string;
180
- blockerTextSample?: string;
181
- blockerScanTextSample?: string;
182
- visibleButtonLabels: string[];
183
- }): ChatGptBrowserStatus["blocker"] | undefined;
184
- export declare function computePromptAcceptanceDeadline(timeoutMs: number, startedAt: number): number;
185
- export declare function computePageDiscoveryTimeout(timeoutMs: number): number;
186
- export declare function normalizeChatGptTargetUrl(value: string): string;
187
- export declare function chatGptUrlsReferToSameTarget(currentUrl: string, expectedUrl: string): boolean;
188
- export declare function selectChatGptPage(pages: DevtoolsPage[], targetUrl?: string, visibilityByPage?: Map<string, string>): DevtoolsPage | undefined;
189
- export declare function chatGptPageSelectionBlocker(pages: DevtoolsPage[], targetUrl?: string, visibilityByPage?: Map<string, string>): ChatGptBrowserStatus["blocker"] | undefined;
190
- export declare function assertVisibleChatGptTab(visibilityState: string | undefined, url: string, targetUrl?: string): void;
191
- export declare function isTabActivationEnabled(env?: NodeJS.ProcessEnv): boolean;
192
- export declare function assertChatGptTargetUrlMatches(currentUrl: string, targetUrl: string): void;
193
- export declare function assertChatGptTargetTabAvailable(targetUrl: string): void;
194
- export declare function assertChatGptPageAvailable(): never;
195
- export declare function assertChatGptReadyForPrompt(loggedInLikely: boolean, hasComposer: boolean): void;
196
- export declare function chatGptVisibilityBlocker(visibilityState: string | undefined, url: string | undefined): ChatGptBrowserStatus["blocker"] | undefined;
197
- /**
198
- * Environment for the browser spawn. On Linux shells without DISPLAY or
199
- * WAYLAND_DISPLAY (common in WSL where a non-login shell does not export
200
- * them), a present WSLg X socket means a desktop is actually available -
201
- * inject DISPLAY=:0 so Chrome does not exit immediately with code 1.
202
- */
203
- export declare function browserLaunchEnv(platform?: NodeJS.Platform, env?: Record<string, string | undefined>, hasX0Socket?: () => boolean): Record<string, string | undefined>;
204
- export declare function openChatGptBrowser(options?: ChatGptBrowserOptions): ChatGptBrowserLaunch;
205
- export declare function getChatGptBrowserStatus(options?: {
206
- port?: number;
207
- timeoutMs?: number;
208
- }): Promise<ChatGptBrowserStatus>;
209
- export declare function menuOpenExpression(): string;
210
- export declare function menuClosedExpression(): string;
211
- export declare function menuItemLabelMatches(text: string, candidates: readonly string[]): boolean;
212
- export declare function menuItemPresentExpression(label: string | readonly string[]): string;
213
- export declare function modelButtonRectExpression(): string;
214
- export declare function menuItemRectExpression(label: string | readonly string[]): string;
215
- export declare function submenuItemRectExpression(label: string | readonly string[]): string;
216
- export declare function proRadioRectExpression(): string;
217
- export declare function proSubmenuExpanderRectExpression(): string;
218
- export declare function projectItemRectExpression(name: string): string;
219
- export declare function newProjectButtonRectExpression(): string;
220
- export declare function newProjectNameInputRectExpression(): string;
221
- export declare function newProjectNameInputVisibleExpression(): string;
222
- export declare function sendChatGptPrompt(options: SendChatGptPromptOptions): Promise<SendChatGptPromptResult>;
223
- export interface ChatGptModelOption {
224
- label: string;
225
- kind: "radio" | "submenu";
226
- checked: boolean;
227
- }
228
- export interface ListChatGptModelOptionsResult {
229
- url: string;
230
- options: ChatGptModelOption[];
231
- }
232
- export declare function modelMenuOptionsExpression(): string;
233
- export declare function listChatGptModelOptions(input?: {
234
- port?: number;
235
- timeoutMs?: number;
236
- }): Promise<ListChatGptModelOptionsResult>;
237
- export declare function resolveCdpTimeoutMs(explicit?: number): number;
238
- export declare const CHATGPT_THINKING_PLACEHOLDER_JS = "ansLines.length <= 1 && (/^(\uC0DD\uAC01\\s*\uC911|thinking)$/i.test(ansStripped) || /^thought (for|about)\\b.*$/i.test(ansStripped) || /\\d+\\s*s\\s*\uB3D9\uC548\\s*\uC0DD\uAC01\uD568$/.test(ansStripped) || /(^|\\s)(\uC0DD\uAC01\\s*\uC911|thinking)$/i.test(ansStripped))";
239
- export declare function statusExpression(): string;
240
- export declare function prepareComposerExpression(): string;
241
- export declare function composerTextStateExpression(expectedText?: string): string;
242
- export declare function submitExpression(): string;
243
- /**
244
- * Ordered browser candidates for the current platform: PATH binary names
245
- * first, then well-known absolute install locations (macOS app bundles,
246
- * Windows Program Files/LOCALAPPDATA, and Windows-host browsers under WSL).
247
- * WSL detection cannot rely on WSL_DISTRO_NAME alone: non-login shells
248
- * (measured live) may not carry it, so WSL_INTEROP and the kernel string are
249
- * probed too.
250
- */
251
- export declare function chromeCommandCandidates(platform?: NodeJS.Platform, env?: Record<string, string | undefined>, isWsl?: () => boolean): string[];
252
- export {};