@youdie006/prodex 0.16.29 → 0.16.30

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.
@@ -1246,13 +1246,15 @@ async function selectProject(cdp, options) {
1246
1246
  // project - which would silently send the prompt into the wrong project.
1247
1247
  const alreadyInRequestedProject = await cdp.evaluate(`(() => {
1248
1248
  if (!/^https:\\/\\/chatgpt\\.com\\/g\\/g-p-/.test(location.href)) return false;
1249
- const name = ${JSON.stringify(options.project)};
1250
- // Equality, not substring: a stalled cross-project navigation must not
1251
- // be accepted just because the current project's name CONTAINS the
1252
- // requested one (e.g. sitting on "Research Lab" while "Research" was
1253
- // requested), which would silently send into the wrong project.
1254
- if ((document.title || "").trim() === name) return true;
1255
- return [...document.querySelectorAll('h1,[role="heading"]')].some((h) => (h.innerText || "").trim() === name);
1249
+ const name = ${JSON.stringify(options.project)}.toLowerCase();
1250
+ // Case-insensitive EQUALITY (not substring): matches the case-insensitive
1251
+ // sidebar-row lookup (so "codex" is accepted while sitting on "Codex"),
1252
+ // but a stalled cross-project navigation must NOT be accepted just because
1253
+ // the current project's name CONTAINS the requested one (e.g. "Research
1254
+ // Lab" while "Research" was requested) - that would send into the wrong
1255
+ // project.
1256
+ if ((document.title || "").trim().toLowerCase() === name) return true;
1257
+ return [...document.querySelectorAll('h1,[role="heading"]')].some((h) => (h.innerText || "").trim().toLowerCase() === name);
1256
1258
  })()`);
1257
1259
  if (!alreadyInRequestedProject) {
1258
1260
  throw new Error(`Clicking project "${options.project}" did not navigate the visible tab. If the tab is already inside this project, omit --project and retry.`);
@@ -1379,11 +1381,27 @@ export async function sendChatGptPrompt(options) {
1379
1381
  };
1380
1382
  let beforeSubmit;
1381
1383
  let submitButtonFound = false;
1384
+ const sendWarnings = [];
1382
1385
  const cdp = await connectCdp(page.webSocketDebuggerUrl);
1383
1386
  try {
1384
1387
  await cdp.send("Runtime.enable");
1385
1388
  await selectProject(cdp, options);
1386
- await selectModelReasoning(cdp, options);
1389
+ try {
1390
+ await selectModelReasoning(cdp, options);
1391
+ }
1392
+ catch (modelError) {
1393
+ // Pro sub-mode isn't exposed in this UI yet (staged rollout). Pro itself is
1394
+ // already selected by this point, so degrade to plain Pro with a warning
1395
+ // instead of failing the whole consult - an agent's --pro-mode 확장 becomes
1396
+ // a successful plain-Pro send rather than a hard block it has to retry past.
1397
+ const modelMsg = modelError instanceof Error ? modelError.message : String(modelError);
1398
+ if (options.proMode && /does not expose Pro sub-modes/.test(modelMsg)) {
1399
+ sendWarnings.push("pro_mode_unavailable: this ChatGPT UI does not expose Pro sub-modes yet, so the consult was sent as plain Pro. Drop --pro-mode (or run `prodex setup --clear-pro-mode`).");
1400
+ }
1401
+ else {
1402
+ throw modelError;
1403
+ }
1404
+ }
1387
1405
  // Capture the answer baseline AFTER any project navigation or model switch
1388
1406
  // so assistant-message counts compare within the thread we actually send
1389
1407
  // into; a --project/--project-new hop lands on a page with its own counts.
@@ -1520,7 +1538,7 @@ export async function sendChatGptPrompt(options) {
1520
1538
  title: completed.title,
1521
1539
  answer: completed.answer.trim(),
1522
1540
  modelHints: completed.modelHints,
1523
- warnings: []
1541
+ warnings: [...sendWarnings]
1524
1542
  };
1525
1543
  }
1526
1544
  // Timed out while the answer was still streaming: salvage the partial text
@@ -1534,6 +1552,7 @@ export async function sendChatGptPrompt(options) {
1534
1552
  answer: completed.answer.trim(),
1535
1553
  modelHints: completed.modelHints,
1536
1554
  warnings: [
1555
+ ...sendWarnings,
1537
1556
  `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.`
1538
1557
  ]
1539
1558
  };
package/dist/cli-pro.js CHANGED
@@ -545,7 +545,19 @@ export async function runAskProCommand(rest, io) {
545
545
  }
546
546
  const targetCwd = resolveCwdFlag(io.cwd, parsedAskPro.optionArgs);
547
547
  const targetStore = new BridgeStore(targetCwd);
548
- const files = readRepeatedFlag(parsedAskPro.optionArgs, "--file");
548
+ const files = readRepeatedFlag(parsedAskPro.optionArgs, "--file").map((file) => {
549
+ if (!path.isAbsolute(file))
550
+ return file;
551
+ // Accept an absolute --file that points INSIDE the repo by converting it to
552
+ // the repo-relative path the reader requires (agents naturally pass absolute
553
+ // paths); reject one that escapes the repo so a file outside the project
554
+ // (secrets, ~/.ssh, ...) is not attached to a consult by mistake.
555
+ const rel = path.relative(targetCwd, path.resolve(file));
556
+ if (rel === "" || rel.startsWith("..") || path.isAbsolute(rel)) {
557
+ throw new Error(`--file "${file}" is outside the repo root (${targetCwd}). Pass a path inside the repo (absolute or relative), or point --cwd at that repo.`);
558
+ }
559
+ return rel;
560
+ });
549
561
  const targetUrl = readFlag(parsedAskPro.optionArgs, "--target-url");
550
562
  const normalizedTargetUrl = targetUrl ? normalizeChatGptTargetUrl(targetUrl) : undefined;
551
563
  if (!normalizedTargetUrl && parsedAskPro.optionArgs.includes("--confirm-target")) {
@@ -559,12 +571,15 @@ export async function runAskProCommand(rest, io) {
559
571
  throw new Error("--json applies to the visible-browser send output; the dry-run preview does not support it.");
560
572
  }
561
573
  const prompt = parsedAskPro.promptParts.join(" ").trim();
562
- if (!prompt) {
574
+ const usingStdin = parsedAskPro.optionArgs.includes("--stdin");
575
+ // A prompt is required UNLESS --stdin supplies one: `git diff | prodex ask
576
+ // --stdin` (piped text is the whole prompt) is as valid as `... --stdin
577
+ // "review this"` (positional instruction + piped data below it).
578
+ if (!prompt && !usingStdin) {
563
579
  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").');
564
580
  }
565
581
  let promptText = prompt;
566
- if (parsedAskPro.optionArgs.includes("--stdin")) {
567
- // Unix ergonomics: `git diff | prodex ask --stdin "review this"`.
582
+ if (usingStdin) {
568
583
  const piped = ((await io.readStdin?.()) ?? "").trim();
569
584
  if (!piped) {
570
585
  throw new Error("--stdin was set but no piped input was received on stdin (pipe something in, e.g. `git diff | prodex ask --stdin \"review\"`).");
@@ -573,7 +588,7 @@ export async function runAskProCommand(rest, io) {
573
588
  if (piped.length > MAX_STDIN_CHARS) {
574
589
  throw new Error(`--stdin input is too large (${piped.length} chars > ${MAX_STDIN_CHARS}); attach a file with --file instead.`);
575
590
  }
576
- promptText = `${prompt}\n\n--- piped input (stdin) ---\n${piped}`;
591
+ promptText = prompt ? `${prompt}\n\n--- piped input (stdin) ---\n${piped}` : piped;
577
592
  }
578
593
  const browserDefaults = await loadBrowserDefaults(targetCwd);
579
594
  const explicitProject = readFlag(parsedAskPro.optionArgs, "--project");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@youdie006/prodex",
3
- "version": "0.16.29",
3
+ "version": "0.16.30",
4
4
  "description": "Local receipt bus for coordinating Codex execution with ChatGPT Pro/Projects consultation.",
5
5
  "author": "youdie006",
6
6
  "license": "MIT",