@duckmind/dm-windows-x64 0.60.4 → 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,6 +1,490 @@
1
- import{createHash as dm}from"node:crypto";import{spawn as nm}from"node:child_process";import{existsSync as V,mkdirSync as g,readFileSync as Am}from"node:fs";import Tm from"node:http";import{homedir as Lm,platform as $m,tmpdir as c}from"node:os";import{dirname as N,join as f,resolve as Y}from"node:path";import{fileURLToPath as wm}from"node:url";import{spawn as P}from"node:child_process";import{existsSync as p}from"node:fs";import{homedir as W,platform as H}from"node:os";import J from"node:path";var x=900000,E=16384,K=5,t=1000;function Q(...m){return J.win32.join(...m)}function _(m,o){if(o&&!m.includes(o))m.push(o)}function a(m,o,i){let L=m.PATH??m.Path??m.path??"",T=o==="win32"?";":J.delimiter,n=o==="win32"?(m.PATHEXT??".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean):[""],A=[];for(let w of L.split(T).filter(Boolean))for(let M of i){if(o==="win32"&&J.win32.extname(M)){_(A,Q(w,M));continue}for(let d of n){let h=o==="win32"?`${M}${d.toLowerCase()}`:M;_(A,o==="win32"?Q(w,h):J.join(w,h))}}return A}function v({env:m=process.env,platformName:o=H(),homeDir:i=W(),exists:L=p}={}){let T=[];if(o==="win32"){for(let n of[m.LOCALAPPDATA,m.ProgramFiles,m["ProgramFiles(x86)"]]){if(!n)continue;_(T,Q(n,"Google","Chrome","Application","chrome.exe")),_(T,Q(n,"Chromium","Application","chrome.exe"))}_(T,i?Q(i,"AppData","Local","Google","Chrome","Application","chrome.exe"):void 0),T.push(...a(m,o,["chrome","chromium"]))}else if(o==="darwin"){for(let n of["/Applications",J.join(i,"Applications")])_(T,J.join(n,"Google Chrome.app","Contents","MacOS","Google Chrome")),_(T,J.join(n,"Chromium.app","Contents","MacOS","Chromium"));T.push(...a(m,o,["google-chrome-stable","google-chrome","chromium","chromium-browser"]))}else if(o==="linux"){for(let n of["/usr/bin/google-chrome-stable","/usr/bin/google-chrome","/usr/bin/chromium","/usr/bin/chromium-browser","/snap/bin/chromium","/usr/local/bin/google-chrome","/usr/local/bin/chromium"])_(T,n);T.push(...a(m,o,["google-chrome-stable","google-chrome","chromium","chromium-browser"]))}return T.find((n)=>L(n))}function C(m,...o){return{command:m,args:o}}function z(m,o){if(o===0)return m;return m.map(({command:i,args:L})=>C("sudo","-n",i,...L))}function r({platformName:m=H(),uid:o=typeof process.getuid==="function"?process.getuid():void 0}={}){if(m==="darwin")return[{id:"homebrew-google-chrome",commands:[C("brew","install","--cask","google-chrome")]}];if(m==="win32"){let i=["install","--id","Google.Chrome","-e","--silent","--accept-package-agreements","--accept-source-agreements","--disable-interactivity"];return[{id:"winget-user-google-chrome",commands:[C("winget",...i.slice(0,4),"--scope","user",...i.slice(4))]},{id:"winget-machine-google-chrome",commands:[C("winget",...i)]},{id:"chocolatey-google-chrome",commands:[C("choco","install","googlechrome","-y","--no-progress")]}]}if(m==="linux")return[{id:"apt-chromium",commands:z([C("apt-get","update"),C("apt-get","install","-y","chromium")],o)},{id:"apt-chromium-browser",commands:z([C("apt-get","update"),C("apt-get","install","-y","chromium-browser")],o)},{id:"dnf-chromium",commands:z([C("dnf","install","-y","chromium")],o)},{id:"yum-chromium",commands:z([C("yum","install","-y","chromium")],o)},{id:"pacman-chromium",commands:z([C("pacman","-S","--noconfirm","--needed","chromium")],o)},{id:"zypper-chromium",commands:z([C("zypper","--non-interactive","install","chromium")],o)},{id:"apk-chromium",commands:z([C("apk","add","chromium")],o)}];return[]}function e(m=process.env){let o=m.DM_CUA_AUTO_INSTALL;if(o===void 0)return!0;return!["0","false","no","off"].includes(o.trim().toLowerCase())}function mm(m){let o=Number(m.DM_CUA_INSTALL_TIMEOUT_MS);return Number.isFinite(o)&&o>0?o:x}function F(m,o){let i=`${m}${o}`;if(Buffer.byteLength(i)<=E)return i;return i.slice(-E)}function om(m,o,{env:i=process.env,timeoutMs:L=mm(i)}={}){return new Promise((T)=>{let n=!1,A="",w,M=P(m,o,{env:i,shell:!1,stdio:["ignore","pipe","pipe"],windowsHide:!0}),d=(h)=>{if(n)return;n=!0,clearTimeout(w),T(h)};M.stdout?.on("data",(h)=>{A=F(A,h.toString())}),M.stderr?.on("data",(h)=>{A=F(A,h.toString())}),M.on("error",(h)=>{d({status:h.code==="ENOENT"?"unavailable":"failed",exitCode:null,message:`${h.message}${A?`
2
- ${A.trim()}`:""}`})}),M.on("close",(h,$)=>{d({status:h===0?"ok":"failed",exitCode:h,message:A.trim()||($?`installer terminated by signal ${$}`:"")})}),w=setTimeout(()=>{M.kill(),d({status:"timeout",exitCode:null,message:`installer timed out after ${L}ms`})},L)})}function im(m){if(m.startsWith("homebrew-"))return"Homebrew";if(m.startsWith("winget-"))return"winget";return m.split("-")[0]}function hm(m){let o=m.message?`: ${m.message}`:"";return`${m.id} ${m.status}${o}`}async function b({env:m=process.env,platformName:o=H(),uid:i=typeof process.getuid==="function"?process.getuid():void 0,findExecutable:L=()=>v({env:m,platformName:o,homeDir:W()}),runCommand:T=(A,w)=>om(A,w,{env:m}),sleep:n=(A)=>new Promise((w)=>{setTimeout(w,A)})}={}){let A=L();if(A)return{status:"existing",path:A,method:null,attempts:[]};if(!e(m))throw Error("Chrome was not found and automatic installation is disabled by DM_CUA_AUTO_INSTALL.");let w=r({platformName:o,uid:i});if(w.length===0)throw Error(`Chrome was not found and no automatic Chrome installer is defined for ${o}.`);let M=[];for(let h of w){let $={status:"ok",exitCode:0,message:""};for(let D of h.commands)if($=await T(D.command,D.args),$.status!=="ok")break;if($.status!=="ok"){M.push({id:h.id,status:$.status,exitCode:$.exitCode,message:$.message});continue}let U;for(let D=0;D<K;D+=1){if(U=L(),U)break;if(D<K-1)await n(t)}if(U)return M.push({id:h.id,status:"installed",exitCode:$.exitCode,message:$.message}),{status:"installed",path:U,method:h.id,attempts:M};M.push({id:h.id,status:"missing-after-success",exitCode:$.exitCode,message:`${im(h.id)} reported success, but Chrome was not found`})}let d=M.map(hm).join("; ");throw Error(`Chrome automatic installation failed. ${d}`)}var q=N(wm(import.meta.url)),Bm=Y(q,".."),Mm=9322,Cm=400,j=60000,um=/^(about:|https?:\/\/|file:|chrome-error:\/\/)/i,fm={chrome:"Chrome",cloak:"CloakBrowser"};function O(m){let o=Number.parseInt(String(m??""),10);return Number.isInteger(o)&&o>1024&&o<65535?o:null}function Um(m){return dm("sha256").update(String(m||"default")).digest("hex")}function km(m){let o=String(m||"chrome").trim().toLowerCase();if(o==="cloakbrowser"||o==="cloak-browser")return"cloak";if(o==="chromium"||o==="google-chrome"||o==="chrome")return"chrome";return o.replace(/[^a-z0-9_.-]/g,"-")||"chrome"}function Xm(m,o){let i=m.DM_CUA_BROWSER_PATH||(o==="cloak"?m.DM_CUA_CLOAK_PATH||m.CLOAKBROWSER_BINARY_PATH:void 0)||m.CHROME_PATH;return i?Y(String(i)):void 0}function _m(m=process.env,o=process.cwd()){let i=m.DM_CUA_SCOPE||m.DM_CODING_AGENT_DIR||m.PI_CODING_AGENT_DIR||o,L=Um(Y(String(i||"."))),T=String(m.DM_CUA_INSTANCE_ID||L.slice(0,12)).replace(/[^a-zA-Z0-9_.-]/g,"-"),n=km(m.DM_CUA_BROWSER||m.DM_BROWSER_CUA_BROWSER||m.DM_CUA_BROWSER_KIND),A=m.DM_CUA_BROWSER_DISPLAY_NAME||fm[n]||n,w=Xm(m,n),M=O(m.DM_CUA_PORT)??O(m.GREEDY_SEARCH_PORT)??Mm+Number.parseInt(L.slice(0,8),16)%Cm,d=n==="chrome"?"chrome":n,h=Y(m.DM_CUA_PROFILE_DIR||f(c(),`dm-cua-${d}-profile-${T}`)),$=$m()==="win32"?c():"/tmp";return{instanceId:T,browserKind:n,browserDisplayName:A,browserExecutablePath:w,browserExecutableIsExplicit:Boolean(w),browserInstallation:void 0,port:M,profileDir:h,pidFile:f(h,"chrome.pid"),modeFile:f(h,"chrome-mode"),pagesCache:f(h,"cdp-pages.json"),socketDir:f($,`dm-cua-cdp-${T}`)}}var u=_m(),Ym=u.port,Sm=u.profileDir;function k(){return{kind:u.browserKind,displayName:u.browserDisplayName,executablePath:u.browserExecutablePath,installation:u.browserInstallation??null,profileDir:u.profileDir,port:u.port}}async function zm(m=u,{env:o=process.env,exists:i=V,ensureChrome:L=b}={}){if(m.browserKind==="cloak"&&!m.browserExecutablePath)throw Error(["CloakBrowser selected for browser CUA, but no executable path was provided.","Install/pre-download CloakBrowser yourself, then set DM_CUA_BROWSER_PATH, DM_CUA_CLOAK_PATH, CLOAKBROWSER_BINARY_PATH, or CHROME_PATH.","DM does not bundle the compiled CloakBrowser binary because its upstream license allows use but not redistribution."].join(" "));if(m.browserExecutablePath&&!i(m.browserExecutablePath))throw Error(`${m.browserDisplayName} executable not found: ${m.browserExecutablePath}`);if(m.browserExecutablePath)return m;if(m.browserKind!=="chrome")throw Error(`${m.browserDisplayName} browser CUA requires an explicit executable path via DM_CUA_BROWSER_PATH.`);let T=await L({env:o});return m.browserExecutablePath=T.path,m.browserInstallation={status:T.status,method:T.method,attempts:T.attempts.map(({id:n,status:A,exitCode:w})=>({id:n,status:A,exitCode:w}))},m}var Z;async function Jm(){if(!Z)Z=zm(u).catch((m)=>{throw Z=void 0,m});return Z}function B(){let o=[Y(q,"..","greedysearch-dm"),Y(q,"..","..","greedysearch-dm")].map((i)=>({launchScript:f(i,"bin","launch.mjs"),cdpScript:f(i,"bin","cdp.mjs")}));for(let i of o)if(V(i.launchScript)&&V(i.cdpScript))return i;return o[0]}function ym(m=u,o=process.env){let i=o.GREEDY_SEARCH_VISIBLE??o.DM_CUA_VISIBLE??o.DM_BROWSER_CUA_VISIBLE;return{...o,...m.browserExecutablePath?{CHROME_PATH:m.browserExecutablePath}:{},CDP_PROFILE_DIR:m.profileDir.replace(/\\/g,"/"),CDP_PAGES_CACHE:m.pagesCache.replace(/\\/g,"/"),CDP_SOCKET_DIR:m.socketDir.replace(/\\/g,"/"),GREEDY_SEARCH_BROWSER_LABEL:m.browserDisplayName,GREEDY_SEARCH_PORT:String(m.port),GREEDY_SEARCH_PROFILE_DIR:m.profileDir.replace(/\\/g,"/"),GREEDY_SEARCH_PID_FILE:m.pidFile.replace(/\\/g,"/"),GREEDY_SEARCH_MODE_FILE:m.modeFile.replace(/\\/g,"/"),GREEDY_SEARCH_ALLOW_PORT_CLEANUP:o.GREEDY_SEARCH_ALLOW_PORT_CLEANUP??"0",...i===void 0?{}:{GREEDY_SEARCH_VISIBLE:i}}}function s(){return ym(u,process.env)}function Dm(m){return um.test(m)}function Qm(m){return m.split(/\r?\n/).map((o)=>o.trimEnd()).filter(Boolean).map((o)=>{let i=o.trim().split(/\s{2,}/).filter(Boolean);if(i.length===0)return null;let L=i[0];if(i.length===1)return{target:L,title:"",url:""};if(i.length===2&&Dm(i[1]))return{target:L,title:"",url:i[1]};let T=i.at(-1)??"",n=i.slice(1,-1).join(" ");return{target:L,title:n,url:T}}).filter(Boolean)}function Vm(m){let o=String(m??"").trim();if(!o)throw Error("selector is required for action=click");if(/:contains\s*\(/i.test(o))throw Error(":contains(...) is not a standard CSS selector. Use browser_cua(snapshot) to inspect a standard selector, or browser_cua(evaluate) for text matching.");return o}function Zm(){let m=process.env.DM_CODING_AGENT_DIR||process.env.PI_CODING_AGENT_DIR;return m?Y(m):f(Lm(),".dm","agent")}function am(){let m=f(Zm(),"cua","screenshots");g(m,{recursive:!0});let o=new Date().toISOString().replace(/[:.]/g,"-");return f(m,`browser-${o}.png`)}function G(m,o,{env:i=process.env,timeoutMs:L=j}={}){return new Promise((T,n)=>{let A=nm("node",[m,...o],{env:i,stdio:["ignore","pipe","pipe"]}),w="",M="",d=!1,h=setTimeout(()=>{if(d)return;d=!0,A.kill("SIGTERM"),n(Error(`Timed out running ${m}`))},L);A.stdout.on("data",($)=>{w+=$.toString()}),A.stderr.on("data",($)=>{M+=$.toString()}),A.on("error",($)=>{if(d)return;d=!0,clearTimeout(h),n($)}),A.on("close",($)=>{if(d)return;if(d=!0,clearTimeout(h),$!==0){n(Error((M||w||`exit ${$}`).trim()));return}T({stdout:w,stderr:M})})})}function Hm(m,o){return new Promise((i,L)=>{let T=Tm.request({host:"127.0.0.1",port:Ym,path:o,method:m},(n)=>{let A="";n.on("data",(w)=>{A+=w.toString()}),n.on("end",()=>{if((n.statusCode??500)>=400){L(Error(`Browser CDP HTTP ${n.statusCode}: ${A.trim()}`));return}try{i(A?JSON.parse(A):{})}catch(w){L(w)}})});T.on("error",L),T.end()})}async function qm(m="about:blank"){return Hm("PUT",`/json/new?${encodeURIComponent(m)}`)}async function X(m,o={}){let{cdpScript:i}=B();return G(i,m,{env:s(),timeoutMs:o.timeoutMs??j})}async function I(){let m=await X(["list"]);return Qm(m.stdout)}function S(m,o){if(!o)return m[0]??null;let i=o.toUpperCase();return m.find((L)=>L.target.toUpperCase().startsWith(i))??null}async function l(){let{launchScript:m,cdpScript:o}=B();if(!V(m)||!V(o))throw Error("Bundled browser helpers are missing. Rebuild DM so dm-cua and greedysearch-dm are staged together.");return{launchScript:m,cdpScript:o}}async function R(){let{launchScript:m}=await l();await Jm(),await G(m,[],{env:s(),timeoutMs:j});let o=await I();if(o.length===0)await qm("about:blank"),o=await I();return o}async function y(m){let o=await R(),i=S(o,m);if(!i){if(m)throw Error(`No browser tab matching prefix "${m}". Run browser_cua(list) first.`);throw Error("No browser tab available after browser bootstrap.")}return{tab:i.target,page:i,pages:o}}async function lm({action:m,tab:o,url:i,selector:L,x:T,y:n,text:A,expression:w,outputPath:M}={}){switch(m){case"list":{let d=await R();return{status:"ok",action:m,browser:k(),pages:d}}case"navigate":{if(!i)throw Error("url is required for action=navigate");let d=await y(o),h=await X(["nav",d.tab,i],{timeoutMs:90000}),$=await I(),U=S($,d.tab)??d.page;return{status:"ok",action:m,browser:k(),tab:d.tab,url:U?.url||i,title:U?.title||"",message:h.stdout.trim(),pages:$}}case"snapshot":{let d=await y(o),h=await X(["snap",d.tab]);return{status:"ok",action:m,browser:k(),tab:d.tab,title:d.page?.title||"",url:d.page?.url||"",snapshot:h.stdout.trim()}}case"screenshot":{let d=await y(o),h=M?Y(M):am();g(N(h),{recursive:!0});let $=await X(["shot",d.tab,h],{timeoutMs:120000}),U=Am(h).toString("base64");return{status:"ok",action:m,browser:k(),tab:d.tab,title:d.page?.title||"",url:d.page?.url||"",imagePath:h,imageBase64:U,mimeType:"image/png",message:$.stdout.trim()}}case"click":{let d=Vm(L),h=await y(o),$=await X(["click",h.tab,d]);return{status:"ok",action:m,browser:k(),tab:h.tab,selector:d,message:$.stdout.trim()}}case"click_xy":{if(typeof T!=="number"||Number.isNaN(T)||typeof n!=="number"||Number.isNaN(n))throw Error("x and y are required numbers for action=click_xy");let d=await y(o),h=await X(["clickxy",d.tab,String(T),String(n)]);return{status:"ok",action:m,browser:k(),tab:d.tab,x:T,y:n,message:h.stdout.trim()}}case"type":{if(A==null||A==="")throw Error("text is required for action=type");let d=await y(o),h=await X(["type",d.tab,A]);return{status:"ok",action:m,browser:k(),tab:d.tab,textLength:A.length,message:h.stdout.trim()}}case"evaluate":{if(!w)throw Error("expression is required for action=evaluate");let d=await y(o),h=await X(["eval",d.tab,w]);return{status:"ok",action:m,browser:k(),tab:d.tab,value:h.stdout.trim()}}case"stop":{let{launchScript:d}=await l();try{await X(["stop"])}catch{}let h=await G(d,["--kill"],{env:s(),timeoutMs:30000});return{status:"ok",action:m,browser:k(),message:(h.stdout||"Stopped browser CUA lane.").trim()}}default:throw Error(`Unsupported browser action: ${m}`)}}function Rm(m){switch(m.action){case"list":{if(!Array.isArray(m.pages)||m.pages.length===0)return"Browser CUA is ready, but no pages are open.";return["Open browser tabs:",...m.pages.map((o)=>o.url?`${o.target} ${o.title||"(untitled)"} ${o.url}`:`${o.target} ${o.title||"(untitled)"}`)].join(`
3
- `)}case"navigate":return[`Navigated browser tab ${m.tab} to ${m.url||"(unknown URL)"}`,m.title?`Title: ${m.title}`:""].filter(Boolean).join(`
4
- `);case"snapshot":return[`Browser snapshot for ${m.tab}`,m.snapshot||"(empty snapshot)"].join(`
5
- `);case"screenshot":return[`Saved browser screenshot for ${m.tab} at ${m.imagePath}`,m.title?`Title: ${m.title}`:"",m.url?`URL: ${m.url}`:""].filter(Boolean).join(`
6
- `);case"click":return`Clicked selector ${JSON.stringify(m.selector)} on browser tab ${m.tab}`;case"click_xy":return`Clicked browser tab ${m.tab} at CSS coordinates (${m.x}, ${m.y})`;case"type":return`Typed ${m.textLength} characters into browser tab ${m.tab}`;case"evaluate":return`Browser evaluate result for ${m.tab}: ${m.value}`;case"stop":return m.message||"Stopped browser CUA lane.";default:return JSON.stringify(m,null,2)}}export{Vm as validateBrowserSelector,lm as runBrowserAction,_m as resolveBrowserRuntimeConfig,ym as resolveBrowserProcessEnv,B as resolveBrowserHelperPaths,Rm as renderBrowserActionText,Qm as parseListOutput,zm as ensureBrowserSelectionUsable,R as ensureBrowserReady,Sm as BROWSER_PROFILE_DIR};
1
+ import { createHash } from "node:crypto";
2
+ import { spawn } from "node:child_process";
3
+ import { existsSync, mkdirSync, readFileSync } from "node:fs";
4
+ import http from "node:http";
5
+ import { homedir, platform, tmpdir } from "node:os";
6
+ import { dirname, join, resolve } from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+ import { ensureChromeAvailable } from "./browser-install.mjs";
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+ const ROOT_DIR = resolve(__dirname, "..");
11
+ const DEFAULT_CUA_PORT_BASE = 9322;
12
+ const DEFAULT_CUA_PORT_RANGE = 400;
13
+ const DEFAULT_TIMEOUT_MS = 60000;
14
+ const URLISH_PATTERN = /^(about:|https?:\/\/|file:|chrome-error:\/\/)/i;
15
+ const BROWSER_DISPLAY_NAMES = {
16
+ chrome: "Chrome",
17
+ cloak: "CloakBrowser"
18
+ };
19
+ function parsePort(value) {
20
+ const port = Number.parseInt(String(value ?? ""), 10);
21
+ return Number.isInteger(port) && port > 1024 && port < 65535 ? port : null;
22
+ }
23
+ function hashScope(value) {
24
+ return createHash("sha256").update(String(value || "default")).digest("hex");
25
+ }
26
+ function normalizeBrowserKind(value) {
27
+ const raw = String(value || "chrome").trim().toLowerCase();
28
+ if (raw === "cloakbrowser" || raw === "cloak-browser")
29
+ return "cloak";
30
+ if (raw === "chromium" || raw === "google-chrome" || raw === "chrome")
31
+ return "chrome";
32
+ return raw.replace(/[^a-z0-9_.-]/g, "-") || "chrome";
33
+ }
34
+ function resolveBrowserExecutablePath(env, browserKind) {
35
+ const explicit = env.DM_CUA_BROWSER_PATH || (browserKind === "cloak" ? env.DM_CUA_CLOAK_PATH || env.CLOAKBROWSER_BINARY_PATH : undefined) || env.CHROME_PATH;
36
+ return explicit ? resolve(String(explicit)) : undefined;
37
+ }
38
+ export function resolveBrowserRuntimeConfig(env = process.env, scope = process.cwd()) {
39
+ const rawScope = env.DM_CUA_SCOPE || env.DM_CODING_AGENT_DIR || env.PI_CODING_AGENT_DIR || scope;
40
+ const scopeHash = hashScope(resolve(String(rawScope || ".")));
41
+ const instanceId = String(env.DM_CUA_INSTANCE_ID || scopeHash.slice(0, 12)).replace(/[^a-zA-Z0-9_.-]/g, "-");
42
+ const browserKind = normalizeBrowserKind(env.DM_CUA_BROWSER || env.DM_BROWSER_CUA_BROWSER || env.DM_CUA_BROWSER_KIND);
43
+ const browserDisplayName = env.DM_CUA_BROWSER_DISPLAY_NAME || BROWSER_DISPLAY_NAMES[browserKind] || browserKind;
44
+ const browserExecutablePath = resolveBrowserExecutablePath(env, browserKind);
45
+ const port = parsePort(env.DM_CUA_PORT) ?? parsePort(env.GREEDY_SEARCH_PORT) ?? DEFAULT_CUA_PORT_BASE + Number.parseInt(scopeHash.slice(0, 8), 16) % DEFAULT_CUA_PORT_RANGE;
46
+ const profileSuffix = browserKind === "chrome" ? "chrome" : browserKind;
47
+ const profileDir = resolve(env.DM_CUA_PROFILE_DIR || join(tmpdir(), `dm-cua-${profileSuffix}-profile-${instanceId}`));
48
+ const socketBaseDir = platform() === "win32" ? tmpdir() : "/tmp";
49
+ return {
50
+ instanceId,
51
+ browserKind,
52
+ browserDisplayName,
53
+ browserExecutablePath,
54
+ browserExecutableIsExplicit: Boolean(browserExecutablePath),
55
+ browserInstallation: undefined,
56
+ port,
57
+ profileDir,
58
+ pidFile: join(profileDir, "chrome.pid"),
59
+ modeFile: join(profileDir, "chrome-mode"),
60
+ pagesCache: join(profileDir, "cdp-pages.json"),
61
+ socketDir: join(socketBaseDir, `dm-cua-cdp-${instanceId}`)
62
+ };
63
+ }
64
+ const BROWSER_CONFIG = resolveBrowserRuntimeConfig();
65
+ const CHROME_PORT = BROWSER_CONFIG.port;
66
+ export const BROWSER_PROFILE_DIR = BROWSER_CONFIG.profileDir;
67
+ function browserSummary() {
68
+ return {
69
+ kind: BROWSER_CONFIG.browserKind,
70
+ displayName: BROWSER_CONFIG.browserDisplayName,
71
+ executablePath: BROWSER_CONFIG.browserExecutablePath,
72
+ installation: BROWSER_CONFIG.browserInstallation ?? null,
73
+ profileDir: BROWSER_CONFIG.profileDir,
74
+ port: BROWSER_CONFIG.port
75
+ };
76
+ }
77
+ export async function ensureBrowserSelectionUsable(config = BROWSER_CONFIG, {
78
+ env = process.env,
79
+ exists = existsSync,
80
+ ensureChrome = ensureChromeAvailable
81
+ } = {}) {
82
+ if (config.browserKind === "cloak" && !config.browserExecutablePath) {
83
+ throw new Error([
84
+ "CloakBrowser selected for browser CUA, but no executable path was provided.",
85
+ "Install/pre-download CloakBrowser yourself, then set DM_CUA_BROWSER_PATH, DM_CUA_CLOAK_PATH, CLOAKBROWSER_BINARY_PATH, or CHROME_PATH.",
86
+ "DM does not bundle the compiled CloakBrowser binary because its upstream license allows use but not redistribution."
87
+ ].join(" "));
88
+ }
89
+ if (config.browserExecutablePath && !exists(config.browserExecutablePath)) {
90
+ throw new Error(`${config.browserDisplayName} executable not found: ${config.browserExecutablePath}`);
91
+ }
92
+ if (config.browserExecutablePath)
93
+ return config;
94
+ if (config.browserKind !== "chrome") {
95
+ throw new Error(`${config.browserDisplayName} browser CUA requires an explicit executable path via DM_CUA_BROWSER_PATH.`);
96
+ }
97
+ const selection = await ensureChrome({ env });
98
+ config.browserExecutablePath = selection.path;
99
+ config.browserInstallation = {
100
+ status: selection.status,
101
+ method: selection.method,
102
+ attempts: selection.attempts.map(({ id, status, exitCode }) => ({
103
+ id,
104
+ status,
105
+ exitCode
106
+ }))
107
+ };
108
+ return config;
109
+ }
110
+ let browserSelectionPromise;
111
+ async function ensureConfiguredBrowserSelectionUsable() {
112
+ if (!browserSelectionPromise) {
113
+ browserSelectionPromise = ensureBrowserSelectionUsable(BROWSER_CONFIG).catch((error) => {
114
+ browserSelectionPromise = undefined;
115
+ throw error;
116
+ });
117
+ }
118
+ return browserSelectionPromise;
119
+ }
120
+ export function resolveBrowserHelperPaths() {
121
+ const helperRoots = [
122
+ resolve(__dirname, "..", "greedysearch-dm"),
123
+ resolve(__dirname, "..", "..", "greedysearch-dm")
124
+ ];
125
+ const candidates = helperRoots.map((root) => ({
126
+ launchScript: join(root, "bin", "launch.mjs"),
127
+ cdpScript: join(root, "bin", "cdp.mjs")
128
+ }));
129
+ for (const candidate of candidates) {
130
+ if (existsSync(candidate.launchScript) && existsSync(candidate.cdpScript)) {
131
+ return candidate;
132
+ }
133
+ }
134
+ return candidates[0];
135
+ }
136
+ export function resolveBrowserProcessEnv(config = BROWSER_CONFIG, env = process.env) {
137
+ const visible = env.GREEDY_SEARCH_VISIBLE ?? env.DM_CUA_VISIBLE ?? env.DM_BROWSER_CUA_VISIBLE;
138
+ return {
139
+ ...env,
140
+ ...config.browserExecutablePath ? { CHROME_PATH: config.browserExecutablePath } : {},
141
+ CDP_PROFILE_DIR: config.profileDir.replace(/\\/g, "/"),
142
+ CDP_PAGES_CACHE: config.pagesCache.replace(/\\/g, "/"),
143
+ CDP_SOCKET_DIR: config.socketDir.replace(/\\/g, "/"),
144
+ GREEDY_SEARCH_BROWSER_LABEL: config.browserDisplayName,
145
+ GREEDY_SEARCH_PORT: String(config.port),
146
+ GREEDY_SEARCH_PROFILE_DIR: config.profileDir.replace(/\\/g, "/"),
147
+ GREEDY_SEARCH_PID_FILE: config.pidFile.replace(/\\/g, "/"),
148
+ GREEDY_SEARCH_MODE_FILE: config.modeFile.replace(/\\/g, "/"),
149
+ GREEDY_SEARCH_ALLOW_PORT_CLEANUP: env.GREEDY_SEARCH_ALLOW_PORT_CLEANUP ?? "0",
150
+ ...visible === undefined ? {} : { GREEDY_SEARCH_VISIBLE: visible }
151
+ };
152
+ }
153
+ function browserEnv() {
154
+ return resolveBrowserProcessEnv(BROWSER_CONFIG, process.env);
155
+ }
156
+ function looksLikeUrl(value) {
157
+ return URLISH_PATTERN.test(value);
158
+ }
159
+ export function parseListOutput(output) {
160
+ return output.split(/\r?\n/).map((line) => line.trimEnd()).filter(Boolean).map((line) => {
161
+ const parts = line.trim().split(/\s{2,}/).filter(Boolean);
162
+ if (parts.length === 0) {
163
+ return null;
164
+ }
165
+ const target = parts[0];
166
+ if (parts.length === 1) {
167
+ return { target, title: "", url: "" };
168
+ }
169
+ if (parts.length === 2 && looksLikeUrl(parts[1])) {
170
+ return { target, title: "", url: parts[1] };
171
+ }
172
+ const url = parts.at(-1) ?? "";
173
+ const title = parts.slice(1, -1).join(" ");
174
+ return { target, title, url };
175
+ }).filter(Boolean);
176
+ }
177
+ export function validateBrowserSelector(selector) {
178
+ const value = String(selector ?? "").trim();
179
+ if (!value) {
180
+ throw new Error("selector is required for action=click");
181
+ }
182
+ if (/:contains\s*\(/i.test(value)) {
183
+ throw new Error(":contains(...) is not a standard CSS selector. Use browser_cua(snapshot) to inspect a standard selector, or browser_cua(evaluate) for text matching.");
184
+ }
185
+ return value;
186
+ }
187
+ function resolveAgentDir() {
188
+ const explicit = process.env.DM_CODING_AGENT_DIR || process.env.PI_CODING_AGENT_DIR;
189
+ return explicit ? resolve(explicit) : join(homedir(), ".dm", "agent");
190
+ }
191
+ function defaultScreenshotPath() {
192
+ const dir = join(resolveAgentDir(), "cua", "screenshots");
193
+ mkdirSync(dir, { recursive: true });
194
+ const stamp = new Date().toISOString().replace(/[:.]/g, "-");
195
+ return join(dir, `browser-${stamp}.png`);
196
+ }
197
+ function runNodeScript(scriptPath, args, { env = process.env, timeoutMs = DEFAULT_TIMEOUT_MS } = {}) {
198
+ return new Promise((resolvePromise, rejectPromise) => {
199
+ const child = spawn("node", [scriptPath, ...args], {
200
+ env,
201
+ stdio: ["ignore", "pipe", "pipe"]
202
+ });
203
+ let stdout = "";
204
+ let stderr = "";
205
+ let settled = false;
206
+ const timeout = setTimeout(() => {
207
+ if (settled)
208
+ return;
209
+ settled = true;
210
+ child.kill("SIGTERM");
211
+ rejectPromise(new Error(`Timed out running ${scriptPath}`));
212
+ }, timeoutMs);
213
+ child.stdout.on("data", (chunk) => {
214
+ stdout += chunk.toString();
215
+ });
216
+ child.stderr.on("data", (chunk) => {
217
+ stderr += chunk.toString();
218
+ });
219
+ child.on("error", (error) => {
220
+ if (settled)
221
+ return;
222
+ settled = true;
223
+ clearTimeout(timeout);
224
+ rejectPromise(error);
225
+ });
226
+ child.on("close", (code) => {
227
+ if (settled)
228
+ return;
229
+ settled = true;
230
+ clearTimeout(timeout);
231
+ if (code !== 0) {
232
+ rejectPromise(new Error((stderr || stdout || `exit ${code}`).trim()));
233
+ return;
234
+ }
235
+ resolvePromise({ stdout, stderr });
236
+ });
237
+ });
238
+ }
239
+ function requestJson(method, requestPath) {
240
+ return new Promise((resolvePromise, rejectPromise) => {
241
+ const req = http.request({
242
+ host: "127.0.0.1",
243
+ port: CHROME_PORT,
244
+ path: requestPath,
245
+ method
246
+ }, (response) => {
247
+ let body = "";
248
+ response.on("data", (chunk) => {
249
+ body += chunk.toString();
250
+ });
251
+ response.on("end", () => {
252
+ if ((response.statusCode ?? 500) >= 400) {
253
+ rejectPromise(new Error(`Browser CDP HTTP ${response.statusCode}: ${body.trim()}`));
254
+ return;
255
+ }
256
+ try {
257
+ resolvePromise(body ? JSON.parse(body) : {});
258
+ } catch (error) {
259
+ rejectPromise(error);
260
+ }
261
+ });
262
+ });
263
+ req.on("error", rejectPromise);
264
+ req.end();
265
+ });
266
+ }
267
+ async function createBlankTarget(url = "about:blank") {
268
+ return requestJson("PUT", `/json/new?${encodeURIComponent(url)}`);
269
+ }
270
+ async function runCdp(args, options = {}) {
271
+ const { cdpScript } = resolveBrowserHelperPaths();
272
+ return runNodeScript(cdpScript, args, { env: browserEnv(), timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS });
273
+ }
274
+ async function listPages() {
275
+ const result = await runCdp(["list"]);
276
+ return parseListOutput(result.stdout);
277
+ }
278
+ function findPage(pages, prefix) {
279
+ if (!prefix)
280
+ return pages[0] ?? null;
281
+ const upper = prefix.toUpperCase();
282
+ return pages.find((page) => page.target.toUpperCase().startsWith(upper)) ?? null;
283
+ }
284
+ async function ensureBrowserHelpersExist() {
285
+ const { launchScript, cdpScript } = resolveBrowserHelperPaths();
286
+ if (!existsSync(launchScript) || !existsSync(cdpScript)) {
287
+ throw new Error("Bundled browser helpers are missing. Rebuild DM so dm-cua and greedysearch-dm are staged together.");
288
+ }
289
+ return { launchScript, cdpScript };
290
+ }
291
+ export async function ensureBrowserReady() {
292
+ const { launchScript } = await ensureBrowserHelpersExist();
293
+ await ensureConfiguredBrowserSelectionUsable();
294
+ await runNodeScript(launchScript, [], { env: browserEnv(), timeoutMs: DEFAULT_TIMEOUT_MS });
295
+ let pages = await listPages();
296
+ if (pages.length === 0) {
297
+ await createBlankTarget("about:blank");
298
+ pages = await listPages();
299
+ }
300
+ return pages;
301
+ }
302
+ async function resolveTabContext(tabPrefix) {
303
+ const pages = await ensureBrowserReady();
304
+ const page = findPage(pages, tabPrefix);
305
+ if (!page) {
306
+ if (tabPrefix) {
307
+ throw new Error(`No browser tab matching prefix "${tabPrefix}". Run browser_cua(list) first.`);
308
+ }
309
+ throw new Error("No browser tab available after browser bootstrap.");
310
+ }
311
+ return { tab: page.target, page, pages };
312
+ }
313
+ export async function runBrowserAction({
314
+ action,
315
+ tab,
316
+ url,
317
+ selector,
318
+ x,
319
+ y,
320
+ text,
321
+ expression,
322
+ outputPath
323
+ } = {}) {
324
+ switch (action) {
325
+ case "list": {
326
+ const pages = await ensureBrowserReady();
327
+ return { status: "ok", action, browser: browserSummary(), pages };
328
+ }
329
+ case "navigate": {
330
+ if (!url)
331
+ throw new Error("url is required for action=navigate");
332
+ const context = await resolveTabContext(tab);
333
+ const result = await runCdp(["nav", context.tab, url], { timeoutMs: 90000 });
334
+ const pages = await listPages();
335
+ const page = findPage(pages, context.tab) ?? context.page;
336
+ return {
337
+ status: "ok",
338
+ action,
339
+ browser: browserSummary(),
340
+ tab: context.tab,
341
+ url: page?.url || url,
342
+ title: page?.title || "",
343
+ message: result.stdout.trim(),
344
+ pages
345
+ };
346
+ }
347
+ case "snapshot": {
348
+ const context = await resolveTabContext(tab);
349
+ const result = await runCdp(["snap", context.tab]);
350
+ return {
351
+ status: "ok",
352
+ action,
353
+ browser: browserSummary(),
354
+ tab: context.tab,
355
+ title: context.page?.title || "",
356
+ url: context.page?.url || "",
357
+ snapshot: result.stdout.trim()
358
+ };
359
+ }
360
+ case "screenshot": {
361
+ const context = await resolveTabContext(tab);
362
+ const imagePath = outputPath ? resolve(outputPath) : defaultScreenshotPath();
363
+ mkdirSync(dirname(imagePath), { recursive: true });
364
+ const result = await runCdp(["shot", context.tab, imagePath], { timeoutMs: 120000 });
365
+ const imageBase64 = readFileSync(imagePath).toString("base64");
366
+ return {
367
+ status: "ok",
368
+ action,
369
+ browser: browserSummary(),
370
+ tab: context.tab,
371
+ title: context.page?.title || "",
372
+ url: context.page?.url || "",
373
+ imagePath,
374
+ imageBase64,
375
+ mimeType: "image/png",
376
+ message: result.stdout.trim()
377
+ };
378
+ }
379
+ case "click": {
380
+ const safeSelector = validateBrowserSelector(selector);
381
+ const context = await resolveTabContext(tab);
382
+ const result = await runCdp(["click", context.tab, safeSelector]);
383
+ return {
384
+ status: "ok",
385
+ action,
386
+ browser: browserSummary(),
387
+ tab: context.tab,
388
+ selector: safeSelector,
389
+ message: result.stdout.trim()
390
+ };
391
+ }
392
+ case "click_xy": {
393
+ if (typeof x !== "number" || Number.isNaN(x) || typeof y !== "number" || Number.isNaN(y)) {
394
+ throw new Error("x and y are required numbers for action=click_xy");
395
+ }
396
+ const context = await resolveTabContext(tab);
397
+ const result = await runCdp(["clickxy", context.tab, String(x), String(y)]);
398
+ return {
399
+ status: "ok",
400
+ action,
401
+ browser: browserSummary(),
402
+ tab: context.tab,
403
+ x,
404
+ y,
405
+ message: result.stdout.trim()
406
+ };
407
+ }
408
+ case "type": {
409
+ if (text == null || text === "")
410
+ throw new Error("text is required for action=type");
411
+ const context = await resolveTabContext(tab);
412
+ const result = await runCdp(["type", context.tab, text]);
413
+ return {
414
+ status: "ok",
415
+ action,
416
+ browser: browserSummary(),
417
+ tab: context.tab,
418
+ textLength: text.length,
419
+ message: result.stdout.trim()
420
+ };
421
+ }
422
+ case "evaluate": {
423
+ if (!expression)
424
+ throw new Error("expression is required for action=evaluate");
425
+ const context = await resolveTabContext(tab);
426
+ const result = await runCdp(["eval", context.tab, expression]);
427
+ return {
428
+ status: "ok",
429
+ action,
430
+ browser: browserSummary(),
431
+ tab: context.tab,
432
+ value: result.stdout.trim()
433
+ };
434
+ }
435
+ case "stop": {
436
+ const { launchScript } = await ensureBrowserHelpersExist();
437
+ try {
438
+ await runCdp(["stop"]);
439
+ } catch {}
440
+ const result = await runNodeScript(launchScript, ["--kill"], { env: browserEnv(), timeoutMs: 30000 });
441
+ return {
442
+ status: "ok",
443
+ action,
444
+ browser: browserSummary(),
445
+ message: (result.stdout || "Stopped browser CUA lane.").trim()
446
+ };
447
+ }
448
+ default:
449
+ throw new Error(`Unsupported browser action: ${action}`);
450
+ }
451
+ }
452
+ export function renderBrowserActionText(result) {
453
+ switch (result.action) {
454
+ case "list": {
455
+ if (!Array.isArray(result.pages) || result.pages.length === 0) {
456
+ return "Browser CUA is ready, but no pages are open.";
457
+ }
458
+ return [
459
+ "Open browser tabs:",
460
+ ...result.pages.map((page) => page.url ? `${page.target} ${page.title || "(untitled)"} ${page.url}` : `${page.target} ${page.title || "(untitled)"}`)
461
+ ].join(`
462
+ `);
463
+ }
464
+ case "navigate":
465
+ return [`Navigated browser tab ${result.tab} to ${result.url || "(unknown URL)"}`, result.title ? `Title: ${result.title}` : ""].filter(Boolean).join(`
466
+ `);
467
+ case "snapshot":
468
+ return [`Browser snapshot for ${result.tab}`, result.snapshot || "(empty snapshot)"].join(`
469
+ `);
470
+ case "screenshot":
471
+ return [
472
+ `Saved browser screenshot for ${result.tab} at ${result.imagePath}`,
473
+ result.title ? `Title: ${result.title}` : "",
474
+ result.url ? `URL: ${result.url}` : ""
475
+ ].filter(Boolean).join(`
476
+ `);
477
+ case "click":
478
+ return `Clicked selector ${JSON.stringify(result.selector)} on browser tab ${result.tab}`;
479
+ case "click_xy":
480
+ return `Clicked browser tab ${result.tab} at CSS coordinates (${result.x}, ${result.y})`;
481
+ case "type":
482
+ return `Typed ${result.textLength} characters into browser tab ${result.tab}`;
483
+ case "evaluate":
484
+ return `Browser evaluate result for ${result.tab}: ${result.value}`;
485
+ case "stop":
486
+ return result.message || "Stopped browser CUA lane.";
487
+ default:
488
+ return JSON.stringify(result, null, 2);
489
+ }
490
+ }