@youdie006/prodex 0.40.35 → 0.40.37

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.
@@ -311,12 +311,19 @@ export const CHATGPT_RENDERED_MESSAGE_TEXT_JS = `
311
311
  }
312
312
  const lines = findAll(node, (n) => hasClass(n, "cm-line")).map((line) => line.textContent || "");
313
313
  const pre = tagOf(node) === "pre" ? node : findAll(node, (n) => tagOf(n) === "pre")[0];
314
- const code = fullCode !== null ? trimEnd(fullCode) : lines.length > 0 ? lines.join(NL) : trimEnd(pre ? pre.textContent || "" : node.textContent || "");
314
+ // Some blocks keep their text in a source element with no <pre>
315
+ // (measured 2026-10-19), and the whole block's text carries the toolbar.
316
+ const source = pre || findAll(node, (n) => attr(n, "data-code-block-source") !== null)[0];
317
+ const code = fullCode !== null ? trimEnd(fullCode) : lines.length > 0 ? lines.join(NL) : trimEnd(source ? source.textContent || "" : node.textContent || "");
315
318
  const editor = findAll(node, (n) => attr(n, "data-language") !== null)[0];
316
319
  const toolbar = findAll(node, (n) => attr(n, "data-markdown-copy") === "exclude")[0];
317
320
  const label = toolbar ? collapse(findAll(toolbar, (n) => n.nodeType === 1 && tagOf(n) === "div" && children(n).every((c) => c.nodeType === 3)).map((n) => n.textContent || "")[0] || "").trim() : "";
318
321
  const language = ((editor && attr(editor, "data-language")) || label).trim().toLowerCase();
319
- return BT + BT + BT + language + NL + code + NL + BT + BT + BT;
322
+ // A fence must be longer than any backtick run inside, or a block that
323
+ // shows a fenced example is closed early by it.
324
+ const longestRun = (code.match(new RegExp(BT + "+", "g")) || []).reduce((most, run) => Math.max(most, run.length), 0);
325
+ const fence = BT.repeat(Math.max(3, longestRun + 1));
326
+ return fence + language + NL + code + NL + fence;
320
327
  };
321
328
  const table = (node) => {
322
329
  const cell = (value) => collapse(value).trim().split("|").join(String.fromCharCode(92) + "|");
@@ -492,6 +499,17 @@ const CHATGPT_GENERATING_CONTROL_PATTERN = /\bstop\s+(?:generating|responding|re
492
499
  // is matched only inside the composer's form, where the send control lives,
493
500
  // so a "Stop" elsewhere on the page cannot answer for it.
494
501
  export const CHATGPT_STREAMING_SELECTOR = '[data-testid="stop-button"],form button[aria-label="Stop"],form button[aria-label="중지"],[data-message-author-role="assistant"][aria-busy="true"],[data-message-author-role="assistant"] [aria-busy="true"]';
502
+ /**
503
+ * Whether a streaming signal is actually on screen. Measured 2026-10-20: after
504
+ * a Pro answer finished, the composer kept a zero-size, undisplayed "Stop"
505
+ * button, and its mere presence held a finished answer as "generating" for
506
+ * the whole budget. A message drawn through display: contents has no box of
507
+ * its own, so its children decide.
508
+ */
509
+ export const CHATGPT_STREAMING_SIGNAL_JS = `
510
+ const streamingSignalVisible = () =>
511
+ [...document.querySelectorAll(${JSON.stringify(CHATGPT_STREAMING_SELECTOR)})].some((node) =>
512
+ node.getClientRects().length > 0 || [...(node.children || [])].some((child) => child.getClientRects().length > 0));`;
495
513
  export function defaultChatGptProfileDir() {
496
514
  return path.join(os.homedir(), ".local", "share", "prodex", "chrome-chatgpt-pro");
497
515
  }
@@ -5325,11 +5343,10 @@ export const CHATGPT_THINKING_PLACEHOLDER_JS = `ansLines.length <= 1 && (/^(생
5325
5343
  export function statusExpression() {
5326
5344
  const excludedTextSelector = JSON.stringify(CHATGPT_RUNTIME_BLOCKER_TEXT_EXCLUDED_ANCESTORS);
5327
5345
  const blockerScanExcludedSelector = JSON.stringify(CHATGPT_BLOCKER_SCAN_EXCLUDED_ANCESTORS);
5328
- const streamingSelector = JSON.stringify(CHATGPT_STREAMING_SELECTOR);
5329
5346
  const responseChoiceSelector = JSON.stringify(CHATGPT_RESPONSE_CHOICE_SELECTOR);
5330
5347
  const generatingControlPattern = JSON.stringify(CHATGPT_GENERATING_CONTROL_PATTERN.source);
5331
5348
  const generatingControlFlags = JSON.stringify(CHATGPT_GENERATING_CONTROL_PATTERN.flags);
5332
- return `(() => {${CHATGPT_MODEL_HINTS_JS}
5349
+ return `(() => {${CHATGPT_MODEL_HINTS_JS}${CHATGPT_STREAMING_SIGNAL_JS}
5333
5350
  ${composerExpressionHelpers()}
5334
5351
  const text = document.body?.innerText || "";
5335
5352
  const runtimeExcludedTextSelector = ${excludedTextSelector};
@@ -5380,7 +5397,7 @@ export function statusExpression() {
5380
5397
  blockerScanTextSample: blockerScanText.slice(0, 12000),
5381
5398
  visibleButtonLabels,
5382
5399
  hasComposer,
5383
- generating: placeholder || Boolean(document.querySelector(${streamingSelector})) || visibleButtonLabels.some((label) => generatingControlPattern.test(label)),
5400
+ generating: placeholder || streamingSignalVisible() || visibleButtonLabels.some((label) => generatingControlPattern.test(label)),
5384
5401
  awaitingResponseChoice: Boolean(document.querySelector(${responseChoiceSelector})),
5385
5402
  modelHints: modelHintLines(),
5386
5403
  openDialogText: (([...document.querySelectorAll('[role="dialog"]')].find((d) => d.offsetWidth || d.offsetHeight || d.getClientRects().length)?.innerText) || "").trim().slice(0, 200)
@@ -6426,11 +6443,10 @@ function composerExpressionHelpers() {
6426
6443
  export function answerExpression() {
6427
6444
  const excludedTextSelector = JSON.stringify(CHATGPT_RUNTIME_BLOCKER_TEXT_EXCLUDED_ANCESTORS);
6428
6445
  const blockerScanExcludedSelector = JSON.stringify(CHATGPT_BLOCKER_SCAN_EXCLUDED_ANCESTORS);
6429
- const streamingSelector = JSON.stringify(CHATGPT_STREAMING_SELECTOR);
6430
6446
  const responseChoiceSelector = JSON.stringify(CHATGPT_RESPONSE_CHOICE_SELECTOR);
6431
6447
  const generatingControlPattern = JSON.stringify(CHATGPT_GENERATING_CONTROL_PATTERN.source);
6432
6448
  const generatingControlFlags = JSON.stringify(CHATGPT_GENERATING_CONTROL_PATTERN.flags);
6433
- return `(() => {${CHATGPT_MODEL_HINTS_JS}
6449
+ return `(() => {${CHATGPT_MODEL_HINTS_JS}${CHATGPT_STREAMING_SIGNAL_JS}
6434
6450
  const text = document.body?.innerText || "";
6435
6451
  const excludedTextSelector = ${excludedTextSelector};
6436
6452
  const blockerScanExcludedSelector = ${blockerScanExcludedSelector};
@@ -6497,7 +6513,7 @@ export function answerExpression() {
6497
6513
  blockerTextSample: visibleTextOutsideMessages(excludedTextSelector).slice(0, 12000),
6498
6514
  blockerScanTextSample: visibleTextOutsideMessages(blockerScanExcludedSelector).slice(0, 12000),
6499
6515
  visibleButtonLabels: buttons,
6500
- generating: placeholder || Boolean(document.querySelector(${streamingSelector})) || buttons.some((label) => generatingControlPattern.test(label)),
6516
+ generating: placeholder || streamingSignalVisible() || buttons.some((label) => generatingControlPattern.test(label)),
6501
6517
  awaitingResponseChoice: Boolean(document.querySelector(${responseChoiceSelector})),
6502
6518
  assistantMessageCount: assistantMessages.length,
6503
6519
  userMessageCount: userMessages.length,
package/docs/releasing.md CHANGED
@@ -16,6 +16,45 @@ Release target: `v0.40.13` on `main`. The GitHub Release records the resolved co
16
16
 
17
17
  The release adds a guarded, opt-in transition and more accurate blocker reporting. It does not claim that headless Pro consultation is verified on either deployment target or that authentication can be retained indefinitely.
18
18
 
19
+ ## 0.40.36 Release Record
20
+
21
+ Release commit `eee7a7c` on `main` (cut after PR #30, merge `1e53f4e`); tag `v0.40.36`.
22
+
23
+ **Publishing**
24
+ - `publish.yml` run 39220885039 passed all six platforms on the first attempt, then npm publish with SLSA v1 provenance and the GitHub Release (2026-10-19 08:10 UTC).
25
+ - `npm run release:verify` passed on the release commit after a build.
26
+
27
+ **Installed**
28
+ - WSL, M3, Mac mini: `prodex --version` reports 0.40.36. Running services were not restarted: 9 stdio MCP servers on WSL and 2 on M3 keep older code until their clients reconnect. The Mac mini had none running.
29
+ - The maintainer watchdog checkout was pulled to `eee7a7c` and rebuilt.
30
+
31
+ **Verified live**
32
+ - Host, installed 0.40.36: an answer with a markdown code block showing a fenced `js` example was saved with a four-backtick fence and no toolbar label line. Code blocks with HTML entities and backslashes, a table with an escaped pipe, and inline and display math were saved intact.
33
+
34
+ **Browser container**
35
+ - `container/redesign-patches` `3233bdc` ports PR #30 by hand. Image `3945d3d38af0`; branch suite 2042 passed, 3 skipped. Swapped under the send lock check. Live: the same nested block was saved intact.
36
+
37
+ **Known and left as is**
38
+ - An answer's text `&lt;tag&gt;` is saved as `<tag>`, as displayed; rendering the saved markdown again may treat it as an HTML tag.
39
+
40
+ ## 0.40.35 Release Record
41
+
42
+ Release commit `4c711c7` on `main` (cut after PR #29, merge `fdf7501`); tag `v0.40.35`.
43
+
44
+ **Publishing**
45
+ - `publish.yml` run 39211269776 passed all six platforms on the first attempt, then npm publish with SLSA v1 provenance and the GitHub Release (2026-10-19 06:40 UTC).
46
+ - `npm run release:verify` passed on the release commit after a build.
47
+
48
+ **Installed**
49
+ - WSL, M3, Mac mini: `prodex --version` reports 0.40.35. Running services were not restarted: 10 stdio MCP servers on WSL and 2 on M3 keep older code until their clients reconnect. The Mac mini had none running.
50
+ - The maintainer watchdog checkout was pulled to `4c711c7` and rebuilt.
51
+
52
+ **Verified live**
53
+ - Host, installed 0.40.35: the special-character stdin prompt (paths, HTML tags and entities, markdown, a link, `$`, tabs, a table, Korean) answered `SPECIAL_OK`; 0.40.34 had refused it as `request_mismatch`.
54
+
55
+ **Browser container**
56
+ - `container/redesign-patches` `62b713e` ports PR #29. Image `9b46b724ea31`; branch suite 2041 passed, 3 skipped. Swapped under the send lock check. Live: the same prompt answered `SPECIAL_OK` (the previous image refused it); smoke `PRODEX_PRO_SMOKE_OK`.
57
+
19
58
  ## 0.40.34 Release Record
20
59
 
21
60
  Release commit `574e5f0` on `main` (cut after PR #28, merge `1d93de4`); tag `v0.40.34`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@youdie006/prodex",
3
- "version": "0.40.35",
3
+ "version": "0.40.37",
4
4
  "description": "Local receipt bus for coordinating Codex execution with ChatGPT Pro/Projects consultation.",
5
5
  "author": "youdie006",
6
6
  "license": "MIT",