@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,349 +1,39 @@
1
- #!/usr/bin/env node
2
- import{randomInt as rt}from"node:crypto";import{spawn as it}from"node:child_process";import{basename as ot}from"node:path";import{dirname as lt,join as ft}from"node:path";import{fileURLToPath as at}from"node:url";function ut(n=process.env,t=process.execPath){let e=n.GREEDY_SEARCH_NODE||n.NODE_BINARY||n.NODE;if(e?.trim())return e.trim();let i=ot(t||"").toLowerCase();if(i==="node"||i==="node.exe")return t;return"node"}var gt=lt(at(import.meta.url)),st=ft(gt,"..","bin","cdp.mjs"),ht=new Set(["list","snap","eval","shot","html","nav","net","click","clickxy","type","loadall","evalraw","browse","stop","--tab"]);function ct(n){if(!Array.isArray(n)||n.length===0)throw Error("cdp: args must be a non-empty array");if(n[0]==="test")return n.map((t,e)=>_n(t,e));if(!ht.has(n[0]))throw Error(`cdp: unknown subcommand '${n[0]}'`);return n.map((t,e)=>_n(t,e))}function _n(n,t){if(typeof n!=="string")throw Error(`cdp: argv[${t}] must be a string (got ${typeof n})`);if(n.includes("\x00"))throw Error(`cdp: argv[${t}] contains a null byte`);return n}function a(n,t=30000){return en(n,null,t)}function en(n,t=null,e=30000){let i=ct(n);return new Promise((r,o)=>{let u=it(ut(),[st,...i],{stdio:[t==null?"ignore":"pipe","pipe","pipe"]});if(t!=null)u.stdin.write(t),u.stdin.end();let l="",g="";u.stdout.on("data",(s)=>l+=s),u.stderr.on("data",(s)=>g+=s);let f=setTimeout(()=>{u.kill(),o(Error(`cdp timeout: ${n[0]}`))},e);u.on("close",(s)=>{if(clearTimeout(f),s===0)r(l.trim());else o(Error(g.trim()||`cdp exit ${s}`))})})}function mn(n,t){let e=(u)=>String(u??"").replace(/\s+/g," ").trim(),i=e(n),r=e(t);if(!r)return!1;if(i.length<Math.ceil(r.length*0.8))return!1;let o=Math.min(40,r.length);return i.includes(r.slice(0,o))&&i.includes(r.slice(-o))}async function $n(n){if(n)return n;let t=(await a(["list"])).split(`
3
- `)[0]?.slice(0,8);if(!t)throw Error("No Chrome tabs found. Is Chrome running with --remote-debugging-port=9222?");let e=await a(["evalraw",t,"Target.createTarget",'{"url":"about:blank"}']),i;try{i=JSON.parse(e)}catch(u){throw Error(`Target.createTarget returned invalid JSON: ${u.message}`)}let{targetId:r}=i;if(!r)throw Error("Target.createTarget did not return a targetId");await a(["list"]);let o=r.slice(0,8);try{await bn(o)}catch(u){process.stderr.write(`[getOrOpenTab] stealth injection failed: ${u.message}
4
- `)}return o}async function bn(n){await a(["evalraw",n,"Page.addScriptToEvaluateOnNewDocument",JSON.stringify({source:`
5
- (function() {
6
- // ── Runtime.enable / CDP detection masking ──────────────
7
- try { delete window.__REBROWSER_RUNTIME_ENABLE; } catch(_) {}
8
- try { delete window.__REBROWSER_DEVTOOLS; } catch(_) {}
9
- try { delete window.__nightmare; } catch(_) {}
10
- try { delete window.__phantom; } catch(_) {}
11
- try { delete window.callPhantom; } catch(_) {}
12
- try { delete window._phantom; } catch(_) {}
13
- try { delete window.Buffer; } catch(_) {}
14
-
15
- // Real Chrome without automation should not expose navigator.webdriver at all.
16
- // A literal false or an own-property getter returning undefined is itself a
17
- // common stealth tell; remove both instance and prototype properties when the
18
- // descriptor is configurable (as it is with --disable-blink-features).
19
- try { delete navigator.webdriver; } catch(_) {}
20
- try { delete Navigator.prototype.webdriver; } catch(_) {}
21
- Object.defineProperty(navigator, 'vendor', { get: () => 'Google Inc.', configurable: true });
22
- Object.defineProperty(navigator, 'platform', { get: () => 'Win32', configurable: true });
23
- Object.defineProperty(navigator, 'maxTouchPoints', { get: () => 0, configurable: true });
24
- Object.defineProperty(navigator, 'pdfViewerEnabled', { get: () => true, configurable: true });
25
- Object.defineProperty(navigator, 'productSub', { get: () => '20030107', configurable: true });
26
- Object.defineProperty(navigator, 'product', { get: () => 'Gecko', configurable: true });
27
- var __greedyMimeTypes = null;
28
- function __makeMimeTypes() {
29
- var pdf = { type: 'application/pdf', suffixes: 'pdf', description: 'Portable Document Format', enabledPlugin: null };
30
- var textPdf = { type: 'text/pdf', suffixes: 'pdf', description: 'Portable Document Format', enabledPlugin: null };
31
- try { Object.setPrototypeOf(pdf, MimeType.prototype); } catch(_) {}
32
- try { Object.setPrototypeOf(textPdf, MimeType.prototype); } catch(_) {}
33
- var m = [pdf, textPdf];
34
- try { Object.setPrototypeOf(m, MimeTypeArray.prototype); } catch(_) {}
35
- m.item = function item(i) { return this[i] || null; };
36
- m.namedItem = function namedItem(name) { return Array.prototype.find.call(this, function(x) { return x && x.type === name; }) || null; };
37
- return m;
38
- }
39
- Object.defineProperty(navigator, 'plugins', {
40
- get: () => {
41
- __greedyMimeTypes = __greedyMimeTypes || __makeMimeTypes();
42
- var plugin0 = { name: 'Chrome PDF Plugin', filename: 'internal-pdf-viewer', description: 'Portable Document Format' };
43
- var plugin1 = { name: 'Chrome PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai', description: '' };
44
- var plugin2 = { name: 'Native Client', filename: 'internal-nacl-plugin', description: '' };
45
- try { Object.setPrototypeOf(plugin0, Plugin.prototype); } catch(_) {}
46
- try { Object.setPrototypeOf(plugin1, Plugin.prototype); } catch(_) {}
47
- try { Object.setPrototypeOf(plugin2, Plugin.prototype); } catch(_) {}
48
- var p = [plugin0, plugin1, plugin2];
49
- p.item = function item(i) { return this[i] || null; };
50
- p.namedItem = function namedItem(name) { return Array.prototype.find.call(this, function(x) { return x && x.name === name; }) || null; };
51
- p.refresh = function refresh() {};
52
- try { Object.setPrototypeOf(p, PluginArray.prototype); } catch(_) {}
53
- try {
54
- __greedyMimeTypes[0].enabledPlugin = p[0];
55
- __greedyMimeTypes[1].enabledPlugin = p[0];
56
- } catch(_) {}
57
- return p;
58
- },
59
- configurable: true,
60
- });
61
- Object.defineProperty(navigator, 'mimeTypes', {
62
- get: () => {
63
- __greedyMimeTypes = __greedyMimeTypes || __makeMimeTypes();
64
- return __greedyMimeTypes;
65
- },
66
- configurable: true,
67
- });
68
- Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'], configurable: true });
69
- try {
70
- Object.defineProperty(navigator, 'connection', { get: () => ({ effectiveType: '4g', rtt: 50, downlink: 10, downlinkMax: Infinity, saveData: false }), configurable: true });
71
- } catch(_) {}
72
- if (!navigator.mediaDevices) {
73
- Object.defineProperty(navigator, 'mediaDevices', {
74
- get: () => ({
75
- enumerateDevices: () => Promise.resolve([
76
- { deviceId: 'default', kind: 'audioinput', label: '', groupId: 'default' },
77
- { deviceId: 'default', kind: 'audiooutput', label: '', groupId: 'default' },
78
- { deviceId: '', kind: 'videoinput', label: '', groupId: '' },
79
- ]),
80
- getUserMedia: () => Promise.reject(new DOMException('NotAllowedError')),
81
- getDisplayMedia: () => Promise.reject(new DOMException('NotAllowedError')),
82
- }),
83
- configurable: true,
84
- });
85
- }
86
- // ── Missing platform APIs (headless often lacks these) ─
87
- try {
88
- if (!navigator.share) {
89
- navigator.share = function() { return Promise.reject(new Error('NotAllowedError')); };
90
- }
91
- } catch(_) {}
92
- try {
93
- if (!navigator.contentIndex) {
94
- Object.defineProperty(navigator, 'contentIndex', { get: () => ({ add: function() {}, delete: function() {}, getAll: function() { return Promise.resolve([]); } }), configurable: true });
95
- }
96
- } catch(_) {}
97
-
98
- if (!window.chrome) {
99
- window.chrome = {
100
- app: { isInstalled: false, InstallState: {}, RunningState: {} },
101
- runtime: {
102
- OnInstalledReason: {}, OnRestartRequiredReason: {}, PlatformArch: {}, PlatformNaclArch: {}, PlatformOs: {}, RequestUpdateCheckStatus: {},
103
- connect: () => ({}), sendMessage: () => {}, onMessage: { addListener: () => {} }
104
- },
105
- 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' }; },
106
- csi: function() { var t = Date.now(); return { onloadT: t - 2000, startE: t - 5000, pageT: 'back', tran: 2 }; },
107
- };
108
- }
109
- var __greedyNativeFns = [];
110
- function __markNative(fn) { try { __greedyNativeFns.push(fn); } catch(_) {} return fn; }
111
-
112
- var origQuery = navigator.permissions?.query;
113
- if (origQuery) {
114
- navigator.permissions.query = __markNative(function query(params) {
115
- if (params && params.name === 'notifications') return Promise.resolve({ state: Notification.permission || 'default', onchange: null });
116
- return origQuery.apply(this, arguments);
117
- });
118
- }
119
- try {
120
- var getParam = WebGLRenderingContext.prototype.getParameter;
121
- WebGLRenderingContext.prototype.getParameter = __markNative(function getParameter(p) {
122
- if (p === 37445) return 'Intel Inc.';
123
- if (p === 37446) return 'Intel Iris OpenGL Engine';
124
- return getParam.call(this, p);
125
- });
126
- } catch(_) {}
127
- // ── WebGL readPixels noise ──────────────────────────
128
- // CreepJS and other fingerprinters draw content with WebGL and read back the
129
- // rendered pixels. Adding subtle noise breaks rendering-based fingerprinting.
130
- try {
131
- var origReadPixels = WebGLRenderingContext.prototype.readPixels;
132
- WebGLRenderingContext.prototype.readPixels = __markNative(function readPixels(x, y, width, height, format, type, pixels) {
133
- var result = origReadPixels.call(this, x, y, width, height, format, type, pixels);
134
- if (pixels && pixels.length > 0) {
135
- pixels[0] ^= 1;
136
- }
137
- return result;
138
- });
139
- } catch(_) {}
140
- Object.defineProperty(navigator, 'hardwareConcurrency', { get: () => 8, configurable: true });
141
- Object.defineProperty(navigator, 'deviceMemory', { get: () => 8, configurable: true });
142
-
143
- // ── Canvas fingerprint noise ─────────────────────────
144
- // Headless rendering engines produce slightly different canvas output
145
- // than headed Chrome. Subtle noise breaks hash-based fingerprinting.
146
- try {
147
- var __canvasNoise = ((Date.now() & 0xFF) | 1);
148
- var origFill = CanvasRenderingContext2D.prototype.fillText;
149
- CanvasRenderingContext2D.prototype.fillText = __markNative(function fillText() {
150
- this.globalAlpha = 0.9995;
151
- return origFill.apply(this, arguments);
152
- });
153
- } catch(_) {}
154
- try {
155
- var origStroke = CanvasRenderingContext2D.prototype.strokeText;
156
- CanvasRenderingContext2D.prototype.strokeText = __markNative(function strokeText() {
157
- this.globalAlpha = 0.9995;
158
- return origStroke.apply(this, arguments);
159
- });
160
- } catch(_) {}
161
- try {
162
- var origToDataURL = HTMLCanvasElement.prototype.toDataURL;
163
- HTMLCanvasElement.prototype.toDataURL = __markNative(function toDataURL() {
164
- var ctx = this.getContext('2d');
165
- if (ctx) {
166
- // Spread noise across canvas to break hash-based fingerprinting.
167
- // Uses a deterministic pattern so it's consistent per page load
168
- // but varies between sessions.
169
- var w = this.width, h = this.height;
170
- if (w > 0 && h > 0) {
171
- var imgData = ctx.getImageData(0, 0, Math.min(w, 4), Math.min(h, 4));
172
- if (imgData && imgData.data) {
173
- for (var __i = 0; __i < imgData.data.length; __i += 4) {
174
- imgData.data[__i] ^= (__canvasNoise + __i) & 0xFF;
175
- }
176
- ctx.putImageData(imgData, 0, 0);
177
- }
178
- }
179
- }
180
- return origToDataURL.apply(this, arguments);
181
- });
182
- } catch(_) {}
183
-
184
- // ── AudioContext fingerprint noise ────────────────────
185
- // Headless Chrome's AudioContext produces slightly different output.
186
- // Subtle noise breaks audio-based fingerprinting.
187
- try {
188
- var __audioSeed = ((Date.now() & 0x1F) | 1);
189
- var origGetChannelData = AudioBuffer.prototype.getChannelData;
190
- AudioBuffer.prototype.getChannelData = __markNative(function getChannelData(channel) {
191
- var data = origGetChannelData.call(this, channel);
192
- for (var __i = 0; __i < data.length; __i += 64) {
193
- data[__i] *= 0.99999;
194
- }
195
- return data;
196
- });
197
- } catch(_) {}
198
-
199
- // ── window outer dimensions ──────────────────────────
200
- // outerWidth/Height = 0 in headless — a well-known bot signal.
201
- // Mirror innerWidth/Height (set by --window-size flag) so the ratio is sane.
202
- try {
203
- if (!window.outerWidth) Object.defineProperty(window, 'outerWidth', { get: () => window.innerWidth || 1920, configurable: true });
204
- if (!window.outerHeight) Object.defineProperty(window, 'outerHeight', { get: () => window.innerHeight || 1080, configurable: true });
205
- } catch(_) {}
206
-
207
- // ── screen properties ─────────────────────────────────
208
- // Headless Chrome often reports an 800x600 screen even when the viewport is
209
- // 1920x1080. Keep screen metrics internally consistent with our launch flags.
210
- try {
211
- Object.defineProperty(screen, 'width', { get: () => 1920, configurable: true });
212
- Object.defineProperty(screen, 'height', { get: () => 1080, configurable: true });
213
- Object.defineProperty(screen, 'availWidth', { get: () => 1920, configurable: true });
214
- Object.defineProperty(screen, 'availHeight', { get: () => 1040, configurable: true });
215
- Object.defineProperty(screen, 'colorDepth', { get: () => 24, configurable: true });
216
- Object.defineProperty(screen, 'pixelDepth', { get: () => 24, configurable: true });
217
- } catch(_) {}
218
-
219
- // ── navigator.userAgentData (UA Client Hints) ─────────
220
- // Derive version from the UA string already set by --user-agent flag so the
221
- // two APIs are always consistent. Removes any "HeadlessChrome" brand entry.
222
- try {
223
- var _uaMajor = (navigator.userAgent.match(new RegExp('Chrome/([0-9]+)')) || [])[1] || '136';
224
- var _uaFull = (navigator.userAgent.match(new RegExp('Chrome/([0-9.]+)')) || [])[1] || (_uaMajor + '.0.0.0');
225
- var _brands = [
226
- { brand: 'Not)A;Brand', version: '99' },
227
- { brand: 'Google Chrome', version: _uaMajor },
228
- { brand: 'Chromium', version: _uaMajor },
229
- ];
230
- Object.defineProperty(navigator, 'userAgentData', {
231
- get: function() {
232
- return {
233
- brands: _brands, mobile: false, platform: 'Windows',
234
- getHighEntropyValues: function() {
235
- return Promise.resolve({
236
- architecture: 'x86', bitness: '64',
237
- brands: _brands,
238
- fullVersionList: [
239
- { brand: 'Not)A;Brand', version: '99.0.0.0' },
240
- { brand: 'Google Chrome', version: _uaFull },
241
- { brand: 'Chromium', version: _uaFull },
242
- ],
243
- mobile: false, model: '', platform: 'Windows',
244
- platformVersion: '15.0.0', uaFullVersion: _uaFull, wow64: false,
245
- });
246
- },
247
- toJSON: function() { return { brands: _brands, mobile: false, platform: 'Windows' }; },
248
- };
249
- },
250
- configurable: true,
251
- });
252
- } catch(_) {}
253
-
254
- // ── CDP Runtime serialization guard ──────────────────
255
- // Sites detect CDP by putting a getter on Error.prototype.stack
256
- // and checking if console.log triggers it (only happens when
257
- // Runtime domain is enabled). We monkey-patch console methods to
258
- // strip custom getters from arguments before they reach CDP.
259
- try {
260
- var _origLog = console.log, _origError = console.error,
261
- _origWarn = console.warn, _origDebug = console.debug,
262
- _origInfo = console.info;
263
- var _safeArg = function(a) {
264
- if (a instanceof Error) {
265
- try { return new Error(a.message); } catch(_) { return a; }
266
- }
267
- return a;
268
- };
269
- console.log = __markNative(function log() { return _origLog.apply(console, Array.prototype.map.call(arguments, _safeArg)); });
270
- console.error = __markNative(function error() { return _origError.apply(console, Array.prototype.map.call(arguments, _safeArg)); });
271
- console.warn = __markNative(function warn() { return _origWarn.apply(console, Array.prototype.map.call(arguments, _safeArg)); });
272
- console.debug = __markNative(function debug() { return _origDebug.apply(console, Array.prototype.map.call(arguments, _safeArg)); });
273
- console.info = __markNative(function info() { return _origInfo.apply(console, Array.prototype.map.call(arguments, _safeArg)); });
274
- } catch(_) {}
275
-
276
- // ── Native function masking ──────────────────────────
277
- // Patched APIs should not stringify as user-defined stealth code.
278
- try {
279
- var __nativeToString = Function.prototype.toString;
280
- Function.prototype.toString = function toString() {
281
- if (__greedyNativeFns.indexOf(this) !== -1) {
282
- var name = this.name || '';
283
- return 'function ' + name + '() { [native code] }';
284
- }
285
- return __nativeToString.call(this);
286
- };
287
- } catch(_) {}
288
- })();
289
- `})])}var rn={postNav:800,postNavSlow:1200,postClick:300,postType:300,inputPoll:400,copyPoll:600,afterVerify:1500};function q(n){let t=n*0.2,e=rt(-Math.floor(t),Math.floor(t)+1);return Math.max(50,Math.round(n+e))}async function B(n,t,e=15000,i=500){let r=String.raw`
290
- new Promise((resolve) => {
291
- const _deadline = Date.now() + ${e};
292
- const _baseInterval = ${i};
293
-
294
- function _jitter(ms) {
295
- return Math.max(50, ms + (Math.random() * ms * 0.4 - ms * 0.2));
296
- }
297
-
298
- function _poll() {
299
- try {
300
- if (document.querySelector('${t}')) { resolve(true); return; }
301
- if (Date.now() < _deadline) { setTimeout(_poll, _jitter(_baseInterval)); }
302
- else { resolve(false); }
303
- } catch(_) { resolve(false); }
304
- }
305
-
306
- _poll();
307
- })
308
- `;return await a(["eval",n,r],e+5000)==="true"}async function Wn(n){let t=n.indexOf("--stdin");if(t===-1)return n;let e=await new Promise((r)=>{let o="";process.stdin.setEncoding("utf8"),process.stdin.on("data",(u)=>o+=u),process.stdin.on("end",()=>r(o.trim()))}),i=[...n];return i[t]=e,i}function xn(n){let t=n.includes("--short"),e=n.filter((l)=>l!=="--short"),i=e.indexOf("--tab"),r=i===-1?null:e[i+1];if(i!==-1)e=e.filter((l,g)=>g!==i&&g!==i+1);let o=e.indexOf("--locale"),u=o===-1?null:e[o+1];if(o!==-1)e=e.filter((l,g)=>g!==o&&g!==o+1);return{query:e.join(" "),tabPrefix:r,short:t,locale:u}}function jn(n,t){if(!n.length||n[0]==="--help")process.stderr.write(t),process.exit(1)}function Jn(n,t,e=300){if(!t||n.length<=e)return n;let i=n.slice(0,e),r=i.lastIndexOf(" ");return r>0?`${i.slice(0,r)}…`:`${i}…`}function Nn(n){process.stdout.write(`${JSON.stringify(n,null,2)}
309
- `)}function y(n,t,e=null){if(!n||typeof n!=="object")return;let i=e?` (+${Date.now()-e}ms)`:"";if(n.lastStage=t,!Array.isArray(n.stages))n.stages=[];n.stages.push({stage:t,at:Date.now()});let r=n.engine||"extractor";console.error(`[${r}] stage: ${t}${i}`)}function on({engine:n,mode:t="headless",clipboardEmpty:e=null,fallbackUsed:i=null,blockedBy:r=null,verificationResult:o=null,inputReady:u=null,durationMs:l=null,lastStage:g=null,stages:f=null}={}){return{engine:n,mode:t,clipboardEmpty:e,fallbackUsed:i,blockedBy:r,verificationResult:o,inputReady:u,durationMs:l,lastStage:g,stages:f}}function Zn(n,t=null){if(t){let e=JSON.stringify({_envelope:t,error:n.message});process.stdout.write(`${e}
310
- `)}process.stderr.write(`Error: ${n.message}
311
- `),process.exit(1)}import{spawn as Pt,execFileSync as hn,execSync as de}from"node:child_process";import{basename as pt}from"node:path";function Xn(n=process.env,t=process.execPath){let e=n.GREEDY_SEARCH_NODE||n.NODE_BINARY||n.NODE;if(e?.trim())return e.trim();let i=pt(t||"").toLowerCase();if(i==="node"||i==="node.exe")return t;return"node"}import{existsSync as Sn,readFileSync as cn,renameSync as Kt,unlinkSync as $,writeFileSync as E}from"node:fs";import pn from"node:http";import{platform as Ln}from"node:os";import{existsSync as qn}from"node:fs";import{platform as wt}from"node:os";import{join as x}from"node:path";function _(n){let t=wt()==="win32",e=process.env.SystemRoot||"C:\\Windows",i={win32:{powershell:x(e,"System32","WindowsPowerShell","v1.0","powershell.exe"),powershell_ise:x(e,"System32","WindowsPowerShell","v1.0","powershell_ise.exe"),netstat:x(e,"System32","netstat.exe"),taskkill:x(e,"System32","taskkill.exe"),tasklist:x(e,"System32","tasklist.exe"),cmd:x(e,"System32","cmd.exe")},unix:{ps:"/usr/bin/ps",lsof:"/usr/bin/lsof",ss:"/usr/sbin/ss",grep:"/usr/bin/grep",kill:"/usr/bin/kill"}},r=t?i.win32:i.unix,o=n.toLowerCase();if(r[o]&&qn(r[o]))return r[o];if(t&&o==="netstat"){let u=x(e,"Sysnative","netstat.exe");if(qn(u))return u}return n}import{execFileSync as yt}from"node:child_process";import{platform as vt}from"node:os";function _t(n,t){return yt(_(n),t,{encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:5000})}function mt(n){let t=String(n||"").match(/\b(\d+)\b/);return t?Number.parseInt(t[1],10):null}function $t(n){return mt(n)}function bt(n,t){for(let e of String(n||"").split(/\r?\n/)){if(!/\bLISTEN\b/i.test(e))continue;if(!e.trim().split(/\s+/).slice(0,6).some((o)=>o.endsWith(`:${t}`)))continue;let r=e.match(/\bpid=(\d+)\b/);if(r)return Number.parseInt(r[1],10)}return null}function Wt(n,t){for(let e of String(n||"").split(/\r?\n/)){let i=e.trim().split(/\s+/);if(i.length<5||i[0].toUpperCase()!=="TCP")continue;if(!i.at(-2)?.toUpperCase().startsWith("LISTEN"))continue;if(!i[1].endsWith(`:${t}`))continue;let r=Number.parseInt(i.at(-1),10);if(Number.isInteger(r)&&r>0)return r}return null}function un(n,t,e){try{return n(t,e)}catch{return null}}function P(n,{platformName:t=vt(),run:e=_t}={}){if(t==="win32")return Wt(un(e,"netstat",["-ano","-p","TCP"]),n);let i=un(e,"lsof",["-nP",`-iTCP:${n}`,"-sTCP:LISTEN","-t"]),r=$t(i);if(r)return r;if(t!=="linux")return null;return bt(un(e,"ss",["-ltnp"]),n)}import{existsSync as Bn}from"node:fs";import{dirname as xt,join as Q}from"node:path";import{fileURLToPath as jt}from"node:url";function Qn(n){return jt(new URL(".",n)).replace(/^\/([A-Z]:)/,"$1")}function Jt(n,t=Bn){let e=new Set;for(let i of n.filter(Boolean)){if(e.has(i))continue;if(e.add(i),t(i))return i}return n.filter(Boolean).at(-1)}function kn({moduleDir:n,entrypoint:t=process.argv[1],env:e=process.env,exists:i=Bn}={}){let r=t?xt(t):null,o=e.GREEDY_SEARCH_EXTENSION_DIR?.trim()||null;return Jt([n?Q(n,"..","..","bin","launch.mjs"):null,n?Q(n,"launch.mjs"):null,o?Q(o,"bin","launch.mjs"):null,r?Q(r,"launch.mjs"):null,r?Q(r,"..","bin","launch.mjs"):null],i)}import{existsSync as K,mkdirSync as Nt,readFileSync as Vn,writeFileSync as Zt}from"node:fs";import{homedir as Xt}from"node:os";import{join as Un}from"node:path";import{tmpdir as qt}from"node:os";function Bt(n,t=9222){let e=Number.parseInt(String(n??""),10);return Number.isInteger(e)&&e>1024&&e<65535?e:t}var Qt=qt().replaceAll("\\","/"),h=Bt(process.env.GREEDY_SEARCH_PORT),c=(process.env.GREEDY_SEARCH_PROFILE_DIR||process.env.CDP_PROFILE_DIR||`${Qt}/greedysearch-chrome-profile`).replaceAll("\\","/"),k=`${c}/DevToolsActivePort`,C=process.env.GREEDY_SEARCH_PID_FILE||`${c}/browser.pid`,kt=process.env.CDP_PAGES_CACHE||`${c}/cdp-pages.json`,W=process.env.GREEDY_SEARCH_MODE_FILE||`${c}/browser-mode`,zn=process.env.GREEDY_SEARCH_METADATA_FILE||`${c}/browser-metadata.json`,An=process.env.GREEDY_SEARCH_LAUNCH_LOCK_FILE||`${c}/browser-launch.lock`,Y=process.env.GREEDY_SEARCH_ACTIVITY_FILE||`${c}/browser-last-activity`,ze=(process.env.CDP_SOCKET_DIR||`${c}/cdp-sockets`).replaceAll("\\","/"),Ae=`${c}/visible-recovery.jsonl`,fn=Un(Xt(),".dm"),m=Un(fn,"greedyconfig"),an=["perplexity","google","chatgpt","gemini"],gn="gemini";function Ht(){try{if(K(m)){let n=Vn(m,"utf8"),t=JSON.parse(n);if(Array.isArray(t.engines)&&t.engines.length>0&&t.engines.every((e)=>typeof e==="string")){let e=t.engines.filter((r)=>ln[r]),i=t.engines.filter((r)=>!ln[r]);if(i.length>0)process.stderr.write(`[greedysearch] Warning: ignoring unknown engine(s) in ${m}: ${i.join(", ")}
312
- [greedysearch] Available engines: ${Object.keys(ln).join(", ")}
313
- `);if(e.length>0)return e;process.stderr.write(`[greedysearch] Warning: no valid engines in ${m}, falling back to defaults: ${an.join(", ")}
314
- `)}}}catch{}return an}function Vt(){try{if(!K(fn))Nt(fn,{recursive:!0});if(!K(m))Zt(m,JSON.stringify({engines:an,synthesizer:gn},null,2)+`
315
- `,"utf8")}catch{}}Vt();var Hn=["gemini","chatgpt"];function Ut(){try{if(K(m)){let n=Vn(m,"utf8"),t=JSON.parse(n);if(typeof t.synthesizer==="string"){let e=t.synthesizer.toLowerCase();if(Hn.includes(e))return e;process.stderr.write(`[greedysearch] Warning: unknown synthesizer "${t.synthesizer}" in ${m}
316
- [greedysearch] Available synthesizers: ${Hn.join(", ")}
317
- [greedysearch] Falling back to default: ${gn}
318
- `)}}}catch{}return gn}var ln={perplexity:"perplexity.mjs",p:"perplexity.mjs",bing:"bing-copilot.mjs",b:"bing-copilot.mjs",google:"google-ai.mjs",g:"google-ai.mjs",gemini:"gemini.mjs",gem:"gemini.mjs",chatgpt:"chatgpt.mjs",gpt:"chatgpt.mjs","semantic-scholar":"semantic-scholar.mjs",semanticscholar:"semantic-scholar.mjs",s2:"semantic-scholar.mjs",logically:"logically.mjs",log:"logically.mjs"},Me=Ht();var Oe=Ut(),De=Math.max(1,Number.parseInt(process.env.GREEDY_FETCH_CONCURRENCY||"5",10)||5);process.env.CDP_PROFILE_DIR=c;import{execFileSync as Mn,execSync as Ke}from"node:child_process";import{existsSync as G,mkdirSync as zt,readFileSync as X,unlinkSync as J,writeFileSync as N}from"node:fs";import{platform as At}from"node:os";var S=zn,j=An,Mt=15000,L=C,F=W,H=Y;function V(n){if(!Number.isInteger(n)||n<=0)return!1;try{return process.kill(n,0),!0}catch{return!1}}function Ot(n){if(!V(n))return null;try{if(At()==="win32")return Mn(_("powershell"),["-NoProfile","-NonInteractive","-Command",`(Get-CimInstance Win32_Process -Filter "ProcessId = ${n}").CommandLine`],{encoding:"utf8",windowsHide:!0,timeout:5000}).trim()||null;return Mn(_("ps"),["-p",String(n),"-o","command="],{encoding:"utf8",timeout:5000}).trim()||null}catch{return null}}function Dt(n,t,e=h){if(!n)return!1;let i=(u)=>String(u||"").replaceAll("\\","/").toLowerCase(),r=i(n),o=i(t);return r.includes(o)&&r.includes(`--remote-debugging-port=${e}`)&&!r.includes("--type=")}function On(n,t,e=h){return Dt(Ot(n),t,e)}function Tt(n=h){return P(n)}function U(){try{if(G(S)){let n=X(S,"utf8"),t=JSON.parse(n);if(t&&typeof t.tempDir==="string"&&typeof t.debugPort==="number")return{browserPid:Number.isInteger(t.browserPid)?t.browserPid:void 0,debugPort:t.debugPort,tempDir:t.tempDir,clientPids:Array.isArray(t.clientPids)?t.clientPids.filter((e)=>Number.isInteger(e)&&e>0):[],sessionMode:t.sessionMode==="visible"?"visible":"headless",lastActivity:Number.isFinite(t.lastActivity)?t.lastActivity:0,launchedAt:Number.isFinite(t.launchedAt)?t.launchedAt:0}}}catch{}try{let n=G(L)?Number.parseInt(X(L,"utf8").trim(),10)||void 0:void 0,t=G(F)?X(F,"utf8").trim()==="visible"?"visible":"headless":"headless",e=G(H)?Number.parseInt(X(H,"utf8").trim(),10)||0:0;return{browserPid:n,debugPort:h,tempDir:c,clientPids:n?[n]:[],sessionMode:t,lastActivity:e,launchedAt:0}}catch{return null}}function R(n){try{N(S,JSON.stringify({browserPid:n.browserPid,debugPort:n.debugPort,tempDir:n.tempDir,clientPids:[...new Set(n.clientPids.filter((t)=>t>0))],sessionMode:n.sessionMode,lastActivity:n.lastActivity,launchedAt:n.launchedAt},null,2),"utf8")}catch{}try{if(n.browserPid)N(L,String(n.browserPid),"utf8")}catch{}try{N(F,n.sessionMode,"utf8")}catch{}try{N(H,String(n.lastActivity),"utf8")}catch{}}function Dn(){try{J(S)}catch{}try{J(L)}catch{}try{J(F)}catch{}try{J(H)}catch{}}function sn(n){if(!n)return n;let t={...n,clientPids:[...new Set([...n.clientPids,process.pid].filter((e)=>V(e)||e===process.pid))]};return R(t),t}function I(n){let t=Date.now();try{if(n)R({...n,lastActivity:t});else N(H,String(t),"utf8")}catch{}}var Fe=Number.parseInt(process.env.GREEDY_SEARCH_IDLE_TIMEOUT_MINUTES||"5",10)||5;var Tn=!1;function Pn(){if(process.env.GREEDY_SEARCH_RESEARCH_CHILD)return;if(Tn)return;Tn=!0;let n=U();if(!n)return;if(n.browserPid){if(!V(n.browserPid)){Dn();return}if(!On(n.browserPid,n.tempDir,n.debugPort))Dn();else R({...n,clientPids:n.clientPids.filter((r)=>V(r))})}let t=Tt();if(t&&t!==n.browserPid)if(On(t,c,h))R({browserPid:t,debugPort:h,tempDir:c,clientPids:[t],sessionMode:n.sessionMode,lastActivity:Date.now(),launchedAt:Date.now()});else process.stderr.write(`[greedysearch] Refusing to kill unverified listener ${t} on port ${h}.
319
- `)}function Kn(){zt(c,{recursive:!0});try{let n=JSON.stringify({pid:process.pid,ts:Date.now()});return N(j,n,{encoding:"utf8",flag:"wx"}),{acquired:!0,release:()=>{try{let t=X(j,"utf8");if(JSON.parse(t).pid===process.pid)J(j)}catch{}}}}catch(n){if(n?.code!=="EEXIST")return{acquired:!1,release:()=>{}}}try{let n=X(j,"utf8"),t=JSON.parse(n),e=Date.now()-(t.ts||0);if(!V(t.pid)||e>Mt){try{J(j)}catch{}try{let r=JSON.stringify({pid:process.pid,ts:Date.now()});return N(j,r,{encoding:"utf8",flag:"wx"}),{acquired:!0,release:()=>{try{J(j)}catch{}}}}catch{return{acquired:!1,release:()=>{}}}}}catch{}return{acquired:!1,release:()=>{}}}var Ct=Qn(import.meta.url),Cn=C,z=Y,Yt=Number.parseInt(process.env.GREEDY_SEARCH_IDLE_TIMEOUT_MINUTES||"5",10)||5,Gt=Number.parseInt(process.env.GREEDY_SEARCH_VISIBLE_IDLE_TIMEOUT_MINUTES||"60",10)||60;function St(n,t=h){let e=String(n||"").toLowerCase();if(!e.includes(`--remote-debugging-port=${t}`)||e.includes("--type="))return null;return e.includes("--headless")}function Fn(){try{let n=tn(),t=n?Rn(n):null,e=St(t);if(e!==null){try{E(W,e?"headless":"visible","utf8")}catch{}return e}}catch{}try{if(!Sn(W))return!0;return cn(W,"utf8").trim()==="headless"}catch{return!0}}function Lt(){try{E(z,String(Date.now()),"utf8")}catch{}try{let n=U();if(n)I(n)}catch{}}function Rn(n){try{if(Ln()==="win32")return hn(_("powershell"),["-NoProfile","-NonInteractive","-Command",`(Get-CimInstance Win32_Process -Filter "ProcessId = ${n}").CommandLine`],{encoding:"utf8",windowsHide:!0,timeout:5000}).trim()||null;return hn(_("ps"),["-p",String(n),"-o","command="],{encoding:"utf8",timeout:5000}).trim()||null}catch{return null}}function Ft(n){let t=String(n||"").replaceAll("\\","/"),e=c.replaceAll("\\","/");return t.includes(`--remote-debugging-port=${h}`)&&t.includes(`--user-data-dir=${e}`)&&!t.includes("--type=")}function tn(){return P(h)}function nn(){let n=tn();return n&&Ft(Rn(n))?n:null}async function Rt(n=1500){let t=nn();if(!t)return process.stderr.write(`[greedysearch] Refusing to close an unverified listener.
320
- `),!1;try{let i=await new Promise((o,u)=>{let l=pn.get(`http://localhost:${h}/json/version`,(g)=>{let f="";g.on("data",(s)=>f+=s),g.on("end",()=>{try{o(JSON.parse(f))}catch{u(Error("bad JSON"))}})});l.on("error",u),l.setTimeout(1000,()=>{l.destroy(),u(Error("timeout"))})}),r=new globalThis.WebSocket(i.webSocketDebuggerUrl);await new Promise((o)=>{r.onopen=()=>{if(nn()!==t){r.close(),o();return}r.send(JSON.stringify({id:1,method:"Browser.close"})),setTimeout(()=>{r.close(),o()},200)},r.onerror=()=>o(),setTimeout(o,1000)})}catch{}let e=Date.now()+n;while(Date.now()<e){let i=tn();if(!i)return!0;if(i!==t)return!1;await new Promise((r)=>setTimeout(r,150))}return It(t)}function It(n=null){try{let t=nn();if(!t||n&&t!==n)return!1;if(Ln()==="win32")hn(_("taskkill"),["/F","/PID",String(t)],{stdio:"ignore",windowsHide:!0});else process.kill(t,"SIGKILL");return!0}catch{return!1}}async function In(){if(!nn()){if(tn())process.stderr.write(`[greedysearch] Refusing to kill an unverified listener.
321
- `);return!1}if(!await b(500)){try{$(Cn)}catch{}try{$(z)}catch{}try{$(W)}catch{}return!1}let t=await Rt(1500);try{$(Cn)}catch{}try{$(z)}catch{}try{$(W)}catch{}if(t)process.stderr.write(`[greedysearch] Killed Chrome on port ${h}.
322
- `);return t}var Yn=In;async function dt(){let t=Fn()?Yt:Gt;if(t<=0)return!1;if(!Sn(z))return Lt(),!1;try{let e=Number.parseInt(cn(z,"utf8").trim(),10);if(!e)return!1;if((Date.now()-e)/60000>=t)return In()}catch{}return!1}function b(n=3000){return new Promise((t)=>{let e=pn.get(`http://localhost:${h}/json/version`,(i)=>{i.resume(),t(i.statusCode===200)});e.on("error",()=>t(!1)),e.setTimeout(n,()=>{e.destroy(),t(!1)})})}async function Gn(n=3000,t=200){let e=Date.now()+n;while(Date.now()<e){if(!await b(300))return!0;await new Promise((i)=>setTimeout(i,t))}return!await b(300)}async function d(){let n=`${k}.lock`,t=`${k}.tmp`,e=5000,i=1000,r=await new Promise((o)=>{let u=Date.now(),l=()=>{try{let g=JSON.stringify({pid:process.pid,ts:Date.now()});E(n,g,{encoding:"utf8",flag:"wx"}),o(!0)}catch(g){if(g?.code!=="EEXIST"){if(Date.now()-u<1000)setTimeout(l,50);else o(!1);return}try{let f=cn(n,"utf8").trim(),s=f.startsWith("{")?JSON.parse(f):{ts:Number(f)},v=Number(s?.ts)||0;if(v>0&&Date.now()-v>5000)try{$(n)}catch{}if(Date.now()-u<1000)setTimeout(l,50);else o(!1)}catch{if(Date.now()-u<1000)setTimeout(l,50);else o(!1)}}};l()});try{let o=await new Promise((g,f)=>{let s=pn.get(`http://localhost:${h}/json/version`,(v)=>{let A="";v.on("data",(M)=>A+=M),v.on("end",()=>g(A))});s.on("error",f),s.setTimeout(3000,()=>{s.destroy(),f(Error("timeout"))})}),{webSocketDebuggerUrl:u}=JSON.parse(o),l=new URL(u).pathname;if(r){E(t,`${h}
323
- ${l}`,"utf8");try{$(k)}catch{}Kt(t,k)}}catch{}finally{if(r)try{$(n)}catch{}}}async function dn(){if(process.env.GREEDY_SEARCH_RESEARCH_CHILD&&await b()){await d();try{let o=U();if(o)I(o),sn(o)}catch{}return}Pn();let n=await dt(),t=n?!1:await b();if(!t&&!n)await new Promise((o)=>setTimeout(o,500)),t=await b();let e=!1;if(t){let o=Fn(),u=process.env.GREEDY_SEARCH_VISIBLE==="1";if(!u&&!o)process.stderr.write(`[greedysearch] Visible Chrome detected — switching to headless mode...
324
- `),await Yn(),await Gn(),e=!0;else if(u&&o)process.stderr.write(`[greedysearch] Headless Chrome detected — switching to visible mode...
325
- `),await Yn(),await Gn(),e=!0}if(e?!1:await b()){await d();try{let o=U();if(o)I(o),sn(o)}catch{}return}let r=Kn();if(!r.acquired){let o=Date.now()+12000,u=!1;while(Date.now()<o){if(u=await b(500),u)break;await new Promise((l)=>setTimeout(l,250))}if(u){await d();return}}try{if(await b(1000)){await d();return}process.stderr.write(`GreedySearch Chrome not running on port ${h} — auto-launching...
326
- `);let l=[kn({moduleDir:Ct,entrypoint:process.argv[1],env:process.env})];if(process.env.GREEDY_SEARCH_VISIBLE!=="1")l.push("--headless");await new Promise((g,f)=>{Pt(Xn(),l,{stdio:["ignore",process.stderr,process.stderr]}).on("close",(v)=>v===0?g():f(Error("launch.mjs failed")))})}finally{r.release()}}var Et=`
327
- (function() {
328
- // Google consent page (consent.google.com)
329
- var g = document.querySelector('#L2AGLb, button[jsname="b3VHJd"], .tHlp8d');
330
- if (g) { g.click(); return 'google'; }
331
-
332
- // OneTrust (used by many sites including Stack Overflow)
333
- var ot = document.querySelector('#onetrust-accept-btn-handler, .onetrust-accept-btn-handler');
334
- if (ot) { ot.click(); return 'onetrust'; }
335
-
336
- // Generic "accept all" / "agree" buttons
337
- var btns = Array.from(document.querySelectorAll('button, a[role=button]'));
338
- var accept = btns.find(b => /^(accept all|accept cookies|agree|i agree|got it|allow all|allow cookies)$/i.test(b.innerText?.trim()));
339
- if (accept) { accept.click(); return 'generic:' + accept.innerText.trim(); }
340
-
341
- return null;
342
- })()
343
- `;async function wn(n,t){let e=await t(["eval",n,Et]).catch(()=>null);if(e&&e!=="null")await new Promise((i)=>setTimeout(i,1500))}var w={input:'textarea[name="newMessage"]',submitButton:'button[aria-label="Submit search"]',answerContainer:'div[class*="prose"]',paperCard:'a[data-testid="search-result"]',exportButton:'button[aria-label="Export"]'};async function ne(n){return await a(["eval",n,`(() => {
1
+ import {
2
+ buildEnvelope,
3
+ cdp,
4
+ cdpWithInput,
5
+ formatAnswer,
6
+ getOrOpenTab,
7
+ handleError,
8
+ jitter,
9
+ logStage,
10
+ outputJson,
11
+ parseArgs,
12
+ prepareArgs,
13
+ TIMING,
14
+ validateQuery,
15
+ verifyInputText,
16
+ waitForSelector
17
+ } from "./common.mjs";
18
+ import { ensureChrome } from "../src/search/chrome.mjs";
19
+ import { dismissConsent } from "./consent.mjs";
20
+ const SELECTORS = {
21
+ input: 'textarea[name="newMessage"]',
22
+ submitButton: 'button[aria-label="Submit search"]',
23
+ answerContainer: 'div[class*="prose"]',
24
+ paperCard: 'a[data-testid="search-result"]',
25
+ exportButton: 'button[aria-label="Export"]'
26
+ };
27
+ async function detectSignUpWall(tab) {
28
+ const code = `(() => {
344
29
  const url = document.location.href || '';
345
30
  return url.indexOf('/sign-up/') !== -1 || url.indexOf('redirect_url=') !== -1;
346
- })()`]).catch(()=>"false")==="true"}async function En(n){try{return JSON.parse(await a(["eval",n,`(() => {
31
+ })()`;
32
+ const result = await cdp(["eval", tab, code]).catch(() => "false");
33
+ return result === "true";
34
+ }
35
+ async function detectStaleClerkSession(tab) {
36
+ const code = `(() => {
347
37
  const url = document.location.href || '';
348
38
  const title = document.title || '';
349
39
  const text = document.body?.innerText || '';
@@ -354,14 +44,91 @@ ${l}`,"utf8");try{$(k)}catch{}Kt(t,k)}}catch{}finally{if(r)try{$(n)}catch{}}}asy
354
44
  text.includes('refresh_request_origin_azp_mismatch') ||
355
45
  (text.includes('HTTP ERROR 405') && text.includes('This page isn'));
356
46
  return JSON.stringify({ stale, url, title, text: text.slice(0, 500) });
357
- })()`],5000))}catch{return{stale:!1,url:"",title:"",text:""}}}async function te(n){for(let t of["https://consensus.app","https://clerk.consensus.app"])await a(["evalraw",n,"Storage.clearDataForOrigin",JSON.stringify({origin:t,storageTypes:"all"})]).catch((e)=>{console.error(`[consensus] Warning: failed to clear stale auth storage for ${t}: ${e.message}`)})}async function nt(n,t,e){let i=await En(n);if(!i.stale)return!1;if(y(t,"auth-storage-reset",e),console.error(`[consensus] Detected stale Clerk/Consensus auth state (${i.title||i.url}) — clearing per-origin storage and retrying navigation`),t.fallbackUsed="clear-stale-consensus-auth",await te(n),await a(["nav",n,"https://consensus.app/"],20000),await new Promise((o)=>setTimeout(o,900)),(await En(n)).stale)throw t.blockedBy="signin",t.verificationResult="needs-human",Error("Consensus auth session is stale — visible Chrome is open. Please sign in again, then rerun the search.");return!0}async function ee(n,t){await a(["click",n,w.input]),await new Promise((u)=>setTimeout(u,q(200)));let e=async()=>{await en(["type",n,"--stdin"],t),await new Promise((u)=>setTimeout(u,q(300)))},i=async()=>{let u=await a(["eval",n,`JSON.stringify(document.querySelector('${w.input}')?.value || '')`]);try{return JSON.parse(u)}catch{return u}},r=async()=>mn(await i(),t),o=async()=>{await a(["eval",n,`(() => {
358
- const el = document.querySelector('${w.input}');
47
+ })()`;
48
+ try {
49
+ return JSON.parse(await cdp(["eval", tab, code], 5000));
50
+ } catch {
51
+ return { stale: false, url: "", title: "", text: "" };
52
+ }
53
+ }
54
+ async function clearConsensusAuthStorage(tab) {
55
+ for (const origin of [
56
+ "https://consensus.app",
57
+ "https://clerk.consensus.app"
58
+ ]) {
59
+ await cdp([
60
+ "evalraw",
61
+ tab,
62
+ "Storage.clearDataForOrigin",
63
+ JSON.stringify({ origin, storageTypes: "all" })
64
+ ]).catch((e) => {
65
+ console.error(`[consensus] Warning: failed to clear stale auth storage for ${origin}: ${e.message}`);
66
+ });
67
+ }
68
+ }
69
+ async function recoverStaleClerkSession(tab, env, startTime) {
70
+ const before = await detectStaleClerkSession(tab);
71
+ if (!before.stale)
72
+ return false;
73
+ logStage(env, "auth-storage-reset", startTime);
74
+ console.error(`[consensus] Detected stale Clerk/Consensus auth state (${before.title || before.url}) — clearing per-origin storage and retrying navigation`);
75
+ env.fallbackUsed = "clear-stale-consensus-auth";
76
+ await clearConsensusAuthStorage(tab);
77
+ await cdp(["nav", tab, "https://consensus.app/"], 20000);
78
+ await new Promise((r) => setTimeout(r, 900));
79
+ const after = await detectStaleClerkSession(tab);
80
+ if (after.stale) {
81
+ env.blockedBy = "signin";
82
+ env.verificationResult = "needs-human";
83
+ throw new Error("Consensus auth session is stale — visible Chrome is open. Please sign in again, then rerun the search.");
84
+ }
85
+ return true;
86
+ }
87
+ async function typeIntoConsensus(tab, text) {
88
+ await cdp(["click", tab, SELECTORS.input]);
89
+ await new Promise((r) => setTimeout(r, jitter(200)));
90
+ const type = async () => {
91
+ await cdpWithInput(["type", tab, "--stdin"], text);
92
+ await new Promise((r) => setTimeout(r, jitter(300)));
93
+ };
94
+ const readInput = async () => {
95
+ const raw = await cdp([
96
+ "eval",
97
+ tab,
98
+ `JSON.stringify(document.querySelector('${SELECTORS.input}')?.value || '')`
99
+ ]);
100
+ try {
101
+ return JSON.parse(raw);
102
+ } catch {
103
+ return raw;
104
+ }
105
+ };
106
+ const verify = async () => verifyInputText(await readInput(), text);
107
+ const clear = async () => {
108
+ await cdp([
109
+ "eval",
110
+ tab,
111
+ `(() => {
112
+ const el = document.querySelector('${SELECTORS.input}');
359
113
  if (!el) return false;
360
114
  el.focus();
361
115
  el.select();
362
116
  document.execCommand('delete');
363
117
  return true;
364
- })()`])};if(await e(),await r())return;if(await o(),await e(),!await r())throw Error("Consensus input did not accept text — input verification failed")}async function re(n){let e=await a(["eval",n,`(() => {
118
+ })()`
119
+ ]);
120
+ };
121
+ await type();
122
+ if (await verify())
123
+ return;
124
+ await clear();
125
+ await type();
126
+ if (!await verify()) {
127
+ throw new Error("Consensus input did not accept text — input verification failed");
128
+ }
129
+ }
130
+ async function installPapersDetailsInterceptor(tab) {
131
+ const code = `(() => {
365
132
  if (window.__pdiInstalled) return 'already';
366
133
  window.__pdiInstalled = true;
367
134
  const _origOpen = XMLHttpRequest.prototype.open;
@@ -402,9 +169,14 @@ ${l}`,"utf8");try{$(k)}catch{}Kt(t,k)}}catch{}finally{if(r)try{$(n)}catch{}}}asy
402
169
  return _origSend.apply(this, arguments);
403
170
  };
404
171
  return 'installed';
405
- })()`]);return e==="installed"||e==="already"}async function ie(n,t=1e4,e=0){let i=`new Promise((resolve) => {
406
- const _deadline = Date.now() + ${t};
407
- const _after = ${e};
172
+ })()`;
173
+ const r = await cdp(["eval", tab, code]);
174
+ return r === "installed" || r === "already";
175
+ }
176
+ async function fetchPapersDetailsResponse(tab, timeoutMs = 1e4, afterTs = 0) {
177
+ const code = `new Promise((resolve) => {
178
+ const _deadline = Date.now() + ${timeoutMs};
179
+ const _after = ${afterTs};
408
180
  function _check() {
409
181
  const store = window.__papersDetailsResps;
410
182
  const latest = store && store.latest;
@@ -424,10 +196,24 @@ ${l}`,"utf8");try{$(k)}catch{}Kt(t,k)}}catch{}finally{if(r)try{$(n)}catch{}}}asy
424
196
  }
425
197
  }
426
198
  _check();
427
- })`,r=await a(["eval",n,i],t+5000);try{return JSON.parse(r)}catch{return{ok:!1,reason:"invalid-json"}}}async function oe(n,t=8){if(!await B(n,w.paperCard,20000,500).catch(()=>!1))return console.error("[consensus] Warning: no paper cards appeared within 20s — Load More will be skipped"),0;let i=String.raw`
199
+ })`;
200
+ const result = await cdp(["eval", tab, code], timeoutMs + 5000);
201
+ try {
202
+ return JSON.parse(result);
203
+ } catch {
204
+ return { ok: false, reason: "invalid-json" };
205
+ }
206
+ }
207
+ async function expandReferences(tab, maxClicks = 8) {
208
+ const ready = await waitForSelector(tab, SELECTORS.paperCard, 20000, 500).catch(() => false);
209
+ if (!ready) {
210
+ console.error("[consensus] Warning: no paper cards appeared within 20s — Load More will be skipped");
211
+ return 0;
212
+ }
213
+ const code = String.raw`
428
214
  new Promise((resolve) => {
429
- const _paperSel = '${w.paperCard}';
430
- const _maxClicks = ${t};
215
+ const _paperSel = '${SELECTORS.paperCard}';
216
+ const _maxClicks = ${maxClicks};
431
217
  function _findLoadMore() {
432
218
  const btns = Array.from(document.querySelectorAll('button'));
433
219
  return btns.find(b => {
@@ -460,10 +246,23 @@ ${l}`,"utf8");try{$(k)}catch{}Kt(t,k)}}catch{}finally{if(r)try{$(n)}catch{}}}asy
460
246
  }
461
247
  _loop();
462
248
  })
463
- `,r=await a(["eval",n,i],t*5000+5000).catch(()=>null);if(!r)return 0;try{let{clicks:o,cards:u}=JSON.parse(r);return console.error(`[consensus] expandReferences: ${o} click(s), ${u} card(s)`),o}catch{return 0}}async function ue(n,t=1200){let e=String.raw`
249
+ `;
250
+ const result = await cdp(["eval", tab, code], maxClicks * 5000 + 5000).catch(() => null);
251
+ if (!result)
252
+ return 0;
253
+ try {
254
+ const { clicks, cards } = JSON.parse(result);
255
+ console.error(`[consensus] expandReferences: ${clicks} click(s), ${cards} card(s)`);
256
+ return clicks;
257
+ } catch {
258
+ return 0;
259
+ }
260
+ }
261
+ async function waitForCardsStable(tab, maxMs = 1200) {
262
+ const code = String.raw`
464
263
  new Promise((resolve) => {
465
- const _sel = '${w.paperCard}';
466
- const _deadline = Date.now() + ${t};
264
+ const _sel = '${SELECTORS.paperCard}';
265
+ const _deadline = Date.now() + ${maxMs};
467
266
  let _last = document.querySelectorAll(_sel).length;
468
267
  function _poll() {
469
268
  const cur = document.querySelectorAll(_sel).length;
@@ -474,20 +273,93 @@ ${l}`,"utf8");try{$(k)}catch{}Kt(t,k)}}catch{}finally{if(r)try{$(n)}catch{}}}asy
474
273
  }
475
274
  setTimeout(_poll, 300);
476
275
  })
477
- `;await a(["eval",n,e],t+3000).catch(()=>null)}async function le(n){return await a(["eval",n,`(() => {
478
- const btn = document.querySelector('${w.exportButton}');
276
+ `;
277
+ await cdp(["eval", tab, code], maxMs + 3000).catch(() => null);
278
+ }
279
+ async function clickExportCsv(tab) {
280
+ await cdp([
281
+ "eval",
282
+ tab,
283
+ `(() => {
284
+ const btn = document.querySelector('${SELECTORS.exportButton}');
479
285
  if (!btn) return 'no-export';
480
286
  btn.scrollIntoView({ block: 'center' });
481
287
  btn.click();
482
288
  return 'opened';
483
- })()`]),await new Promise((e)=>setTimeout(e,600)),await a(["eval",n,`(() => {
289
+ })()`
290
+ ]);
291
+ await new Promise((r) => setTimeout(r, 600));
292
+ const clicked = await cdp([
293
+ "eval",
294
+ tab,
295
+ `(() => {
484
296
  const btn = Array.from(document.querySelectorAll('button'))
485
297
  .find(b => /\\.CSV/.test((b.innerText || '').trim()));
486
298
  if (!btn) return 'no-csv';
487
299
  btn.click();
488
300
  return 'clicked';
489
- })()`])}function fe(n){let t=n?.paperDetailsListByPaperId||{},e=Object.keys(t),i=[];for(let r=0;r<e.length;r++){let o=t[e[r]]||{},u=o.url_slug||"",l=o.paper_id||o.hash_paper_id||e[r],g;if(u)g=`https://consensus.app/papers/${u}/${l}/`;else if(o.provider_url)g=o.provider_url;else if(o.doi)g=`https://doi.org/${o.doi}`;else g=`https://consensus.app/paper/${l}`;let f=[],s=o.badges||{};if(s.study_type==="rct")f.push("RCT");else if(s.study_type==="meta_analysis")f.push("META-ANALYSIS");else if(s.study_type==="systematic_review")f.push("SYSTEMATIC REVIEW");else if(s.study_type)f.push(s.study_type.toUpperCase());if(s.rigorous_journal)f.push("RIGOROUS JOURNAL");if(s.very_rigorous_journal)f.push("VERY RIGOROUS JOURNAL");if(s.highly_cited_paper)f.push("HIGHLY CITED");if(s.large_human_trial)f.push("LARGE HUMAN TRIAL");if(o.is_retracted)f.push("RETRACTED");if(o.open_access_pdf_url)f.push("OPEN ACCESS");i.push({title:o.title||"",url:g,rank:r+1,authors:Array.isArray(o.authors)?o.authors:[],year:o.year||null,journal:o.journal||o.publisher_name||null,doi:o.doi||null,citation_count:o.citation_count||0,snippet:o.abstract_takeaway||"",tags:f})}return i}async function tt(n){let t=`(() => {
490
- const cards = Array.from(document.querySelectorAll('${w.paperCard}'));
301
+ })()`
302
+ ]);
303
+ return clicked;
304
+ }
305
+ function buildSourcesFromApi(respData) {
306
+ const map = respData?.paperDetailsListByPaperId || {};
307
+ const ids = Object.keys(map);
308
+ const sources = [];
309
+ for (let i = 0;i < ids.length; i++) {
310
+ const p = map[ids[i]] || {};
311
+ const urlSlug = p.url_slug || "";
312
+ const paperId = p.paper_id || p.hash_paper_id || ids[i];
313
+ let url;
314
+ if (urlSlug) {
315
+ url = `https://consensus.app/papers/${urlSlug}/${paperId}/`;
316
+ } else if (p.provider_url) {
317
+ url = p.provider_url;
318
+ } else if (p.doi) {
319
+ url = `https://doi.org/${p.doi}`;
320
+ } else {
321
+ url = `https://consensus.app/paper/${paperId}`;
322
+ }
323
+ const tags = [];
324
+ const badges = p.badges || {};
325
+ if (badges.study_type === "rct")
326
+ tags.push("RCT");
327
+ else if (badges.study_type === "meta_analysis")
328
+ tags.push("META-ANALYSIS");
329
+ else if (badges.study_type === "systematic_review")
330
+ tags.push("SYSTEMATIC REVIEW");
331
+ else if (badges.study_type)
332
+ tags.push(badges.study_type.toUpperCase());
333
+ if (badges.rigorous_journal)
334
+ tags.push("RIGOROUS JOURNAL");
335
+ if (badges.very_rigorous_journal)
336
+ tags.push("VERY RIGOROUS JOURNAL");
337
+ if (badges.highly_cited_paper)
338
+ tags.push("HIGHLY CITED");
339
+ if (badges.large_human_trial)
340
+ tags.push("LARGE HUMAN TRIAL");
341
+ if (p.is_retracted)
342
+ tags.push("RETRACTED");
343
+ if (p.open_access_pdf_url)
344
+ tags.push("OPEN ACCESS");
345
+ sources.push({
346
+ title: p.title || "",
347
+ url,
348
+ rank: i + 1,
349
+ authors: Array.isArray(p.authors) ? p.authors : [],
350
+ year: p.year || null,
351
+ journal: p.journal || p.publisher_name || null,
352
+ doi: p.doi || null,
353
+ citation_count: p.citation_count || 0,
354
+ snippet: p.abstract_takeaway || "",
355
+ tags
356
+ });
357
+ }
358
+ return sources;
359
+ }
360
+ async function extractSourcesFromDom(tab) {
361
+ const code = `(() => {
362
+ const cards = Array.from(document.querySelectorAll('${SELECTORS.paperCard}'));
491
363
  const sources = [];
492
364
  const seen = new Set();
493
365
  for (const card of cards) {
@@ -509,8 +381,147 @@ ${l}`,"utf8");try{$(k)}catch{}Kt(t,k)}}catch{}finally{if(r)try{$(n)}catch{}}}asy
509
381
  sources.push({ title, url, rank, snippet, tags });
510
382
  }
511
383
  return JSON.stringify(sources);
512
- })()`,e=await a(["eval",n,t],1e4);try{return JSON.parse(e)}catch{return[]}}async function ae(n){let t=`(() => {
513
- const prose = document.querySelector('${w.answerContainer}');
384
+ })()`;
385
+ const result = await cdp(["eval", tab, code], 1e4);
386
+ try {
387
+ return JSON.parse(result);
388
+ } catch {
389
+ return [];
390
+ }
391
+ }
392
+ async function extractAnswer(tab) {
393
+ const code = `(() => {
394
+ const prose = document.querySelector('${SELECTORS.answerContainer}');
514
395
  return prose?.innerText?.trim() || '';
515
- })()`;return await a(["eval",n,t],1e4)||""}var ge=`Usage: node extractors/consensus.mjs "<query>" [--tab <prefix>]
516
- `;async function se(){let n=await Wn(process.argv.slice(2));jn(n,ge);let{query:t,tabPrefix:e,short:i}=xn(n),r=Date.now(),u={engine:"consensus",mode:process.env.GREEDY_SEARCH_VISIBLE==="1"?"visible":"headless",clipboardEmpty:null,fallbackUsed:null,blockedBy:null,verificationResult:null,inputReady:null};try{if(process.env.GREEDY_SEARCH_VISIBLE!=="1"&&process.env.GREEDY_SEARCH_ALWAYS_VISIBLE!=="1")process.env.GREEDY_SEARCH_HEADLESS="1";if(await dn(),!e)await a(["list"]);let l=await $n(e),g=await a(["eval",l,"document.location.href"]).catch(()=>""),f=!1;try{let p=new URL(g).hostname.toLowerCase();f=p==="consensus.app"||p.endsWith(".consensus.app")}catch{}if(!f)y(u,"nav",r),await a(["nav",l,"https://consensus.app/"],20000),await new Promise((p)=>setTimeout(p,600));await nt(l,u,r),await wn(l,a),y(u,"input-wait",r);let s=await B(l,w.input,15000,400);if(u.inputReady=s,!s)if(await nt(l,u,r)){let D=await B(l,w.input,15000,400);if(u.inputReady=D,D)await wn(l,a);else throw Error("Consensus input not found after stale auth recovery — page may not have loaded or is in unexpected state")}else throw Error("Consensus input not found — page may not have loaded or is in unexpected state");if(await new Promise((p)=>setTimeout(p,q(rn.postClick))),y(u,"type-and-submit",r),await ee(l,t),await new Promise((p)=>setTimeout(p,q(rn.postType))),await a(["eval",l,`document.querySelector('${w.submitButton}')?.click()`]),await new Promise((p)=>setTimeout(p,2000)),await ne(l))throw u.blockedBy="signin",Error("Consensus requires sign-in — please sign in or create a free account in the visible browser window. Once signed in, cookies persist for future runs.");y(u,"answer-wait",r),await B(l,w.answerContainer,30000,500),await re(l),y(u,"expand-refs",r);let v=await oe(l,8);if(v===0)console.error("[consensus] Note: 'Load more results' button not present (initial page has all references)");else console.error(`[consensus] Clicked 'Load more results' ${v} time(s) to expand references`);await ue(l,1200),y(u,"csv-click",r);let A=Number(await a(["eval",l,"Date.now()"]).catch(()=>"0")||0),M=await le(l);if(M!=="clicked")console.error(`[consensus] Export → .CSV click did not register (${M}) — falling back to DOM`);y(u,"wait-csv-resp",r);let O=await ie(l,12000,A),Z=[],yn="dom";if(O.ok)Z=fe(O.data),yn="api-intercept",console.error(`[consensus] Captured /api/papers/details/ response with ${O.count} paper(s)`);else console.error(`[consensus] /api/papers/details/ response not captured (${O.reason}) — falling back to DOM cards`);y(u,"extract",r);let vn=await ae(l);if(!vn)throw Error("No answer extracted — Consensus may not have responded");if(Z.length===0)Z=await tt(l);else{let p=await tt(l),D=new Set(Z.map((T)=>T.url));for(let T of p)if(!D.has(T.url))Z.push(T)}let et=await a(["eval",l,"document.location.href"]).catch(()=>"");u.durationMs=Date.now()-r,u.sourcePath=yn,y(u,"done",r),Nn({query:t,url:et,answer:Jn(vn,i),sources:Z,_envelope:on(u)})}catch(l){u.durationMs=Date.now()-r,Zn(l,on(u))}}se();
396
+ })()`;
397
+ return await cdp(["eval", tab, code], 1e4) || "";
398
+ }
399
+ const USAGE = `Usage: node extractors/consensus.mjs "<query>" [--tab <prefix>]
400
+ `;
401
+ async function main() {
402
+ const args = await prepareArgs(process.argv.slice(2));
403
+ validateQuery(args, USAGE);
404
+ const { query, tabPrefix, short } = parseArgs(args);
405
+ const startTime = Date.now();
406
+ const mode = process.env.GREEDY_SEARCH_VISIBLE === "1" ? "visible" : "headless";
407
+ const env = {
408
+ engine: "consensus",
409
+ mode,
410
+ clipboardEmpty: null,
411
+ fallbackUsed: null,
412
+ blockedBy: null,
413
+ verificationResult: null,
414
+ inputReady: null
415
+ };
416
+ try {
417
+ if (process.env.GREEDY_SEARCH_VISIBLE !== "1" && process.env.GREEDY_SEARCH_ALWAYS_VISIBLE !== "1") {
418
+ process.env.GREEDY_SEARCH_HEADLESS = "1";
419
+ }
420
+ await ensureChrome();
421
+ if (!tabPrefix)
422
+ await cdp(["list"]);
423
+ const tab = await getOrOpenTab(tabPrefix);
424
+ const currentUrl = await cdp(["eval", tab, "document.location.href"]).catch(() => "");
425
+ let onConsensus = false;
426
+ try {
427
+ const host = new URL(currentUrl).hostname.toLowerCase();
428
+ onConsensus = host === "consensus.app" || host.endsWith(".consensus.app");
429
+ } catch {}
430
+ if (!onConsensus) {
431
+ logStage(env, "nav", startTime);
432
+ await cdp(["nav", tab, "https://consensus.app/"], 20000);
433
+ await new Promise((r) => setTimeout(r, 600));
434
+ }
435
+ await recoverStaleClerkSession(tab, env, startTime);
436
+ await dismissConsent(tab, cdp);
437
+ logStage(env, "input-wait", startTime);
438
+ const inputReady = await waitForSelector(tab, SELECTORS.input, 15000, 400);
439
+ env.inputReady = inputReady;
440
+ if (!inputReady) {
441
+ const recovered = await recoverStaleClerkSession(tab, env, startTime);
442
+ if (recovered) {
443
+ const retryInputReady = await waitForSelector(tab, SELECTORS.input, 15000, 400);
444
+ env.inputReady = retryInputReady;
445
+ if (retryInputReady) {
446
+ await dismissConsent(tab, cdp);
447
+ } else {
448
+ throw new Error("Consensus input not found after stale auth recovery — page may not have loaded or is in unexpected state");
449
+ }
450
+ } else {
451
+ throw new Error("Consensus input not found — page may not have loaded or is in unexpected state");
452
+ }
453
+ }
454
+ await new Promise((r) => setTimeout(r, jitter(TIMING.postClick)));
455
+ logStage(env, "type-and-submit", startTime);
456
+ await typeIntoConsensus(tab, query);
457
+ await new Promise((r) => setTimeout(r, jitter(TIMING.postType)));
458
+ await cdp([
459
+ "eval",
460
+ tab,
461
+ `document.querySelector('${SELECTORS.submitButton}')?.click()`
462
+ ]);
463
+ await new Promise((r) => setTimeout(r, 2000));
464
+ if (await detectSignUpWall(tab)) {
465
+ env.blockedBy = "signin";
466
+ throw new Error("Consensus requires sign-in — please sign in or create a free account in the visible browser window. Once signed in, cookies persist for future runs.");
467
+ }
468
+ logStage(env, "answer-wait", startTime);
469
+ await waitForSelector(tab, SELECTORS.answerContainer, 30000, 500);
470
+ await installPapersDetailsInterceptor(tab);
471
+ logStage(env, "expand-refs", startTime);
472
+ const clicks = await expandReferences(tab, 8);
473
+ if (clicks === 0) {
474
+ console.error("[consensus] Note: 'Load more results' button not present (initial page has all references)");
475
+ } else {
476
+ console.error(`[consensus] Clicked 'Load more results' ${clicks} time(s) to expand references`);
477
+ }
478
+ await waitForCardsStable(tab, 1200);
479
+ logStage(env, "csv-click", startTime);
480
+ const preClickTs = Number(await cdp(["eval", tab, "Date.now()"]).catch(() => "0") || 0);
481
+ const csvResult = await clickExportCsv(tab);
482
+ if (csvResult !== "clicked") {
483
+ console.error(`[consensus] Export → .CSV click did not register (${csvResult}) — falling back to DOM`);
484
+ }
485
+ logStage(env, "wait-csv-resp", startTime);
486
+ const csvResp = await fetchPapersDetailsResponse(tab, 12000, preClickTs);
487
+ let sources = [];
488
+ let sourcePath = "dom";
489
+ if (csvResp.ok) {
490
+ sources = buildSourcesFromApi(csvResp.data);
491
+ sourcePath = "api-intercept";
492
+ console.error(`[consensus] Captured /api/papers/details/ response with ${csvResp.count} paper(s)`);
493
+ } else {
494
+ console.error(`[consensus] /api/papers/details/ response not captured (${csvResp.reason}) — falling back to DOM cards`);
495
+ }
496
+ logStage(env, "extract", startTime);
497
+ const answer = await extractAnswer(tab);
498
+ if (!answer) {
499
+ throw new Error("No answer extracted — Consensus may not have responded");
500
+ }
501
+ if (sources.length === 0) {
502
+ sources = await extractSourcesFromDom(tab);
503
+ } else {
504
+ const domSources = await extractSourcesFromDom(tab);
505
+ const apiUrls = new Set(sources.map((s) => s.url));
506
+ for (const ds of domSources) {
507
+ if (!apiUrls.has(ds.url))
508
+ sources.push(ds);
509
+ }
510
+ }
511
+ const finalUrl = await cdp(["eval", tab, "document.location.href"]).catch(() => "");
512
+ env.durationMs = Date.now() - startTime;
513
+ env.sourcePath = sourcePath;
514
+ logStage(env, "done", startTime);
515
+ outputJson({
516
+ query,
517
+ url: finalUrl,
518
+ answer: formatAnswer(answer, short),
519
+ sources,
520
+ _envelope: buildEnvelope(env)
521
+ });
522
+ } catch (e) {
523
+ env.durationMs = Date.now() - startTime;
524
+ handleError(e, buildEnvelope(env));
525
+ }
526
+ }
527
+ main();