@duckmind/dm-windows-x64 0.60.6 → 0.60.9

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 (197) hide show
  1. package/dm.exe +0 -0
  2. package/extensions/.dm-extensions.json +70 -123
  3. package/extensions/dm-9router-ext/src/index.js +410 -5
  4. package/extensions/dm-caveman/extensions/caveman.js +283 -12
  5. package/extensions/dm-cliproxy/index.js +182 -2
  6. package/extensions/dm-cliproxy/scripts/check-config-migration.js +66 -8
  7. package/extensions/dm-cliproxy/src/apply.js +228 -1
  8. package/extensions/dm-cliproxy/src/cache.js +42 -1
  9. package/extensions/dm-cliproxy/src/commands.js +50 -2
  10. package/extensions/dm-cliproxy/src/compat.js +81 -1
  11. package/extensions/dm-cliproxy/src/config.js +192 -2
  12. package/extensions/dm-cliproxy/src/conflicts.js +46 -1
  13. package/extensions/dm-cliproxy/src/fetch-models.js +190 -1
  14. package/extensions/dm-cliproxy/src/fetch-usage.js +41 -1
  15. package/extensions/dm-cliproxy/src/log.js +23 -1
  16. package/extensions/dm-cliproxy/src/status-quota.js +77 -1
  17. package/extensions/dm-cliproxy/src/ui-frame.js +50 -1
  18. package/extensions/dm-cliproxy/src/ui-hub/hub.js +199 -2
  19. package/extensions/dm-cliproxy/src/ui-hub/index.js +26 -2
  20. package/extensions/dm-cliproxy/src/ui-hub/shell.js +46 -1
  21. package/extensions/dm-cliproxy/src/ui-hub/view-diagnostics.js +69 -1
  22. package/extensions/dm-cliproxy/src/ui-hub/view-models.js +379 -1
  23. package/extensions/dm-cliproxy/src/ui-hub/view-usage.js +106 -1
  24. package/extensions/dm-cliproxy/src/ui-picker/catalog.js +41 -1
  25. package/extensions/dm-cliproxy/src/ui-picker/mutate.js +115 -1
  26. package/extensions/dm-cliproxy/src/ui-picker/prompt-confirm.js +35 -1
  27. package/extensions/dm-cliproxy/src/ui-picker/prompt-name.js +62 -1
  28. package/extensions/dm-cliproxy/src/ui-picker/providers.js +45 -1
  29. package/extensions/dm-cliproxy/src/ui-picker/render-text.js +34 -1
  30. package/extensions/dm-cliproxy/src/ui-picker/rows.js +57 -1
  31. package/extensions/dm-cliproxy/src/ui-setup.js +260 -2
  32. package/extensions/dm-cliproxy/src/ui-usage.js +151 -1
  33. package/extensions/dm-cliproxy/src/usage-shared-cache.js +97 -1
  34. package/extensions/dm-context/src/context.js +144 -1
  35. package/extensions/dm-context/src/index.js +339 -7
  36. package/extensions/dm-context/src/utils.js +9 -1
  37. package/extensions/dm-cua/bin/browser-cua.mjs +73 -8
  38. package/extensions/dm-cua/index.js +75 -6
  39. package/extensions/dm-cua/src/browser-cua-lib.mjs +490 -6
  40. package/extensions/dm-cua/src/browser-install.mjs +331 -2
  41. package/extensions/dm-fff/src/index.js +688 -12
  42. package/extensions/dm-fff/src/query.js +60 -1
  43. package/extensions/dm-goal/src/goal.js +894 -23
  44. package/extensions/dm-image2/index.js +103 -9
  45. package/extensions/dm-image2/src/image-lib.mjs +1275 -8
  46. package/extensions/dm-subagents/install.mjs +62 -8
  47. package/extensions/dm-subagents/src/agents/agent-management.js +1190 -36
  48. package/extensions/dm-subagents/src/agents/agent-memory.js +216 -6
  49. package/extensions/dm-subagents/src/agents/agent-scope.js +5 -1
  50. package/extensions/dm-subagents/src/agents/agent-selection.js +20 -1
  51. package/extensions/dm-subagents/src/agents/agent-serializer.js +120 -5
  52. package/extensions/dm-subagents/src/agents/agents.js +1139 -11
  53. package/extensions/dm-subagents/src/agents/chain-serializer.js +299 -11
  54. package/extensions/dm-subagents/src/agents/frontmatter.js +65 -6
  55. package/extensions/dm-subagents/src/agents/identity.js +29 -1
  56. package/extensions/dm-subagents/src/agents/proactive-skills.js +141 -1
  57. package/extensions/dm-subagents/src/agents/skills.js +614 -6
  58. package/extensions/dm-subagents/src/extension/config.js +35 -2
  59. package/extensions/dm-subagents/src/extension/control-notices.js +69 -4
  60. package/extensions/dm-subagents/src/extension/doctor.js +172 -15
  61. package/extensions/dm-subagents/src/extension/fanout-child.js +158 -231
  62. package/extensions/dm-subagents/src/extension/index.js +521 -359
  63. package/extensions/dm-subagents/src/extension/rpc.js +266 -7
  64. package/extensions/dm-subagents/src/extension/schemas.js +275 -1
  65. package/extensions/dm-subagents/src/extension/tool-description.js +111 -6
  66. package/extensions/dm-subagents/src/intercom/intercom-bridge.js +126 -4
  67. package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +452 -5
  68. package/extensions/dm-subagents/src/intercom/result-intercom.js +319 -3
  69. package/extensions/dm-subagents/src/profiles/profiles.js +458 -3
  70. package/extensions/dm-subagents/src/runs/background/async-execution.js +834 -40
  71. package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +435 -14
  72. package/extensions/dm-subagents/src/runs/background/async-resume.js +334 -8
  73. package/extensions/dm-subagents/src/runs/background/async-status.js +313 -12
  74. package/extensions/dm-subagents/src/runs/background/chain-append.js +245 -2
  75. package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +136 -1
  76. package/extensions/dm-subagents/src/runs/background/completion-batcher.js +94 -1
  77. package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +54 -1
  78. package/extensions/dm-subagents/src/runs/background/control-channel.js +190 -1
  79. package/extensions/dm-subagents/src/runs/background/fleet-view.js +483 -17
  80. package/extensions/dm-subagents/src/runs/background/notify.js +129 -3
  81. package/extensions/dm-subagents/src/runs/background/parallel-groups.js +34 -1
  82. package/extensions/dm-subagents/src/runs/background/result-watcher.js +236 -6
  83. package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +76 -4
  84. package/extensions/dm-subagents/src/runs/background/run-status.js +427 -23
  85. package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +487 -4
  86. package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +306 -9
  87. package/extensions/dm-subagents/src/runs/background/subagent-runner.js +2849 -73
  88. package/extensions/dm-subagents/src/runs/background/top-level-async.js +5 -1
  89. package/extensions/dm-subagents/src/runs/background/wait.js +206 -11
  90. package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +1013 -12
  91. package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +980 -101
  92. package/extensions/dm-subagents/src/runs/foreground/execution.js +1165 -45
  93. package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +3157 -222
  94. package/extensions/dm-subagents/src/runs/shared/acceptance.js +835 -3
  95. package/extensions/dm-subagents/src/runs/shared/chain-outputs.js +104 -1
  96. package/extensions/dm-subagents/src/runs/shared/completion-guard.js +116 -3
  97. package/extensions/dm-subagents/src/runs/shared/dm-args.js +208 -1
  98. package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +90 -1
  99. package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +282 -1
  100. package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +148 -1
  101. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +305 -1
  102. package/extensions/dm-subagents/src/runs/shared/model-fallback.js +194 -1
  103. package/extensions/dm-subagents/src/runs/shared/model-scope.js +65 -1
  104. package/extensions/dm-subagents/src/runs/shared/nested-events.js +851 -8
  105. package/extensions/dm-subagents/src/runs/shared/nested-path.js +41 -1
  106. package/extensions/dm-subagents/src/runs/shared/nested-render.js +105 -1
  107. package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +81 -4
  108. package/extensions/dm-subagents/src/runs/shared/run-history.js +51 -4
  109. package/extensions/dm-subagents/src/runs/shared/single-output.js +149 -8
  110. package/extensions/dm-subagents/src/runs/shared/structured-output.js +58 -1
  111. package/extensions/dm-subagents/src/runs/shared/subagent-control.js +166 -5
  112. package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +329 -13
  113. package/extensions/dm-subagents/src/runs/shared/tool-budget.js +73 -1
  114. package/extensions/dm-subagents/src/runs/shared/turn-budget.js +47 -4
  115. package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +196 -1
  116. package/extensions/dm-subagents/src/runs/shared/worktree.js +435 -3
  117. package/extensions/dm-subagents/src/shared/artifacts.js +92 -2
  118. package/extensions/dm-subagents/src/shared/atomic-json.js +55 -1
  119. package/extensions/dm-subagents/src/shared/child-transcript.js +167 -5
  120. package/extensions/dm-subagents/src/shared/file-coalescer.js +25 -1
  121. package/extensions/dm-subagents/src/shared/fork-context.js +147 -4
  122. package/extensions/dm-subagents/src/shared/formatters.js +98 -7
  123. package/extensions/dm-subagents/src/shared/jsonl-writer.js +56 -2
  124. package/extensions/dm-subagents/src/shared/model-info.js +62 -1
  125. package/extensions/dm-subagents/src/shared/post-exit-stdio-guard.js +68 -1
  126. package/extensions/dm-subagents/src/shared/session-identity.js +6 -1
  127. package/extensions/dm-subagents/src/shared/session-tokens.js +39 -2
  128. package/extensions/dm-subagents/src/shared/settings.js +198 -11
  129. package/extensions/dm-subagents/src/shared/status-format.js +53 -1
  130. package/extensions/dm-subagents/src/shared/types.js +184 -6
  131. package/extensions/dm-subagents/src/shared/utils.js +462 -2
  132. package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +288 -1
  133. package/extensions/dm-subagents/src/slash/prompt-workflows.js +297 -7
  134. package/extensions/dm-subagents/src/slash/slash-bridge.js +118 -1
  135. package/extensions/dm-subagents/src/slash/slash-commands.js +1287 -31
  136. package/extensions/dm-subagents/src/slash/slash-live-state.js +240 -4
  137. package/extensions/dm-subagents/src/tui/render-helpers.js +64 -1
  138. package/extensions/dm-subagents/src/tui/render.js +1542 -4
  139. package/extensions/dm-usage/index.js +1294 -9
  140. package/extensions/greedysearch-dm/bin/cdp-greedy.mjs +40 -9
  141. package/extensions/greedysearch-dm/bin/cdp-headless.mjs +5 -2
  142. package/extensions/greedysearch-dm/bin/cdp-visible.mjs +5 -2
  143. package/extensions/greedysearch-dm/bin/cdp.mjs +896 -30
  144. package/extensions/greedysearch-dm/bin/gschrome.mjs +30 -2
  145. package/extensions/greedysearch-dm/bin/kill-visible.mjs +7 -2
  146. package/extensions/greedysearch-dm/bin/launch-visible.mjs +13 -2
  147. package/extensions/greedysearch-dm/bin/launch.mjs +282 -10
  148. package/extensions/greedysearch-dm/bin/mcp.mjs +386 -361
  149. package/extensions/greedysearch-dm/bin/search.mjs +620 -540
  150. package/extensions/greedysearch-dm/bin/visible.mjs +22 -2
  151. package/extensions/greedysearch-dm/extractors/bing-copilot.mjs +329 -579
  152. package/extensions/greedysearch-dm/extractors/chatgpt.mjs +301 -583
  153. package/extensions/greedysearch-dm/extractors/common.mjs +408 -32
  154. package/extensions/greedysearch-dm/extractors/consensus.mjs +376 -365
  155. package/extensions/greedysearch-dm/extractors/consent.mjs +303 -14
  156. package/extensions/greedysearch-dm/extractors/gemini.mjs +228 -592
  157. package/extensions/greedysearch-dm/extractors/google-ai.mjs +78 -499
  158. package/extensions/greedysearch-dm/extractors/logically.mjs +270 -347
  159. package/extensions/greedysearch-dm/extractors/perplexity.mjs +243 -581
  160. package/extensions/greedysearch-dm/extractors/selectors.mjs +32 -1
  161. package/extensions/greedysearch-dm/extractors/semantic-scholar.mjs +130 -317
  162. package/extensions/greedysearch-dm/index.js +123 -23
  163. package/extensions/greedysearch-dm/src/fetcher.mjs +576 -2
  164. package/extensions/greedysearch-dm/src/formatters/results.js +95 -10
  165. package/extensions/greedysearch-dm/src/formatters/sources.js +57 -1
  166. package/extensions/greedysearch-dm/src/formatters/synthesis.js +49 -1
  167. package/extensions/greedysearch-dm/src/github.mjs +222 -7
  168. package/extensions/greedysearch-dm/src/reddit.mjs +145 -14
  169. package/extensions/greedysearch-dm/src/search/browser-lifecycle.mjs +340 -9
  170. package/extensions/greedysearch-dm/src/search/challenge-detect.mjs +112 -4
  171. package/extensions/greedysearch-dm/src/search/chrome.mjs +486 -285
  172. package/extensions/greedysearch-dm/src/search/constants.mjs +109 -8
  173. package/extensions/greedysearch-dm/src/search/defaults.mjs +10 -1
  174. package/extensions/greedysearch-dm/src/search/engines.mjs +79 -9
  175. package/extensions/greedysearch-dm/src/search/fetch-source.mjs +441 -349
  176. package/extensions/greedysearch-dm/src/search/file-sources.mjs +29 -7
  177. package/extensions/greedysearch-dm/src/search/minimize.mjs +86 -1
  178. package/extensions/greedysearch-dm/src/search/output.mjs +51 -5
  179. package/extensions/greedysearch-dm/src/search/paths.mjs +48 -1
  180. package/extensions/greedysearch-dm/src/search/pdf.mjs +63 -2
  181. package/extensions/greedysearch-dm/src/search/port-pid.mjs +69 -1
  182. package/extensions/greedysearch-dm/src/search/progress.mjs +109 -2
  183. package/extensions/greedysearch-dm/src/search/query.mjs +21 -1
  184. package/extensions/greedysearch-dm/src/search/recovery.mjs +49 -1
  185. package/extensions/greedysearch-dm/src/search/research.mjs +2227 -458
  186. package/extensions/greedysearch-dm/src/search/scale-aware.mjs +61 -11
  187. package/extensions/greedysearch-dm/src/search/simple-research.mjs +396 -805
  188. package/extensions/greedysearch-dm/src/search/sources.mjs +412 -1
  189. package/extensions/greedysearch-dm/src/search/synthesis-runner.mjs +127 -12
  190. package/extensions/greedysearch-dm/src/search/synthesis.mjs +202 -12
  191. package/extensions/greedysearch-dm/src/tools/greedy-search-handler.js +209 -23
  192. package/extensions/greedysearch-dm/src/tools/shared.js +226 -10
  193. package/extensions/greedysearch-dm/src/utils/content.mjs +35 -4
  194. package/extensions/greedysearch-dm/src/utils/helpers.js +22 -1
  195. package/extensions/greedysearch-dm/src/utils/node-runtime.mjs +10 -1
  196. package/extensions/greedysearch-dm/src/utils/system-cmds.mjs +61 -1
  197. package/package.json +1 -1
@@ -1,571 +1,28 @@
1
- #!/usr/bin/env node
2
- import{randomInt as r}from"node:crypto";import{spawn as t}from"node:child_process";import{basename as a}from"node:path";function k(Q=process.env,$=process.execPath){let Z=Q.GREEDY_SEARCH_NODE||Q.NODE_BINARY||Q.NODE;if(Z?.trim())return Z.trim();let K=a($||"").toLowerCase();if(K==="node"||K==="node.exe")return $;return"node"}import{dirname as e,join as QQ}from"node:path";import{fileURLToPath as ZQ}from"node:url";var $Q=e(ZQ(import.meta.url)),KQ=QQ($Q,"..","bin","cdp.mjs"),WQ=new Set(["list","snap","eval","shot","html","nav","net","click","clickxy","type","loadall","evalraw","browse","stop","--tab"]);function JQ(Q){if(!Array.isArray(Q)||Q.length===0)throw Error("cdp: args must be a non-empty array");if(Q[0]==="test")return Q.map(($,Z)=>y($,Z));if(!WQ.has(Q[0]))throw Error(`cdp: unknown subcommand '${Q[0]}'`);return Q.map(($,Z)=>y($,Z))}function y(Q,$){if(typeof Q!=="string")throw Error(`cdp: argv[${$}] must be a string (got ${typeof Q})`);if(Q.includes("\x00"))throw Error(`cdp: argv[${$}] contains a null byte`);return Q}function B(Q,$=30000){return UQ(Q,null,$)}function UQ(Q,$=null,Z=30000){let K=JQ(Q);return new Promise((J,X)=>{let W=t(k(),[KQ,...K],{stdio:[$==null?"ignore":"pipe","pipe","pipe"]});if($!=null)W.stdin.write($),W.stdin.end();let U="",Y="";W.stdout.on("data",(z)=>U+=z),W.stderr.on("data",(z)=>Y+=z);let q=setTimeout(()=>{W.kill(),X(Error(`cdp timeout: ${Q[0]}`))},Z);W.on("close",(z)=>{if(clearTimeout(q),z===0)J(U.trim());else X(Error(Y.trim()||`cdp exit ${z}`))})})}async function E(Q){if(Q)return Q;let Z=(await B(["list"])).split(`
3
- `)[0]?.slice(0,8);if(!Z)throw Error("No Chrome tabs found. Is Chrome running with --remote-debugging-port=9222?");let K=await B(["evalraw",Z,"Target.createTarget",'{"url":"about:blank"}']),J;try{J=JSON.parse(K)}catch(U){throw Error(`Target.createTarget returned invalid JSON: ${U.message}`)}let{targetId:X}=J;if(!X)throw Error("Target.createTarget did not return a targetId");await B(["list"]);let W=X.slice(0,8);try{await XQ(W)}catch(U){process.stderr.write(`[getOrOpenTab] stealth injection failed: ${U.message}
4
- `)}return W}async function T(Q,$){let Z=`
5
- (() => {
6
- window.${$} = null;
7
- const _clipboard = navigator.clipboard;
8
- if (!_clipboard) return;
9
- const _origWriteText = typeof _clipboard.writeText === 'function'
10
- ? _clipboard.writeText.bind(_clipboard)
11
- : null;
12
- const _origWrite = typeof _clipboard.write === 'function'
13
- ? _clipboard.write.bind(_clipboard)
14
- : null;
15
-
16
- _clipboard.writeText = function(text) {
17
- window.${$} = String(text ?? '');
18
- if (!_origWriteText) return Promise.resolve();
19
- // The OS/browser clipboard write may be denied in automated Chrome or
20
- // when the tab is not focused. We only need the captured text; returning
21
- // a resolved promise prevents the page from surfacing a misleading
22
- // "failed to copy" toast after our interceptor already succeeded.
23
- return Promise.resolve(_origWriteText(text)).catch(() => undefined);
24
- };
25
-
26
- _clipboard.write = async function(items) {
27
- try {
28
- for (const item of items || []) {
29
- if (item.types && item.types.includes('text/plain')) {
30
- const blob = await item.getType('text/plain');
31
- window.${$} = await blob.text();
32
- break;
33
- }
34
- }
35
- } catch(e) {}
36
- if (!_origWrite) return undefined;
37
- try { return await _origWrite(items); }
38
- catch (_) { return undefined; }
39
- };
40
- })();
41
- `;await B(["eval",Q,Z])}async function S(Q,$,Z,K={}){let{timeoutMs:J=2600}=K,X=K.retryClick!=null?K.retryClick:Math.floor(J*0.4),W=String.raw`
42
- new Promise((resolve) => {
43
- const _deadline = Date.now() + ${J};
44
- const _retryAt = Date.now() + ${X};
45
- let _retried = false;
46
- function _click() { try { ${$}; } catch(_) {} }
47
- function _poll() {
48
- const val = window.${Z};
49
- if (val) { resolve(val); return; }
50
- if (!_retried && Date.now() >= _retryAt) {
51
- _retried = true;
52
- _click();
53
- }
54
- if (Date.now() < _deadline) { setTimeout(_poll, 100); }
55
- else { resolve(window.${Z} || ''); }
56
- }
57
- _click();
58
- _poll();
59
- })
60
- `;return await B(["eval",Q,W],J+5000).catch(()=>"")}async function XQ(Q){await B(["evalraw",Q,"Page.addScriptToEvaluateOnNewDocument",JSON.stringify({source:`
61
- (function() {
62
- // ── Runtime.enable / CDP detection masking ──────────────
63
- try { delete window.__REBROWSER_RUNTIME_ENABLE; } catch(_) {}
64
- try { delete window.__REBROWSER_DEVTOOLS; } catch(_) {}
65
- try { delete window.__nightmare; } catch(_) {}
66
- try { delete window.__phantom; } catch(_) {}
67
- try { delete window.callPhantom; } catch(_) {}
68
- try { delete window._phantom; } catch(_) {}
69
- try { delete window.Buffer; } catch(_) {}
70
-
71
- // Real Chrome without automation should not expose navigator.webdriver at all.
72
- // A literal false or an own-property getter returning undefined is itself a
73
- // common stealth tell; remove both instance and prototype properties when the
74
- // descriptor is configurable (as it is with --disable-blink-features).
75
- try { delete navigator.webdriver; } catch(_) {}
76
- try { delete Navigator.prototype.webdriver; } catch(_) {}
77
- Object.defineProperty(navigator, 'vendor', { get: () => 'Google Inc.', configurable: true });
78
- Object.defineProperty(navigator, 'platform', { get: () => 'Win32', configurable: true });
79
- Object.defineProperty(navigator, 'maxTouchPoints', { get: () => 0, configurable: true });
80
- Object.defineProperty(navigator, 'pdfViewerEnabled', { get: () => true, configurable: true });
81
- Object.defineProperty(navigator, 'productSub', { get: () => '20030107', configurable: true });
82
- Object.defineProperty(navigator, 'product', { get: () => 'Gecko', configurable: true });
83
- var __greedyMimeTypes = null;
84
- function __makeMimeTypes() {
85
- var pdf = { type: 'application/pdf', suffixes: 'pdf', description: 'Portable Document Format', enabledPlugin: null };
86
- var textPdf = { type: 'text/pdf', suffixes: 'pdf', description: 'Portable Document Format', enabledPlugin: null };
87
- try { Object.setPrototypeOf(pdf, MimeType.prototype); } catch(_) {}
88
- try { Object.setPrototypeOf(textPdf, MimeType.prototype); } catch(_) {}
89
- var m = [pdf, textPdf];
90
- try { Object.setPrototypeOf(m, MimeTypeArray.prototype); } catch(_) {}
91
- m.item = function item(i) { return this[i] || null; };
92
- m.namedItem = function namedItem(name) { return Array.prototype.find.call(this, function(x) { return x && x.type === name; }) || null; };
93
- return m;
94
- }
95
- Object.defineProperty(navigator, 'plugins', {
96
- get: () => {
97
- __greedyMimeTypes = __greedyMimeTypes || __makeMimeTypes();
98
- var plugin0 = { name: 'Chrome PDF Plugin', filename: 'internal-pdf-viewer', description: 'Portable Document Format' };
99
- var plugin1 = { name: 'Chrome PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai', description: '' };
100
- var plugin2 = { name: 'Native Client', filename: 'internal-nacl-plugin', description: '' };
101
- try { Object.setPrototypeOf(plugin0, Plugin.prototype); } catch(_) {}
102
- try { Object.setPrototypeOf(plugin1, Plugin.prototype); } catch(_) {}
103
- try { Object.setPrototypeOf(plugin2, Plugin.prototype); } catch(_) {}
104
- var p = [plugin0, plugin1, plugin2];
105
- p.item = function item(i) { return this[i] || null; };
106
- p.namedItem = function namedItem(name) { return Array.prototype.find.call(this, function(x) { return x && x.name === name; }) || null; };
107
- p.refresh = function refresh() {};
108
- try { Object.setPrototypeOf(p, PluginArray.prototype); } catch(_) {}
109
- try {
110
- __greedyMimeTypes[0].enabledPlugin = p[0];
111
- __greedyMimeTypes[1].enabledPlugin = p[0];
112
- } catch(_) {}
113
- return p;
114
- },
115
- configurable: true,
116
- });
117
- Object.defineProperty(navigator, 'mimeTypes', {
118
- get: () => {
119
- __greedyMimeTypes = __greedyMimeTypes || __makeMimeTypes();
120
- return __greedyMimeTypes;
121
- },
122
- configurable: true,
123
- });
124
- Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'], configurable: true });
125
- try {
126
- Object.defineProperty(navigator, 'connection', { get: () => ({ effectiveType: '4g', rtt: 50, downlink: 10, downlinkMax: Infinity, saveData: false }), configurable: true });
127
- } catch(_) {}
128
- if (!navigator.mediaDevices) {
129
- Object.defineProperty(navigator, 'mediaDevices', {
130
- get: () => ({
131
- enumerateDevices: () => Promise.resolve([
132
- { deviceId: 'default', kind: 'audioinput', label: '', groupId: 'default' },
133
- { deviceId: 'default', kind: 'audiooutput', label: '', groupId: 'default' },
134
- { deviceId: '', kind: 'videoinput', label: '', groupId: '' },
135
- ]),
136
- getUserMedia: () => Promise.reject(new DOMException('NotAllowedError')),
137
- getDisplayMedia: () => Promise.reject(new DOMException('NotAllowedError')),
138
- }),
139
- configurable: true,
140
- });
141
- }
142
- // ── Missing platform APIs (headless often lacks these) ─
143
- try {
144
- if (!navigator.share) {
145
- navigator.share = function() { return Promise.reject(new Error('NotAllowedError')); };
146
- }
147
- } catch(_) {}
148
- try {
149
- if (!navigator.contentIndex) {
150
- Object.defineProperty(navigator, 'contentIndex', { get: () => ({ add: function() {}, delete: function() {}, getAll: function() { return Promise.resolve([]); } }), configurable: true });
151
- }
152
- } catch(_) {}
153
-
154
- if (!window.chrome) {
155
- window.chrome = {
156
- app: { isInstalled: false, InstallState: {}, RunningState: {} },
157
- runtime: {
158
- OnInstalledReason: {}, OnRestartRequiredReason: {}, PlatformArch: {}, PlatformNaclArch: {}, PlatformOs: {}, RequestUpdateCheckStatus: {},
159
- connect: () => ({}), sendMessage: () => {}, onMessage: { addListener: () => {} }
160
- },
161
- loadTimes: function() { return { requestTime: 0, startLoadTime: Date.now() - 5000, commitLoadTime: Date.now() - 3000, finishDocumentLoadTime: Date.now() - 2000, finishLoadTime: Date.now() - 1000, firstPaintTime: Date.now() - 800, navigationType: 'Other', wasFetchedViaSpdy: true, wasNpnNegotiated: true, npnNegotiatedProtocol: 'h2', wasAlternateProtocolAvailable: false, connectionInfo: 'http/2' }; },
162
- csi: function() { var t = Date.now(); return { onloadT: t - 2000, startE: t - 5000, pageT: 'back', tran: 2 }; },
163
- };
164
- }
165
- var __greedyNativeFns = [];
166
- function __markNative(fn) { try { __greedyNativeFns.push(fn); } catch(_) {} return fn; }
167
-
168
- var origQuery = navigator.permissions?.query;
169
- if (origQuery) {
170
- navigator.permissions.query = __markNative(function query(params) {
171
- if (params && params.name === 'notifications') return Promise.resolve({ state: Notification.permission || 'default', onchange: null });
172
- return origQuery.apply(this, arguments);
173
- });
174
- }
175
- try {
176
- var getParam = WebGLRenderingContext.prototype.getParameter;
177
- WebGLRenderingContext.prototype.getParameter = __markNative(function getParameter(p) {
178
- if (p === 37445) return 'Intel Inc.';
179
- if (p === 37446) return 'Intel Iris OpenGL Engine';
180
- return getParam.call(this, p);
181
- });
182
- } catch(_) {}
183
- // ── WebGL readPixels noise ──────────────────────────
184
- // CreepJS and other fingerprinters draw content with WebGL and read back the
185
- // rendered pixels. Adding subtle noise breaks rendering-based fingerprinting.
186
- try {
187
- var origReadPixels = WebGLRenderingContext.prototype.readPixels;
188
- WebGLRenderingContext.prototype.readPixels = __markNative(function readPixels(x, y, width, height, format, type, pixels) {
189
- var result = origReadPixels.call(this, x, y, width, height, format, type, pixels);
190
- if (pixels && pixels.length > 0) {
191
- pixels[0] ^= 1;
192
- }
193
- return result;
194
- });
195
- } catch(_) {}
196
- Object.defineProperty(navigator, 'hardwareConcurrency', { get: () => 8, configurable: true });
197
- Object.defineProperty(navigator, 'deviceMemory', { get: () => 8, configurable: true });
198
-
199
- // ── Canvas fingerprint noise ─────────────────────────
200
- // Headless rendering engines produce slightly different canvas output
201
- // than headed Chrome. Subtle noise breaks hash-based fingerprinting.
202
- try {
203
- var __canvasNoise = ((Date.now() & 0xFF) | 1);
204
- var origFill = CanvasRenderingContext2D.prototype.fillText;
205
- CanvasRenderingContext2D.prototype.fillText = __markNative(function fillText() {
206
- this.globalAlpha = 0.9995;
207
- return origFill.apply(this, arguments);
208
- });
209
- } catch(_) {}
210
- try {
211
- var origStroke = CanvasRenderingContext2D.prototype.strokeText;
212
- CanvasRenderingContext2D.prototype.strokeText = __markNative(function strokeText() {
213
- this.globalAlpha = 0.9995;
214
- return origStroke.apply(this, arguments);
215
- });
216
- } catch(_) {}
217
- try {
218
- var origToDataURL = HTMLCanvasElement.prototype.toDataURL;
219
- HTMLCanvasElement.prototype.toDataURL = __markNative(function toDataURL() {
220
- var ctx = this.getContext('2d');
221
- if (ctx) {
222
- // Spread noise across canvas to break hash-based fingerprinting.
223
- // Uses a deterministic pattern so it's consistent per page load
224
- // but varies between sessions.
225
- var w = this.width, h = this.height;
226
- if (w > 0 && h > 0) {
227
- var imgData = ctx.getImageData(0, 0, Math.min(w, 4), Math.min(h, 4));
228
- if (imgData && imgData.data) {
229
- for (var __i = 0; __i < imgData.data.length; __i += 4) {
230
- imgData.data[__i] ^= (__canvasNoise + __i) & 0xFF;
231
- }
232
- ctx.putImageData(imgData, 0, 0);
233
- }
234
- }
235
- }
236
- return origToDataURL.apply(this, arguments);
237
- });
238
- } catch(_) {}
239
-
240
- // ── AudioContext fingerprint noise ────────────────────
241
- // Headless Chrome's AudioContext produces slightly different output.
242
- // Subtle noise breaks audio-based fingerprinting.
243
- try {
244
- var __audioSeed = ((Date.now() & 0x1F) | 1);
245
- var origGetChannelData = AudioBuffer.prototype.getChannelData;
246
- AudioBuffer.prototype.getChannelData = __markNative(function getChannelData(channel) {
247
- var data = origGetChannelData.call(this, channel);
248
- for (var __i = 0; __i < data.length; __i += 64) {
249
- data[__i] *= 0.99999;
250
- }
251
- return data;
252
- });
253
- } catch(_) {}
254
-
255
- // ── window outer dimensions ──────────────────────────
256
- // outerWidth/Height = 0 in headless — a well-known bot signal.
257
- // Mirror innerWidth/Height (set by --window-size flag) so the ratio is sane.
258
- try {
259
- if (!window.outerWidth) Object.defineProperty(window, 'outerWidth', { get: () => window.innerWidth || 1920, configurable: true });
260
- if (!window.outerHeight) Object.defineProperty(window, 'outerHeight', { get: () => window.innerHeight || 1080, configurable: true });
261
- } catch(_) {}
262
-
263
- // ── screen properties ─────────────────────────────────
264
- // Headless Chrome often reports an 800x600 screen even when the viewport is
265
- // 1920x1080. Keep screen metrics internally consistent with our launch flags.
266
- try {
267
- Object.defineProperty(screen, 'width', { get: () => 1920, configurable: true });
268
- Object.defineProperty(screen, 'height', { get: () => 1080, configurable: true });
269
- Object.defineProperty(screen, 'availWidth', { get: () => 1920, configurable: true });
270
- Object.defineProperty(screen, 'availHeight', { get: () => 1040, configurable: true });
271
- Object.defineProperty(screen, 'colorDepth', { get: () => 24, configurable: true });
272
- Object.defineProperty(screen, 'pixelDepth', { get: () => 24, configurable: true });
273
- } catch(_) {}
274
-
275
- // ── navigator.userAgentData (UA Client Hints) ─────────
276
- // Derive version from the UA string already set by --user-agent flag so the
277
- // two APIs are always consistent. Removes any "HeadlessChrome" brand entry.
278
- try {
279
- var _uaMajor = (navigator.userAgent.match(new RegExp('Chrome/([0-9]+)')) || [])[1] || '136';
280
- var _uaFull = (navigator.userAgent.match(new RegExp('Chrome/([0-9.]+)')) || [])[1] || (_uaMajor + '.0.0.0');
281
- var _brands = [
282
- { brand: 'Not)A;Brand', version: '99' },
283
- { brand: 'Google Chrome', version: _uaMajor },
284
- { brand: 'Chromium', version: _uaMajor },
285
- ];
286
- Object.defineProperty(navigator, 'userAgentData', {
287
- get: function() {
288
- return {
289
- brands: _brands, mobile: false, platform: 'Windows',
290
- getHighEntropyValues: function() {
291
- return Promise.resolve({
292
- architecture: 'x86', bitness: '64',
293
- brands: _brands,
294
- fullVersionList: [
295
- { brand: 'Not)A;Brand', version: '99.0.0.0' },
296
- { brand: 'Google Chrome', version: _uaFull },
297
- { brand: 'Chromium', version: _uaFull },
298
- ],
299
- mobile: false, model: '', platform: 'Windows',
300
- platformVersion: '15.0.0', uaFullVersion: _uaFull, wow64: false,
301
- });
302
- },
303
- toJSON: function() { return { brands: _brands, mobile: false, platform: 'Windows' }; },
304
- };
305
- },
306
- configurable: true,
307
- });
308
- } catch(_) {}
309
-
310
- // ── CDP Runtime serialization guard ──────────────────
311
- // Sites detect CDP by putting a getter on Error.prototype.stack
312
- // and checking if console.log triggers it (only happens when
313
- // Runtime domain is enabled). We monkey-patch console methods to
314
- // strip custom getters from arguments before they reach CDP.
315
- try {
316
- var _origLog = console.log, _origError = console.error,
317
- _origWarn = console.warn, _origDebug = console.debug,
318
- _origInfo = console.info;
319
- var _safeArg = function(a) {
320
- if (a instanceof Error) {
321
- try { return new Error(a.message); } catch(_) { return a; }
322
- }
323
- return a;
324
- };
325
- console.log = __markNative(function log() { return _origLog.apply(console, Array.prototype.map.call(arguments, _safeArg)); });
326
- console.error = __markNative(function error() { return _origError.apply(console, Array.prototype.map.call(arguments, _safeArg)); });
327
- console.warn = __markNative(function warn() { return _origWarn.apply(console, Array.prototype.map.call(arguments, _safeArg)); });
328
- console.debug = __markNative(function debug() { return _origDebug.apply(console, Array.prototype.map.call(arguments, _safeArg)); });
329
- console.info = __markNative(function info() { return _origInfo.apply(console, Array.prototype.map.call(arguments, _safeArg)); });
330
- } catch(_) {}
331
-
332
- // ── Native function masking ──────────────────────────
333
- // Patched APIs should not stringify as user-defined stealth code.
334
- try {
335
- var __nativeToString = Function.prototype.toString;
336
- Function.prototype.toString = function toString() {
337
- if (__greedyNativeFns.indexOf(this) !== -1) {
338
- var name = this.name || '';
339
- return 'function ' + name + '() { [native code] }';
340
- }
341
- return __nativeToString.call(this);
342
- };
343
- } catch(_) {}
344
- })();
345
- `})])}function f(Q){if(!Q)return[];let $=[],Z=0;while(Z<Q.length&&$.length<10){let K=Q.indexOf("[",Z);if(K===-1)break;let J=Q.indexOf("](",K);if(J===-1)break;let X=J+2,W=-1;for(let U=X;U<Q.length;U++){let Y=Q[U];if(Y===")"){W=U;break}if(/\s/.test(Y))break}if(W!==-1){let U=Q.slice(K+1,J),Y=Q.slice(X,W);if(/^https?:\/\//i.test(Y)&&U){if(!$.some((q)=>q.url===Y))$.push({title:U,url:Y})}Z=W+1}else Z=K+1}return $}function YQ(Q){if(!Q)return!1;for(let $=0;$<Q.length;$++){let Z=Q.charCodeAt($);if(Z<48||Z>57)return!1}return!0}function h(Q){if(!Q)return[];let $=[],Z=new Map,K=/^\[(\d+)\]:\s*(https?:\/\/[^\s"]+)(?:\s+"([^"]*)")?/gm,J;while((J=K.exec(Q))!==null){let W=J[1],U=J[2],Y=J[3]||"";Z.set(W,{url:U,title:Y})}let X=0;while(X<Q.length){let W=Q.indexOf("[",X);if(W===-1)break;let U=Q.indexOf("]",W+1);if(U===-1)break;if(Q[U+1]!=="["){X=W+1;continue}let Y=Q.indexOf("]",U+2);if(Y===-1)break;let q=Q.slice(W+1,U),z=Q.slice(U+2,Y);if(YQ(z)){let D=Z.get(z);if(D&&!$.some((H)=>H.url===D.url))$.push({title:q.trim()||D.title||"",url:D.url})}X=Y+1}return $}function L(Q){let $=Q*0.2,Z=r(-Math.floor($),Math.floor($)+1);return Math.max(50,Math.round(Q+Z))}async function R(Q,$={}){let{timeout:Z=20000,interval:K=600,stableRounds:J=3,selector:X="document.body",minLength:W=0,isStreamingExpr:U="false"}=$,Y=String.raw`
346
- new Promise((resolve, reject) => {
347
- const _deadline = Date.now() + ${Z};
348
- const _baseInterval = ${K};
349
- const _stableRounds = ${J};
350
- const _minLength = ${W};
351
- let _lastLen = -1;
352
- let _stableCount = 0;
353
-
354
- function _jitter(ms) {
355
- return Math.max(50, ms + (Math.random() * ms * 0.4 - ms * 0.2));
356
- }
357
-
358
- function _poll() {
359
- try {
360
- // Re-query DOM each tick — element may not exist at eval start
361
- const el = ${X};
362
- const cur = el?.textContent?.length ?? 0;
363
- const streaming = ${U};
364
- if (streaming) {
365
- if (cur !== _lastLen) _lastLen = cur;
366
- _stableCount = 0;
367
- } else if (cur >= _minLength) {
368
- if (cur === _lastLen) {
369
- _stableCount++;
370
- if (_stableCount >= _stableRounds) { resolve(cur); return; }
371
- } else {
372
- _lastLen = cur;
373
- _stableCount = 0;
374
- }
375
- }
376
- if (Date.now() < _deadline) {
377
- setTimeout(_poll, _jitter(_baseInterval));
378
- } else {
379
- if (_lastLen >= _minLength && !streaming) { resolve(_lastLen); }
380
- else { reject(new Error('Generation did not stabilise within ${Z}ms')); }
381
- }
382
- } catch(e) { reject(e); }
383
- }
384
-
385
- _poll();
386
- })
387
- `,q=await B(["eval",Q,Y],Z+1e4),z=parseInt(q,10)||0;if(z>=W)return z;throw Error(`Generation did not stabilise within ${Z}ms`)}async function v(Q,$,Z=15000,K=500){let J=String.raw`
388
- new Promise((resolve) => {
389
- const _deadline = Date.now() + ${Z};
390
- const _baseInterval = ${K};
391
-
392
- function _jitter(ms) {
393
- return Math.max(50, ms + (Math.random() * ms * 0.4 - ms * 0.2));
394
- }
395
-
396
- function _poll() {
397
- try {
398
- if (document.querySelector('${$}')) { resolve(true); return; }
399
- if (Date.now() < _deadline) { setTimeout(_poll, _jitter(_baseInterval)); }
400
- else { resolve(false); }
401
- } catch(_) { resolve(false); }
402
- }
403
-
404
- _poll();
405
- })
406
- `;return await B(["eval",Q,J],Z+5000)==="true"}async function x(Q){let $=Q.indexOf("--stdin");if($===-1)return Q;let Z=await new Promise((J)=>{let X="";process.stdin.setEncoding("utf8"),process.stdin.on("data",(W)=>X+=W),process.stdin.on("end",()=>J(X.trim()))}),K=[...Q];return K[$]=Z,K}function g(Q){let $=Q.includes("--short"),Z=Q.filter((Y)=>Y!=="--short"),K=Z.indexOf("--tab"),J=K===-1?null:Z[K+1];if(K!==-1)Z=Z.filter((Y,q)=>q!==K&&q!==K+1);let X=Z.indexOf("--locale"),W=X===-1?null:Z[X+1];if(X!==-1)Z=Z.filter((Y,q)=>q!==X&&q!==X+1);return{query:Z.join(" "),tabPrefix:J,short:$,locale:W}}function b(Q,$){if(!Q.length||Q[0]==="--help")process.stderr.write($),process.exit(1)}function m(Q,$,Z=300){if(!$||Q.length<=Z)return Q;let K=Q.slice(0,Z),J=K.lastIndexOf(" ");return J>0?`${K.slice(0,J)}…`:`${K}…`}function u(Q){process.stdout.write(`${JSON.stringify(Q,null,2)}
407
- `)}function O(Q,$,Z=null){if(!Q||typeof Q!=="object")return;let K=Z?` (+${Date.now()-Z}ms)`:"";if(Q.lastStage=$,!Array.isArray(Q.stages))Q.stages=[];Q.stages.push({stage:$,at:Date.now()});let J=Q.engine||"extractor";console.error(`[${J}] stage: ${$}${K}`)}function C({engine:Q,mode:$="headless",clipboardEmpty:Z=null,fallbackUsed:K=null,blockedBy:J=null,verificationResult:X=null,inputReady:W=null,durationMs:U=null,lastStage:Y=null,stages:q=null}={}){return{engine:Q,mode:$,clipboardEmpty:Z,fallbackUsed:K,blockedBy:J,verificationResult:X,inputReady:W,durationMs:U,lastStage:Y,stages:q}}function p(Q,$=null){if($){let Z=JSON.stringify({_envelope:$,error:Q.message});process.stdout.write(`${Z}
408
- `)}process.stderr.write(`Error: ${Q.message}
409
- `),process.exit(1)}import{randomInt as qQ}from"node:crypto";import{existsSync as zQ,readFileSync as DQ}from"node:fs";import HQ from"node:http";var BQ=`
410
- (function() {
411
- // Google consent page (consent.google.com)
412
- var g = document.querySelector('#L2AGLb, button[jsname="b3VHJd"], .tHlp8d');
413
- if (g) { g.click(); return 'google'; }
414
-
415
- // OneTrust (used by many sites including Stack Overflow)
416
- var ot = document.querySelector('#onetrust-accept-btn-handler, .onetrust-accept-btn-handler');
417
- if (ot) { ot.click(); return 'onetrust'; }
418
-
419
- // Generic "accept all" / "agree" buttons
420
- var btns = Array.from(document.querySelectorAll('button, a[role=button]'));
421
- var accept = btns.find(b => /^(accept all|accept cookies|agree|i agree|got it|allow all|allow cookies)$/i.test(b.innerText?.trim()));
422
- if (accept) { accept.click(); return 'generic:' + accept.innerText.trim(); }
423
-
424
- return null;
425
- })()
426
- `,GQ=`
427
- (function() {
428
- var url = document.location.href;
429
-
430
- // --- Google "sorry" page (hard CAPTCHA, can't auto-solve) ---
431
- if (url.includes('/sorry/') || url.includes('sorry.google')) return 'sorry-page';
432
-
433
- // --- Microsoft account verification page ---
434
- if (url.includes('login.microsoftonline.com') || url.includes('login.live.com') || url.includes('account.microsoft.com')) {
435
- var msBtns = Array.from(document.querySelectorAll('button, input[type=submit], a'));
436
- var msVerify = msBtns.find(b => /verify|continue|next/i.test(b.innerText?.trim() || b.value || ''));
437
- if (msVerify) { msVerify.setAttribute('data-gs-verify','1'); return JSON.stringify({t:'sel',s:'[data-gs-verify="1"]',txt:msVerify.innerText?.trim()||msVerify.value}); }
438
- }
439
-
440
- // --- Copilot / modal verification ---
441
- var modal = document.querySelector('[role="dialog"], .b_modal, [class*="verify"], [class*="challenge"]');
442
- if (modal) {
443
- var modalBtns = Array.from(modal.querySelectorAll('button, a[role="button"], input[type="submit"]'));
444
- var actionBtn = modalBtns.find(b => /^(continue|verify|submit|next|i agree|accept|got it)$/i.test(b.innerText?.trim() || b.value || ''));
445
- if (actionBtn) { actionBtn.setAttribute('data-gs-verify','1'); return JSON.stringify({t:'sel',s:'[data-gs-verify="1"]',txt:actionBtn.innerText?.trim()}); }
446
- }
447
-
448
- // --- Turnstile / Cloudflare challenge iframe (return coordinates for humanClickXY) ---
449
- var turnstileIframe = document.querySelector('iframe[src*="challenges.cloudflare.com"], iframe[src*="turnstile"], iframe[title*="challenge"]');
450
- if (turnstileIframe) {
451
- var r = turnstileIframe.getBoundingClientRect();
452
- return JSON.stringify({t:'xy',x:r.left+30,y:r.top+r.height/2});
453
- }
454
-
455
- // --- Cloudflare Turnstile widget inside closed shadow DOM (Copilot, etc.) ---
456
- // The iframe is not queryable from main document, but the host container
457
- // (#cf-turnstile) and the hidden response input are. When only the
458
- // hidden response input matches (no #cf-turnstile host and no visible
459
- // iframe), the actual challenge widget is rendered inside a closed
460
- // shadow DOM and cannot be auto-clicked. Return a sentinel so callers
461
- // know to surface this as needs-human verification instead of wasting
462
- // time on a doomed waitForSelector.
463
- var cfTurnstileHost = document.querySelector('#cf-turnstile');
464
- if (cfTurnstileHost) {
465
- var r2 = cfTurnstileHost.getBoundingClientRect();
466
- return JSON.stringify({t:'xy',x:r2.left+r2.width/2,y:r2.top+r2.height/2});
467
- }
468
- // Hidden cf-chl-widget-*_response input present but no visible host:
469
- // the widget is in closed shadow DOM. Signal this so handleVerification
470
- // can return 'needs-human' rather than 'clear'.
471
- var cfResponseInput = document.querySelector('input[name="cf-turnstile-response"], [id^="cf-chl-widget-"][id$="_response"]');
472
- if (cfResponseInput && cfResponseInput.value === '') {
473
- return 'cf-closed-shadow-dom';
474
- }
475
-
476
- // --- Cloudflare challenge page ---
477
- var cfCheckbox = document.querySelector('#cf-stage input[type="checkbox"], .ctp-checkbox-container input');
478
- if (cfCheckbox) { cfCheckbox.setAttribute('data-gs-verify','1'); return JSON.stringify({t:'sel',s:'[data-gs-verify="1"]',txt:'cloudflare-checkbox'}); }
479
- var cfBtn = document.querySelector('#challenge-form button, .cf-challenge button');
480
- if (cfBtn) { cfBtn.setAttribute('data-gs-verify','1'); return JSON.stringify({t:'sel',s:'[data-gs-verify="1"]',txt:cfBtn.innerText?.trim()}); }
481
-
482
- // --- Microsoft "I am human" button ---
483
- var msHumanBtn = document.querySelector('button[id*="i0"], button[id*="id__"]');
484
- if (msHumanBtn && /verify|human|robot|continue/i.test(msHumanBtn.innerText?.trim())) {
485
- msHumanBtn.setAttribute('data-gs-verify','1'); return JSON.stringify({t:'sel',s:'[data-gs-verify="1"]',txt:msHumanBtn.innerText.trim()});
486
- }
487
-
488
- // --- Generic verify/continue/proceed buttons (catch-all) ---
489
- // IMPORTANT: exclude sign-in / OAuth buttons (e.g. "Continue with Google",
490
- // "Continue with email", "Login or sign up for free"). These appear on
491
- // many sites (Perplexity, ChatGPT, etc.) when the user isn't logged in,
492
- // and clicking them triggers a sign-in flow that takes us to a login
493
- // wall — a much worse outcome than the original search failure we were
494
- // trying to recover from. The exclusion list must cover both OAuth
495
- // providers AND generic "sign in / log in / with email" patterns.
496
- var btns = Array.from(document.querySelectorAll('button, input[type=submit], a[role=button]'));
497
- var verify = btns.find(b => {
498
- var t = (b.innerText?.trim() || b.value || '').toLowerCase();
499
- var isVerifyLike = (t === 'continue' || t === 'proceed' || t === 'next' ||
500
- t.startsWith('verify ') || t.startsWith('human ') || t === 'i am human' || t.includes('robot check')) &&
501
- !t.includes('verified') && !document.querySelector('iframe[src*="recaptcha"]');
502
- if (!isVerifyLike) return false;
503
- // Exclude OAuth / sign-in buttons to prevent accidental login flows
504
- // — covers "Continue with Google", "Continue with Apple", "Continue
505
- // with email", "Login or sign up", "Log in", "Sign in", "Sign up",
506
- // "Single sign-on", and the visible panel "Login or sign up for free"
507
- // text. The previous list missed "email" and "sso" which let the
508
- // auto-click land on the email/SSO sign-in buttons on Perplexity's
509
- // anonymous-mode homepage, navigating us into a login flow.
510
- var isSignIn = new RegExp("sign.?in|log.?in|sign.?up|with\\s+(google|apple|email|github|facebook|microsoft|sso)|sso|auth", "i").test(t);
511
- return !isSignIn;
512
- });
513
- if (verify) { verify.setAttribute('data-gs-verify','1'); return JSON.stringify({t:'sel',s:'[data-gs-verify="1"]',txt:verify.innerText?.trim()||verify.value}); }
514
-
515
- // --- Google reCAPTCHA checkbox ---
516
- var recaptchaCheckbox = document.querySelector('.recaptcha-checkbox-unchecked, input[type=checkbox][id*="recaptcha"]');
517
- if (recaptchaCheckbox) { recaptchaCheckbox.setAttribute('data-gs-verify','1'); return JSON.stringify({t:'sel',s:'[data-gs-verify="1"]',txt:'recaptcha'}); }
518
-
519
- return null;
520
- })()
521
- `,VQ=`
522
- (function() {
523
- var url = document.location.href;
524
- var isVerifyPage = url.includes('/sorry/') ||
525
- url.includes('challenges.cloudflare.com') ||
526
- url.includes('login.microsoftonline.com') ||
527
- document.querySelector('#challenge-running, #challenge-stage, .cf-turnstile, [role="dialog"]');
528
- if (!isVerifyPage) return 'cleared';
529
-
530
- var btns = Array.from(document.querySelectorAll('button, input[type=submit], a[role=button]'));
531
- var btn = btns.find(b => {
532
- var t = (b.innerText?.trim() || b.value || '').toLowerCase();
533
- var isVerifyLike = t.includes('verify') || t.includes('human') || t.includes('robot') || t.includes('continue') || t.includes('next') || t.includes('submit');
534
- if (!isVerifyLike) return false;
535
- var isSignIn = /sign.in|log.in|google|microsoft|apple|facebook|github|auth/i.test(t);
536
- return !isSignIn;
537
- });
538
- if (btn) { btn.setAttribute('data-gs-verify','1'); return JSON.stringify({t:'sel',s:'[data-gs-verify="1"]',txt:btn.innerText?.trim()||btn.value}); }
539
-
540
- var cf = document.querySelector('#cf-stage input[type="checkbox"], .cf-turnstile input');
541
- if (cf) { cf.setAttribute('data-gs-verify','1'); return JSON.stringify({t:'sel',s:'[data-gs-verify="1"]',txt:'turnstile'}); }
542
-
543
- // Cloudflare Turnstile widget inside closed shadow DOM (detected via host container)
544
- var cfTurnstileHost = document.querySelector('#cf-turnstile, [id^="cf-chl-widget-"]');
545
- if (cfTurnstileHost) { return 'still-verifying'; }
546
-
547
- var modal = document.querySelector('[role="dialog"], .b_modal, [class*="verify"]');
548
- if (modal) {
549
- var modalBtn = modal.querySelector('button, a[role="button"]');
550
- if (modalBtn) { modalBtn.setAttribute('data-gs-verify','1'); return JSON.stringify({t:'sel',s:'[data-gs-verify="1"]',txt:modalBtn.innerText?.trim()}); }
551
- }
552
-
553
- return 'still-verifying';
554
- })()
555
- `;async function c(Q,$){let Z=await $(["eval",Q,BQ]).catch(()=>null);if(Z&&Z!=="null")await new Promise((K)=>setTimeout(K,1500))}function j(Q,$){return qQ(Q*1000,$*1000)/1000}async function jQ(Q,$){if(!globalThis.WebSocket)return;let Z=process.env.CDP_PROFILE_DIR;if(!Z)return;let K=`${Z.replaceAll("\\","/")}/DevToolsActivePort`;if(!zQ(K))return;let J=DQ(K,"utf8").trim().split(`
556
- `)[0],X=await new Promise((Y,q)=>{let z=HQ.get(`http://localhost:${J}/json/version`,(D)=>{let H="";D.on("data",(G)=>H+=G),D.on("end",()=>{try{Y(JSON.parse(H))}catch{q(Error("bad JSON"))}})});z.on("error",q),z.setTimeout(1000,()=>{z.destroy(),q(Error("timeout"))})}),W=new globalThis.WebSocket(X.webSocketDebuggerUrl),U=0;await new Promise((Y)=>{W.onopen=async()=>{let q=(H,G)=>new Promise((M)=>{let _=++U,N=(A)=>{if(JSON.parse(A.data).id===_)W.removeEventListener("message",N),M()};W.addEventListener("message",N),W.send(JSON.stringify({id:_,method:H,params:G}))}),z=Q+j(-2,2),D=$+j(-2,2);await q("Input.dispatchMouseEvent",{type:"mouseMoved",x:z,y:D,button:"none"}),await new Promise((H)=>setTimeout(H,j(80,160))),await q("Input.dispatchMouseEvent",{type:"mousePressed",x:z,y:D,button:"left",clickCount:1}),await new Promise((H)=>setTimeout(H,j(30,80))),await q("Input.dispatchMouseEvent",{type:"mouseReleased",x:z+j(-1,1),y:D+j(-1,1),button:"left",clickCount:1}),setTimeout(()=>{W.close(),Y()},200)},W.onerror=()=>Y(),setTimeout(Y,3000)})}async function l(Q,$,Z,K){let J=Number.parseFloat(Z),X=Number.parseFloat(K);if(Number.isNaN(J)||Number.isNaN(X))throw Error(`humanClickXY: invalid coordinates (${Z}, ${K})`);let W={button:"left",clickCount:1,modifiers:0},U=J+j(-3,3),Y=X+j(-3,3);await $(["evalraw",Q,"Input.dispatchMouseEvent",JSON.stringify({...W,type:"mouseMoved",x:U,y:Y})]),await new Promise((G)=>setTimeout(G,j(80,180)));let q=J+j(-2,2),z=X+j(-2,2);await $(["evalraw",Q,"Input.dispatchMouseEvent",JSON.stringify({...W,type:"mousePressed",x:q,y:z})]),await new Promise((G)=>setTimeout(G,j(30,90)));let D=q+j(-1,1),H=z+j(-1,1);return await $(["evalraw",Q,"Input.dispatchMouseEvent",JSON.stringify({...W,type:"mouseReleased",x:D,y:H})]),await jQ(J,X).catch(()=>{}),await new Promise((G)=>setTimeout(G,j(100,300))),`human-clicked at (${J.toFixed(0)}, ${X.toFixed(0)})`}async function NQ(Q,$,Z){let K=await $(["eval",Q,`(function() {
557
- var el = document.querySelector('${Z.replace(/\\/g,"\\\\").replace(/'/g,"\\'")}');
558
- if (!el) return 'null';
559
- var r = el.getBoundingClientRect();
560
- return JSON.stringify({x: r.left + r.width / 2, y: r.top + r.height / 2, w: r.width, h: r.height});
561
- })()`]).catch(()=>"null");if(!K||K==="null")return null;let J=JSON.parse(K);if(J.w===0||J.h===0||J.x===0&&J.y===0)return null;let{x:X,y:W}=J;return l(Q,$,X,W)}function i(Q,$,Z){if(!Z||Z==="null"||Z==="cleared"||Z==="still-verifying"||Z==="cf-closed-shadow-dom")return Promise.resolve("no-challenge");try{let K=JSON.parse(Z);if(K.t==="sel"&&K.s)return process.stderr.write(`[greedysearch] Human-clicking "${K.txt}" via CDP...
562
- `),NQ(Q,$,K.s).then((J)=>J!==null?"clicked":"cant-click");if(K.t==="xy"){if(!K.x&&!K.y)return Promise.resolve("cant-click");return process.stderr.write(`[greedysearch] Human-clicking at (${K.x.toFixed(0)}, ${K.y.toFixed(0)})...
563
- `),l(Q,$,K.x,K.y).then(()=>"clicked")}}catch{}return Promise.resolve("no-challenge")}async function OQ(Q,$){let Z=await $(["eval",Q,GQ]).catch(()=>null);if(Z==="cf-closed-shadow-dom"){let K=await AQ(Q,$).catch(()=>null);if(K)return K;return Z}if(Z&&Z!=="null")return Z;return null}async function AQ(Q,$){if(typeof $!=="function")return null;await $(["evalraw",Q,"DOM.enable","{}"]).catch(()=>{});let Z=await $(["evalraw",Q,"DOM.getDocument",JSON.stringify({depth:-1,pierce:!0})]).catch(()=>null);if(!Z)return null;let K;try{K=JSON.parse(Z)}catch{return null}if(K.error||!K.root)return null;let J=K.root;return await d(J,Q,$)}async function d(Q,$,Z){if(!Q)return null;let K=[];if(Q.shadowRoots&&Q.shadowRoots.length>0)for(let J of Q.shadowRoots)K.push(J);if(Q.children)for(let J of Q.children)K.push(J);for(let J of K){if(J.nodeName==="IFRAME"){let W=J.attributes||[],U=W.indexOf("src"),Y=U>=0?W[U+1]:"";if(Y&&/challenges\.cloudflare\.com|turnstile/i.test(Y)&&J.backendNodeId){let q=await Z(["evalraw",$,"DOM.getBoxModel",JSON.stringify({backendNodeId:J.backendNodeId})]).catch(()=>null);if(!q)continue;let z;try{z=JSON.parse(q)}catch{continue}let D=z?.model?.content||z?.result?.model?.content;if(!D||D.length<8)continue;let H=D[0],G=D[1],M=D[4],_=D[5],N=M-H,A=_-G;if(N<50||A<20)continue;let V=H+N*0.25,I=G+A*0.5;return process.stderr.write(`[greedysearch] Found CF iframe via CDP pierce at (${H.toFixed(0)}, ${G.toFixed(0)}) ${N.toFixed(0)}x${A.toFixed(0)}, clicking checkbox at (${V.toFixed(0)}, ${I.toFixed(0)})
564
- `),JSON.stringify({t:"xy",x:V,y:I})}}let X=await d(J,$,Z);if(X)return X}return null}async function n(Q,$,Z=30000){let K=await OQ(Q,$);if(!K)return"clear";if(K==="sorry-page"){process.stderr.write(`[greedysearch] Google CAPTCHA detected — please solve it in the browser window (waiting up to ${Math.floor(Z/1000)}s)...
565
- `);let X=Date.now()+Z;while(Date.now()<X)if(await new Promise((U)=>setTimeout(U,2000)),!(await $(["eval",Q,"document.location.href"]).catch(()=>"")).includes("/sorry/"))return"cleared-by-user";return"needs-human"}let J=await i(Q,$,K);if(J==="clicked"){await new Promise((W)=>setTimeout(W,2000));let X=Date.now()+Z;while(Date.now()<X){let W=await $(["eval",Q,VQ]).catch(()=>null);if(W==="cleared"||!W||W==="null")return process.stderr.write(`[greedysearch] Verification cleared.
566
- `),"clicked";if(W!=="still-verifying")await i(Q,$,W),await new Promise((U)=>setTimeout(U,2000));else await new Promise((U)=>setTimeout(U,1500))}return process.stderr.write(`[greedysearch] Verification may require manual intervention.
567
- `),"needs-human"}if(J==="cant-click")return process.stderr.write(`[greedysearch] Verification challenge detected but cannot be auto-clicked — please solve it manually in the visible browser window.
568
- `),"needs-human";return"clear"}var w="__chatgptClipboard",P="div.ProseMirror",MQ='button[data-testid="send-button"]',_Q='button[data-testid="copy-turn-action-button"]',F=String.raw`(() => {
1
+ import {
2
+ buildEnvelope,
3
+ cdp,
4
+ clickCopyAndAwaitClipboard,
5
+ formatAnswer,
6
+ getOrOpenTab,
7
+ handleError,
8
+ injectClipboardInterceptor,
9
+ jitter,
10
+ logStage,
11
+ outputJson,
12
+ parseArgs,
13
+ parseSourcesFromMarkdown,
14
+ parseSourcesFromMarkdownRefStyle,
15
+ prepareArgs,
16
+ validateQuery,
17
+ waitForSelector,
18
+ waitForStreamComplete
19
+ } from "./common.mjs";
20
+ import { dismissConsent, handleVerification } from "./consent.mjs";
21
+ const GLOBAL_VAR = "__chatgptClipboard";
22
+ const PROSE_SELECTOR = "div.ProseMirror";
23
+ const SEND_SELECTOR = 'button[data-testid="send-button"]';
24
+ const COPY_SELECTOR = 'button[data-testid="copy-turn-action-button"]';
25
+ const CHATGPT_RESPONSE_SELECTOR = String.raw`(() => {
569
26
  const all = document.querySelectorAll('[data-message-author-role]');
570
27
  let lastUserIdx = -1;
571
28
  for (let i = 0; i < all.length; i++) {
@@ -581,32 +38,120 @@ import{randomInt as r}from"node:crypto";import{spawn as t}from"node:child_proces
581
38
  }
582
39
  }
583
40
  return bestEl;
584
- })()`,FQ=`(() => {
585
- window.${w} = '';
586
- const assistant = ${F};
587
- const assistantCopy = assistant?.querySelector('${_Q}');
41
+ })()`;
42
+ const CHATGPT_ASSISTANT_COPY_CLICK_EXPR = `(() => {
43
+ window.${GLOBAL_VAR} = '';
44
+ const assistant = ${CHATGPT_RESPONSE_SELECTOR};
45
+ const assistantCopy = assistant?.querySelector('${COPY_SELECTOR}');
588
46
  if (assistantCopy) assistantCopy.click();
589
- })()`;async function IQ(Q,$){await B(["click",Q,P]),await new Promise((X)=>setTimeout(X,L(200)));let Z=await B(["eval",Q,`(() => {
590
- const editor = document.querySelector('${P}');
47
+ })()`;
48
+ async function typeAndSubmit(tab, query) {
49
+ await cdp(["click", tab, PROSE_SELECTOR]);
50
+ await new Promise((r) => setTimeout(r, jitter(200)));
51
+ const typeResult = await cdp([
52
+ "eval",
53
+ tab,
54
+ `(() => {
55
+ const editor = document.querySelector('${PROSE_SELECTOR}');
591
56
  if (!editor) return 'no-editor';
592
57
  editor.focus();
593
- const ok = document.execCommand('insertText', false, ${JSON.stringify($)});
58
+ const ok = document.execCommand('insertText', false, ${JSON.stringify(query)});
594
59
  return ok ? 'ok' : 'exec-failed';
595
- })()`],5000);if(Z!=="ok")throw Error(`ChatGPT type failed: ${Z}`);await new Promise((X)=>setTimeout(X,L(300)));let K=`
60
+ })()`
61
+ ], 5000);
62
+ if (typeResult !== "ok") {
63
+ throw new Error(`ChatGPT type failed: ${typeResult}`);
64
+ }
65
+ await new Promise((r) => setTimeout(r, jitter(300)));
66
+ const sendCode = `
596
67
  (() => {
597
- const btn = document.querySelector('${MQ}');
68
+ const btn = document.querySelector('${SEND_SELECTOR}');
598
69
  if (!btn) return 'no-send';
599
70
  if (btn.disabled) return 'send-disabled';
600
71
  btn.click();
601
72
  return 'ok';
602
73
  })()
603
- `,J=await B(["eval",Q,K]);if(J==="no-send")throw Error("ChatGPT send button not found");if(J==="send-disabled")throw Error("ChatGPT send button disabled — query was not registered");await new Promise((X)=>setTimeout(X,L(300)))}var LQ=String.raw`(() => {
604
- const el = ${F};
74
+ `;
75
+ const sendResult = await cdp(["eval", tab, sendCode]);
76
+ if (sendResult === "no-send")
77
+ throw new Error("ChatGPT send button not found");
78
+ if (sendResult === "send-disabled")
79
+ throw new Error("ChatGPT send button disabled — query was not registered");
80
+ await new Promise((r) => setTimeout(r, jitter(300)));
81
+ }
82
+ const CHATGPT_STREAMING_EXPR = String.raw`(() => {
83
+ const el = ${CHATGPT_RESPONSE_SELECTOR};
605
84
  return !!el?.querySelector('.streaming-animation,[data-is-streaming="true"]') ||
606
85
  !!document.querySelector('button[data-testid="stop-button"], button[aria-label="Stop generating"], button[aria-label*="Stop"]');
607
- })()`;async function o(Q,{required:$=!1}={}){try{return await B(["evalraw",Q,"Page.bringToFront","{}"],5000),!0}catch(Z){if(console.error(`[chatgpt] bringToFront failed: ${Z.message}`),$)throw Z;return!1}}async function s(Q,$=35000){await o(Q,{required:!0});let Z=0,K=!1,J=setInterval(()=>{if(K)return;if(Z>=8){console.error("[chatgpt] stopping foreground keepalive after 8 attempts"),clearInterval(J);return}Z++,K=!0,o(Q).finally(()=>{K=!1})},4000);try{return await R(Q,{timeout:$,interval:700,stableRounds:5,minLength:1,selector:F,isStreamingExpr:LQ})}finally{clearInterval(J)}}async function CQ(Q,$=15000){let Z=Date.now()+$,K=0,J=0;while(Date.now()<Z){let X=await B(["eval",Q,`${F}?.innerText?.length ?? 0`],4000).catch(()=>"0"),W=parseInt(X,10)||0;if(W>=1&&W===K){if(J++,J>=3)return W}else K=W,J=0;await new Promise((U)=>setTimeout(U,1200))}return K}async function PQ(Q){let $=await B(["eval",Q,String.raw`
86
+ })()`;
87
+ async function bringToFront(tab, { required = false } = {}) {
88
+ try {
89
+ await cdp(["evalraw", tab, "Page.bringToFront", "{}"], 5000);
90
+ return true;
91
+ } catch (error) {
92
+ console.error(`[chatgpt] bringToFront failed: ${error.message}`);
93
+ if (required)
94
+ throw error;
95
+ return false;
96
+ }
97
+ }
98
+ async function waitForResponse(tab, timeoutMs = 35000) {
99
+ await bringToFront(tab, { required: true });
100
+ let foregroundAttempts = 0;
101
+ let foregroundInFlight = false;
102
+ const keepForeground = setInterval(() => {
103
+ if (foregroundInFlight)
104
+ return;
105
+ if (foregroundAttempts >= 8) {
106
+ console.error("[chatgpt] stopping foreground keepalive after 8 attempts");
107
+ clearInterval(keepForeground);
108
+ return;
109
+ }
110
+ foregroundAttempts++;
111
+ foregroundInFlight = true;
112
+ bringToFront(tab).finally(() => {
113
+ foregroundInFlight = false;
114
+ });
115
+ }, 4000);
116
+ try {
117
+ return await waitForStreamComplete(tab, {
118
+ timeout: timeoutMs,
119
+ interval: 700,
120
+ stableRounds: 5,
121
+ minLength: 1,
122
+ selector: CHATGPT_RESPONSE_SELECTOR,
123
+ isStreamingExpr: CHATGPT_STREAMING_EXPR
124
+ });
125
+ } finally {
126
+ clearInterval(keepForeground);
127
+ }
128
+ }
129
+ async function pollForResponseNodeSide(tab, maxMs = 15000) {
130
+ const deadline = Date.now() + maxMs;
131
+ let lastLen = 0;
132
+ let stableRounds = 0;
133
+ while (Date.now() < deadline) {
134
+ const result = await cdp(["eval", tab, `${CHATGPT_RESPONSE_SELECTOR}?.innerText?.length ?? 0`], 4000).catch(() => "0");
135
+ const len = parseInt(result, 10) || 0;
136
+ if (len >= 1 && len === lastLen) {
137
+ stableRounds++;
138
+ if (stableRounds >= 3)
139
+ return len;
140
+ } else {
141
+ lastLen = len;
142
+ stableRounds = 0;
143
+ }
144
+ await new Promise((r) => setTimeout(r, 1200));
145
+ }
146
+ return lastLen;
147
+ }
148
+ async function extractAnswerFromDom(tab) {
149
+ const raw = await cdp([
150
+ "eval",
151
+ tab,
152
+ String.raw`
608
153
  (() => {
609
- const assistant = ${F};
154
+ const assistant = ${CHATGPT_RESPONSE_SELECTOR};
610
155
  if (!assistant) {
611
156
  const skipped = document.querySelector('[data-message-author-role="user"]')
612
157
  ? 'no-assistant-response'
@@ -632,5 +177,178 @@ import{randomInt as r}from"node:crypto";import{spawn as t}from"node:child_proces
632
177
  }
633
178
  return JSON.stringify({ answer, sources, streaming });
634
179
  })()
635
- `]);try{return JSON.parse($)}catch(Z){return console.error(`[chatgpt] DOM fallback JSON parse failed (${$.length} chars): ${Z.message}`),{answer:"",sources:[],skipped:"parse-error"}}}async function wQ(Q,$){let Z=await S(Q,FQ,w,{timeoutMs:2600,retryClick:600});$.clipboardEmpty=!Z;let K=null;if(!Z){if(K=await PQ(Q),K.streaming)return{answer:"",sources:[],skipped:"still-streaming"};Z=K.answer,$.fallbackUsed=Z?"dom":null}if(Z){let Y=Z.trim(),q=Y.length>=5&&Y.length<=50&&/\s|[.,!?;:]/.test(Y),z=Y.length>50,D=Y.split(/\s+/).filter(Boolean),H=(Y.match(/\b[a-z0-9-]+\.(?:com|org|net|dev|io)\b/gi)||[]).length,G=H>=4&&H>=Math.max(3,Math.floor(D.length/3));if(!q&&!z||G)return console.error(`[chatgpt] DOM fallback answer suspicious (${Y.length} chars, domainRepeats=${H}: ${JSON.stringify(Y.slice(0,80))}) — returning empty for caller to retry`),$.fallbackUsed=null,{answer:"",sources:[],skipped:G?"citation-stub":"header-stub"}}if(!Z)return{answer:"",sources:[],skipped:"no-answer"};let J=f(Z),X=h(Z),W=new Map;for(let Y of[...K?.sources||[],...X,...J])if(Y?.url&&!W.has(Y.url))W.set(Y.url,Y);let U=Array.from(W.values()).slice(0,10);return{answer:Z.trim(),sources:U}}var kQ=`Usage: node extractors/chatgpt.mjs "<query>" [--tab <prefix>]
636
- `;async function yQ(){let Q=await x(process.argv.slice(2));b(Q,kQ);let{query:$,tabPrefix:Z,short:K}=g(Q),J=Date.now(),W={engine:"chatgpt",mode:process.env.GREEDY_SEARCH_VISIBLE==="1"?"visible":"headless",clipboardEmpty:null,fallbackUsed:null,blockedBy:null,verificationResult:null,inputReady:null};try{if(!Z)await B(["list"]);let U=await E(Z),Y=await B(["eval",U,"document.location.href"]).catch(()=>""),q=!1;try{q=new URL(Y).hostname.toLowerCase()==="chatgpt.com"}catch{}if(!q)O(W,"nav",J),await B(["nav",U,"https://chatgpt.com"],20000),await new Promise((V)=>setTimeout(V,600));O(W,"consent",J),await c(U,B),O(W,"verification",J);let z=await n(U,B,1e4);if(W.verificationResult=z,z==="needs-human")throw W.blockedBy="cloudflare-closed-shadow-dom",Error("ChatGPT is showing a Cloudflare Turnstile challenge that auto-clicking could not clear — please solve it in the visible browser window");if(z==="clicked")await new Promise((V)=>setTimeout(V,2500));O(W,"input-wait",J);let D=await v(U,P,8000,400);if(W.inputReady=D,!D){let V=await B(["eval",U,"document.body?.innerText || ''"]).catch(()=>"");if(/sign in|log in|sign up|\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7|login/i.test(V))throw Error("ChatGPT requires sign-in — please sign in in the visible browser window");throw Error("ChatGPT input not found — page may be blocked or in unexpected state")}O(W,"clipboard-inject",J),await T(U,w),O(W,"type-and-submit",J),await IQ(U,$),O(W,"stream-wait",J);let H=0;try{H=await s(U,20000)}catch(V){O(W,"stream-poll-fallback",J),H=await CQ(U,15000)}if(W.assistantTextLen=H,H<1)console.error("[chatgpt] Warning: assistant response may not have completed");O(W,"extract",J);let G;for(let V=0;V<3;V++){if(V>0)try{await s(U,1e4)}catch{}if(G=await wQ(U,W),G.answer)break;if(V<2)console.error(`[chatgpt] Extract attempt ${V+1} returned empty, retrying in 2s...`),await new Promise((I)=>setTimeout(I,2000))}let{answer:M,sources:_,skipped:N}=G;if(!M){W.blockedBy="no-response",W.skipped=N||null;let V="ChatGPT returned no answer — assistant never responded";if(N==="no-user-message")V="ChatGPT still on homepage — query was not submitted";else if(N==="no-assistant-response")V="ChatGPT did not return an assistant response after submit";else if(N==="header-stub"||N==="citation-stub"||N==="still-streaming")V="ChatGPT response did not finish rendering after 3 retries — assistant never rendered the body";throw Error(V)}O(W,"done",J);let A=await B(["eval",U,"document.location.href"]).catch(()=>"https://chatgpt.com");W.durationMs=Date.now()-J,u({query:$,url:A,answer:m(M,K),sources:_,_envelope:C(W)})}catch(U){W.durationMs=Date.now()-J,console.error(`[chatgpt] error during stage '${W.lastStage||"unknown"}': ${U.message}`),p(U,C(W))}}yQ();
180
+ `
181
+ ]);
182
+ try {
183
+ return JSON.parse(raw);
184
+ } catch (error) {
185
+ console.error(`[chatgpt] DOM fallback JSON parse failed (${raw.length} chars): ${error.message}`);
186
+ return { answer: "", sources: [], skipped: "parse-error" };
187
+ }
188
+ }
189
+ async function extractAnswer(tab, env) {
190
+ let answer = await clickCopyAndAwaitClipboard(tab, CHATGPT_ASSISTANT_COPY_CLICK_EXPR, GLOBAL_VAR, { timeoutMs: 2600, retryClick: 600 });
191
+ env.clipboardEmpty = !answer;
192
+ let domFallback = null;
193
+ if (!answer) {
194
+ domFallback = await extractAnswerFromDom(tab);
195
+ if (domFallback.streaming) {
196
+ return { answer: "", sources: [], skipped: "still-streaming" };
197
+ }
198
+ answer = domFallback.answer;
199
+ env.fallbackUsed = answer ? "dom" : null;
200
+ }
201
+ if (answer) {
202
+ const trimmed = answer.trim();
203
+ const looksLikeShortAnswer = trimmed.length >= 5 && trimmed.length <= 50 && /\s|[.,!?;:]/.test(trimmed);
204
+ const looksLikeLongAnswer = trimmed.length > 50;
205
+ const words = trimmed.split(/\s+/).filter(Boolean);
206
+ const domainRepeats = (trimmed.match(/\b[a-z0-9-]+\.(?:com|org|net|dev|io)\b/gi) || []).length;
207
+ const looksLikeCitationStub = domainRepeats >= 4 && domainRepeats >= Math.max(3, Math.floor(words.length / 3));
208
+ if (!looksLikeShortAnswer && !looksLikeLongAnswer || looksLikeCitationStub) {
209
+ console.error(`[chatgpt] DOM fallback answer suspicious (${trimmed.length} chars, domainRepeats=${domainRepeats}: ${JSON.stringify(trimmed.slice(0, 80))}) — returning empty for caller to retry`);
210
+ env.fallbackUsed = null;
211
+ return {
212
+ answer: "",
213
+ sources: [],
214
+ skipped: looksLikeCitationStub ? "citation-stub" : "header-stub"
215
+ };
216
+ }
217
+ }
218
+ if (!answer) {
219
+ return { answer: "", sources: [], skipped: "no-answer" };
220
+ }
221
+ const sourcesInline = parseSourcesFromMarkdown(answer);
222
+ const sourcesRef = parseSourcesFromMarkdownRefStyle(answer);
223
+ const sourceMap = new Map;
224
+ for (const s of [
225
+ ...domFallback?.sources || [],
226
+ ...sourcesRef,
227
+ ...sourcesInline
228
+ ]) {
229
+ if (s?.url && !sourceMap.has(s.url))
230
+ sourceMap.set(s.url, s);
231
+ }
232
+ const sources = Array.from(sourceMap.values()).slice(0, 10);
233
+ return { answer: answer.trim(), sources };
234
+ }
235
+ const USAGE = `Usage: node extractors/chatgpt.mjs "<query>" [--tab <prefix>]
236
+ `;
237
+ async function main() {
238
+ const args = await prepareArgs(process.argv.slice(2));
239
+ validateQuery(args, USAGE);
240
+ const { query, tabPrefix, short } = parseArgs(args);
241
+ const startTime = Date.now();
242
+ const mode = process.env.GREEDY_SEARCH_VISIBLE === "1" ? "visible" : "headless";
243
+ const env = {
244
+ engine: "chatgpt",
245
+ mode,
246
+ clipboardEmpty: null,
247
+ fallbackUsed: null,
248
+ blockedBy: null,
249
+ verificationResult: null,
250
+ inputReady: null
251
+ };
252
+ try {
253
+ if (!tabPrefix)
254
+ await cdp(["list"]);
255
+ const tab = await getOrOpenTab(tabPrefix);
256
+ const currentUrl = await cdp(["eval", tab, "document.location.href"]).catch(() => "");
257
+ let onChatGPT = false;
258
+ try {
259
+ onChatGPT = new URL(currentUrl).hostname.toLowerCase() === "chatgpt.com";
260
+ } catch {}
261
+ if (!onChatGPT) {
262
+ logStage(env, "nav", startTime);
263
+ await cdp(["nav", tab, "https://chatgpt.com"], 20000);
264
+ await new Promise((r) => setTimeout(r, 600));
265
+ }
266
+ logStage(env, "consent", startTime);
267
+ await dismissConsent(tab, cdp);
268
+ logStage(env, "verification", startTime);
269
+ const verificationResult = await handleVerification(tab, cdp, 1e4);
270
+ env.verificationResult = verificationResult;
271
+ if (verificationResult === "needs-human") {
272
+ env.blockedBy = "cloudflare-closed-shadow-dom";
273
+ throw new Error("ChatGPT is showing a Cloudflare Turnstile challenge that auto-clicking could not clear — please solve it in the visible browser window");
274
+ }
275
+ if (verificationResult === "clicked") {
276
+ await new Promise((r) => setTimeout(r, 2500));
277
+ }
278
+ logStage(env, "input-wait", startTime);
279
+ const inputReady = await waitForSelector(tab, PROSE_SELECTOR, 8000, 400);
280
+ env.inputReady = inputReady;
281
+ if (!inputReady) {
282
+ const bodyText = await cdp([
283
+ "eval",
284
+ tab,
285
+ `document.body?.innerText || ''`
286
+ ]).catch(() => "");
287
+ if (/sign in|log in|sign up|\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7|login/i.test(bodyText)) {
288
+ throw new Error("ChatGPT requires sign-in — please sign in in the visible browser window");
289
+ }
290
+ throw new Error("ChatGPT input not found — page may be blocked or in unexpected state");
291
+ }
292
+ logStage(env, "clipboard-inject", startTime);
293
+ await injectClipboardInterceptor(tab, GLOBAL_VAR);
294
+ logStage(env, "type-and-submit", startTime);
295
+ await typeAndSubmit(tab, query);
296
+ logStage(env, "stream-wait", startTime);
297
+ let asstLen = 0;
298
+ try {
299
+ asstLen = await waitForResponse(tab, 20000);
300
+ } catch (e) {
301
+ logStage(env, "stream-poll-fallback", startTime);
302
+ asstLen = await pollForResponseNodeSide(tab, 15000);
303
+ }
304
+ env.assistantTextLen = asstLen;
305
+ if (asstLen < 1) {
306
+ console.error("[chatgpt] Warning: assistant response may not have completed");
307
+ }
308
+ logStage(env, "extract", startTime);
309
+ let extractResult;
310
+ for (let attempt = 0;attempt < 3; attempt++) {
311
+ if (attempt > 0) {
312
+ try {
313
+ await waitForResponse(tab, 1e4);
314
+ } catch {}
315
+ }
316
+ extractResult = await extractAnswer(tab, env);
317
+ if (extractResult.answer)
318
+ break;
319
+ if (attempt < 2) {
320
+ console.error(`[chatgpt] Extract attempt ${attempt + 1} returned empty, retrying in 2s...`);
321
+ await new Promise((r) => setTimeout(r, 2000));
322
+ }
323
+ }
324
+ const { answer, sources, skipped } = extractResult;
325
+ if (!answer) {
326
+ env.blockedBy = "no-response";
327
+ env.skipped = skipped || null;
328
+ let message = "ChatGPT returned no answer — assistant never responded";
329
+ if (skipped === "no-user-message") {
330
+ message = "ChatGPT still on homepage — query was not submitted";
331
+ } else if (skipped === "no-assistant-response") {
332
+ message = "ChatGPT did not return an assistant response after submit";
333
+ } else if (skipped === "header-stub" || skipped === "citation-stub" || skipped === "still-streaming") {
334
+ message = "ChatGPT response did not finish rendering after 3 retries — assistant never rendered the body";
335
+ }
336
+ throw new Error(message);
337
+ }
338
+ logStage(env, "done", startTime);
339
+ const finalUrl = await cdp(["eval", tab, "document.location.href"]).catch(() => "https://chatgpt.com");
340
+ env.durationMs = Date.now() - startTime;
341
+ outputJson({
342
+ query,
343
+ url: finalUrl,
344
+ answer: formatAnswer(answer, short),
345
+ sources,
346
+ _envelope: buildEnvelope(env)
347
+ });
348
+ } catch (e) {
349
+ env.durationMs = Date.now() - startTime;
350
+ console.error(`[chatgpt] error during stage '${env.lastStage || "unknown"}': ${e.message}`);
351
+ handleError(e, buildEnvelope(env));
352
+ }
353
+ }
354
+ main();