@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,44 +1,775 @@
1
- #!/usr/bin/env node
2
- import{spawn as MQ}from"node:child_process";import{createHash as YQ}from"node:crypto";import{basename as XQ}from"node:path";function m(Q=process.env,z=process.execPath){let $=Q.GREEDY_SEARCH_NODE||Q.NODE_BINARY||Q.NODE;if($?.trim())return $.trim();let V=XQ(z||"").toLowerCase();if(V==="node"||V==="node.exe")return z;return"node"}import{existsSync as d,mkdirSync as wQ,readdirSync as jQ,readFileSync as v,unlinkSync as O,writeFileSync as s}from"node:fs";import e from"node:net";import{homedir as f,platform as u,tmpdir as kQ}from"node:os";import{join as k}from"node:path";import{existsSync as y,mkdirSync as WQ,readFileSync as l,writeFileSync as ZQ}from"node:fs";import{homedir as KQ}from"node:os";import{join as i}from"node:path";import{tmpdir as qQ}from"node:os";function BQ(Q,z=9222){let $=Number.parseInt(String(Q??""),10);return Number.isInteger($)&&$>1024&&$<65535?$:z}var JQ=qQ().replaceAll("\\","/"),tQ=BQ(process.env.GREEDY_SEARCH_PORT),U=(process.env.GREEDY_SEARCH_PROFILE_DIR||process.env.CDP_PROFILE_DIR||`${JQ}/greedysearch-chrome-profile`).replaceAll("\\","/"),dQ=`${U}/DevToolsActivePort`,sQ=process.env.GREEDY_SEARCH_PID_FILE||`${U}/browser.pid`,N=process.env.CDP_PAGES_CACHE||`${U}/cdp-pages.json`,eQ=process.env.GREEDY_SEARCH_MODE_FILE||`${U}/browser-mode`,Q0=process.env.GREEDY_SEARCH_METADATA_FILE||`${U}/browser-metadata.json`,$0=process.env.GREEDY_SEARCH_LAUNCH_LOCK_FILE||`${U}/browser-launch.lock`,V0=process.env.GREEDY_SEARCH_ACTIVITY_FILE||`${U}/browser-last-activity`,j=(process.env.CDP_SOCKET_DIR||`${U}/cdp-sockets`).replaceAll("\\","/"),z0=`${U}/visible-recovery.jsonl`,R=i(KQ(),".dm"),M=i(R,"greedyconfig"),P=["perplexity","google","chatgpt","gemini"],D="gemini";function bQ(){try{if(y(M)){let Q=l(M,"utf8"),z=JSON.parse(Q);if(Array.isArray(z.engines)&&z.engines.length>0&&z.engines.every(($)=>typeof $==="string")){let $=z.engines.filter((X)=>C[X]),V=z.engines.filter((X)=>!C[X]);if(V.length>0)process.stderr.write(`[greedysearch] Warning: ignoring unknown engine(s) in ${M}: ${V.join(", ")}
3
- [greedysearch] Available engines: ${Object.keys(C).join(", ")}
4
- `);if($.length>0)return $;process.stderr.write(`[greedysearch] Warning: no valid engines in ${M}, falling back to defaults: ${P.join(", ")}
5
- `)}}}catch{}return P}function HQ(){try{if(!y(R))WQ(R,{recursive:!0});if(!y(M))ZQ(M,JSON.stringify({engines:P,synthesizer:D},null,2)+`
6
- `,"utf8")}catch{}}HQ();var p=["gemini","chatgpt"];function UQ(){try{if(y(M)){let Q=l(M,"utf8"),z=JSON.parse(Q);if(typeof z.synthesizer==="string"){let $=z.synthesizer.toLowerCase();if(p.includes($))return $;process.stderr.write(`[greedysearch] Warning: unknown synthesizer "${z.synthesizer}" in ${M}
7
- [greedysearch] Available synthesizers: ${p.join(", ")}
8
- [greedysearch] Falling back to default: ${D}
9
- `)}}}catch{}return D}var C={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"},X0=bQ();var W0=UQ(),Z0=Math.max(1,Number.parseInt(process.env.GREEDY_FETCH_CONCURRENCY||"5",10)||5);process.env.CDP_PROFILE_DIR=U;var c=90000,QQ=30000,a=1200000,LQ=20,o=300,r=8,OQ=YQ("sha256").update(j).digest("hex").slice(0,12);function $Q(Q){if(u()==="win32")return`\\\\.\\pipe\\dm-cdp-${OQ}-${Q}`;return wQ(j,{recursive:!0}),k(j,`${Q}.sock`)}function GQ(){let Q=u();if(Q==="win32")return k(f(),"AppData","Local","Google","Chrome","User Data","DevToolsActivePort");if(Q==="darwin")return k(f(),"Library","Application Support","Google","Chrome","DevToolsActivePort");return k(f(),".config","google-chrome","DevToolsActivePort")}function VQ(){let Q=process.env.CDP_PROFILE_DIR;if(Q){let V=`${Q.replaceAll("\\","/")}/DevToolsActivePort`;if(d(V)){let X=v(V,"utf8").trim().split(`
10
- `);return`ws://localhost:${X[0]}${X[1]}`}throw Error(`GreedySearch DevToolsActivePort not found at ${V}. Refusing to fall back to the main Chrome session.`)}let z=GQ(),$=v(z,"utf8").trim().split(`
11
- `);return`ws://localhost:${$[0]}${$[1]}`}var A=(Q)=>new Promise((z)=>setTimeout(z,Q));function S(){if(u()==="win32")return[];try{return jQ(j).filter((Q)=>Q.endsWith(".sock")).map((Q)=>({targetId:Q.slice(0,-5),socketPath:k(j,Q)}))}catch{return[]}}function _(Q,z,$="target",V=""){let X=Q.toUpperCase(),W=z.filter((Z)=>Z.toUpperCase().startsWith(X));if(W.length===0){let Z=V?` ${V}`:"";throw Error(`No ${$} matching prefix "${Q}".${Z}`)}if(W.length>1)throw Error(`Ambiguous prefix "${Q}" — matches ${W.length} ${$}s. Use more characters.`);return W[0]}function AQ(Q){if(Q.length===0)return r;let z=Math.max(...Q.map(($)=>$.length));for(let $=r;$<=z;$++)if(new Set(Q.map((X)=>X.slice(0,$).toUpperCase())).size===Q.length)return $;return z}class h{#Q;#X=0;#$=new Map;#V=new Map;#z=[];async connect(Q){return new Promise((z,$)=>{this.#Q=new WebSocket(Q),this.#Q.onopen=()=>z(),this.#Q.onerror=(V)=>$(Error(`WebSocket error: ${V.message||V.type}`)),this.#Q.onclose=()=>this.#z.forEach((V)=>V()),this.#Q.onmessage=(V)=>{let X=JSON.parse(V.data);if(X.id&&this.#$.has(X.id)){let{resolve:W,reject:Z}=this.#$.get(X.id);if(this.#$.delete(X.id),X.error)Z(Error(X.error.message));else W(X.result)}else if(X.method&&this.#V.has(X.method))for(let W of[...this.#V.get(X.method)])W(X.params||{},X)}})}send(Q,z={},$){let V=++this.#X;return new Promise((X,W)=>{this.#$.set(V,{resolve:X,reject:W});let Z={id:V,method:Q,params:z};if($)Z.sessionId=$;this.#Q.send(JSON.stringify(Z)),setTimeout(()=>{if(this.#$.has(V))this.#$.delete(V),W(Error(`Timeout: ${Q}`))},c)})}onEvent(Q,z){if(!this.#V.has(Q))this.#V.set(Q,new Set);let $=this.#V.get(Q);return $.add(z),()=>{if($.delete(z),$.size===0)this.#V.delete(Q)}}waitForEvent(Q,z=c){let $=!1,V,X;return{promise:new Promise((Z,q)=>{V=this.onEvent(Q,(H)=>{if($)return;$=!0,clearTimeout(X),V(),Z(H)}),X=setTimeout(()=>{if($)return;$=!0,V(),q(Error(`Timeout waiting for event: ${Q}`))},z)}),cancel(){if($)return;$=!0,clearTimeout(X),V?.()}}}onClose(Q){this.#z.push(Q)}close(){this.#Q.close()}}async function x(Q){let{targetInfos:z}=await Q.send("Target.getTargets");return z.filter(($)=>$.type==="page"&&(!$.url.startsWith("chrome://")||$.url==="chrome://newtab/"))}function zQ(Q){let z=AQ(Q.map(($)=>$.targetId));return Q.map(($)=>{let V=$.targetId.slice(0,z).padEnd(z),X=$.title.substring(0,54).padEnd(54);return`${V} ${X} ${$.url}`}).join(`
12
- `)}function yQ(Q,z=!1){let $=Q.role?.value||"",V=Q.name?.value??"",X=Q.value?.value;if(z&&$==="InlineTextBox")return!1;return $!=="none"&&$!=="generic"&&!(V===""&&(X===""||X==null))}function NQ(Q,z){let $=Q.role?.value||"",V=Q.name?.value??"",X=Q.value?.value,Z=`${" ".repeat(Math.min(z,10))}[${$}]`;if(V!=="")Z+=` ${V}`;if(!(X===""||X==null))Z+=` = ${JSON.stringify(X)}`;return Z}function FQ(Q,z,$){let V=[],X=new Set;for(let W of Q.childIds||[]){let Z=z.get(W);if(Z&&!X.has(Z.nodeId))X.add(Z.nodeId),V.push(Z)}for(let W of $.get(Q.nodeId)||[])if(!X.has(W.nodeId))X.add(W.nodeId),V.push(W);return V}async function TQ(Q,z,$=!1){let{nodes:V}=await Q.send("Accessibility.getFullAXTree",{},z),X=new Map(V.map((K)=>[K.nodeId,K])),W=new Map;for(let K of V){if(!K.parentId)continue;if(!W.has(K.parentId))W.set(K.parentId,[]);W.get(K.parentId).push(K)}let Z=[],q=new Set;function H(K,B){if(!K||q.has(K.nodeId))return;if(q.add(K.nodeId),yQ(K,$))Z.push(NQ(K,B));for(let J of FQ(K,X,W))H(J,B+1)}let b=V.filter((K)=>!K.parentId||!X.has(K.parentId));for(let K of b)H(K,0);for(let K of V)H(K,0);return Z.join(`
13
- `)}var L=new Map;async function n(Q,z){let $=[],V=Q.onEvent("Runtime.executionContextCreated",(q)=>{if(q?.context)$.push(q.context)});await Q.send("Runtime.enable",{},z),await A(100),V(),await Q.send("Runtime.disable",{},z).catch(()=>{});let X=null;try{X=(await Q.send("Page.getFrameTree",{},z))?.frameTree?.frame?.id??null}catch{}let W=$.filter((q)=>q.auxData?.isDefault&&q.auxData?.type==="default");return(X&&W.find((q)=>q.auxData?.frameId===X)||W[0]||null)?.id??null}async function w(Q,z,$){let V=L.get(z);if(V==null){if(V=await n(Q,z),V==null)throw Error("Failed to capture main execution context — is the page loaded?");L.set(z,V)}async function X(W){let Z=await Q.send("Runtime.evaluate",{expression:$,contextId:W,returnByValue:!0,awaitPromise:!0},z);if(Z.exceptionDetails)throw Error(Z.exceptionDetails.text||Z.exceptionDetails.exception?.description);let q=Z.result.value;return typeof q==="object"?JSON.stringify(q,null,2):String(q??"")}try{return await X(V)}catch(W){if(L.delete(z),V=await n(Q,z),V==null)throw Error("Failed to re-capture execution context after navigation");return L.set(z,V),X(V)}}async function CQ(Q,z,$){let V=1;try{let q=await Q.send("Page.getLayoutMetrics",{},z);V=q.visualViewport?.clientWidth?q.cssVisualViewport?.clientWidth?Math.round(q.visualViewport.clientWidth/q.cssVisualViewport.clientWidth*100)/100:1:1;let{deviceScaleFactor:H}=await Q.send("Emulation.getDeviceMetricsOverride",{},z).catch(()=>({}));if(H)V=H}catch{}if(V===1)try{let q=await w(Q,z,"window.devicePixelRatio"),H=Number.parseFloat(q);if(H>0)V=H}catch{}let{data:X}=await Q.send("Page.captureScreenshot",{format:"png"},z),W=$||k(kQ(),"screenshot.png");s(W,Buffer.from(X,"base64"));let Z=[W];if(Z.push(`Screenshot saved. Device pixel ratio (DPR): ${V}`),Z.push("Coordinate mapping:"),Z.push(` Screenshot pixels → CSS pixels (for CDP Input events): divide by ${V}`),Z.push(` e.g. screenshot point (${Math.round(100*V)}, ${Math.round(200*V)}) → CSS (100, 200) → use clickxy <target> 100 200`),V!==1)Z.push(` On this ${V}x display: CSS px = screenshot px / ${V} ≈ screenshot px × ${Math.round(100/V)/100}`);return Z.join(`
14
- `)}async function RQ(Q,z,$){let V=$?`document.querySelector(${JSON.stringify($)})?.outerHTML || 'Element not found'`:"document.documentElement.outerHTML";return w(Q,z,V)}async function PQ(Q,z,$=QQ){let V=Date.now()+$,X="",W;while(Date.now()<V){try{let Z=await w(Q,z,"document.readyState");if(X=Z,Z==="complete")return}catch(Z){W=Z}await A(200)}if(X)throw Error(`Timed out waiting for navigation to finish (last readyState: ${X})`);if(W)throw Error(`Timed out waiting for navigation to finish (${W.message})`);throw Error("Timed out waiting for navigation to finish")}async function DQ(Q,z,$){await Q.send("Page.enable",{},z);let V=Q.waitForEvent("Page.loadEventFired",QQ),X=await Q.send("Page.navigate",{url:$},z);if(X.errorText)throw V.cancel(),Error(X.errorText);L.delete(z);let W=!1;if(X.loaderId)try{await V.promise,W=!0}catch{}else V.cancel();if(!W)await PQ(Q,z,5000);return`Navigated to ${$}`}async function fQ(Q,z){let $=await w(Q,z,`JSON.stringify(performance.getEntriesByType('resource').map(e => ({
1
+ import { spawn } from "node:child_process";
2
+ import { createHash } from "node:crypto";
3
+ import { nodeRuntimeCommand } from "../src/utils/node-runtime.mjs";
4
+ import {
5
+ existsSync,
6
+ mkdirSync,
7
+ readdirSync,
8
+ readFileSync,
9
+ unlinkSync,
10
+ writeFileSync
11
+ } from "node:fs";
12
+ import net from "node:net";
13
+ import { homedir, platform, tmpdir } from "node:os";
14
+ import { join } from "node:path";
15
+ import {
16
+ GREEDY_CDP_SOCKET_DIR,
17
+ PAGES_CACHE
18
+ } from "../src/search/constants.mjs";
19
+ const TIMEOUT = 90000;
20
+ const NAVIGATION_TIMEOUT = 30000;
21
+ const IDLE_TIMEOUT = 20 * 60 * 1000;
22
+ const DAEMON_CONNECT_RETRIES = 20;
23
+ const DAEMON_CONNECT_DELAY = 300;
24
+ const MIN_TARGET_PREFIX_LEN = 8;
25
+ const SOCKET_SCOPE = createHash("sha256").update(GREEDY_CDP_SOCKET_DIR).digest("hex").slice(0, 12);
26
+ function sockPath(targetId) {
27
+ if (platform() === "win32")
28
+ return `\\\\.\\pipe\\dm-cdp-${SOCKET_SCOPE}-${targetId}`;
29
+ mkdirSync(GREEDY_CDP_SOCKET_DIR, { recursive: true });
30
+ return join(GREEDY_CDP_SOCKET_DIR, `${targetId}.sock`);
31
+ }
32
+ function getDevToolsActivePortPath() {
33
+ const os = platform();
34
+ if (os === "win32")
35
+ return join(homedir(), "AppData", "Local", "Google", "Chrome", "User Data", "DevToolsActivePort");
36
+ if (os === "darwin")
37
+ return join(homedir(), "Library", "Application Support", "Google", "Chrome", "DevToolsActivePort");
38
+ return join(homedir(), ".config", "google-chrome", "DevToolsActivePort");
39
+ }
40
+ function getWsUrl() {
41
+ const profileDir = process.env.CDP_PROFILE_DIR;
42
+ if (profileDir) {
43
+ const p = `${profileDir.replaceAll("\\", "/")}/DevToolsActivePort`;
44
+ if (existsSync(p)) {
45
+ const lines = readFileSync(p, "utf8").trim().split(`
46
+ `);
47
+ return `ws://localhost:${lines[0]}${lines[1]}`;
48
+ }
49
+ throw new Error(`GreedySearch DevToolsActivePort not found at ${p}. Refusing to fall back to the main Chrome session.`);
50
+ }
51
+ const portFile = getDevToolsActivePortPath();
52
+ const lines = readFileSync(portFile, "utf8").trim().split(`
53
+ `);
54
+ return `ws://localhost:${lines[0]}${lines[1]}`;
55
+ }
56
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
57
+ function listDaemonSockets() {
58
+ if (platform() === "win32")
59
+ return [];
60
+ try {
61
+ return readdirSync(GREEDY_CDP_SOCKET_DIR).filter((f) => f.endsWith(".sock")).map((f) => ({
62
+ targetId: f.slice(0, -5),
63
+ socketPath: join(GREEDY_CDP_SOCKET_DIR, f)
64
+ }));
65
+ } catch {
66
+ return [];
67
+ }
68
+ }
69
+ function resolvePrefix(prefix, candidates, noun = "target", missingHint = "") {
70
+ const upper = prefix.toUpperCase();
71
+ const matches = candidates.filter((candidate) => candidate.toUpperCase().startsWith(upper));
72
+ if (matches.length === 0) {
73
+ const hint = missingHint ? ` ${missingHint}` : "";
74
+ throw new Error(`No ${noun} matching prefix "${prefix}".${hint}`);
75
+ }
76
+ if (matches.length > 1) {
77
+ throw new Error(`Ambiguous prefix "${prefix}" — matches ${matches.length} ${noun}s. Use more characters.`);
78
+ }
79
+ return matches[0];
80
+ }
81
+ function getDisplayPrefixLength(targetIds) {
82
+ if (targetIds.length === 0)
83
+ return MIN_TARGET_PREFIX_LEN;
84
+ const maxLen = Math.max(...targetIds.map((id) => id.length));
85
+ for (let len = MIN_TARGET_PREFIX_LEN;len <= maxLen; len++) {
86
+ const prefixes = new Set(targetIds.map((id) => id.slice(0, len).toUpperCase()));
87
+ if (prefixes.size === targetIds.length)
88
+ return len;
89
+ }
90
+ return maxLen;
91
+ }
92
+
93
+ class CDP {
94
+ #ws;
95
+ #id = 0;
96
+ #pending = new Map;
97
+ #eventHandlers = new Map;
98
+ #closeHandlers = [];
99
+ async connect(wsUrl) {
100
+ return new Promise((res, rej) => {
101
+ this.#ws = new WebSocket(wsUrl);
102
+ this.#ws.onopen = () => res();
103
+ this.#ws.onerror = (e) => rej(new Error(`WebSocket error: ${e.message || e.type}`));
104
+ this.#ws.onclose = () => this.#closeHandlers.forEach((h) => h());
105
+ this.#ws.onmessage = (ev) => {
106
+ const msg = JSON.parse(ev.data);
107
+ if (msg.id && this.#pending.has(msg.id)) {
108
+ const { resolve, reject } = this.#pending.get(msg.id);
109
+ this.#pending.delete(msg.id);
110
+ if (msg.error)
111
+ reject(new Error(msg.error.message));
112
+ else
113
+ resolve(msg.result);
114
+ } else if (msg.method && this.#eventHandlers.has(msg.method)) {
115
+ for (const handler of [...this.#eventHandlers.get(msg.method)]) {
116
+ handler(msg.params || {}, msg);
117
+ }
118
+ }
119
+ };
120
+ });
121
+ }
122
+ send(method, params = {}, sessionId) {
123
+ const id = ++this.#id;
124
+ return new Promise((resolve, reject) => {
125
+ this.#pending.set(id, { resolve, reject });
126
+ const msg = { id, method, params };
127
+ if (sessionId)
128
+ msg.sessionId = sessionId;
129
+ this.#ws.send(JSON.stringify(msg));
130
+ setTimeout(() => {
131
+ if (this.#pending.has(id)) {
132
+ this.#pending.delete(id);
133
+ reject(new Error(`Timeout: ${method}`));
134
+ }
135
+ }, TIMEOUT);
136
+ });
137
+ }
138
+ onEvent(method, handler) {
139
+ if (!this.#eventHandlers.has(method))
140
+ this.#eventHandlers.set(method, new Set);
141
+ const handlers = this.#eventHandlers.get(method);
142
+ handlers.add(handler);
143
+ return () => {
144
+ handlers.delete(handler);
145
+ if (handlers.size === 0)
146
+ this.#eventHandlers.delete(method);
147
+ };
148
+ }
149
+ waitForEvent(method, timeout = TIMEOUT) {
150
+ let settled = false;
151
+ let off;
152
+ let timer;
153
+ const promise = new Promise((resolve, reject) => {
154
+ off = this.onEvent(method, (params) => {
155
+ if (settled)
156
+ return;
157
+ settled = true;
158
+ clearTimeout(timer);
159
+ off();
160
+ resolve(params);
161
+ });
162
+ timer = setTimeout(() => {
163
+ if (settled)
164
+ return;
165
+ settled = true;
166
+ off();
167
+ reject(new Error(`Timeout waiting for event: ${method}`));
168
+ }, timeout);
169
+ });
170
+ return {
171
+ promise,
172
+ cancel() {
173
+ if (settled)
174
+ return;
175
+ settled = true;
176
+ clearTimeout(timer);
177
+ off?.();
178
+ }
179
+ };
180
+ }
181
+ onClose(handler) {
182
+ this.#closeHandlers.push(handler);
183
+ }
184
+ close() {
185
+ this.#ws.close();
186
+ }
187
+ }
188
+ async function getPages(cdp) {
189
+ const { targetInfos } = await cdp.send("Target.getTargets");
190
+ return targetInfos.filter((t) => t.type === "page" && (!t.url.startsWith("chrome://") || t.url === "chrome://newtab/"));
191
+ }
192
+ function formatPageList(pages) {
193
+ const prefixLen = getDisplayPrefixLength(pages.map((p) => p.targetId));
194
+ return pages.map((p) => {
195
+ const id = p.targetId.slice(0, prefixLen).padEnd(prefixLen);
196
+ const title = p.title.substring(0, 54).padEnd(54);
197
+ return `${id} ${title} ${p.url}`;
198
+ }).join(`
199
+ `);
200
+ }
201
+ function shouldShowAxNode(node, compact = false) {
202
+ const role = node.role?.value || "";
203
+ const name = node.name?.value ?? "";
204
+ const value = node.value?.value;
205
+ if (compact && role === "InlineTextBox")
206
+ return false;
207
+ return role !== "none" && role !== "generic" && !(name === "" && (value === "" || value == null));
208
+ }
209
+ function formatAxNode(node, depth) {
210
+ const role = node.role?.value || "";
211
+ const name = node.name?.value ?? "";
212
+ const value = node.value?.value;
213
+ const indent = " ".repeat(Math.min(depth, 10));
214
+ let line = `${indent}[${role}]`;
215
+ if (name !== "")
216
+ line += ` ${name}`;
217
+ if (!(value === "" || value == null))
218
+ line += ` = ${JSON.stringify(value)}`;
219
+ return line;
220
+ }
221
+ function orderedAxChildren(node, nodesById, childrenByParent) {
222
+ const children = [];
223
+ const seen = new Set;
224
+ for (const childId of node.childIds || []) {
225
+ const child = nodesById.get(childId);
226
+ if (child && !seen.has(child.nodeId)) {
227
+ seen.add(child.nodeId);
228
+ children.push(child);
229
+ }
230
+ }
231
+ for (const child of childrenByParent.get(node.nodeId) || []) {
232
+ if (!seen.has(child.nodeId)) {
233
+ seen.add(child.nodeId);
234
+ children.push(child);
235
+ }
236
+ }
237
+ return children;
238
+ }
239
+ async function snapshotStr(cdp, sid, compact = false) {
240
+ const { nodes } = await cdp.send("Accessibility.getFullAXTree", {}, sid);
241
+ const nodesById = new Map(nodes.map((node) => [node.nodeId, node]));
242
+ const childrenByParent = new Map;
243
+ for (const node of nodes) {
244
+ if (!node.parentId)
245
+ continue;
246
+ if (!childrenByParent.has(node.parentId))
247
+ childrenByParent.set(node.parentId, []);
248
+ childrenByParent.get(node.parentId).push(node);
249
+ }
250
+ const lines = [];
251
+ const visited = new Set;
252
+ function visit(node, depth) {
253
+ if (!node || visited.has(node.nodeId))
254
+ return;
255
+ visited.add(node.nodeId);
256
+ if (shouldShowAxNode(node, compact))
257
+ lines.push(formatAxNode(node, depth));
258
+ for (const child of orderedAxChildren(node, nodesById, childrenByParent)) {
259
+ visit(child, depth + 1);
260
+ }
261
+ }
262
+ const roots = nodes.filter((node) => !node.parentId || !nodesById.has(node.parentId));
263
+ for (const root of roots)
264
+ visit(root, 0);
265
+ for (const node of nodes)
266
+ visit(node, 0);
267
+ return lines.join(`
268
+ `);
269
+ }
270
+ const _mainCtx = new Map;
271
+ async function captureMainContext(cdp, sid) {
272
+ const contexts = [];
273
+ const off = cdp.onEvent("Runtime.executionContextCreated", (params) => {
274
+ if (params?.context)
275
+ contexts.push(params.context);
276
+ });
277
+ await cdp.send("Runtime.enable", {}, sid);
278
+ await sleep(100);
279
+ off();
280
+ await cdp.send("Runtime.disable", {}, sid).catch(() => {});
281
+ let rootFrameId = null;
282
+ try {
283
+ const ft = await cdp.send("Page.getFrameTree", {}, sid);
284
+ rootFrameId = ft?.frameTree?.frame?.id ?? null;
285
+ } catch {}
286
+ const defaults = contexts.filter((ctx) => ctx.auxData?.isDefault && ctx.auxData?.type === "default");
287
+ const main = rootFrameId && defaults.find((ctx) => ctx.auxData?.frameId === rootFrameId) || defaults[0] || null;
288
+ return main?.id ?? null;
289
+ }
290
+ async function evalStr(cdp, sid, expression) {
291
+ let contextId = _mainCtx.get(sid);
292
+ if (contextId == null) {
293
+ contextId = await captureMainContext(cdp, sid);
294
+ if (contextId == null) {
295
+ throw new Error("Failed to capture main execution context — is the page loaded?");
296
+ }
297
+ _mainCtx.set(sid, contextId);
298
+ }
299
+ async function _evalWith(cid) {
300
+ const result = await cdp.send("Runtime.evaluate", {
301
+ expression,
302
+ contextId: cid,
303
+ returnByValue: true,
304
+ awaitPromise: true
305
+ }, sid);
306
+ if (result.exceptionDetails) {
307
+ throw new Error(result.exceptionDetails.text || result.exceptionDetails.exception?.description);
308
+ }
309
+ const val = result.result.value;
310
+ return typeof val === "object" ? JSON.stringify(val, null, 2) : String(val ?? "");
311
+ }
312
+ try {
313
+ return await _evalWith(contextId);
314
+ } catch (_e) {
315
+ _mainCtx.delete(sid);
316
+ contextId = await captureMainContext(cdp, sid);
317
+ if (contextId == null) {
318
+ throw new Error("Failed to re-capture execution context after navigation");
319
+ }
320
+ _mainCtx.set(sid, contextId);
321
+ return _evalWith(contextId);
322
+ }
323
+ }
324
+ async function shotStr(cdp, sid, filePath) {
325
+ let dpr = 1;
326
+ try {
327
+ const metrics = await cdp.send("Page.getLayoutMetrics", {}, sid);
328
+ dpr = metrics.visualViewport?.clientWidth ? metrics.cssVisualViewport?.clientWidth ? Math.round(metrics.visualViewport.clientWidth / metrics.cssVisualViewport.clientWidth * 100) / 100 : 1 : 1;
329
+ const { deviceScaleFactor } = await cdp.send("Emulation.getDeviceMetricsOverride", {}, sid).catch(() => ({}));
330
+ if (deviceScaleFactor)
331
+ dpr = deviceScaleFactor;
332
+ } catch {}
333
+ if (dpr === 1) {
334
+ try {
335
+ const raw = await evalStr(cdp, sid, "window.devicePixelRatio");
336
+ const parsed = Number.parseFloat(raw);
337
+ if (parsed > 0)
338
+ dpr = parsed;
339
+ } catch {}
340
+ }
341
+ const { data } = await cdp.send("Page.captureScreenshot", { format: "png" }, sid);
342
+ const out = filePath || join(tmpdir(), "screenshot.png");
343
+ writeFileSync(out, Buffer.from(data, "base64"));
344
+ const lines = [out];
345
+ lines.push(`Screenshot saved. Device pixel ratio (DPR): ${dpr}`);
346
+ lines.push(`Coordinate mapping:`);
347
+ lines.push(` Screenshot pixels → CSS pixels (for CDP Input events): divide by ${dpr}`);
348
+ lines.push(` e.g. screenshot point (${Math.round(100 * dpr)}, ${Math.round(200 * dpr)}) → CSS (100, 200) → use clickxy <target> 100 200`);
349
+ if (dpr !== 1) {
350
+ lines.push(` On this ${dpr}x display: CSS px = screenshot px / ${dpr} ≈ screenshot px × ${Math.round(100 / dpr) / 100}`);
351
+ }
352
+ return lines.join(`
353
+ `);
354
+ }
355
+ async function htmlStr(cdp, sid, selector) {
356
+ const expr = selector ? `document.querySelector(${JSON.stringify(selector)})?.outerHTML || 'Element not found'` : `document.documentElement.outerHTML`;
357
+ return evalStr(cdp, sid, expr);
358
+ }
359
+ async function waitForDocumentReady(cdp, sid, timeoutMs = NAVIGATION_TIMEOUT) {
360
+ const deadline = Date.now() + timeoutMs;
361
+ let lastState = "";
362
+ let lastError;
363
+ while (Date.now() < deadline) {
364
+ try {
365
+ const state = await evalStr(cdp, sid, "document.readyState");
366
+ lastState = state;
367
+ if (state === "complete")
368
+ return;
369
+ } catch (e) {
370
+ lastError = e;
371
+ }
372
+ await sleep(200);
373
+ }
374
+ if (lastState) {
375
+ throw new Error(`Timed out waiting for navigation to finish (last readyState: ${lastState})`);
376
+ }
377
+ if (lastError) {
378
+ throw new Error(`Timed out waiting for navigation to finish (${lastError.message})`);
379
+ }
380
+ throw new Error("Timed out waiting for navigation to finish");
381
+ }
382
+ async function navStr(cdp, sid, url) {
383
+ await cdp.send("Page.enable", {}, sid);
384
+ const loadEvent = cdp.waitForEvent("Page.loadEventFired", NAVIGATION_TIMEOUT);
385
+ const result = await cdp.send("Page.navigate", { url }, sid);
386
+ if (result.errorText) {
387
+ loadEvent.cancel();
388
+ throw new Error(result.errorText);
389
+ }
390
+ _mainCtx.delete(sid);
391
+ let loadFired = false;
392
+ if (result.loaderId) {
393
+ try {
394
+ await loadEvent.promise;
395
+ loadFired = true;
396
+ } catch {}
397
+ } else {
398
+ loadEvent.cancel();
399
+ }
400
+ if (!loadFired) {
401
+ await waitForDocumentReady(cdp, sid, 5000);
402
+ }
403
+ return `Navigated to ${url}`;
404
+ }
405
+ async function netStr(cdp, sid) {
406
+ const raw = await evalStr(cdp, sid, `JSON.stringify(performance.getEntriesByType('resource').map(e => ({
15
407
  name: e.name.substring(0, 120), type: e.initiatorType,
16
408
  duration: Math.round(e.duration), size: e.transferSize
17
- })))`);return JSON.parse($).map((V)=>`${String(V.duration).padStart(5)}ms ${String(V.size||"?").padStart(8)}B ${V.type.padEnd(8)} ${V.name}`).join(`
18
- `)}async function vQ(Q,z,$){if(!$)throw Error("CSS selector required");let V=`
409
+ })))`);
410
+ return JSON.parse(raw).map((e) => `${String(e.duration).padStart(5)}ms ${String(e.size || "?").padStart(8)}B ${e.type.padEnd(8)} ${e.name}`).join(`
411
+ `);
412
+ }
413
+ async function clickStr(cdp, sid, selector) {
414
+ if (!selector)
415
+ throw new Error("CSS selector required");
416
+ const expr = `
19
417
  (function() {
20
- const el = document.querySelector(${JSON.stringify($)});
21
- if (!el) return { ok: false, error: 'Element not found: ' + ${JSON.stringify($)} };
418
+ const el = document.querySelector(${JSON.stringify(selector)});
419
+ if (!el) return { ok: false, error: 'Element not found: ' + ${JSON.stringify(selector)} };
22
420
  el.scrollIntoView({ block: 'center' });
23
421
  el.click();
24
422
  return { ok: true, tag: el.tagName, text: el.textContent.trim().substring(0, 80) };
25
423
  })()
26
- `,X=await w(Q,z,V),W=JSON.parse(X);if(!W.ok)throw Error(W.error);return`Clicked <${W.tag}> "${W.text}"`}async function _Q(Q,z,$,V){let X=Number.parseFloat($),W=Number.parseFloat(V);if(Number.isNaN(X)||Number.isNaN(W))throw Error("x and y must be numbers (CSS pixels)");let Z={x:X,y:W,button:"left",clickCount:1,modifiers:0};return await Q.send("Input.dispatchMouseEvent",{...Z,type:"mouseMoved"},z),await Q.send("Input.dispatchMouseEvent",{...Z,type:"mousePressed"},z),await A(50),await Q.send("Input.dispatchMouseEvent",{...Z,type:"mouseReleased"},z),`Clicked at CSS (${X}, ${W})`}async function xQ(Q,z,$){if($==null||$==="")throw Error("text required");return await Q.send("Input.insertText",{text:$},z),`Typed ${$.length} characters`}async function uQ(Q,z,$,V=1500){if(!$)throw Error("CSS selector required");V=Math.min(Math.max(Number.parseInt(V,10)||1500,100),30000);let X=0,W=Date.now()+300000;while(Date.now()<W){if(await w(Q,z,`!!document.querySelector(${JSON.stringify($)})`)!=="true")break;let q=`
424
+ `;
425
+ const result = await evalStr(cdp, sid, expr);
426
+ const r = JSON.parse(result);
427
+ if (!r.ok)
428
+ throw new Error(r.error);
429
+ return `Clicked <${r.tag}> "${r.text}"`;
430
+ }
431
+ async function clickXyStr(cdp, sid, x, y) {
432
+ const cx = Number.parseFloat(x);
433
+ const cy = Number.parseFloat(y);
434
+ if (Number.isNaN(cx) || Number.isNaN(cy))
435
+ throw new Error("x and y must be numbers (CSS pixels)");
436
+ const base = { x: cx, y: cy, button: "left", clickCount: 1, modifiers: 0 };
437
+ await cdp.send("Input.dispatchMouseEvent", { ...base, type: "mouseMoved" }, sid);
438
+ await cdp.send("Input.dispatchMouseEvent", { ...base, type: "mousePressed" }, sid);
439
+ await sleep(50);
440
+ await cdp.send("Input.dispatchMouseEvent", { ...base, type: "mouseReleased" }, sid);
441
+ return `Clicked at CSS (${cx}, ${cy})`;
442
+ }
443
+ async function typeStr(cdp, sid, text) {
444
+ if (text == null || text === "")
445
+ throw new Error("text required");
446
+ await cdp.send("Input.insertText", { text }, sid);
447
+ return `Typed ${text.length} characters`;
448
+ }
449
+ async function loadAllStr(cdp, sid, selector, intervalMs = 1500) {
450
+ if (!selector)
451
+ throw new Error("CSS selector required");
452
+ intervalMs = Math.min(Math.max(Number.parseInt(intervalMs, 10) || 1500, 100), 30000);
453
+ let clicks = 0;
454
+ const deadline = Date.now() + 5 * 60 * 1000;
455
+ while (Date.now() < deadline) {
456
+ const exists = await evalStr(cdp, sid, `!!document.querySelector(${JSON.stringify(selector)})`);
457
+ if (exists !== "true")
458
+ break;
459
+ const clickExpr = `
27
460
  (function() {
28
- const el = document.querySelector(${JSON.stringify($)});
461
+ const el = document.querySelector(${JSON.stringify(selector)});
29
462
  if (!el) return false;
30
463
  el.scrollIntoView({ block: 'center' });
31
464
  el.click();
32
465
  return true;
33
466
  })()
34
- `;if(await w(Q,z,q)!=="true")break;X++,await A(V)}return`Clicked "${$}" ${X} time(s) until it disappeared`}async function SQ(Q,z,$,V){if(!$)throw Error('CDP method required (e.g. "DOM.getDocument")');let X={};if(V)try{X=JSON.parse(V)}catch{throw Error(`Invalid JSON params: ${V}`)}let W=await Q.send($,X,z);return JSON.stringify(W,null,2)}async function hQ(Q,z,$){if(!z)throw Error("Browser CDP method required");let V={};if($)try{V=JSON.parse($)}catch{throw Error(`Invalid JSON params: ${$}`)}let X=await Q.send(z,V);return JSON.stringify(X,null,2)}async function EQ(Q){let z=$Q(Q),$=new h;try{await $.connect(VQ())}catch(K){process.stderr.write(`Daemon: cannot connect to Chrome: ${K.message}
35
- `),process.exit(1)}let V;try{V=(await $.send("Target.attachToTarget",{targetId:Q,flatten:!0})).sessionId}catch(K){process.stderr.write(`Daemon: attach failed: ${K.message}
36
- `),$.close(),process.exit(1)}let X=!0;function W(){if(!X)return;X=!1,b.close();try{O(z)}catch{}$.close(),process.exit(0)}$.onEvent("Target.targetDestroyed",(K)=>{if(K.targetId===Q)W()}),$.onEvent("Target.detachedFromTarget",(K)=>{if(K.sessionId===V)W()}),$.onClose(()=>W()),process.on("SIGTERM",W),process.on("SIGINT",W);let Z=setTimeout(W,a);function q(){clearTimeout(Z),Z=setTimeout(W,a)}async function H({cmd:K,args:B}){q();try{let J;switch(K){case"list":{let Y=await x($);J=zQ(Y);break}case"list_raw":{let Y=await x($);J=JSON.stringify(Y);break}case"snap":case"snapshot":J=await TQ($,V,!0);break;case"eval":J=await w($,V,B[0]);break;case"shot":case"screenshot":J=await CQ($,V,B[0]);break;case"html":J=await RQ($,V,B[0]);break;case"nav":case"navigate":J=await DQ($,V,B[0]);break;case"net":case"network":J=await fQ($,V);break;case"click":J=await vQ($,V,B[0]);break;case"clickxy":J=await _Q($,V,B[0],B[1]);break;case"type":J=await xQ($,V,B[0]);break;case"loadall":J=await uQ($,V,B[0],B[1]?Number.parseInt(B[1],10):1500);break;case"evalraw":J=await SQ($,V,B[0],B[1]);break;case"browse":case"browserraw":J=await hQ($,B[0],B[1]);break;case"stop":return{ok:!0,result:"",stopAfter:!0};default:return{ok:!1,error:`Unknown command: ${K}`}}return{ok:!0,result:J??""}}catch(J){return{ok:!1,error:J.message}}}let b=e.createServer((K)=>{let B="";K.on("data",(J)=>{B+=J.toString();let Y=B.split(`
37
- `);B=Y.pop();for(let E of Y){if(!E.trim())continue;let T;try{T=JSON.parse(E)}catch{K.write(`${JSON.stringify({ok:!1,error:"Invalid JSON request",id:null})}
38
- `);continue}H(T).then((I)=>{let g=`${JSON.stringify({...I,id:T.id})}
39
- `;if(I.stopAfter)K.end(g,W);else K.write(g)})}})});try{O(z)}catch{}b.listen(z)}function G(Q){return new Promise((z,$)=>{let V=e.connect(Q);V.on("connect",()=>z(V)),V.on("error",$)})}async function IQ(Q){let z=$Q(Q);try{return await G(z)}catch{}try{O(z)}catch{}MQ(m(),[process.argv[1],"_daemon",Q],{detached:!0,stdio:"ignore"}).unref();let V=[50,100,200],X=Date.now()+LQ*o,W=0;while(Date.now()<X){try{return await G(z)}catch{}let Z=V[W]??o;W++,await A(Z)}throw Error("Daemon failed to start — did you click Allow in Chrome?")}function F(Q,z){return new Promise(($,V)=>{let X="",W=!1,Z=()=>{Q.off("data",q),Q.off("error",H),Q.off("end",b),Q.off("close",K)},q=(B)=>{X+=B.toString();let J=X.indexOf(`
40
- `);if(J===-1)return;W=!0,Z();try{$(JSON.parse(X.slice(0,J)))}catch(Y){V(Error(`Invalid JSON response from daemon: ${Y.message}`))}Q.end()},H=(B)=>{if(W)return;W=!0,Z(),V(B)},b=()=>{if(W)return;W=!0,Z(),V(Error("Connection closed before response"))},K=()=>{if(W)return;W=!0,Z(),V(Error("Connection closed before response"))};Q.on("data",q),Q.on("error",H),Q.on("end",b),Q.on("close",K),z.id=1,Q.write(`${JSON.stringify(z)}
41
- `)})}function gQ(){return S()[0]?.socketPath||null}async function mQ(Q){let z=S();if(Q){let $=_(Q,z.map((X)=>X.targetId),"daemon"),V=z.find((X)=>X.targetId===$);try{let X=await G(V.socketPath);await F(X,{cmd:"stop"})}catch{try{O(V.socketPath)}catch{}}return}for(let $ of z)try{let V=await G($.socketPath);await F(V,{cmd:"stop"})}catch{try{O($.socketPath)}catch{}}}var t=`cdp - lightweight Chrome DevTools Protocol CLI (no Puppeteer)
467
+ `;
468
+ const clicked = await evalStr(cdp, sid, clickExpr);
469
+ if (clicked !== "true")
470
+ break;
471
+ clicks++;
472
+ await sleep(intervalMs);
473
+ }
474
+ return `Clicked "${selector}" ${clicks} time(s) until it disappeared`;
475
+ }
476
+ async function evalRawStr(cdp, sid, method, paramsJson) {
477
+ if (!method)
478
+ throw new Error('CDP method required (e.g. "DOM.getDocument")');
479
+ let params = {};
480
+ if (paramsJson) {
481
+ try {
482
+ params = JSON.parse(paramsJson);
483
+ } catch {
484
+ throw new Error(`Invalid JSON params: ${paramsJson}`);
485
+ }
486
+ }
487
+ const result = await cdp.send(method, params, sid);
488
+ return JSON.stringify(result, null, 2);
489
+ }
490
+ async function browserRawStr(cdp, method, paramsJson) {
491
+ if (!method)
492
+ throw new Error("Browser CDP method required");
493
+ let params = {};
494
+ if (paramsJson) {
495
+ try {
496
+ params = JSON.parse(paramsJson);
497
+ } catch {
498
+ throw new Error(`Invalid JSON params: ${paramsJson}`);
499
+ }
500
+ }
501
+ const result = await cdp.send(method, params);
502
+ return JSON.stringify(result, null, 2);
503
+ }
504
+ async function runDaemon(targetId) {
505
+ const sp = sockPath(targetId);
506
+ const cdp = new CDP;
507
+ try {
508
+ await cdp.connect(getWsUrl());
509
+ } catch (e) {
510
+ process.stderr.write(`Daemon: cannot connect to Chrome: ${e.message}
511
+ `);
512
+ process.exit(1);
513
+ }
514
+ let sessionId;
515
+ try {
516
+ const res = await cdp.send("Target.attachToTarget", {
517
+ targetId,
518
+ flatten: true
519
+ });
520
+ sessionId = res.sessionId;
521
+ } catch (e) {
522
+ process.stderr.write(`Daemon: attach failed: ${e.message}
523
+ `);
524
+ cdp.close();
525
+ process.exit(1);
526
+ }
527
+ let alive = true;
528
+ function shutdown() {
529
+ if (!alive)
530
+ return;
531
+ alive = false;
532
+ server.close();
533
+ try {
534
+ unlinkSync(sp);
535
+ } catch {}
536
+ cdp.close();
537
+ process.exit(0);
538
+ }
539
+ cdp.onEvent("Target.targetDestroyed", (params) => {
540
+ if (params.targetId === targetId)
541
+ shutdown();
542
+ });
543
+ cdp.onEvent("Target.detachedFromTarget", (params) => {
544
+ if (params.sessionId === sessionId)
545
+ shutdown();
546
+ });
547
+ cdp.onClose(() => shutdown());
548
+ process.on("SIGTERM", shutdown);
549
+ process.on("SIGINT", shutdown);
550
+ let idleTimer = setTimeout(shutdown, IDLE_TIMEOUT);
551
+ function resetIdle() {
552
+ clearTimeout(idleTimer);
553
+ idleTimer = setTimeout(shutdown, IDLE_TIMEOUT);
554
+ }
555
+ async function handleCommand({ cmd, args }) {
556
+ resetIdle();
557
+ try {
558
+ let result;
559
+ switch (cmd) {
560
+ case "list": {
561
+ const pages = await getPages(cdp);
562
+ result = formatPageList(pages);
563
+ break;
564
+ }
565
+ case "list_raw": {
566
+ const pages = await getPages(cdp);
567
+ result = JSON.stringify(pages);
568
+ break;
569
+ }
570
+ case "snap":
571
+ case "snapshot":
572
+ result = await snapshotStr(cdp, sessionId, true);
573
+ break;
574
+ case "eval":
575
+ result = await evalStr(cdp, sessionId, args[0]);
576
+ break;
577
+ case "shot":
578
+ case "screenshot":
579
+ result = await shotStr(cdp, sessionId, args[0]);
580
+ break;
581
+ case "html":
582
+ result = await htmlStr(cdp, sessionId, args[0]);
583
+ break;
584
+ case "nav":
585
+ case "navigate":
586
+ result = await navStr(cdp, sessionId, args[0]);
587
+ break;
588
+ case "net":
589
+ case "network":
590
+ result = await netStr(cdp, sessionId);
591
+ break;
592
+ case "click":
593
+ result = await clickStr(cdp, sessionId, args[0]);
594
+ break;
595
+ case "clickxy":
596
+ result = await clickXyStr(cdp, sessionId, args[0], args[1]);
597
+ break;
598
+ case "type":
599
+ result = await typeStr(cdp, sessionId, args[0]);
600
+ break;
601
+ case "loadall":
602
+ result = await loadAllStr(cdp, sessionId, args[0], args[1] ? Number.parseInt(args[1], 10) : 1500);
603
+ break;
604
+ case "evalraw":
605
+ result = await evalRawStr(cdp, sessionId, args[0], args[1]);
606
+ break;
607
+ case "browse":
608
+ case "browserraw":
609
+ result = await browserRawStr(cdp, args[0], args[1]);
610
+ break;
611
+ case "stop":
612
+ return { ok: true, result: "", stopAfter: true };
613
+ default:
614
+ return { ok: false, error: `Unknown command: ${cmd}` };
615
+ }
616
+ return { ok: true, result: result ?? "" };
617
+ } catch (e) {
618
+ return { ok: false, error: e.message };
619
+ }
620
+ }
621
+ const server = net.createServer((conn) => {
622
+ let buf = "";
623
+ conn.on("data", (chunk) => {
624
+ buf += chunk.toString();
625
+ const lines = buf.split(`
626
+ `);
627
+ buf = lines.pop();
628
+ for (const line of lines) {
629
+ if (!line.trim())
630
+ continue;
631
+ let req;
632
+ try {
633
+ req = JSON.parse(line);
634
+ } catch {
635
+ conn.write(`${JSON.stringify({ ok: false, error: "Invalid JSON request", id: null })}
636
+ `);
637
+ continue;
638
+ }
639
+ handleCommand(req).then((res) => {
640
+ const payload = `${JSON.stringify({ ...res, id: req.id })}
641
+ `;
642
+ if (res.stopAfter)
643
+ conn.end(payload, shutdown);
644
+ else
645
+ conn.write(payload);
646
+ });
647
+ }
648
+ });
649
+ });
650
+ try {
651
+ unlinkSync(sp);
652
+ } catch {}
653
+ server.listen(sp);
654
+ }
655
+ function connectToSocket(sp) {
656
+ return new Promise((resolve, reject) => {
657
+ const conn = net.connect(sp);
658
+ conn.on("connect", () => resolve(conn));
659
+ conn.on("error", reject);
660
+ });
661
+ }
662
+ async function getOrStartTabDaemon(targetId) {
663
+ const sp = sockPath(targetId);
664
+ try {
665
+ return await connectToSocket(sp);
666
+ } catch {}
667
+ try {
668
+ unlinkSync(sp);
669
+ } catch {}
670
+ const child = spawn(nodeRuntimeCommand(), [process.argv[1], "_daemon", targetId], {
671
+ detached: true,
672
+ stdio: "ignore"
673
+ });
674
+ child.unref();
675
+ const backoffSchedule = [50, 100, 200];
676
+ const deadline = Date.now() + DAEMON_CONNECT_RETRIES * DAEMON_CONNECT_DELAY;
677
+ let attempt = 0;
678
+ while (Date.now() < deadline) {
679
+ try {
680
+ return await connectToSocket(sp);
681
+ } catch {}
682
+ const delay = backoffSchedule[attempt] ?? DAEMON_CONNECT_DELAY;
683
+ attempt++;
684
+ await sleep(delay);
685
+ }
686
+ throw new Error("Daemon failed to start — did you click Allow in Chrome?");
687
+ }
688
+ function sendCommand(conn, req) {
689
+ return new Promise((resolve, reject) => {
690
+ let buf = "";
691
+ let settled = false;
692
+ const cleanup = () => {
693
+ conn.off("data", onData);
694
+ conn.off("error", onError);
695
+ conn.off("end", onEnd);
696
+ conn.off("close", onClose);
697
+ };
698
+ const onData = (chunk) => {
699
+ buf += chunk.toString();
700
+ const idx = buf.indexOf(`
701
+ `);
702
+ if (idx === -1)
703
+ return;
704
+ settled = true;
705
+ cleanup();
706
+ try {
707
+ resolve(JSON.parse(buf.slice(0, idx)));
708
+ } catch (error) {
709
+ reject(new Error(`Invalid JSON response from daemon: ${error.message}`));
710
+ }
711
+ conn.end();
712
+ };
713
+ const onError = (error) => {
714
+ if (settled)
715
+ return;
716
+ settled = true;
717
+ cleanup();
718
+ reject(error);
719
+ };
720
+ const onEnd = () => {
721
+ if (settled)
722
+ return;
723
+ settled = true;
724
+ cleanup();
725
+ reject(new Error("Connection closed before response"));
726
+ };
727
+ const onClose = () => {
728
+ if (settled)
729
+ return;
730
+ settled = true;
731
+ cleanup();
732
+ reject(new Error("Connection closed before response"));
733
+ };
734
+ conn.on("data", onData);
735
+ conn.on("error", onError);
736
+ conn.on("end", onEnd);
737
+ conn.on("close", onClose);
738
+ req.id = 1;
739
+ conn.write(`${JSON.stringify(req)}
740
+ `);
741
+ });
742
+ }
743
+ function findAnyDaemonSocket() {
744
+ return listDaemonSockets()[0]?.socketPath || null;
745
+ }
746
+ async function stopDaemons(targetPrefix) {
747
+ const daemons = listDaemonSockets();
748
+ if (targetPrefix) {
749
+ const targetId = resolvePrefix(targetPrefix, daemons.map((d) => d.targetId), "daemon");
750
+ const daemon = daemons.find((d) => d.targetId === targetId);
751
+ try {
752
+ const conn = await connectToSocket(daemon.socketPath);
753
+ await sendCommand(conn, { cmd: "stop" });
754
+ } catch {
755
+ try {
756
+ unlinkSync(daemon.socketPath);
757
+ } catch {}
758
+ }
759
+ return;
760
+ }
761
+ for (const daemon of daemons) {
762
+ try {
763
+ const conn = await connectToSocket(daemon.socketPath);
764
+ await sendCommand(conn, { cmd: "stop" });
765
+ } catch {
766
+ try {
767
+ unlinkSync(daemon.socketPath);
768
+ } catch {}
769
+ }
770
+ }
771
+ }
772
+ const USAGE = `cdp - lightweight Chrome DevTools Protocol CLI (no Puppeteer)
42
773
 
43
774
  Usage: cdp <command> [args]
44
775
 
@@ -56,5 +787,140 @@ Usage: cdp <command> [args]
56
787
  evalraw <target> <method> [json] Send a raw CDP command; returns JSON result
57
788
  browse <target> <method> [json] Send a browser-level CDP command (no session)
58
789
  stop [target] Stop daemon(s)
59
- `,pQ=new Set(["snap","snapshot","eval","shot","screenshot","html","nav","navigate","net","network","click","clickxy","type","loadall","evalraw","browse","browserraw"]);async function lQ(){let[Q,...z]=process.argv.slice(2);if(Q==="_daemon"){await EQ(z[0]);return}if(!Q||Q==="help"||Q==="--help"||Q==="-h")console.log(t),process.exit(0);if(Q==="list"||Q==="ls"){let b,K=gQ();if(K)try{let B=await G(K),J=await F(B,{cmd:"list_raw"});if(J.ok)b=JSON.parse(J.result)}catch{}if(!b){let B=new h;await B.connect(VQ()),b=await x(B),B.close()}s(N,JSON.stringify(b)),console.log(zQ(b)),process.exit(0)}if(Q==="stop"){await mQ(z[0]);return}if(!pQ.has(Q))console.error(`Unknown command: ${Q}
60
- `),console.log(t),process.exit(1);let $=z[0];if(!$)console.error('Error: target ID required. Run "cdp list" first.'),process.exit(1);let V,X=S().map((b)=>b.targetId);if(X.filter((b)=>b.toUpperCase().startsWith($.toUpperCase())).length>0)V=_($,X,"daemon");else{if(!d(N))console.error('No page list cached. Run "cdp list" first.'),process.exit(1);let b=JSON.parse(v(N,"utf8"));V=_($,b.map((K)=>K.targetId),"target",'Run "cdp list".')}let Z=await IQ(V),q=z.slice(1);if(Q==="eval"){let b=q.join(" ");if(!b)console.error("Error: expression required"),process.exit(1);q[0]=b}else if(Q==="type"){let b=q.join(" ");if(q[0]==="--stdin")b=await new Promise((K)=>{let B="";if(process.stdin.setEncoding("utf8"),process.stdin.on("data",(J)=>B+=J),process.stdin.on("end",()=>K(B)),process.stdin.isTTY)K("")});if(!b)console.error("Error: text required"),process.exit(1);q[0]=b}else if(Q==="evalraw"){if(!q[0])console.error("Error: CDP method required"),process.exit(1);if(q.length>2)q[1]=q.slice(1).join(" ")}else if(Q==="browse"||Q==="browserraw"){if(!q[0])console.error("Error: browser CDP method required"),process.exit(1);if(q.length>2)q[1]=q.slice(1).join(" ")}if((Q==="nav"||Q==="navigate")&&!q[0])console.error("Error: URL required"),process.exit(1);let H=await F(Z,{cmd:Q,args:q});if(H.ok){if(H.result)console.log(H.result)}else console.error("Error:",H.error),process.exitCode=1}lQ().catch((Q)=>{console.error(Q.message),process.exit(1)});
790
+ `;
791
+ const NEEDS_TARGET = new Set([
792
+ "snap",
793
+ "snapshot",
794
+ "eval",
795
+ "shot",
796
+ "screenshot",
797
+ "html",
798
+ "nav",
799
+ "navigate",
800
+ "net",
801
+ "network",
802
+ "click",
803
+ "clickxy",
804
+ "type",
805
+ "loadall",
806
+ "evalraw",
807
+ "browse",
808
+ "browserraw"
809
+ ]);
810
+ async function main() {
811
+ const [cmd, ...args] = process.argv.slice(2);
812
+ if (cmd === "_daemon") {
813
+ await runDaemon(args[0]);
814
+ return;
815
+ }
816
+ if (!cmd || cmd === "help" || cmd === "--help" || cmd === "-h") {
817
+ console.log(USAGE);
818
+ process.exit(0);
819
+ }
820
+ if (cmd === "list" || cmd === "ls") {
821
+ let pages;
822
+ const existingSock = findAnyDaemonSocket();
823
+ if (existingSock) {
824
+ try {
825
+ const conn = await connectToSocket(existingSock);
826
+ const resp = await sendCommand(conn, { cmd: "list_raw" });
827
+ if (resp.ok)
828
+ pages = JSON.parse(resp.result);
829
+ } catch {}
830
+ }
831
+ if (!pages) {
832
+ const cdp = new CDP;
833
+ await cdp.connect(getWsUrl());
834
+ pages = await getPages(cdp);
835
+ cdp.close();
836
+ }
837
+ writeFileSync(PAGES_CACHE, JSON.stringify(pages));
838
+ console.log(formatPageList(pages));
839
+ process.exit(0);
840
+ }
841
+ if (cmd === "stop") {
842
+ await stopDaemons(args[0]);
843
+ return;
844
+ }
845
+ if (!NEEDS_TARGET.has(cmd)) {
846
+ console.error(`Unknown command: ${cmd}
847
+ `);
848
+ console.log(USAGE);
849
+ process.exit(1);
850
+ }
851
+ const targetPrefix = args[0];
852
+ if (!targetPrefix) {
853
+ console.error('Error: target ID required. Run "cdp list" first.');
854
+ process.exit(1);
855
+ }
856
+ let targetId;
857
+ const daemonTargetIds = listDaemonSockets().map((d) => d.targetId);
858
+ const daemonMatches = daemonTargetIds.filter((id) => id.toUpperCase().startsWith(targetPrefix.toUpperCase()));
859
+ if (daemonMatches.length > 0) {
860
+ targetId = resolvePrefix(targetPrefix, daemonTargetIds, "daemon");
861
+ } else {
862
+ if (!existsSync(PAGES_CACHE)) {
863
+ console.error('No page list cached. Run "cdp list" first.');
864
+ process.exit(1);
865
+ }
866
+ const pages = JSON.parse(readFileSync(PAGES_CACHE, "utf8"));
867
+ targetId = resolvePrefix(targetPrefix, pages.map((p) => p.targetId), "target", 'Run "cdp list".');
868
+ }
869
+ const conn = await getOrStartTabDaemon(targetId);
870
+ const cmdArgs = args.slice(1);
871
+ if (cmd === "eval") {
872
+ const expr = cmdArgs.join(" ");
873
+ if (!expr) {
874
+ console.error("Error: expression required");
875
+ process.exit(1);
876
+ }
877
+ cmdArgs[0] = expr;
878
+ } else if (cmd === "type") {
879
+ let text = cmdArgs.join(" ");
880
+ if (cmdArgs[0] === "--stdin") {
881
+ text = await new Promise((resolve) => {
882
+ let data = "";
883
+ process.stdin.setEncoding("utf8");
884
+ process.stdin.on("data", (chunk) => data += chunk);
885
+ process.stdin.on("end", () => resolve(data));
886
+ if (process.stdin.isTTY)
887
+ resolve("");
888
+ });
889
+ }
890
+ if (!text) {
891
+ console.error("Error: text required");
892
+ process.exit(1);
893
+ }
894
+ cmdArgs[0] = text;
895
+ } else if (cmd === "evalraw") {
896
+ if (!cmdArgs[0]) {
897
+ console.error("Error: CDP method required");
898
+ process.exit(1);
899
+ }
900
+ if (cmdArgs.length > 2)
901
+ cmdArgs[1] = cmdArgs.slice(1).join(" ");
902
+ } else if (cmd === "browse" || cmd === "browserraw") {
903
+ if (!cmdArgs[0]) {
904
+ console.error("Error: browser CDP method required");
905
+ process.exit(1);
906
+ }
907
+ if (cmdArgs.length > 2)
908
+ cmdArgs[1] = cmdArgs.slice(1).join(" ");
909
+ }
910
+ if ((cmd === "nav" || cmd === "navigate") && !cmdArgs[0]) {
911
+ console.error("Error: URL required");
912
+ process.exit(1);
913
+ }
914
+ const response = await sendCommand(conn, { cmd, args: cmdArgs });
915
+ if (response.ok) {
916
+ if (response.result)
917
+ console.log(response.result);
918
+ } else {
919
+ console.error("Error:", response.error);
920
+ process.exitCode = 1;
921
+ }
922
+ }
923
+ main().catch((e) => {
924
+ console.error(e.message);
925
+ process.exit(1);
926
+ });