@youdie006/prodex 0.16.25 → 0.16.27

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 CHANGED
@@ -240,7 +240,7 @@ prodex pro browser models
240
240
  ```
241
241
 
242
242
  - `--model` picks the composer model by its exact menu label. `Pro` is verified end-to-end. Models whose menu entry opens a submenu of variants (for example GPT-5.5) are rejected with a clear error instead of silently keeping the previous model; direct variant selection is planned.
243
- - `--pro-mode 기본|확장` selects the Pro sub-mode where the ChatGPT picker exposes one: sub-modes belong to the GPT-5.5 generation ("Pro Standard/Extended use GPT-5.5 Pro" per OpenAI docs), so with the GPT-5.6 generation selected the picker shows a single Pro and this flag fails with guidance. Any effective Pro selection (`--model Pro` or a sub-mode) raises the default `--timeout-ms` to 900000 - Pro reasoning routinely runs for many minutes (an explicit `--timeout-ms` always wins).
243
+ - `--pro-mode 기본|확장` selects the Pro sub-mode where the ChatGPT picker exposes one: sub-modes belong to the GPT-5.5 generation ("Pro Standard/Extended use GPT-5.5 Pro" per OpenAI docs), so with the GPT-5.6 generation selected the picker shows a single Pro and this flag fails with guidance. Any effective Pro selection (`--model Pro` or a sub-mode) raises the default `--timeout-ms` to 1200000 - Pro reasoning routinely runs for many minutes (an explicit `--timeout-ms` always wins).
244
244
  - `--effort 즉시|중간|높음|"매우 높음"` sets the reasoning effort. English aliases `instant`/`medium`/`high`/`max` are accepted. The effort options and Pro share one radio group in ChatGPT, so picking an effort switches the composer to the standard reasoning model and deselects Pro; for the same reason `--pro-mode` and `--effort` cannot be combined.
245
245
  - `--project "name"` enters an existing sidebar project before sending. `--project-new "name"` creates a new project (sidebar 새 프로젝트 popover, committed with Enter) and sends inside it. Neither can be combined with `--target-url` (the project step would navigate away from the confirmed tab), and `--project-new` never comes from saved defaults — creating a project is always an explicit per-ask choice.
246
246
 
@@ -229,8 +229,21 @@ export function hasFreshChatGptAnswer(previousAssistantMessageCount, state) {
229
229
  // changed ChatGPT UI (moved/renamed composer or send control). Distinguish that
230
230
  // from a genuinely clean-but-slow submit and point the user at an update/report
231
231
  // instead of a misleading "raise --timeout-ms".
232
+ // Render a millisecond duration as a human-readable span for timeout guidance:
233
+ // 45000 -> "45s", 90000 -> "1m 30s", 1200000 -> "20 min". Messages keep the raw
234
+ // ms alongside ("20 min (1200000ms)") so the send_timeout blocker can still
235
+ // parse a budget to double.
236
+ export function formatDurationMs(ms) {
237
+ const totalSeconds = Math.max(0, Math.round(ms / 1000));
238
+ if (totalSeconds < 60)
239
+ return `${totalSeconds}s`;
240
+ const minutes = Math.floor(totalSeconds / 60);
241
+ const seconds = totalSeconds % 60;
242
+ return seconds ? `${minutes}m ${seconds}s` : `${minutes} min`;
243
+ }
232
244
  export function acceptanceTimeoutError(ctx) {
233
245
  const uiLikelyChanged = ctx.composerStillHasText || !ctx.submitButtonFound;
246
+ const took = `${formatDurationMs(ctx.timeoutMs)} (${ctx.timeoutMs}ms)`;
234
247
  if (uiLikelyChanged) {
235
248
  const detail = [
236
249
  ctx.composerStillHasText ? "the composer still holds the prompt" : undefined,
@@ -238,12 +251,13 @@ export function acceptanceTimeoutError(ctx) {
238
251
  ]
239
252
  .filter(Boolean)
240
253
  .join(" and ");
241
- return new Error(`Timed out after ${ctx.timeoutMs}ms and ChatGPT never registered the prompt (${detail}). ` +
254
+ return new Error(`Timed out after ${took} and ChatGPT never registered the prompt (${detail}). ` +
242
255
  "The ChatGPT web UI may have changed, so prodex could not submit. Update prodex " +
243
256
  "(npm i -g @youdie006/prodex@latest); if it persists, report it at " +
244
257
  "https://github.com/youdie006/prodex/issues. You can also paste the prompt manually in the visible browser.");
245
258
  }
246
- return new Error(`Timed out after ${ctx.timeoutMs}ms waiting for ChatGPT to accept the prompt. Raise --timeout-ms and retry (Pro extended already uses a higher default).`);
259
+ return new Error(`Timed out after ${took} waiting for ChatGPT to accept the prompt. ` +
260
+ "Pro reasoning can run many minutes. Raise --timeout-ms and retry.");
247
261
  }
248
262
  export function hasPartialChatGptAnswer(previousAssistantMessageCount, state) {
249
263
  return state.assistantMessageCount > previousAssistantMessageCount && isUsableChatGptAnswer(state.answer);
@@ -1415,7 +1429,7 @@ export async function sendChatGptPrompt(options) {
1415
1429
  finally {
1416
1430
  cdp.close();
1417
1431
  }
1418
- emitProgress("sent", `timeout ${Math.round(timeoutMs / 1000)}s`);
1432
+ emitProgress("sent", `budget ${formatDurationMs(timeoutMs)}`);
1419
1433
  const started = Date.now();
1420
1434
  const acceptDeadline = computePromptAcceptanceDeadline(timeoutMs, started);
1421
1435
  let accepted = false;
@@ -1441,6 +1455,26 @@ export async function sendChatGptPrompt(options) {
1441
1455
  emitProgress("waiting", "prompt posting");
1442
1456
  }
1443
1457
  if (!accepted) {
1458
+ // The session can expire mid-send (logged out during a long Pro wait), which
1459
+ // otherwise surfaces as a cryptic "raise --timeout-ms" failure. Re-check the
1460
+ // login state first and, if the session is gone, say so clearly so the user
1461
+ // re-logs in instead of chasing a timeout.
1462
+ try {
1463
+ const status = await evaluateOnPage(page, statusExpression());
1464
+ if (!inferChatGptPageLoggedInLikely(status)) {
1465
+ throw new ChatGptBrowserBlockerError({
1466
+ code: "session_expired",
1467
+ message: "The ChatGPT session is no longer logged in - it likely expired during the send.",
1468
+ retryable: true,
1469
+ next_step: "Run `prodex pro browser login`, log in, then retry."
1470
+ });
1471
+ }
1472
+ }
1473
+ catch (error) {
1474
+ if (error instanceof ChatGptBrowserBlockerError)
1475
+ throw error;
1476
+ // best effort: a CDP eval failure here falls through to the generic timeout
1477
+ }
1444
1478
  // A successful submit clears the composer, so text still sitting there means
1445
1479
  // the send control did not register the prompt — the UI-changed signature.
1446
1480
  let composerStillHasText = false;
@@ -1500,11 +1534,12 @@ export async function sendChatGptPrompt(options) {
1500
1534
  answer: completed.answer.trim(),
1501
1535
  modelHints: completed.modelHints,
1502
1536
  warnings: [
1503
- `answer_incomplete: ChatGPT was still generating after ${timeoutMs}ms, so the answer below may be truncated. Raise --timeout-ms and retry for the full response.`
1537
+ `answer_incomplete: ChatGPT was still generating after ${formatDurationMs(timeoutMs)} (${timeoutMs}ms), so the answer below may be truncated. Raise --timeout-ms and retry for the full response.`
1504
1538
  ]
1505
1539
  };
1506
1540
  }
1507
- throw new Error(`Timed out after ${timeoutMs}ms waiting for ChatGPT to respond. Raise --timeout-ms and retry (Pro extended already uses a higher default).`);
1541
+ throw new Error(`Timed out after ${formatDurationMs(timeoutMs)} (${timeoutMs}ms) waiting for ChatGPT to respond. ` +
1542
+ "Pro reasoning can run many minutes. Raise --timeout-ms and retry.");
1508
1543
  }
1509
1544
  export function modelMenuOptionsExpression() {
1510
1545
  return `(() => {
package/dist/cli-help.js CHANGED
@@ -174,7 +174,7 @@ Use \`prodex pro ask\` for dry-run/manual previews.
174
174
  Use \`prodex pro browser ask\` only when you want an explicit visible-browser send.
175
175
  Model/project selection (visible-browser send):
176
176
  --model "label" Pick the composer model by its exact menu label (verified: Pro). Submenu models (e.g. the GPT-5.6 Sol variants) are rejected for now.
177
- --pro-mode 기본 | 확장 Pro sub-mode (only when the model is Pro); a Pro selection raises the default timeout to 900000 ms
177
+ --pro-mode 기본 | 확장 Pro sub-mode (only when the model is Pro); a Pro selection raises the default timeout to 1200000 ms
178
178
  --effort 즉시|중간|높음|매우 높음 Reasoning effort (aliases: instant/medium/high/max); picking one deselects Pro
179
179
  --project "name" Enter an existing sidebar project first (cannot combine with --target-url)
180
180
  Labels are matched in both the Korean and English (US) UI; run \`prodex pro browser models\` to list what your account shows.
@@ -268,7 +268,7 @@ Commands:
268
268
  Visible-browser sends require a manual browser session and stop on login, captcha, Cloudflare, permission, rate-limit, or usage-limit blockers.
269
269
  Model/project selection (ask):
270
270
  --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.
271
- --pro-mode Pro sub-mode: 기본 (standard) or 확장 (extended), used when the model is Pro. A Pro selection raises the default --timeout-ms to 900000.
271
+ --pro-mode Pro sub-mode: 기본 (standard) or 확장 (extended), used when the model is Pro. A Pro selection raises the default --timeout-ms to 1200000.
272
272
  --effort Reasoning effort: 즉시 / 중간 / 높음 / 매우 높음 (aliases: instant/medium/high/max). Picking an effort switches the composer to the standard reasoning model, deselecting Pro.
273
273
  --project Enter an existing sidebar project before sending. Cannot be combined with --target-url.
274
274
  --pro-mode and --effort cannot be combined. Labels are matched in both the Korean and English (US) ChatGPT UI (e.g. 높음/High, Pro 확장/Pro Extended).
package/dist/cli-pro.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { mkdir, readFile, writeFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { buildDryRunBundle } from "./bundle.js";
4
- import { DEFAULT_CDP_PORT, resolveCdpPort, chatGptVisibilityBlocker, defaultChatGptProfileDir, getChatGptBrowserStatus, listChatGptModelOptions, listChatGptSidebarProjects, normalizeChatGptTargetUrl, openChatGptBrowser, parseProMode, parseReasoningEffort, readLastBrowserLoginLaunch, recordBrowserLoginLaunch, sendChatGptPrompt } from "./chatgpt-browser.js";
4
+ import { DEFAULT_CDP_PORT, resolveCdpPort, chatGptVisibilityBlocker, defaultChatGptProfileDir, formatDurationMs, getChatGptBrowserStatus, listChatGptModelOptions, listChatGptSidebarProjects, normalizeChatGptTargetUrl, openChatGptBrowser, parseProMode, parseReasoningEffort, readLastBrowserLoginLaunch, recordBrowserLoginLaunch, sendChatGptPrompt } from "./chatgpt-browser.js";
5
5
  import { ASK_PRO_BOOLEAN_FLAGS, ASK_PRO_PREVIEW_VALUE_FLAGS, ASK_PRO_VALUE_FLAGS, assertHelpRequestArgs, assertNoExtraArgs, assertOnlyOptions, findHelpFlagIndexBeforePromptDelimiter, formatCliCommand, hasAskProDryRunMode, hasAskProMode, hasAskProSendMode, isHelpSubcommand, parseAskProArgs, printHelpIfRequested, readFlag, readPortFlag, readPositionalsWithOptions, readPositiveIntegerFlag, readRepeatedFlag, resolveCwdFlag, resolveOptionalFileFlag, unknownSubcommandError } from "./cli-args.js";
6
6
  import { printProBrowserHelp, printProHelp } from "./cli-help.js";
7
7
  import { listRawResultsForInspection, listTasksForInspection } from "./cli-ledger.js";
@@ -520,11 +520,11 @@ export function createBrowserSendProgressPrinter(write, heartbeatMs = 10_000) {
520
520
  if (lastWaitingElapsedMs !== undefined && event.elapsedMs - lastWaitingElapsedMs < heartbeatMs)
521
521
  return;
522
522
  lastWaitingElapsedMs = event.elapsedMs;
523
- write(`progress: waiting ${Math.round(event.elapsedMs / 1000)}s${event.detail ? ` (${event.detail})` : ""}`);
523
+ write(`progress: waiting ${formatDurationMs(event.elapsedMs)}${event.detail ? ` (${event.detail})` : ""}`);
524
524
  return;
525
525
  }
526
526
  if (event.phase === "answered") {
527
- write(`progress: answer received after ${Math.round(event.elapsedMs / 1000)}s${event.detail ? ` (${event.detail})` : ""}`);
527
+ write(`progress: answer received after ${formatDurationMs(event.elapsedMs)}${event.detail ? ` (${event.detail})` : ""}`);
528
528
  return;
529
529
  }
530
530
  write(`progress: ${PROGRESS_PHASE_LABELS[event.phase]}${event.detail ? ` (${event.detail})` : ""}`);
@@ -628,7 +628,9 @@ export async function runAskProCommand(rest, io) {
628
628
  // --pro-mode, so --model Pro sends fell back to 90s and chronically timed
629
629
  // out. Any effective Pro selection now defaults to 15 minutes.
630
630
  const effectiveProSelection = selectionProMode !== undefined || (selectionModel !== undefined && /pro/i.test(selectionModel));
631
- const defaultBrowserTimeoutMs = effectiveProSelection ? 900_000 : 90_000;
631
+ // Pro reasoning routinely runs 6-20 minutes; 15 min was still cutting long
632
+ // answers off (field report), so a Pro selection defaults to 20 minutes.
633
+ const defaultBrowserTimeoutMs = effectiveProSelection ? 1_200_000 : 90_000;
632
634
  const browserTimeoutMs = hasSendMode
633
635
  ? (readPositiveIntegerFlag(parsedAskPro.optionArgs, "--timeout-ms") ?? defaultBrowserTimeoutMs)
634
636
  : undefined;
@@ -1075,7 +1077,9 @@ export function browserSendBlockerFromError(error) {
1075
1077
  next_step: "Update prodex (npm i -g @youdie006/prodex@latest); if it persists, report it at https://github.com/youdie006/prodex/issues or paste the prompt manually in the visible browser."
1076
1078
  };
1077
1079
  }
1078
- const timedOut = message.match(/^Timed out after (\d+)ms/);
1080
+ // Match the raw ms whether the message uses the old "after 90000ms" form or
1081
+ // the newer human-readable "after 20 min (1200000ms)" form.
1082
+ const timedOut = message.match(/Timed out after [\s\S]*?(\d+)\s*ms/);
1079
1083
  if (timedOut) {
1080
1084
  // Suggest a concrete doubled budget so the user can paste a rerun command
1081
1085
  // instead of guessing what "raise --timeout-ms" means in milliseconds.
@@ -1085,7 +1089,7 @@ export function browserSendBlockerFromError(error) {
1085
1089
  code: "send_timeout",
1086
1090
  message,
1087
1091
  retryable: true,
1088
- next_step: `Rerun with a bigger budget: \`prodex pro browser ask --timeout-ms ${suggestedMs} "<same prompt>"\`.`
1092
+ next_step: `Rerun with a bigger budget (${formatDurationMs(suggestedMs)}): \`prodex pro browser ask --timeout-ms ${suggestedMs} "<same prompt>"\`.`
1089
1093
  };
1090
1094
  }
1091
1095
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@youdie006/prodex",
3
- "version": "0.16.25",
3
+ "version": "0.16.27",
4
4
  "description": "Local receipt bus for coordinating Codex execution with ChatGPT Pro/Projects consultation.",
5
5
  "author": "youdie006",
6
6
  "license": "MIT",