@duckmind/dm-windows-x64 0.60.6 → 0.60.8

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,594 +1,344 @@
1
- #!/usr/bin/env node
2
- import{randomInt as KZ}from"node:crypto";import{spawn as QZ}from"node:child_process";import{basename as $Z}from"node:path";function k(Z=process.env,Q=process.execPath){let K=Z.GREEDY_SEARCH_NODE||Z.NODE_BINARY||Z.NODE;if(K?.trim())return K.trim();let $=$Z(Q||"").toLowerCase();if($==="node"||$==="node.exe")return Q;return"node"}import{dirname as HZ,join as XZ}from"node:path";import{fileURLToPath as WZ}from"node:url";var JZ=HZ(WZ(import.meta.url)),UZ=XZ(JZ,"..","bin","cdp.mjs"),YZ=new Set(["list","snap","eval","shot","html","nav","net","click","clickxy","type","loadall","evalraw","browse","stop","--tab"]);function GZ(Z){if(!Array.isArray(Z)||Z.length===0)throw Error("cdp: args must be a non-empty array");if(Z[0]==="test")return Z.map((Q,K)=>R(Q,K));if(!YZ.has(Z[0]))throw Error(`cdp: unknown subcommand '${Z[0]}'`);return Z.map((Q,K)=>R(Q,K))}function R(Z,Q){if(typeof Z!=="string")throw Error(`cdp: argv[${Q}] must be a string (got ${typeof Z})`);if(Z.includes("\x00"))throw Error(`cdp: argv[${Q}] contains a null byte`);return Z}function G(Z,Q=30000){return DZ(Z,null,Q)}function DZ(Z,Q=null,K=30000){let $=GZ(Z);return new Promise((H,J)=>{let X=QZ(k(),[UZ,...$],{stdio:[Q==null?"ignore":"pipe","pipe","pipe"]});if(Q!=null)X.stdin.write(Q),X.stdin.end();let W="",U="";X.stdout.on("data",(D)=>W+=D),X.stderr.on("data",(D)=>U+=D);let Y=setTimeout(()=>{X.kill(),J(Error(`cdp timeout: ${Z[0]}`))},K);X.on("close",(D)=>{if(clearTimeout(Y),D===0)H(W.trim());else J(Error(U.trim()||`cdp exit ${D}`))})})}async function w(Z){if(Z)return Z;let K=(await G(["list"])).split(`
3
- `)[0]?.slice(0,8);if(!K)throw Error("No Chrome tabs found. Is Chrome running with --remote-debugging-port=9222?");let $=await G(["evalraw",K,"Target.createTarget",'{"url":"about:blank"}']),H;try{H=JSON.parse($)}catch(W){throw Error(`Target.createTarget returned invalid JSON: ${W.message}`)}let{targetId:J}=H;if(!J)throw Error("Target.createTarget did not return a targetId");await G(["list"]);let X=J.slice(0,8);try{await OZ(X)}catch(W){process.stderr.write(`[getOrOpenTab] stealth injection failed: ${W.message}
4
- `)}return X}async function f(Z,Q){let K=`
5
- (() => {
6
- window.${Q} = 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.${Q} = 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.${Q} = 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 G(["eval",Z,K])}async function y(Z,Q,K,$={}){let{timeoutMs:H=2600}=$,J=$.retryClick!=null?$.retryClick:Math.floor(H*0.4),X=String.raw`
42
- new Promise((resolve) => {
43
- const _deadline = Date.now() + ${H};
44
- const _retryAt = Date.now() + ${J};
45
- let _retried = false;
46
- function _click() { try { ${Q}; } catch(_) {} }
47
- function _poll() {
48
- const val = window.${K};
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.${K} || ''); }
56
- }
57
- _click();
58
- _poll();
59
- })
60
- `;return await G(["eval",Z,X],H+5000).catch(()=>"")}async function OZ(Z){await G(["evalraw",Z,"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;
1
+ import {
2
+ buildEnvelope,
3
+ cdp,
4
+ clickCopyAndAwaitClipboard,
5
+ formatAnswer,
6
+ getOrOpenTab,
7
+ handleError,
8
+ injectClipboardInterceptor,
9
+ jitter,
10
+ outputJson,
11
+ parseArgs,
12
+ parseSourcesFromMarkdown,
13
+ prepareArgs,
14
+ TIMING,
15
+ validateQuery,
16
+ waitForCopyButton,
17
+ waitForSelector,
18
+ waitForStreamComplete
19
+ } from "./common.mjs";
20
+ import {
21
+ detectVerificationChallenge,
22
+ dismissConsent,
23
+ handleVerification
24
+ } from "./consent.mjs";
25
+ import { SELECTORS } from "./selectors.mjs";
26
+ const S = SELECTORS.bing;
27
+ const GLOBAL_VAR = "__bingClipboard";
28
+ async function detectSignInWall(tab) {
29
+ const code = `(() => {
30
+ if (document.querySelector('#userInput')) return false;
31
+ const links = Array.from(document.querySelectorAll('a[href], button'));
32
+ const hasOAuth = links.some(el => {
33
+ const h = (el.href || el.getAttribute('formaction') || '').toLowerCase();
34
+ return h.includes('login.microsoftonline.com')
35
+ || h.includes('appleid.apple.com')
36
+ || h.includes('accounts.google.com');
37
+ });
38
+ return hasOAuth;
39
+ })()`;
40
+ const result = await cdp(["eval", tab, code]).catch(() => "false");
41
+ return result === "true";
42
+ }
43
+ async function extractAnswer(tab, env, query = "") {
44
+ await waitForCopyButton(tab, S.copyButton, { timeout: 2000 }).catch(() => null);
45
+ await new Promise((r) => setTimeout(r, 800));
46
+ let answer = await clickCopyAndPollClipboard(tab, 5000);
47
+ let clipboardEmpty = !answer;
48
+ if (!answer) {
49
+ console.error("[bing] Clipboard empty, retrying copy/poll...");
50
+ answer = await clickCopyAndPollClipboard(tab, 8000);
51
+ clipboardEmpty = !answer;
94
52
  }
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
- });
53
+ if (!answer) {
54
+ answer = await extractFromVisibleDom(tab, query);
55
+ if (answer)
56
+ env.fallbackUsed = "visibleDom";
141
57
  }
142
- // ── Missing platform APIs (headless often lacks these) ─
58
+ if (!answer) {
59
+ answer = await extractFromAccessibilityTree(tab, query);
60
+ if (answer)
61
+ env.fallbackUsed = "accessibilityTree";
62
+ }
63
+ if (!answer) {
64
+ const iframeResult = await extractFromIframes(tab, env);
65
+ answer = iframeResult.answer;
66
+ if (answer)
67
+ env.fallbackUsed = "iframeDom";
68
+ }
69
+ if (!answer)
70
+ throw new Error("Clipboard interceptor returned empty text");
71
+ env.clipboardEmpty = clipboardEmpty;
72
+ const sources = parseSourcesFromMarkdown(answer);
73
+ return { answer: answer.trim(), sources };
74
+ }
75
+ async function clickCopyAndPollClipboard(tab, timeoutMs) {
76
+ const clickExpr = `(() => {
77
+ window.${GLOBAL_VAR} = '';
78
+ const buttons = document.querySelectorAll('${S.copyButton}');
79
+ buttons[buttons.length - 1]?.click();
80
+ })()`;
81
+ return clickCopyAndAwaitClipboard(tab, clickExpr, GLOBAL_VAR, {
82
+ timeoutMs,
83
+ retryClick: timeoutMs * 2
84
+ });
85
+ }
86
+ async function extractFromVisibleDom(tab, query = "") {
143
87
  try {
144
- if (!navigator.share) {
145
- navigator.share = function() { return Promise.reject(new Error('NotAllowedError')); };
88
+ const bodyText = await cdp([
89
+ "eval",
90
+ tab,
91
+ "document.body?.innerText || ''"
92
+ ]).catch(() => "");
93
+ let answer = "";
94
+ if (bodyText && bodyText.includes("Copilot said")) {
95
+ const copilotSplit = bodyText.split(/Copilot said\s*/i);
96
+ const afterCopilot = copilotSplit.pop() || "";
97
+ answer = cleanCopilotArticleText(truncateAtBoilerplate(afterCopilot));
146
98
  }
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 });
99
+ if (!answer) {
100
+ const articlesJson = await cdp([
101
+ "eval",
102
+ tab,
103
+ `JSON.stringify(Array.from(document.querySelectorAll('article')).map(a => a.innerText || '').filter(Boolean))`
104
+ ]).catch(() => "[]");
105
+ const articles = JSON.parse(articlesJson || "[]");
106
+ answer = pickAnswerArticle(articles, query);
151
107
  }
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
- });
108
+ if (answer.length < 20)
109
+ return "";
110
+ console.error(`[bing] Visible DOM extraction succeeded (${answer.length} chars)`);
111
+ return answer;
112
+ } catch (e) {
113
+ console.error(`[bing] Visible DOM extraction failed: ${e.message}`);
114
+ return "";
174
115
  }
116
+ }
117
+ async function extractFromAccessibilityTree(tab, query = "") {
175
118
  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;
119
+ const snap = await cdp(["snap", tab]).catch(() => "");
120
+ if (!snap || await detectVerificationChallenge(tab, cdp))
121
+ return "";
122
+ const articleLines = [];
123
+ for (const line of snap.split(`
124
+ `)) {
125
+ const trimmed = line.trimStart();
126
+ if (!trimmed.toLowerCase().startsWith("[article]"))
127
+ continue;
128
+ const after = trimmed.slice("[article]".length).trimStart();
129
+ if (after)
130
+ articleLines.push(after);
131
+ }
132
+ if (articleLines.length === 0)
133
+ return "";
134
+ const answer = pickAnswerArticle(articleLines, query);
135
+ if (answer.length < 50)
136
+ return "";
137
+ console.error(`[bing] Accessibility extraction succeeded (${answer.length} chars)`);
138
+ return answer;
139
+ } catch (e) {
140
+ console.error(`[bing] Accessibility extraction failed: ${e.message}`);
141
+ return "";
142
+ }
143
+ }
144
+ function pickAnswerArticle(articles, query = "") {
145
+ const normalizedQuery = normalizeForCompare(query);
146
+ const candidates = articles.map((text) => cleanCopilotArticleText(text)).filter((text) => text.length >= 50).filter((text) => {
147
+ if (!normalizedQuery)
148
+ return true;
149
+ const normalizedText = normalizeForCompare(text);
150
+ return !normalizedText.includes(normalizedQuery) || text.length > query.length * 3;
151
+ });
152
+ return candidates.at(-1) || "";
153
+ }
154
+ function normalizeForCompare(text = "") {
155
+ return String(text).toLocaleLowerCase().replace(/\s+/g, " ").trim();
156
+ }
157
+ const BOILERPLATE_MARKERS = [
158
+ "Good response",
159
+ "Bad response",
160
+ "Share message",
161
+ "Copy message",
162
+ "Read aloud",
163
+ "Regenerate",
164
+ "Edit in a page",
165
+ "Message Copilot",
166
+ "Smart"
167
+ ];
168
+ function truncateAtBoilerplate(text) {
169
+ let earliest = text.length;
170
+ for (const marker of BOILERPLATE_MARKERS) {
171
+ let searchFrom = 0;
172
+ while (searchFrom < text.length) {
173
+ const idx = text.indexOf(marker, searchFrom);
174
+ if (idx === -1)
175
+ break;
176
+ const before = idx > 0 ? text[idx - 1] : "";
177
+ const precededByWhitespace = !before || /\s/.test(before);
178
+ const after = text[idx + marker.length] || "";
179
+ const notFollowedByWord = !after || !/\w/.test(after);
180
+ if (precededByWhitespace && notFollowedByWord) {
181
+ if (idx < earliest)
182
+ earliest = idx;
183
+ break;
192
184
  }
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(_) {}
185
+ searchFrom = idx + marker.length;
186
+ }
187
+ }
188
+ return earliest < text.length ? text.slice(0, earliest) : text;
189
+ }
190
+ function cleanCopilotArticleText(text = "") {
191
+ return truncateAtBoilerplate(String(text).replace(/\s+/g, " ")).trim();
192
+ }
193
+ async function extractFromIframes(mainTab, env) {
210
194
  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(_) {}
195
+ const hasCopyBtn = await cdp([
196
+ "eval",
197
+ mainTab,
198
+ `!!document.querySelector('${S.copyButton}')`
199
+ ]).catch(() => "false");
200
+ if (hasCopyBtn === "true")
201
+ return { answer: "" };
202
+ if (await detectVerificationChallenge(mainTab, cdp)) {
203
+ console.error("[bing] Verification challenge detected — content blocked in headless");
204
+ env.blockedBy = "verification";
205
+ return { answer: "" };
206
+ }
207
+ console.error("[bing] Copy button hidden, no Cloudflare — trying DOM extraction...");
208
+ const targetsRaw = await cdp([
209
+ "evalraw",
210
+ mainTab,
211
+ "Target.getTargets",
212
+ "{}"
213
+ ]);
214
+ const targets = JSON.parse(targetsRaw);
215
+ const targetInfos = targets.targetInfos || [];
216
+ const funFrame = targetInfos.find((t) => t.type === "iframe" && t.url.includes("copilot.fun"));
217
+ if (!funFrame) {
218
+ console.error("[bing] No copilot.fun iframe target found");
219
+ return { answer: "" };
220
+ }
221
+ const funTabId = funFrame.targetId.slice(0, 8);
222
+ const innerText = await cdp([
223
+ "eval",
224
+ funTabId,
225
+ `(()=>{const iframe=document.querySelector('iframe'); if(!iframe) return''; try{const doc=iframe.contentDocument||iframe.contentWindow.document; return doc?.body?.innerText?.trim()||''}catch(e){return''}})()`
226
+ ]).catch(() => "");
227
+ if (innerText) {
228
+ console.error(`[bing] DOM extraction succeeded (${innerText.length} chars)`);
229
+ return { answer: innerText };
230
+ }
231
+ console.error("[bing] DOM extraction returned empty — falling through to visible retry");
232
+ } catch (e) {
233
+ console.error(`[bing] DOM extraction failed: ${e.message}`);
234
+ }
235
+ return { answer: "" };
236
+ }
237
+ const USAGE = `Usage: node extractors/bing-copilot.mjs "<query>" [--tab <prefix>]
238
+ `;
239
+ async function main() {
240
+ const args = await prepareArgs(process.argv.slice(2));
241
+ validateQuery(args, USAGE);
242
+ const { query, tabPrefix, short } = parseArgs(args);
243
+ const startTime = Date.now();
244
+ const mode = process.env.GREEDY_SEARCH_VISIBLE === "1" ? "visible" : "headless";
245
+ const env = {
246
+ engine: "bing",
247
+ mode,
248
+ clipboardEmpty: null,
249
+ fallbackUsed: null,
250
+ blockedBy: null,
251
+ verificationResult: null,
252
+ inputReady: null
253
+ };
217
254
  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
- }
255
+ if (!tabPrefix)
256
+ await cdp(["list"]);
257
+ const tab = await getOrOpenTab(tabPrefix);
258
+ const currentUrl = await cdp(["eval", tab, "document.location.href"]).catch(() => "");
259
+ let onCopilot = false;
260
+ try {
261
+ const host = new URL(currentUrl).hostname.toLowerCase();
262
+ onCopilot = host === "copilot.microsoft.com" || host.endsWith(".copilot.microsoft.com");
263
+ } catch {}
264
+ if (!onCopilot) {
265
+ await cdp(["nav", tab, "https://copilot.microsoft.com/"], 20000);
266
+ await new Promise((r) => setTimeout(r, 600));
267
+ }
268
+ await dismissConsent(tab, cdp);
269
+ const verifyResult = await handleVerification(tab, cdp, 1e4);
270
+ env.verificationResult = verifyResult;
271
+ if (verifyResult === "needs-human") {
272
+ throw new Error("Copilot verification required — please solve it manually in the browser window");
273
+ }
274
+ if (verifyResult === "clicked") {
275
+ await new Promise((r) => setTimeout(r, TIMING.afterVerify));
276
+ const currentUrl = await cdp([
277
+ "eval",
278
+ tab,
279
+ "document.location.href"
280
+ ]).catch(() => "");
281
+ let onCopilot = false;
282
+ try {
283
+ const host = new URL(currentUrl).hostname.toLowerCase();
284
+ onCopilot = host === "copilot.microsoft.com" || host.endsWith(".copilot.microsoft.com");
285
+ } catch {}
286
+ if (!onCopilot) {
287
+ await cdp(["nav", tab, "https://copilot.microsoft.com/"], 20000);
288
+ await new Promise((r) => setTimeout(r, 600));
289
+ await dismissConsent(tab, cdp);
235
290
  }
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;
291
+ }
292
+ if (await detectSignInWall(tab)) {
293
+ throw new Error("Copilot requires sign-in — please sign in with Microsoft, Apple, or Google in the visible browser window. Once signed in, cookies persist for future runs.");
294
+ }
295
+ const inputReady = await waitForSelector(tab, S.input, 15000, 500);
296
+ env.inputReady = inputReady;
297
+ await new Promise((r) => setTimeout(r, jitter(300)));
298
+ if (!inputReady) {
299
+ if (await detectSignInWall(tab)) {
300
+ throw new Error("Copilot requires sign-in — please sign in with Microsoft, Apple, or Google in the visible browser window. Once signed in, cookies persist for future runs.");
250
301
  }
251
- return data;
302
+ throw new Error("Copilot input not found — verification may have failed or page is in unexpected state");
303
+ }
304
+ await injectClipboardInterceptor(tab, GLOBAL_VAR);
305
+ await cdp(["click", tab, S.input]);
306
+ await new Promise((r) => setTimeout(r, TIMING.postClick));
307
+ await cdp(["type", tab, query]);
308
+ await new Promise((r) => setTimeout(r, TIMING.postType));
309
+ await cdp([
310
+ "eval",
311
+ tab,
312
+ `document.querySelector('${S.input}')?.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',bubbles:true,keyCode:13})), 'ok'`
313
+ ]);
314
+ setTimeout(() => {
315
+ handleVerification(tab, cdp, 1e4).then((v) => {
316
+ if (v === "clicked") {
317
+ console.error("[bing] Post-submit verification clicked");
318
+ env.verificationResult = "post-submit-clicked";
319
+ }
320
+ }).catch(() => {});
321
+ }, 2000);
322
+ await waitForStreamComplete(tab, {
323
+ timeout: short ? 25000 : 60000,
324
+ minLength: 50,
325
+ selector: "document.querySelector('article') || document.body"
252
326
  });
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,
327
+ const { answer, sources } = await extractAnswer(tab, env, query);
328
+ if (!answer)
329
+ throw new Error("No answer extracted — Copilot may not have responded");
330
+ const finalUrl = await cdp(["eval", tab, "document.location.href"]).catch(() => "");
331
+ env.durationMs = Date.now() - startTime;
332
+ outputJson({
333
+ query,
334
+ url: finalUrl,
335
+ answer: formatAnswer(answer, short),
336
+ sources,
337
+ _envelope: buildEnvelope(env)
307
338
  });
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 h(Z){if(!Z)return[];let Q=[],K=0;while(K<Z.length&&Q.length<10){let $=Z.indexOf("[",K);if($===-1)break;let H=Z.indexOf("](",$);if(H===-1)break;let J=H+2,X=-1;for(let W=J;W<Z.length;W++){let U=Z[W];if(U===")"){X=W;break}if(/\s/.test(U))break}if(X!==-1){let W=Z.slice($+1,H),U=Z.slice(J,X);if(/^https?:\/\//i.test(U)&&W){if(!Q.some((Y)=>Y.url===U))Q.push({title:W,url:U})}K=X+1}else K=$+1}return Q}var E={postNav:800,postNavSlow:1200,postClick:300,postType:300,inputPoll:400,copyPoll:600,afterVerify:1500};async function v(Z,Q,K={}){let{timeout:$=60000,onPoll:H}=K;if(!H){let W=String.raw`
346
- new Promise((resolve) => {
347
- const _deadline = Date.now() + ${$};
348
- function _poll() {
349
- if (document.querySelector('${Q}')) { resolve(true); return; }
350
- if (Date.now() < _deadline) { setTimeout(_poll, 150); }
351
- else { resolve(false); }
352
- }
353
- _poll();
354
- })
355
- `;if(await G(["eval",Z,W],$+5000).catch(()=>"false")==="true")return;throw Error(`Copy button ('${Q}') did not appear within ${$}ms`)}let J=Date.now()+$,X=0;while(Date.now()<J){if(await G(["eval",Z,`!!document.querySelector('${Q}')`]).catch(()=>"false")==="true")return;await H(++X).catch(()=>null),await new Promise((U)=>setTimeout(U,M(E.copyPoll)))}throw Error(`Copy button ('${Q}') did not appear within ${$}ms`)}function M(Z){let Q=Z*0.2,K=KZ(-Math.floor(Q),Math.floor(Q)+1);return Math.max(50,Math.round(Z+K))}async function g(Z,Q={}){let{timeout:K=20000,interval:$=600,stableRounds:H=3,selector:J="document.body",minLength:X=0,isStreamingExpr:W="false"}=Q,U=String.raw`
356
- new Promise((resolve, reject) => {
357
- const _deadline = Date.now() + ${K};
358
- const _baseInterval = ${$};
359
- const _stableRounds = ${H};
360
- const _minLength = ${X};
361
- let _lastLen = -1;
362
- let _stableCount = 0;
363
-
364
- function _jitter(ms) {
365
- return Math.max(50, ms + (Math.random() * ms * 0.4 - ms * 0.2));
366
- }
367
-
368
- function _poll() {
369
- try {
370
- // Re-query DOM each tick — element may not exist at eval start
371
- const el = ${J};
372
- const cur = el?.textContent?.length ?? 0;
373
- const streaming = ${W};
374
- if (streaming) {
375
- if (cur !== _lastLen) _lastLen = cur;
376
- _stableCount = 0;
377
- } else if (cur >= _minLength) {
378
- if (cur === _lastLen) {
379
- _stableCount++;
380
- if (_stableCount >= _stableRounds) { resolve(cur); return; }
381
- } else {
382
- _lastLen = cur;
383
- _stableCount = 0;
384
- }
385
- }
386
- if (Date.now() < _deadline) {
387
- setTimeout(_poll, _jitter(_baseInterval));
388
- } else {
389
- if (_lastLen >= _minLength && !streaming) { resolve(_lastLen); }
390
- else { reject(new Error('Generation did not stabilise within ${K}ms')); }
391
- }
392
- } catch(e) { reject(e); }
393
- }
394
-
395
- _poll();
396
- })
397
- `,Y=await G(["eval",Z,U],K+1e4),D=parseInt(Y,10)||0;if(D>=X)return D;throw Error(`Generation did not stabilise within ${K}ms`)}async function m(Z,Q,K=15000,$=500){let H=String.raw`
398
- new Promise((resolve) => {
399
- const _deadline = Date.now() + ${K};
400
- const _baseInterval = ${$};
401
-
402
- function _jitter(ms) {
403
- return Math.max(50, ms + (Math.random() * ms * 0.4 - ms * 0.2));
404
- }
405
-
406
- function _poll() {
407
- try {
408
- if (document.querySelector('${Q}')) { resolve(true); return; }
409
- if (Date.now() < _deadline) { setTimeout(_poll, _jitter(_baseInterval)); }
410
- else { resolve(false); }
411
- } catch(_) { resolve(false); }
412
- }
413
-
414
- _poll();
415
- })
416
- `;return await G(["eval",Z,H],K+5000)==="true"}async function b(Z){let Q=Z.indexOf("--stdin");if(Q===-1)return Z;let K=await new Promise((H)=>{let J="";process.stdin.setEncoding("utf8"),process.stdin.on("data",(X)=>J+=X),process.stdin.on("end",()=>H(J.trim()))}),$=[...Z];return $[Q]=K,$}function x(Z){let Q=Z.includes("--short"),K=Z.filter((U)=>U!=="--short"),$=K.indexOf("--tab"),H=$===-1?null:K[$+1];if($!==-1)K=K.filter((U,Y)=>Y!==$&&Y!==$+1);let J=K.indexOf("--locale"),X=J===-1?null:K[J+1];if(J!==-1)K=K.filter((U,Y)=>Y!==J&&Y!==J+1);return{query:K.join(" "),tabPrefix:H,short:Q,locale:X}}function u(Z,Q){if(!Z.length||Z[0]==="--help")process.stderr.write(Q),process.exit(1)}function p(Z,Q,K=300){if(!Q||Z.length<=K)return Z;let $=Z.slice(0,K),H=$.lastIndexOf(" ");return H>0?`${$.slice(0,H)}…`:`${$}…`}function l(Z){process.stdout.write(`${JSON.stringify(Z,null,2)}
417
- `)}function P({engine:Z,mode:Q="headless",clipboardEmpty:K=null,fallbackUsed:$=null,blockedBy:H=null,verificationResult:J=null,inputReady:X=null,durationMs:W=null,lastStage:U=null,stages:Y=null}={}){return{engine:Z,mode:Q,clipboardEmpty:K,fallbackUsed:$,blockedBy:H,verificationResult:J,inputReady:X,durationMs:W,lastStage:U,stages:Y}}function d(Z,Q=null){if(Q){let K=JSON.stringify({_envelope:Q,error:Z.message});process.stdout.write(`${K}
418
- `)}process.stderr.write(`Error: ${Z.message}
419
- `),process.exit(1)}import{randomInt as zZ}from"node:crypto";import{existsSync as _Z,readFileSync as NZ}from"node:fs";import qZ from"node:http";var BZ=`
420
- (function() {
421
- // Google consent page (consent.google.com)
422
- var g = document.querySelector('#L2AGLb, button[jsname="b3VHJd"], .tHlp8d');
423
- if (g) { g.click(); return 'google'; }
424
-
425
- // OneTrust (used by many sites including Stack Overflow)
426
- var ot = document.querySelector('#onetrust-accept-btn-handler, .onetrust-accept-btn-handler');
427
- if (ot) { ot.click(); return 'onetrust'; }
428
-
429
- // Generic "accept all" / "agree" buttons
430
- var btns = Array.from(document.querySelectorAll('button, a[role=button]'));
431
- var accept = btns.find(b => /^(accept all|accept cookies|agree|i agree|got it|allow all|allow cookies)$/i.test(b.innerText?.trim()));
432
- if (accept) { accept.click(); return 'generic:' + accept.innerText.trim(); }
433
-
434
- return null;
435
- })()
436
- `,VZ=`
437
- (function() {
438
- var url = document.location.href;
439
-
440
- // --- Google "sorry" page (hard CAPTCHA, can't auto-solve) ---
441
- if (url.includes('/sorry/') || url.includes('sorry.google')) return 'sorry-page';
442
-
443
- // --- Microsoft account verification page ---
444
- if (url.includes('login.microsoftonline.com') || url.includes('login.live.com') || url.includes('account.microsoft.com')) {
445
- var msBtns = Array.from(document.querySelectorAll('button, input[type=submit], a'));
446
- var msVerify = msBtns.find(b => /verify|continue|next/i.test(b.innerText?.trim() || b.value || ''));
447
- if (msVerify) { msVerify.setAttribute('data-gs-verify','1'); return JSON.stringify({t:'sel',s:'[data-gs-verify="1"]',txt:msVerify.innerText?.trim()||msVerify.value}); }
448
- }
449
-
450
- // --- Copilot / modal verification ---
451
- var modal = document.querySelector('[role="dialog"], .b_modal, [class*="verify"], [class*="challenge"]');
452
- if (modal) {
453
- var modalBtns = Array.from(modal.querySelectorAll('button, a[role="button"], input[type="submit"]'));
454
- var actionBtn = modalBtns.find(b => /^(continue|verify|submit|next|i agree|accept|got it)$/i.test(b.innerText?.trim() || b.value || ''));
455
- if (actionBtn) { actionBtn.setAttribute('data-gs-verify','1'); return JSON.stringify({t:'sel',s:'[data-gs-verify="1"]',txt:actionBtn.innerText?.trim()}); }
339
+ } catch (e) {
340
+ env.durationMs = Date.now() - startTime;
341
+ handleError(e, buildEnvelope(env));
456
342
  }
457
-
458
- // --- Turnstile / Cloudflare challenge iframe (return coordinates for humanClickXY) ---
459
- var turnstileIframe = document.querySelector('iframe[src*="challenges.cloudflare.com"], iframe[src*="turnstile"], iframe[title*="challenge"]');
460
- if (turnstileIframe) {
461
- var r = turnstileIframe.getBoundingClientRect();
462
- return JSON.stringify({t:'xy',x:r.left+30,y:r.top+r.height/2});
463
- }
464
-
465
- // --- Cloudflare Turnstile widget inside closed shadow DOM (Copilot, etc.) ---
466
- // The iframe is not queryable from main document, but the host container
467
- // (#cf-turnstile) and the hidden response input are. When only the
468
- // hidden response input matches (no #cf-turnstile host and no visible
469
- // iframe), the actual challenge widget is rendered inside a closed
470
- // shadow DOM and cannot be auto-clicked. Return a sentinel so callers
471
- // know to surface this as needs-human verification instead of wasting
472
- // time on a doomed waitForSelector.
473
- var cfTurnstileHost = document.querySelector('#cf-turnstile');
474
- if (cfTurnstileHost) {
475
- var r2 = cfTurnstileHost.getBoundingClientRect();
476
- return JSON.stringify({t:'xy',x:r2.left+r2.width/2,y:r2.top+r2.height/2});
477
- }
478
- // Hidden cf-chl-widget-*_response input present but no visible host:
479
- // the widget is in closed shadow DOM. Signal this so handleVerification
480
- // can return 'needs-human' rather than 'clear'.
481
- var cfResponseInput = document.querySelector('input[name="cf-turnstile-response"], [id^="cf-chl-widget-"][id$="_response"]');
482
- if (cfResponseInput && cfResponseInput.value === '') {
483
- return 'cf-closed-shadow-dom';
484
- }
485
-
486
- // --- Cloudflare challenge page ---
487
- var cfCheckbox = document.querySelector('#cf-stage input[type="checkbox"], .ctp-checkbox-container input');
488
- if (cfCheckbox) { cfCheckbox.setAttribute('data-gs-verify','1'); return JSON.stringify({t:'sel',s:'[data-gs-verify="1"]',txt:'cloudflare-checkbox'}); }
489
- var cfBtn = document.querySelector('#challenge-form button, .cf-challenge button');
490
- if (cfBtn) { cfBtn.setAttribute('data-gs-verify','1'); return JSON.stringify({t:'sel',s:'[data-gs-verify="1"]',txt:cfBtn.innerText?.trim()}); }
491
-
492
- // --- Microsoft "I am human" button ---
493
- var msHumanBtn = document.querySelector('button[id*="i0"], button[id*="id__"]');
494
- if (msHumanBtn && /verify|human|robot|continue/i.test(msHumanBtn.innerText?.trim())) {
495
- msHumanBtn.setAttribute('data-gs-verify','1'); return JSON.stringify({t:'sel',s:'[data-gs-verify="1"]',txt:msHumanBtn.innerText.trim()});
496
- }
497
-
498
- // --- Generic verify/continue/proceed buttons (catch-all) ---
499
- // IMPORTANT: exclude sign-in / OAuth buttons (e.g. "Continue with Google",
500
- // "Continue with email", "Login or sign up for free"). These appear on
501
- // many sites (Perplexity, ChatGPT, etc.) when the user isn't logged in,
502
- // and clicking them triggers a sign-in flow that takes us to a login
503
- // wall — a much worse outcome than the original search failure we were
504
- // trying to recover from. The exclusion list must cover both OAuth
505
- // providers AND generic "sign in / log in / with email" patterns.
506
- var btns = Array.from(document.querySelectorAll('button, input[type=submit], a[role=button]'));
507
- var verify = btns.find(b => {
508
- var t = (b.innerText?.trim() || b.value || '').toLowerCase();
509
- var isVerifyLike = (t === 'continue' || t === 'proceed' || t === 'next' ||
510
- t.startsWith('verify ') || t.startsWith('human ') || t === 'i am human' || t.includes('robot check')) &&
511
- !t.includes('verified') && !document.querySelector('iframe[src*="recaptcha"]');
512
- if (!isVerifyLike) return false;
513
- // Exclude OAuth / sign-in buttons to prevent accidental login flows
514
- // — covers "Continue with Google", "Continue with Apple", "Continue
515
- // with email", "Login or sign up", "Log in", "Sign in", "Sign up",
516
- // "Single sign-on", and the visible panel "Login or sign up for free"
517
- // text. The previous list missed "email" and "sso" which let the
518
- // auto-click land on the email/SSO sign-in buttons on Perplexity's
519
- // anonymous-mode homepage, navigating us into a login flow.
520
- var isSignIn = new RegExp("sign.?in|log.?in|sign.?up|with\\s+(google|apple|email|github|facebook|microsoft|sso)|sso|auth", "i").test(t);
521
- return !isSignIn;
522
- });
523
- if (verify) { verify.setAttribute('data-gs-verify','1'); return JSON.stringify({t:'sel',s:'[data-gs-verify="1"]',txt:verify.innerText?.trim()||verify.value}); }
524
-
525
- // --- Google reCAPTCHA checkbox ---
526
- var recaptchaCheckbox = document.querySelector('.recaptcha-checkbox-unchecked, input[type=checkbox][id*="recaptcha"]');
527
- if (recaptchaCheckbox) { recaptchaCheckbox.setAttribute('data-gs-verify','1'); return JSON.stringify({t:'sel',s:'[data-gs-verify="1"]',txt:'recaptcha'}); }
528
-
529
- return null;
530
- })()
531
- `,LZ=`
532
- (function() {
533
- var url = document.location.href;
534
- var isVerifyPage = url.includes('/sorry/') ||
535
- url.includes('challenges.cloudflare.com') ||
536
- url.includes('login.microsoftonline.com') ||
537
- document.querySelector('#challenge-running, #challenge-stage, .cf-turnstile, [role="dialog"]');
538
- if (!isVerifyPage) return 'cleared';
539
-
540
- var btns = Array.from(document.querySelectorAll('button, input[type=submit], a[role=button]'));
541
- var btn = btns.find(b => {
542
- var t = (b.innerText?.trim() || b.value || '').toLowerCase();
543
- var isVerifyLike = t.includes('verify') || t.includes('human') || t.includes('robot') || t.includes('continue') || t.includes('next') || t.includes('submit');
544
- if (!isVerifyLike) return false;
545
- var isSignIn = /sign.in|log.in|google|microsoft|apple|facebook|github|auth/i.test(t);
546
- return !isSignIn;
547
- });
548
- if (btn) { btn.setAttribute('data-gs-verify','1'); return JSON.stringify({t:'sel',s:'[data-gs-verify="1"]',txt:btn.innerText?.trim()||btn.value}); }
549
-
550
- var cf = document.querySelector('#cf-stage input[type="checkbox"], .cf-turnstile input');
551
- if (cf) { cf.setAttribute('data-gs-verify','1'); return JSON.stringify({t:'sel',s:'[data-gs-verify="1"]',txt:'turnstile'}); }
552
-
553
- // Cloudflare Turnstile widget inside closed shadow DOM (detected via host container)
554
- var cfTurnstileHost = document.querySelector('#cf-turnstile, [id^="cf-chl-widget-"]');
555
- if (cfTurnstileHost) { return 'still-verifying'; }
556
-
557
- var modal = document.querySelector('[role="dialog"], .b_modal, [class*="verify"]');
558
- if (modal) {
559
- var modalBtn = modal.querySelector('button, a[role="button"]');
560
- if (modalBtn) { modalBtn.setAttribute('data-gs-verify','1'); return JSON.stringify({t:'sel',s:'[data-gs-verify="1"]',txt:modalBtn.innerText?.trim()}); }
561
- }
562
-
563
- return 'still-verifying';
564
- })()
565
- `;async function S(Z,Q){let K=await Q(["eval",Z,BZ]).catch(()=>null);if(K&&K!=="null")await new Promise(($)=>setTimeout($,1500))}function q(Z,Q){return zZ(Z*1000,Q*1000)/1000}async function jZ(Z,Q){if(!globalThis.WebSocket)return;let K=process.env.CDP_PROFILE_DIR;if(!K)return;let $=`${K.replaceAll("\\","/")}/DevToolsActivePort`;if(!_Z($))return;let H=NZ($,"utf8").trim().split(`
566
- `)[0],J=await new Promise((U,Y)=>{let D=qZ.get(`http://localhost:${H}/json/version`,(z)=>{let _="";z.on("data",(N)=>_+=N),z.on("end",()=>{try{U(JSON.parse(_))}catch{Y(Error("bad JSON"))}})});D.on("error",Y),D.setTimeout(1000,()=>{D.destroy(),Y(Error("timeout"))})}),X=new globalThis.WebSocket(J.webSocketDebuggerUrl),W=0;await new Promise((U)=>{X.onopen=async()=>{let Y=(_,N)=>new Promise((j)=>{let O=++W,V=(B)=>{if(JSON.parse(B.data).id===O)X.removeEventListener("message",V),j()};X.addEventListener("message",V),X.send(JSON.stringify({id:O,method:_,params:N}))}),D=Z+q(-2,2),z=Q+q(-2,2);await Y("Input.dispatchMouseEvent",{type:"mouseMoved",x:D,y:z,button:"none"}),await new Promise((_)=>setTimeout(_,q(80,160))),await Y("Input.dispatchMouseEvent",{type:"mousePressed",x:D,y:z,button:"left",clickCount:1}),await new Promise((_)=>setTimeout(_,q(30,80))),await Y("Input.dispatchMouseEvent",{type:"mouseReleased",x:D+q(-1,1),y:z+q(-1,1),button:"left",clickCount:1}),setTimeout(()=>{X.close(),U()},200)},X.onerror=()=>U(),setTimeout(U,3000)})}async function c(Z,Q,K,$){let H=Number.parseFloat(K),J=Number.parseFloat($);if(Number.isNaN(H)||Number.isNaN(J))throw Error(`humanClickXY: invalid coordinates (${K}, ${$})`);let X={button:"left",clickCount:1,modifiers:0},W=H+q(-3,3),U=J+q(-3,3);await Q(["evalraw",Z,"Input.dispatchMouseEvent",JSON.stringify({...X,type:"mouseMoved",x:W,y:U})]),await new Promise((N)=>setTimeout(N,q(80,180)));let Y=H+q(-2,2),D=J+q(-2,2);await Q(["evalraw",Z,"Input.dispatchMouseEvent",JSON.stringify({...X,type:"mousePressed",x:Y,y:D})]),await new Promise((N)=>setTimeout(N,q(30,90)));let z=Y+q(-1,1),_=D+q(-1,1);return await Q(["evalraw",Z,"Input.dispatchMouseEvent",JSON.stringify({...X,type:"mouseReleased",x:z,y:_})]),await jZ(H,J).catch(()=>{}),await new Promise((N)=>setTimeout(N,q(100,300))),`human-clicked at (${H.toFixed(0)}, ${J.toFixed(0)})`}async function EZ(Z,Q,K){let $=await Q(["eval",Z,`(function() {
567
- var el = document.querySelector('${K.replace(/\\/g,"\\\\").replace(/'/g,"\\'")}');
568
- if (!el) return 'null';
569
- var r = el.getBoundingClientRect();
570
- return JSON.stringify({x: r.left + r.width / 2, y: r.top + r.height / 2, w: r.width, h: r.height});
571
- })()`]).catch(()=>"null");if(!$||$==="null")return null;let H=JSON.parse($);if(H.w===0||H.h===0||H.x===0&&H.y===0)return null;let{x:J,y:X}=H;return c(Z,Q,J,X)}function i(Z,Q,K){if(!K||K==="null"||K==="cleared"||K==="still-verifying"||K==="cf-closed-shadow-dom")return Promise.resolve("no-challenge");try{let $=JSON.parse(K);if($.t==="sel"&&$.s)return process.stderr.write(`[greedysearch] Human-clicking "${$.txt}" via CDP...
572
- `),EZ(Z,Q,$.s).then((H)=>H!==null?"clicked":"cant-click");if($.t==="xy"){if(!$.x&&!$.y)return Promise.resolve("cant-click");return process.stderr.write(`[greedysearch] Human-clicking at (${$.x.toFixed(0)}, ${$.y.toFixed(0)})...
573
- `),c(Z,Q,$.x,$.y).then(()=>"clicked")}}catch{}return Promise.resolve("no-challenge")}async function A(Z,Q){let K=await Q(["eval",Z,VZ]).catch(()=>null);if(K==="cf-closed-shadow-dom"){let $=await AZ(Z,Q).catch(()=>null);if($)return $;return K}if(K&&K!=="null")return K;return null}async function AZ(Z,Q){if(typeof Q!=="function")return null;await Q(["evalraw",Z,"DOM.enable","{}"]).catch(()=>{});let K=await Q(["evalraw",Z,"DOM.getDocument",JSON.stringify({depth:-1,pierce:!0})]).catch(()=>null);if(!K)return null;let $;try{$=JSON.parse(K)}catch{return null}if($.error||!$.root)return null;let H=$.root;return await o(H,Z,Q)}async function o(Z,Q,K){if(!Z)return null;let $=[];if(Z.shadowRoots&&Z.shadowRoots.length>0)for(let H of Z.shadowRoots)$.push(H);if(Z.children)for(let H of Z.children)$.push(H);for(let H of $){if(H.nodeName==="IFRAME"){let X=H.attributes||[],W=X.indexOf("src"),U=W>=0?X[W+1]:"";if(U&&/challenges\.cloudflare\.com|turnstile/i.test(U)&&H.backendNodeId){let Y=await K(["evalraw",Q,"DOM.getBoxModel",JSON.stringify({backendNodeId:H.backendNodeId})]).catch(()=>null);if(!Y)continue;let D;try{D=JSON.parse(Y)}catch{continue}let z=D?.model?.content||D?.result?.model?.content;if(!z||z.length<8)continue;let _=z[0],N=z[1],j=z[4],O=z[5],V=j-_,B=O-N;if(V<50||B<20)continue;let C=_+V*0.25,I=N+B*0.5;return process.stderr.write(`[greedysearch] Found CF iframe via CDP pierce at (${_.toFixed(0)}, ${N.toFixed(0)}) ${V.toFixed(0)}x${B.toFixed(0)}, clicking checkbox at (${C.toFixed(0)}, ${I.toFixed(0)})
574
- `),JSON.stringify({t:"xy",x:C,y:I})}}let J=await o(H,Q,K);if(J)return J}return null}async function T(Z,Q,K=30000){let $=await A(Z,Q);if(!$)return"clear";if($==="sorry-page"){process.stderr.write(`[greedysearch] Google CAPTCHA detected — please solve it in the browser window (waiting up to ${Math.floor(K/1000)}s)...
575
- `);let J=Date.now()+K;while(Date.now()<J)if(await new Promise((W)=>setTimeout(W,2000)),!(await Q(["eval",Z,"document.location.href"]).catch(()=>"")).includes("/sorry/"))return"cleared-by-user";return"needs-human"}let H=await i(Z,Q,$);if(H==="clicked"){await new Promise((X)=>setTimeout(X,2000));let J=Date.now()+K;while(Date.now()<J){let X=await Q(["eval",Z,LZ]).catch(()=>null);if(X==="cleared"||!X||X==="null")return process.stderr.write(`[greedysearch] Verification cleared.
576
- `),"clicked";if(X!=="still-verifying")await i(Z,Q,X),await new Promise((W)=>setTimeout(W,2000));else await new Promise((W)=>setTimeout(W,1500))}return process.stderr.write(`[greedysearch] Verification may require manual intervention.
577
- `),"needs-human"}if(H==="cant-click")return process.stderr.write(`[greedysearch] Verification challenge detected but cannot be auto-clicked — please solve it manually in the visible browser window.
578
- `),"needs-human";return"clear"}var n={perplexity:{input:"#ask-input",copyButton:null,sourceItem:"[data-pplx-citation-url]",sourceLink:"a",consent:"#onetrust-accept-btn-handler"},bing:{input:"#userInput",copyButton:'button[data-testid="copy-ai-message-button"]',sourceLink:'a[href^="http"][target="_blank"]',sourceExclude:"copilot.microsoft.com",consent:"#onetrust-accept-btn-handler"},google:{answerContainer:".pWvJNd",sourceLink:'a[href^="http"]',sourceExclude:["google.","gstatic","googleapis"],sourceHeadingParent:"[data-snhf]",consent:'#L2AGLb, button[jsname="b3VHJd"], .tHlp8d'},gemini:{input:"rich-textarea .ql-editor",copyButton:'button:has(mat-icon[data-mat-icon-name="copy"])',sendButton:'button:has(mat-icon[data-mat-icon-name="arrow_upward"]), [data-test-id="send-button"], .send-button',sourcesSidebarButton:"button.legacy-sources-sidebar-button",sourcesExclude:["gemini.google","gstatic","google.com/search"],citationButtonPattern:'button[aria-label*="citation from"]',citationNameRegex:/from\s{1,20}([^.]{1,200})\.\s/}};var L=n.bing,F="__bingClipboard";async function s(Z){return await G(["eval",Z,`(() => {
579
- if (document.querySelector('#userInput')) return false;
580
- const links = Array.from(document.querySelectorAll('a[href], button'));
581
- const hasOAuth = links.some(el => {
582
- const h = (el.href || el.getAttribute('formaction') || '').toLowerCase();
583
- return h.includes('login.microsoftonline.com')
584
- || h.includes('appleid.apple.com')
585
- || h.includes('accounts.google.com');
586
- });
587
- return hasOAuth;
588
- })()`]).catch(()=>"false")==="true"}async function MZ(Z,Q,K=""){await v(Z,L.copyButton,{timeout:2000}).catch(()=>null),await new Promise((X)=>setTimeout(X,800));let $=await a(Z,5000),H=!$;if(!$)console.error("[bing] Clipboard empty, retrying copy/poll..."),$=await a(Z,8000),H=!$;if(!$){if($=await PZ(Z,K),$)Q.fallbackUsed="visibleDom"}if(!$){if($=await SZ(Z,K),$)Q.fallbackUsed="accessibilityTree"}if(!$){if($=(await FZ(Z,Q)).answer,$)Q.fallbackUsed="iframeDom"}if(!$)throw Error("Clipboard interceptor returned empty text");Q.clipboardEmpty=H;let J=h($);return{answer:$.trim(),sources:J}}async function a(Z,Q){let K=`(() => {
589
- window.${F} = '';
590
- const buttons = document.querySelectorAll('${L.copyButton}');
591
- buttons[buttons.length - 1]?.click();
592
- })()`;return y(Z,K,F,{timeoutMs:Q,retryClick:Q*2})}async function PZ(Z,Q=""){try{let K=await G(["eval",Z,"document.body?.innerText || ''"]).catch(()=>""),$="";if(K&&K.includes("Copilot said")){let J=K.split(/Copilot said\s*/i).pop()||"";$=ZZ(e(J))}if(!$){let H=await G(["eval",Z,"JSON.stringify(Array.from(document.querySelectorAll('article')).map(a => a.innerText || '').filter(Boolean))"]).catch(()=>"[]"),J=JSON.parse(H||"[]");$=t(J,Q)}if($.length<20)return"";return console.error(`[bing] Visible DOM extraction succeeded (${$.length} chars)`),$}catch(K){return console.error(`[bing] Visible DOM extraction failed: ${K.message}`),""}}async function SZ(Z,Q=""){try{let K=await G(["snap",Z]).catch(()=>"");if(!K||await A(Z,G))return"";let $=[];for(let J of K.split(`
593
- `)){let X=J.trimStart();if(!X.toLowerCase().startsWith("[article]"))continue;let W=X.slice(9).trimStart();if(W)$.push(W)}if($.length===0)return"";let H=t($,Q);if(H.length<50)return"";return console.error(`[bing] Accessibility extraction succeeded (${H.length} chars)`),H}catch(K){return console.error(`[bing] Accessibility extraction failed: ${K.message}`),""}}function t(Z,Q=""){let K=r(Q);return Z.map((H)=>ZZ(H)).filter((H)=>H.length>=50).filter((H)=>{if(!K)return!0;return!r(H).includes(K)||H.length>Q.length*3}).at(-1)||""}function r(Z=""){return String(Z).toLocaleLowerCase().replace(/\s+/g," ").trim()}var TZ=["Good response","Bad response","Share message","Copy message","Read aloud","Regenerate","Edit in a page","Message Copilot","Smart"];function e(Z){let Q=Z.length;for(let K of TZ){let $=0;while($<Z.length){let H=Z.indexOf(K,$);if(H===-1)break;let J=H>0?Z[H-1]:"",X=!J||/\s/.test(J),W=Z[H+K.length]||"",U=!W||!/\w/.test(W);if(X&&U){if(H<Q)Q=H;break}$=H+K.length}}return Q<Z.length?Z.slice(0,Q):Z}function ZZ(Z=""){return e(String(Z).replace(/\s+/g," ")).trim()}async function FZ(Z,Q){try{if(await G(["eval",Z,`!!document.querySelector('${L.copyButton}')`]).catch(()=>"false")==="true")return{answer:""};if(await A(Z,G))return console.error("[bing] Verification challenge detected — content blocked in headless"),Q.blockedBy="verification",{answer:""};console.error("[bing] Copy button hidden, no Cloudflare — trying DOM extraction...");let $=await G(["evalraw",Z,"Target.getTargets","{}"]),X=(JSON.parse($).targetInfos||[]).find((Y)=>Y.type==="iframe"&&Y.url.includes("copilot.fun"));if(!X)return console.error("[bing] No copilot.fun iframe target found"),{answer:""};let W=X.targetId.slice(0,8),U=await G(["eval",W,"(()=>{const iframe=document.querySelector('iframe'); if(!iframe) return''; try{const doc=iframe.contentDocument||iframe.contentWindow.document; return doc?.body?.innerText?.trim()||''}catch(e){return''}})()"]).catch(()=>"");if(U)return console.error(`[bing] DOM extraction succeeded (${U.length} chars)`),{answer:U};console.error("[bing] DOM extraction returned empty — falling through to visible retry")}catch(K){console.error(`[bing] DOM extraction failed: ${K.message}`)}return{answer:""}}var CZ=`Usage: node extractors/bing-copilot.mjs "<query>" [--tab <prefix>]
594
- `;async function IZ(){let Z=await b(process.argv.slice(2));u(Z,CZ);let{query:Q,tabPrefix:K,short:$}=x(Z),H=Date.now(),X={engine:"bing",mode:process.env.GREEDY_SEARCH_VISIBLE==="1"?"visible":"headless",clipboardEmpty:null,fallbackUsed:null,blockedBy:null,verificationResult:null,inputReady:null};try{if(!K)await G(["list"]);let W=await w(K),U=await G(["eval",W,"document.location.href"]).catch(()=>""),Y=!1;try{let O=new URL(U).hostname.toLowerCase();Y=O==="copilot.microsoft.com"||O.endsWith(".copilot.microsoft.com")}catch{}if(!Y)await G(["nav",W,"https://copilot.microsoft.com/"],20000),await new Promise((O)=>setTimeout(O,600));await S(W,G);let D=await T(W,G,1e4);if(X.verificationResult=D,D==="needs-human")throw Error("Copilot verification required — please solve it manually in the browser window");if(D==="clicked"){await new Promise((B)=>setTimeout(B,E.afterVerify));let O=await G(["eval",W,"document.location.href"]).catch(()=>""),V=!1;try{let B=new URL(O).hostname.toLowerCase();V=B==="copilot.microsoft.com"||B.endsWith(".copilot.microsoft.com")}catch{}if(!V)await G(["nav",W,"https://copilot.microsoft.com/"],20000),await new Promise((B)=>setTimeout(B,600)),await S(W,G)}if(await s(W))throw Error("Copilot requires sign-in — please sign in with Microsoft, Apple, or Google in the visible browser window. Once signed in, cookies persist for future runs.");let z=await m(W,L.input,15000,500);if(X.inputReady=z,await new Promise((O)=>setTimeout(O,M(300))),!z){if(await s(W))throw Error("Copilot requires sign-in — please sign in with Microsoft, Apple, or Google in the visible browser window. Once signed in, cookies persist for future runs.");throw Error("Copilot input not found — verification may have failed or page is in unexpected state")}await f(W,F),await G(["click",W,L.input]),await new Promise((O)=>setTimeout(O,E.postClick)),await G(["type",W,Q]),await new Promise((O)=>setTimeout(O,E.postType)),await G(["eval",W,`document.querySelector('${L.input}')?.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',bubbles:true,keyCode:13})), 'ok'`]),setTimeout(()=>{T(W,G,1e4).then((O)=>{if(O==="clicked")console.error("[bing] Post-submit verification clicked"),X.verificationResult="post-submit-clicked"}).catch(()=>{})},2000),await g(W,{timeout:$?25000:60000,minLength:50,selector:"document.querySelector('article') || document.body"});let{answer:_,sources:N}=await MZ(W,X,Q);if(!_)throw Error("No answer extracted — Copilot may not have responded");let j=await G(["eval",W,"document.location.href"]).catch(()=>"");X.durationMs=Date.now()-H,l({query:Q,url:j,answer:p(_,$),sources:N,_envelope:P(X)})}catch(W){X.durationMs=Date.now()-H,d(W,P(X))}}IZ();
343
+ }
344
+ main();