@duckmind/dm-darwin-x64 0.52.6 → 0.53.1

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 (58) hide show
  1. package/dm +0 -0
  2. package/extensions/.dm-extensions.json +14 -10
  3. package/extensions/dm-cua/bin/browser-cua.mjs +6 -6
  4. package/extensions/dm-cua/index.js +5 -5
  5. package/extensions/dm-cua/src/browser-cua-lib.mjs +2 -2
  6. package/extensions/greedysearch-dm/bin/cdp-greedy.mjs +8 -1
  7. package/extensions/greedysearch-dm/bin/cdp-headless.mjs +1 -1
  8. package/extensions/greedysearch-dm/bin/cdp-visible.mjs +1 -1
  9. package/extensions/greedysearch-dm/bin/cdp.mjs +29 -20
  10. package/extensions/greedysearch-dm/bin/gschrome.mjs +1 -8
  11. package/extensions/greedysearch-dm/bin/kill-visible.mjs +1 -1
  12. package/extensions/greedysearch-dm/bin/launch-visible.mjs +1 -2
  13. package/extensions/greedysearch-dm/bin/launch.mjs +9 -11
  14. package/extensions/greedysearch-dm/bin/mcp.mjs +375 -0
  15. package/extensions/greedysearch-dm/bin/search.mjs +309 -157
  16. package/extensions/greedysearch-dm/bin/visible.mjs +1 -1
  17. package/extensions/greedysearch-dm/extractors/bing-copilot.mjs +205 -68
  18. package/extensions/greedysearch-dm/extractors/chatgpt.mjs +229 -140
  19. package/extensions/greedysearch-dm/extractors/common.mjs +160 -50
  20. package/extensions/greedysearch-dm/extractors/consensus.mjs +226 -93
  21. package/extensions/greedysearch-dm/extractors/consent.mjs +44 -17
  22. package/extensions/greedysearch-dm/extractors/gemini.mjs +277 -138
  23. package/extensions/greedysearch-dm/extractors/google-ai.mjs +172 -64
  24. package/extensions/greedysearch-dm/extractors/logically.mjs +183 -69
  25. package/extensions/greedysearch-dm/extractors/perplexity.mjs +331 -65
  26. package/extensions/greedysearch-dm/extractors/semantic-scholar.mjs +118 -41
  27. package/extensions/greedysearch-dm/index.js +23 -20
  28. package/extensions/greedysearch-dm/package.json +8 -3
  29. package/extensions/greedysearch-dm/skills/greedy-search/skill.md +8 -8
  30. package/extensions/greedysearch-dm/src/fetcher.mjs +2 -2
  31. package/extensions/greedysearch-dm/src/formatters/results.js +8 -6
  32. package/extensions/greedysearch-dm/src/github.mjs +7 -7
  33. package/extensions/greedysearch-dm/src/reddit.mjs +1 -1
  34. package/extensions/greedysearch-dm/src/search/browser-lifecycle.mjs +10 -8
  35. package/extensions/greedysearch-dm/src/search/challenge-detect.mjs +48 -0
  36. package/extensions/greedysearch-dm/src/search/chrome.mjs +130 -54
  37. package/extensions/greedysearch-dm/src/search/constants.mjs +6 -6
  38. package/extensions/greedysearch-dm/src/search/engines.mjs +9 -9
  39. package/extensions/greedysearch-dm/src/search/fetch-source.mjs +160 -83
  40. package/extensions/greedysearch-dm/src/search/minimize.mjs +1 -0
  41. package/extensions/greedysearch-dm/src/search/paths.mjs +1 -1
  42. package/extensions/greedysearch-dm/src/search/pdf.mjs +1 -1
  43. package/extensions/greedysearch-dm/src/search/port-pid.mjs +1 -1
  44. package/extensions/greedysearch-dm/src/search/progress.mjs +2 -0
  45. package/extensions/greedysearch-dm/src/search/recovery.mjs +1 -1
  46. package/extensions/greedysearch-dm/src/search/research.mjs +262 -167
  47. package/extensions/greedysearch-dm/src/search/scale-aware.mjs +11 -0
  48. package/extensions/greedysearch-dm/src/search/simple-research.mjs +827 -0
  49. package/extensions/greedysearch-dm/src/search/synthesis-runner.mjs +9 -9
  50. package/extensions/greedysearch-dm/src/search/synthesis.mjs +10 -10
  51. package/extensions/greedysearch-dm/src/tools/greedy-search-handler.js +23 -20
  52. package/extensions/greedysearch-dm/src/tools/shared.js +10 -9
  53. package/extensions/greedysearch-dm/src/utils/node-runtime.mjs +1 -1
  54. package/package.json +1 -1
  55. package/extensions/greedysearch-dm/src/search/cdp-endpoint.mjs +0 -3
  56. package/extensions/greedysearch-dm/src/search/launcher-paths.mjs +0 -3
  57. package/extensions/greedysearch-dm/src/search/partial-output.mjs +0 -1
  58. package/extensions/greedysearch-dm/src/utils/browser-executable.mjs +0 -1
package/dm CHANGED
Binary file
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "status": "ok",
3
- "prepared_at": "2026-07-14T07:56:40.377202+00:00",
3
+ "prepared_at": "2026-07-23T06:36:46.219447+00:00",
4
4
  "managed_entries": [
5
5
  {
6
6
  "id": "dm-context",
@@ -341,6 +341,8 @@
341
341
  "drop-files"
342
342
  ],
343
343
  "stripped_documentation": [
344
+ "docs/",
345
+ "CHANGELOG.md",
344
346
  "LICENSE",
345
347
  "README.md"
346
348
  ],
@@ -355,6 +357,7 @@
355
357
  "bin/kill-visible.mjs",
356
358
  "bin/launch-visible.mjs",
357
359
  "bin/launch.mjs",
360
+ "bin/mcp.mjs",
358
361
  "bin/search.mjs",
359
362
  "bin/visible.mjs",
360
363
  "extractors/bing-copilot.mjs",
@@ -376,37 +379,38 @@
376
379
  "src/github.mjs",
377
380
  "src/reddit.mjs",
378
381
  "src/search/browser-lifecycle.mjs",
379
- "src/search/cdp-endpoint.mjs",
382
+ "src/search/challenge-detect.mjs",
380
383
  "src/search/chrome.mjs",
381
384
  "src/search/constants.mjs",
382
385
  "src/search/defaults.mjs",
383
386
  "src/search/engines.mjs",
384
387
  "src/search/fetch-source.mjs",
385
388
  "src/search/file-sources.mjs",
386
- "src/search/launcher-paths.mjs",
389
+ "src/search/minimize.mjs",
387
390
  "src/search/output.mjs",
388
- "src/search/partial-output.mjs",
389
391
  "src/search/paths.mjs",
390
392
  "src/search/pdf.mjs",
391
393
  "src/search/port-pid.mjs",
394
+ "src/search/progress.mjs",
392
395
  "src/search/query.mjs",
393
396
  "src/search/recovery.mjs",
394
397
  "src/search/research.mjs",
398
+ "src/search/scale-aware.mjs",
399
+ "src/search/simple-research.mjs",
395
400
  "src/search/sources.mjs",
396
401
  "src/search/synthesis-runner.mjs",
397
402
  "src/search/synthesis.mjs",
398
403
  "src/tools/greedy-search-handler.js",
399
404
  "src/tools/shared.js",
400
405
  "src/types.js",
401
- "src/utils/browser-executable.mjs",
402
406
  "src/utils/content.mjs",
403
407
  "src/utils/helpers.js",
404
408
  "src/utils/node-runtime.mjs",
405
409
  "src/utils/system-cmds.mjs"
406
410
  ],
407
- "file_count": 56,
408
- "before_bytes": 505996,
409
- "after_bytes": 814429,
411
+ "file_count": 58,
412
+ "before_bytes": 609953,
413
+ "after_bytes": 1166455,
410
414
  "externals": [
411
415
  "@duckmind/dm-agent-core",
412
416
  "@duckmind/dm-ai",
@@ -631,8 +635,8 @@
631
635
  "src/browser-cua-lib.mjs"
632
636
  ],
633
637
  "file_count": 3,
634
- "before_bytes": 22123,
635
- "after_bytes": 32514,
638
+ "before_bytes": 22185,
639
+ "after_bytes": 32604,
636
640
  "externals": [
637
641
  "@duckmind/dm-agent-core",
638
642
  "@duckmind/dm-ai",
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import{createHash as x}from"node:crypto";import{spawn as O}from"node:child_process";import{existsSync as U,mkdirSync as F,readFileSync as P}from"node:fs";import R from"node:http";import{homedir as d,platform as S,tmpdir as N}from"node:os";import{dirname as I,join as H,resolve as K}from"node:path";import{fileURLToPath as v}from"node:url";var w=I(v(import.meta.url)),kf=K(w,".."),c=9322,n=400,T=60000,u=/^(about:|https?:\/\/|file:|chrome-error:\/\/)/i,p={chrome:"Chrome",cloak:"CloakBrowser"};function b(f){let J=Number.parseInt(String(f??""),10);return Number.isInteger(J)&&J>1024&&J<65535?J:null}function i(f){return x("sha256").update(String(f||"default")).digest("hex")}function o(f){let J=String(f||"chrome").trim().toLowerCase();if(J==="cloakbrowser"||J==="cloak-browser")return"cloak";if(J==="chromium"||J==="google-chrome"||J==="chrome")return"chrome";return J.replace(/[^a-z0-9_.-]/g,"-")||"chrome"}function l(f,J){let V=f.DM_CUA_BROWSER_PATH||(J==="cloak"?f.DM_CUA_CLOAK_PATH||f.CLOAKBROWSER_BINARY_PATH:void 0)||f.CHROME_PATH;return V?K(String(V)):void 0}function a(f=process.env,J=process.cwd()){let V=f.DM_CUA_SCOPE||f.DM_CODING_AGENT_DIR||f.PI_CODING_AGENT_DIR||J,j=i(K(String(V||"."))),Z=String(f.DM_CUA_INSTANCE_ID||j.slice(0,12)).replace(/[^a-zA-Z0-9_.-]/g,"-"),Q=o(f.DM_CUA_BROWSER||f.DM_BROWSER_CUA_BROWSER||f.DM_CUA_BROWSER_KIND),z=f.DM_CUA_BROWSER_DISPLAY_NAME||p[Q]||Q,A=l(f,Q),M=b(f.DM_CUA_PORT)??b(f.GREEDY_SEARCH_PORT)??c+Number.parseInt(j.slice(0,8),16)%n,$=Q==="chrome"?"chrome":Q,X=K(f.DM_CUA_PROFILE_DIR||H(N(),`dm-cua-${$}-profile-${Z}`)),q=S()==="win32"?N():"/tmp";return{instanceId:Z,browserKind:Q,browserDisplayName:z,browserExecutablePath:A,port:M,profileDir:X,pidFile:H(X,"chrome.pid"),modeFile:H(X,"chrome-mode"),pagesCache:H(X,"cdp-pages.json"),socketDir:H(q,`dm-cua-cdp-${Z}`)}}var Y=a(),s=Y.port,Df=Y.profileDir;function k(){return{kind:Y.browserKind,displayName:Y.browserDisplayName,executablePath:Y.browserExecutablePath,profileDir:Y.profileDir,port:Y.port}}function t(){if(Y.browserKind==="cloak"&&!Y.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(Y.browserExecutablePath&&!U(Y.browserExecutablePath))throw Error(`${Y.browserDisplayName} executable not found: ${Y.browserExecutablePath}`)}function B(){let J=[K(w,"..","greedysearch-dm"),K(w,"..","..","greedysearch-dm")].map((V)=>({launchScript:H(V,"bin","launch.mjs"),cdpScript:H(V,"bin","cdp.mjs")}));for(let V of J)if(U(V.launchScript)&&U(V.cdpScript))return V;return J[0]}function h(){let f=process.env.GREEDY_SEARCH_VISIBLE??process.env.DM_CUA_VISIBLE??process.env.DM_BROWSER_CUA_VISIBLE??"1";return{...process.env,...Y.browserExecutablePath?{CHROME_PATH:Y.browserExecutablePath}:{},CDP_PROFILE_DIR:Y.profileDir.replace(/\\/g,"/"),CDP_PAGES_CACHE:Y.pagesCache.replace(/\\/g,"/"),CDP_SOCKET_DIR:Y.socketDir.replace(/\\/g,"/"),GREEDY_SEARCH_BROWSER_LABEL:Y.browserDisplayName,GREEDY_SEARCH_PORT:String(Y.port),GREEDY_SEARCH_PROFILE_DIR:Y.profileDir.replace(/\\/g,"/"),GREEDY_SEARCH_PID_FILE:Y.pidFile.replace(/\\/g,"/"),GREEDY_SEARCH_MODE_FILE:Y.modeFile.replace(/\\/g,"/"),GREEDY_SEARCH_ALLOW_PORT_CLEANUP:process.env.GREEDY_SEARCH_ALLOW_PORT_CLEANUP??"0",GREEDY_SEARCH_VISIBLE:f}}function r(f){return u.test(f)}function e(f){return f.split(/\r?\n/).map((J)=>J.trimEnd()).filter(Boolean).map((J)=>{let V=J.trim().split(/\s{2,}/).filter(Boolean);if(V.length===0)return null;let j=V[0];if(V.length===1)return{target:j,title:"",url:""};if(V.length===2&&r(V[1]))return{target:j,title:"",url:V[1]};let Z=V.at(-1)??"",Q=V.slice(1,-1).join(" ");return{target:j,title:Q,url:Z}}).filter(Boolean)}function ff(){let f=process.env.DM_CODING_AGENT_DIR||process.env.PI_CODING_AGENT_DIR;return f?K(f):H(d(),".dm","agent")}function Jf(){let f=H(ff(),"cua","screenshots");F(f,{recursive:!0});let J=new Date().toISOString().replace(/[:.]/g,"-");return H(f,`browser-${J}.png`)}function G(f,J,{env:V=process.env,timeoutMs:j=T}={}){return new Promise((Z,Q)=>{let z=O("node",[f,...J],{env:V,stdio:["ignore","pipe","pipe"]}),A="",M="",$=!1,X=setTimeout(()=>{if($)return;$=!0,z.kill("SIGTERM"),Q(Error(`Timed out running ${f}`))},j);z.stdout.on("data",(q)=>{A+=q.toString()}),z.stderr.on("data",(q)=>{M+=q.toString()}),z.on("error",(q)=>{if($)return;$=!0,clearTimeout(X),Q(q)}),z.on("close",(q)=>{if($)return;if($=!0,clearTimeout(X),q!==0){Q(Error((M||A||`exit ${q}`).trim()));return}Z({stdout:A,stderr:M})})})}function Vf(f,J){return new Promise((V,j)=>{let Z=R.request({host:"127.0.0.1",port:s,path:J,method:f},(Q)=>{let z="";Q.on("data",(A)=>{z+=A.toString()}),Q.on("end",()=>{if((Q.statusCode??500)>=400){j(Error(`Browser CDP HTTP ${Q.statusCode}: ${z.trim()}`));return}try{V(z?JSON.parse(z):{})}catch(A){j(A)}})});Z.on("error",j),Z.end()})}async function $f(f="about:blank"){return Vf("PUT",`/json/new?${encodeURIComponent(f)}`)}async function D(f,J={}){let{cdpScript:V}=B();return G(V,f,{env:h(),timeoutMs:J.timeoutMs??T})}async function W(){let f=await D(["list"]);return e(f.stdout)}function g(f,J){if(!J)return f[0]??null;let V=J.toUpperCase();return f.find((j)=>j.target.toUpperCase().startsWith(V))??null}async function m(){let{launchScript:f,cdpScript:J}=B();if(!U(f)||!U(J))throw Error("Bundled browser helpers are missing. Rebuild DM so dm-cua and greedysearch-dm are staged together.");return{launchScript:f,cdpScript:J}}async function C(){t();let{launchScript:f}=await m();await G(f,[],{env:h(),timeoutMs:T});let J=await W();if(J.length===0)await $f("about:blank"),J=await W();return J}async function L(f){let J=await C(),V=g(J,f);if(!V){if(f)throw Error(`No browser tab matching prefix "${f}". Run browser_cua(list) first.`);throw Error("No browser tab available after browser bootstrap.")}return{tab:V.target,page:V,pages:J}}async function _({action:f,tab:J,url:V,selector:j,x:Z,y:Q,text:z,expression:A,outputPath:M}={}){switch(f){case"list":{let $=await C();return{status:"ok",action:f,browser:k(),pages:$}}case"navigate":{if(!V)throw Error("url is required for action=navigate");let $=await L(J),X=await D(["nav",$.tab,V],{timeoutMs:90000}),q=await W(),y=g(q,$.tab)??$.page;return{status:"ok",action:f,browser:k(),tab:$.tab,url:y?.url||V,title:y?.title||"",message:X.stdout.trim(),pages:q}}case"snapshot":{let $=await L(J),X=await D(["snap",$.tab]);return{status:"ok",action:f,browser:k(),tab:$.tab,title:$.page?.title||"",url:$.page?.url||"",snapshot:X.stdout.trim()}}case"screenshot":{let $=await L(J),X=M?K(M):Jf();F(I(X),{recursive:!0});let q=await D(["shot",$.tab,X],{timeoutMs:120000}),y=P(X).toString("base64");return{status:"ok",action:f,browser:k(),tab:$.tab,title:$.page?.title||"",url:$.page?.url||"",imagePath:X,imageBase64:y,mimeType:"image/png",message:q.stdout.trim()}}case"click":{if(!j)throw Error("selector is required for action=click");let $=await L(J),X=await D(["click",$.tab,j]);return{status:"ok",action:f,browser:k(),tab:$.tab,selector:j,message:X.stdout.trim()}}case"click_xy":{if(typeof Z!=="number"||Number.isNaN(Z)||typeof Q!=="number"||Number.isNaN(Q))throw Error("x and y are required numbers for action=click_xy");let $=await L(J),X=await D(["clickxy",$.tab,String(Z),String(Q)]);return{status:"ok",action:f,browser:k(),tab:$.tab,x:Z,y:Q,message:X.stdout.trim()}}case"type":{if(z==null||z==="")throw Error("text is required for action=type");let $=await L(J),X=await D(["type",$.tab,z]);return{status:"ok",action:f,browser:k(),tab:$.tab,textLength:z.length,message:X.stdout.trim()}}case"evaluate":{if(!A)throw Error("expression is required for action=evaluate");let $=await L(J),X=await D(["eval",$.tab,A]);return{status:"ok",action:f,browser:k(),tab:$.tab,value:X.stdout.trim()}}case"stop":{let{launchScript:$}=await m();try{await D(["stop"])}catch{}let X=await G($,["--kill"],{env:h(),timeoutMs:30000});return{status:"ok",action:f,browser:k(),message:(X.stdout||"Stopped browser CUA lane.").trim()}}default:throw Error(`Unsupported browser action: ${f}`)}}function E(f){switch(f.action){case"list":{if(!Array.isArray(f.pages)||f.pages.length===0)return"Browser CUA is ready, but no pages are open.";return["Open browser tabs:",...f.pages.map((J)=>J.url?`${J.target} ${J.title||"(untitled)"} ${J.url}`:`${J.target} ${J.title||"(untitled)"}`)].join(`
3
- `)}case"navigate":return[`Navigated browser tab ${f.tab} to ${f.url||"(unknown URL)"}`,f.title?`Title: ${f.title}`:""].filter(Boolean).join(`
4
- `);case"snapshot":return[`Browser snapshot for ${f.tab}`,f.snapshot||"(empty snapshot)"].join(`
5
- `);case"screenshot":return[`Saved browser screenshot for ${f.tab} at ${f.imagePath}`,f.title?`Title: ${f.title}`:"",f.url?`URL: ${f.url}`:""].filter(Boolean).join(`
6
- `);case"click":return`Clicked selector ${JSON.stringify(f.selector)} on browser tab ${f.tab}`;case"click_xy":return`Clicked browser tab ${f.tab} at CSS coordinates (${f.x}, ${f.y})`;case"type":return`Typed ${f.textLength} characters into browser tab ${f.tab}`;case"evaluate":return`Browser evaluate result for ${f.tab}: ${f.value}`;case"stop":return f.message||"Stopped browser CUA lane.";default:return JSON.stringify(f,null,2)}}function jf(){console.log(`dm-cua browser helper
2
+ import{createHash as R}from"node:crypto";import{spawn as x}from"node:child_process";import{existsSync as W,mkdirSync as h,readFileSync as S}from"node:fs";import P from"node:http";import{homedir as g,platform as d,tmpdir as T}from"node:os";import{dirname as B,join as k,resolve as L}from"node:path";import{fileURLToPath as v}from"node:url";var y=B(v(import.meta.url)),K2=L(y,".."),u=9322,c=400,N=60000,p=/^(about:|https?:\/\/|file:|chrome-error:\/\/)/i,l={chrome:"Chrome",cloak:"CloakBrowser"};function I(J){let V=Number.parseInt(String(J??""),10);return Number.isInteger(V)&&V>1024&&V<65535?V:null}function n(J){return R("sha256").update(String(J||"default")).digest("hex")}function i(J){let V=String(J||"chrome").trim().toLowerCase();if(V==="cloakbrowser"||V==="cloak-browser")return"cloak";if(V==="chromium"||V==="google-chrome"||V==="chrome")return"chrome";return V.replace(/[^a-z0-9_.-]/g,"-")||"chrome"}function o(J,V){let $=J.DM_CUA_BROWSER_PATH||(V==="cloak"?J.DM_CUA_CLOAK_PATH||J.CLOAKBROWSER_BINARY_PATH:void 0)||J.CHROME_PATH;return $?L(String($)):void 0}function a(J=process.env,V=process.cwd()){let $=J.DM_CUA_SCOPE||J.DM_CODING_AGENT_DIR||J.PI_CODING_AGENT_DIR||V,Q=n(L(String($||"."))),Z=String(J.DM_CUA_INSTANCE_ID||Q.slice(0,12)).replace(/[^a-zA-Z0-9_.-]/g,"-"),X=i(J.DM_CUA_BROWSER||J.DM_BROWSER_CUA_BROWSER||J.DM_CUA_BROWSER_KIND),z=J.DM_CUA_BROWSER_DISPLAY_NAME||l[X]||X,A=o(J,X),H=I(J.DM_CUA_PORT)??I(J.GREEDY_SEARCH_PORT)??u+Number.parseInt(Q.slice(0,8),16)%c,j=X==="chrome"?"chrome":X,Y=L(J.DM_CUA_PROFILE_DIR||k(T(),`dm-cua-${j}-profile-${Z}`)),q=d()==="win32"?T():"/tmp";return{instanceId:Z,browserKind:X,browserDisplayName:z,browserExecutablePath:A,port:H,profileDir:Y,pidFile:k(Y,"chrome.pid"),modeFile:k(Y,"chrome-mode"),pagesCache:k(Y,"cdp-pages.json"),socketDir:k(q,`dm-cua-cdp-${Z}`)}}var M=a(),s=M.port,L2=M.profileDir;function D(){return{kind:M.browserKind,displayName:M.browserDisplayName,executablePath:M.browserExecutablePath,profileDir:M.profileDir,port:M.port}}function t(){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&&!W(M.browserExecutablePath))throw Error(`${M.browserDisplayName} executable not found: ${M.browserExecutablePath}`)}function C(){let V=[L(y,"..","greedysearch-dm"),L(y,"..","..","greedysearch-dm")].map(($)=>({launchScript:k($,"bin","launch.mjs"),cdpScript:k($,"bin","cdp.mjs")}));for(let $ of V)if(W($.launchScript)&&W($.cdpScript))return $;return V[0]}function r(J=M,V=process.env){let $=V.GREEDY_SEARCH_VISIBLE??V.DM_CUA_VISIBLE??V.DM_BROWSER_CUA_VISIBLE;return{...V,...J.browserExecutablePath?{CHROME_PATH:J.browserExecutablePath}:{},CDP_PROFILE_DIR:J.profileDir.replace(/\\/g,"/"),CDP_PAGES_CACHE:J.pagesCache.replace(/\\/g,"/"),CDP_SOCKET_DIR:J.socketDir.replace(/\\/g,"/"),GREEDY_SEARCH_BROWSER_LABEL:J.browserDisplayName,GREEDY_SEARCH_PORT:String(J.port),GREEDY_SEARCH_PROFILE_DIR:J.profileDir.replace(/\\/g,"/"),GREEDY_SEARCH_PID_FILE:J.pidFile.replace(/\\/g,"/"),GREEDY_SEARCH_MODE_FILE:J.modeFile.replace(/\\/g,"/"),GREEDY_SEARCH_ALLOW_PORT_CLEANUP:V.GREEDY_SEARCH_ALLOW_PORT_CLEANUP??"0",...$===void 0?{}:{GREEDY_SEARCH_VISIBLE:$}}}function w(){return r(M,process.env)}function e(J){return p.test(J)}function J2(J){return J.split(/\r?\n/).map((V)=>V.trimEnd()).filter(Boolean).map((V)=>{let $=V.trim().split(/\s{2,}/).filter(Boolean);if($.length===0)return null;let Q=$[0];if($.length===1)return{target:Q,title:"",url:""};if($.length===2&&e($[1]))return{target:Q,title:"",url:$[1]};let Z=$.at(-1)??"",X=$.slice(1,-1).join(" ");return{target:Q,title:X,url:Z}}).filter(Boolean)}function V2(){let J=process.env.DM_CODING_AGENT_DIR||process.env.PI_CODING_AGENT_DIR;return J?L(J):k(g(),".dm","agent")}function $2(){let J=k(V2(),"cua","screenshots");h(J,{recursive:!0});let V=new Date().toISOString().replace(/[:.]/g,"-");return k(J,`browser-${V}.png`)}function F(J,V,{env:$=process.env,timeoutMs:Q=N}={}){return new Promise((Z,X)=>{let z=x("node",[J,...V],{env:$,stdio:["ignore","pipe","pipe"]}),A="",H="",j=!1,Y=setTimeout(()=>{if(j)return;j=!0,z.kill("SIGTERM"),X(Error(`Timed out running ${J}`))},Q);z.stdout.on("data",(q)=>{A+=q.toString()}),z.stderr.on("data",(q)=>{H+=q.toString()}),z.on("error",(q)=>{if(j)return;j=!0,clearTimeout(Y),X(q)}),z.on("close",(q)=>{if(j)return;if(j=!0,clearTimeout(Y),q!==0){X(Error((H||A||`exit ${q}`).trim()));return}Z({stdout:A,stderr:H})})})}function j2(J,V){return new Promise(($,Q)=>{let Z=P.request({host:"127.0.0.1",port:s,path:V,method:J},(X)=>{let z="";X.on("data",(A)=>{z+=A.toString()}),X.on("end",()=>{if((X.statusCode??500)>=400){Q(Error(`Browser CDP HTTP ${X.statusCode}: ${z.trim()}`));return}try{$(z?JSON.parse(z):{})}catch(A){Q(A)}})});Z.on("error",Q),Z.end()})}async function Q2(J="about:blank"){return j2("PUT",`/json/new?${encodeURIComponent(J)}`)}async function K(J,V={}){let{cdpScript:$}=C();return F($,J,{env:w(),timeoutMs:V.timeoutMs??N})}async function G(){let J=await K(["list"]);return J2(J.stdout)}function b(J,V){if(!V)return J[0]??null;let $=V.toUpperCase();return J.find((Q)=>Q.target.toUpperCase().startsWith($))??null}async function _(){let{launchScript:J,cdpScript:V}=C();if(!W(J)||!W(V))throw Error("Bundled browser helpers are missing. Rebuild DM so dm-cua and greedysearch-dm are staged together.");return{launchScript:J,cdpScript:V}}async function E(){t();let{launchScript:J}=await _();await F(J,[],{env:w(),timeoutMs:N});let V=await G();if(V.length===0)await Q2("about:blank"),V=await G();return V}async function U(J){let V=await E(),$=b(V,J);if(!$){if(J)throw Error(`No browser tab matching prefix "${J}". Run browser_cua(list) first.`);throw Error("No browser tab available after browser bootstrap.")}return{tab:$.target,page:$,pages:V}}async function O({action:J,tab:V,url:$,selector:Q,x:Z,y:X,text:z,expression:A,outputPath:H}={}){switch(J){case"list":{let j=await E();return{status:"ok",action:J,browser:D(),pages:j}}case"navigate":{if(!$)throw Error("url is required for action=navigate");let j=await U(V),Y=await K(["nav",j.tab,$],{timeoutMs:90000}),q=await G(),f=b(q,j.tab)??j.page;return{status:"ok",action:J,browser:D(),tab:j.tab,url:f?.url||$,title:f?.title||"",message:Y.stdout.trim(),pages:q}}case"snapshot":{let j=await U(V),Y=await K(["snap",j.tab]);return{status:"ok",action:J,browser:D(),tab:j.tab,title:j.page?.title||"",url:j.page?.url||"",snapshot:Y.stdout.trim()}}case"screenshot":{let j=await U(V),Y=H?L(H):$2();h(B(Y),{recursive:!0});let q=await K(["shot",j.tab,Y],{timeoutMs:120000}),f=S(Y).toString("base64");return{status:"ok",action:J,browser:D(),tab:j.tab,title:j.page?.title||"",url:j.page?.url||"",imagePath:Y,imageBase64:f,mimeType:"image/png",message:q.stdout.trim()}}case"click":{if(!Q)throw Error("selector is required for action=click");let j=await U(V),Y=await K(["click",j.tab,Q]);return{status:"ok",action:J,browser:D(),tab:j.tab,selector:Q,message:Y.stdout.trim()}}case"click_xy":{if(typeof Z!=="number"||Number.isNaN(Z)||typeof X!=="number"||Number.isNaN(X))throw Error("x and y are required numbers for action=click_xy");let j=await U(V),Y=await K(["clickxy",j.tab,String(Z),String(X)]);return{status:"ok",action:J,browser:D(),tab:j.tab,x:Z,y:X,message:Y.stdout.trim()}}case"type":{if(z==null||z==="")throw Error("text is required for action=type");let j=await U(V),Y=await K(["type",j.tab,z]);return{status:"ok",action:J,browser:D(),tab:j.tab,textLength:z.length,message:Y.stdout.trim()}}case"evaluate":{if(!A)throw Error("expression is required for action=evaluate");let j=await U(V),Y=await K(["eval",j.tab,A]);return{status:"ok",action:J,browser:D(),tab:j.tab,value:Y.stdout.trim()}}case"stop":{let{launchScript:j}=await _();try{await K(["stop"])}catch{}let Y=await F(j,["--kill"],{env:w(),timeoutMs:30000});return{status:"ok",action:J,browser:D(),message:(Y.stdout||"Stopped browser CUA lane.").trim()}}default:throw Error(`Unsupported browser action: ${J}`)}}function m(J){switch(J.action){case"list":{if(!Array.isArray(J.pages)||J.pages.length===0)return"Browser CUA is ready, but no pages are open.";return["Open browser tabs:",...J.pages.map((V)=>V.url?`${V.target} ${V.title||"(untitled)"} ${V.url}`:`${V.target} ${V.title||"(untitled)"}`)].join(`
3
+ `)}case"navigate":return[`Navigated browser tab ${J.tab} to ${J.url||"(unknown URL)"}`,J.title?`Title: ${J.title}`:""].filter(Boolean).join(`
4
+ `);case"snapshot":return[`Browser snapshot for ${J.tab}`,J.snapshot||"(empty snapshot)"].join(`
5
+ `);case"screenshot":return[`Saved browser screenshot for ${J.tab} at ${J.imagePath}`,J.title?`Title: ${J.title}`:"",J.url?`URL: ${J.url}`:""].filter(Boolean).join(`
6
+ `);case"click":return`Clicked selector ${JSON.stringify(J.selector)} on browser tab ${J.tab}`;case"click_xy":return`Clicked browser tab ${J.tab} at CSS coordinates (${J.x}, ${J.y})`;case"type":return`Typed ${J.textLength} characters into browser tab ${J.tab}`;case"evaluate":return`Browser evaluate result for ${J.tab}: ${J.value}`;case"stop":return J.message||"Stopped browser CUA lane.";default:return JSON.stringify(J,null,2)}}function X2(){console.log(`dm-cua browser helper
7
7
 
8
8
  Usage:
9
9
  node browser-cua.mjs list [--json]
@@ -21,4 +21,4 @@ Usage:
21
21
  - Fresh dedicated browser profiles auto-bootstrap a blank tab.
22
22
  - Default browser is Chrome/Chromium. To try CloakBrowser, set DM_CUA_BROWSER=cloak
23
23
  and point DM_CUA_BROWSER_PATH or CLOAKBROWSER_BINARY_PATH at its Chromium binary.
24
- - screenshot defaults to ~/.dm/agent/cua/screenshots when --output is omitted.`)}function Qf(f){let J={},V=[];for(let j=0;j<f.length;j+=1){let Z=f[j];if(!Z.startsWith("--")){V.push(Z);continue}let Q=Z.indexOf("="),z=Q>2,A=z?Z.slice(2,Q):Z.slice(2);if(A==="json"||A==="help"){J[A]=!0;continue}if(z){J[A]=Z.slice(Q+1);continue}let M=f[j+1];if(typeof M>"u"||M.startsWith("--"))throw Error(`Missing value for --${A}`);J[A]=M,j+=1}return{options:J,positionals:V}}async function Xf(){let{options:f,positionals:J}=Qf(process.argv.slice(2));if(f.help||J.length===0){jf();return}let V=J[0],j=await _({action:V,tab:f.tab,url:f.url,selector:f.selector,x:f.x==null?void 0:Number(f.x),y:f.y==null?void 0:Number(f.y),text:f.text,expression:f.expression,outputPath:f.output});if(f.json){console.log(JSON.stringify(j,null,2));return}console.log(E(j))}try{await Xf()}catch(f){let J=f instanceof Error?f.message:String(f),V=!1;try{V=process.argv.slice(2).includes("--json")}catch{}if(V)console.log(JSON.stringify({status:"error",message:J},null,2));else console.error(J);process.exit(1)}
24
+ - screenshot defaults to ~/.dm/agent/cua/screenshots when --output is omitted.`)}function Y2(J){let V={},$=[];for(let Q=0;Q<J.length;Q+=1){let Z=J[Q];if(!Z.startsWith("--")){$.push(Z);continue}let X=Z.indexOf("="),z=X>2,A=z?Z.slice(2,X):Z.slice(2);if(A==="json"||A==="help"){V[A]=!0;continue}if(z){V[A]=Z.slice(X+1);continue}let H=J[Q+1];if(typeof H>"u"||H.startsWith("--"))throw Error(`Missing value for --${A}`);V[A]=H,Q+=1}return{options:V,positionals:$}}async function Z2(){let{options:J,positionals:V}=Y2(process.argv.slice(2));if(J.help||V.length===0){X2();return}let $=V[0],Q=await O({action:$,tab:J.tab,url:J.url,selector:J.selector,x:J.x==null?void 0:Number(J.x),y:J.y==null?void 0:Number(J.y),text:J.text,expression:J.expression,outputPath:J.output});if(J.json){console.log(JSON.stringify(Q,null,2));return}console.log(m(Q))}try{await Z2()}catch(J){let V=J instanceof Error?J.message:String(J),$=!1;try{$=process.argv.slice(2).includes("--json")}catch{}if($)console.log(JSON.stringify({status:"error",message:V},null,2));else console.error(V);process.exit(1)}
@@ -1,5 +1,5 @@
1
- import{existsSync as S}from"node:fs";import{Type as X}from"@sinclair/typebox";import{createHash as d}from"node:crypto";import{spawn as O}from"node:child_process";import{existsSync as T,mkdirSync as N,readFileSync as c}from"node:fs";import x from"node:http";import{homedir as P,platform as R,tmpdir as F}from"node:os";import{dirname as B,join as M,resolve as K}from"node:path";import{fileURLToPath as v}from"node:url";var g=B(v(import.meta.url)),Df=K(g,".."),p=9322,a=400,W=60000,n=/^(about:|https?:\/\/|file:|chrome-error:\/\/)/i,l={chrome:"Chrome",cloak:"CloakBrowser"};function I(f){let L=Number.parseInt(String(f??""),10);return Number.isInteger(L)&&L>1024&&L<65535?L:null}function o(f){return d("sha256").update(String(f||"default")).digest("hex")}function u(f){let L=String(f||"chrome").trim().toLowerCase();if(L==="cloakbrowser"||L==="cloak-browser")return"cloak";if(L==="chromium"||L==="google-chrome"||L==="chrome")return"chrome";return L.replace(/[^a-z0-9_.-]/g,"-")||"chrome"}function s(f,L){let $=f.DM_CUA_BROWSER_PATH||(L==="cloak"?f.DM_CUA_CLOAK_PATH||f.CLOAKBROWSER_BINARY_PATH:void 0)||f.CHROME_PATH;return $?K(String($)):void 0}function t(f=process.env,L=process.cwd()){let $=f.DM_CUA_SCOPE||f.DM_CODING_AGENT_DIR||f.PI_CODING_AGENT_DIR||L,Q=o(K(String($||"."))),Z=String(f.DM_CUA_INSTANCE_ID||Q.slice(0,12)).replace(/[^a-zA-Z0-9_.-]/g,"-"),Y=u(f.DM_CUA_BROWSER||f.DM_BROWSER_CUA_BROWSER||f.DM_CUA_BROWSER_KIND),k=f.DM_CUA_BROWSER_DISPLAY_NAME||l[Y]||Y,A=s(f,Y),H=I(f.DM_CUA_PORT)??I(f.GREEDY_SEARCH_PORT)??p+Number.parseInt(Q.slice(0,8),16)%a,J=Y==="chrome"?"chrome":Y,V=K(f.DM_CUA_PROFILE_DIR||M(F(),`dm-cua-${J}-profile-${Z}`)),q=R()==="win32"?F():"/tmp";return{instanceId:Z,browserKind:Y,browserDisplayName:k,browserExecutablePath:A,port:H,profileDir:V,pidFile:M(V,"chrome.pid"),modeFile:M(V,"chrome-mode"),pagesCache:M(V,"cdp-pages.json"),socketDir:M(q,`dm-cua-cdp-${Z}`)}}var z=t(),r=z.port,Hf=z.profileDir;function U(){return{kind:z.browserKind,displayName:z.browserDisplayName,executablePath:z.browserExecutablePath,profileDir:z.profileDir,port:z.port}}function e(){if(z.browserKind==="cloak"&&!z.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(z.browserExecutablePath&&!T(z.browserExecutablePath))throw Error(`${z.browserDisplayName} executable not found: ${z.browserExecutablePath}`)}function w(){let L=[K(g,"..","greedysearch-dm"),K(g,"..","..","greedysearch-dm")].map(($)=>({launchScript:M($,"bin","launch.mjs"),cdpScript:M($,"bin","cdp.mjs")}));for(let $ of L)if(T($.launchScript)&&T($.cdpScript))return $;return L[0]}function h(){let f=process.env.GREEDY_SEARCH_VISIBLE??process.env.DM_CUA_VISIBLE??process.env.DM_BROWSER_CUA_VISIBLE??"1";return{...process.env,...z.browserExecutablePath?{CHROME_PATH:z.browserExecutablePath}:{},CDP_PROFILE_DIR:z.profileDir.replace(/\\/g,"/"),CDP_PAGES_CACHE:z.pagesCache.replace(/\\/g,"/"),CDP_SOCKET_DIR:z.socketDir.replace(/\\/g,"/"),GREEDY_SEARCH_BROWSER_LABEL:z.browserDisplayName,GREEDY_SEARCH_PORT:String(z.port),GREEDY_SEARCH_PROFILE_DIR:z.profileDir.replace(/\\/g,"/"),GREEDY_SEARCH_PID_FILE:z.pidFile.replace(/\\/g,"/"),GREEDY_SEARCH_MODE_FILE:z.modeFile.replace(/\\/g,"/"),GREEDY_SEARCH_ALLOW_PORT_CLEANUP:process.env.GREEDY_SEARCH_ALLOW_PORT_CLEANUP??"0",GREEDY_SEARCH_VISIBLE:f}}function ff(f){return n.test(f)}function Lf(f){return f.split(/\r?\n/).map((L)=>L.trimEnd()).filter(Boolean).map((L)=>{let $=L.trim().split(/\s{2,}/).filter(Boolean);if($.length===0)return null;let Q=$[0];if($.length===1)return{target:Q,title:"",url:""};if($.length===2&&ff($[1]))return{target:Q,title:"",url:$[1]};let Z=$.at(-1)??"",Y=$.slice(1,-1).join(" ");return{target:Q,title:Y,url:Z}}).filter(Boolean)}function $f(){let f=process.env.DM_CODING_AGENT_DIR||process.env.PI_CODING_AGENT_DIR;return f?K(f):M(P(),".dm","agent")}function Jf(){let f=M($f(),"cua","screenshots");N(f,{recursive:!0});let L=new Date().toISOString().replace(/[:.]/g,"-");return M(f,`browser-${L}.png`)}function m(f,L,{env:$=process.env,timeoutMs:Q=W}={}){return new Promise((Z,Y)=>{let k=O("node",[f,...L],{env:$,stdio:["ignore","pipe","pipe"]}),A="",H="",J=!1,V=setTimeout(()=>{if(J)return;J=!0,k.kill("SIGTERM"),Y(Error(`Timed out running ${f}`))},Q);k.stdout.on("data",(q)=>{A+=q.toString()}),k.stderr.on("data",(q)=>{H+=q.toString()}),k.on("error",(q)=>{if(J)return;J=!0,clearTimeout(V),Y(q)}),k.on("close",(q)=>{if(J)return;if(J=!0,clearTimeout(V),q!==0){Y(Error((H||A||`exit ${q}`).trim()));return}Z({stdout:A,stderr:H})})})}function Qf(f,L){return new Promise(($,Q)=>{let Z=x.request({host:"127.0.0.1",port:r,path:L,method:f},(Y)=>{let k="";Y.on("data",(A)=>{k+=A.toString()}),Y.on("end",()=>{if((Y.statusCode??500)>=400){Q(Error(`Browser CDP HTTP ${Y.statusCode}: ${k.trim()}`));return}try{$(k?JSON.parse(k):{})}catch(A){Q(A)}})});Z.on("error",Q),Z.end()})}async function Vf(f="about:blank"){return Qf("PUT",`/json/new?${encodeURIComponent(f)}`)}async function D(f,L={}){let{cdpScript:$}=w();return m($,f,{env:h(),timeoutMs:L.timeoutMs??W})}async function G(){let f=await D(["list"]);return Lf(f.stdout)}function b(f,L){if(!L)return f[0]??null;let $=L.toUpperCase();return f.find((Q)=>Q.target.toUpperCase().startsWith($))??null}async function C(){let{launchScript:f,cdpScript:L}=w();if(!T(f)||!T(L))throw Error("Bundled browser helpers are missing. Rebuild DM so dm-cua and greedysearch-dm are staged together.");return{launchScript:f,cdpScript:L}}async function _(){e();let{launchScript:f}=await C();await m(f,[],{env:h(),timeoutMs:W});let L=await G();if(L.length===0)await Vf("about:blank"),L=await G();return L}async function y(f){let L=await _(),$=b(L,f);if(!$){if(f)throw Error(`No browser tab matching prefix "${f}". Run browser_cua(list) first.`);throw Error("No browser tab available after browser bootstrap.")}return{tab:$.target,page:$,pages:L}}async function i({action:f,tab:L,url:$,selector:Q,x:Z,y:Y,text:k,expression:A,outputPath:H}={}){switch(f){case"list":{let J=await _();return{status:"ok",action:f,browser:U(),pages:J}}case"navigate":{if(!$)throw Error("url is required for action=navigate");let J=await y(L),V=await D(["nav",J.tab,$],{timeoutMs:90000}),q=await G(),j=b(q,J.tab)??J.page;return{status:"ok",action:f,browser:U(),tab:J.tab,url:j?.url||$,title:j?.title||"",message:V.stdout.trim(),pages:q}}case"snapshot":{let J=await y(L),V=await D(["snap",J.tab]);return{status:"ok",action:f,browser:U(),tab:J.tab,title:J.page?.title||"",url:J.page?.url||"",snapshot:V.stdout.trim()}}case"screenshot":{let J=await y(L),V=H?K(H):Jf();N(B(V),{recursive:!0});let q=await D(["shot",J.tab,V],{timeoutMs:120000}),j=c(V).toString("base64");return{status:"ok",action:f,browser:U(),tab:J.tab,title:J.page?.title||"",url:J.page?.url||"",imagePath:V,imageBase64:j,mimeType:"image/png",message:q.stdout.trim()}}case"click":{if(!Q)throw Error("selector is required for action=click");let J=await y(L),V=await D(["click",J.tab,Q]);return{status:"ok",action:f,browser:U(),tab:J.tab,selector:Q,message:V.stdout.trim()}}case"click_xy":{if(typeof Z!=="number"||Number.isNaN(Z)||typeof Y!=="number"||Number.isNaN(Y))throw Error("x and y are required numbers for action=click_xy");let J=await y(L),V=await D(["clickxy",J.tab,String(Z),String(Y)]);return{status:"ok",action:f,browser:U(),tab:J.tab,x:Z,y:Y,message:V.stdout.trim()}}case"type":{if(k==null||k==="")throw Error("text is required for action=type");let J=await y(L),V=await D(["type",J.tab,k]);return{status:"ok",action:f,browser:U(),tab:J.tab,textLength:k.length,message:V.stdout.trim()}}case"evaluate":{if(!A)throw Error("expression is required for action=evaluate");let J=await y(L),V=await D(["eval",J.tab,A]);return{status:"ok",action:f,browser:U(),tab:J.tab,value:V.stdout.trim()}}case"stop":{let{launchScript:J}=await C();try{await D(["stop"])}catch{}let V=await m(J,["--kill"],{env:h(),timeoutMs:30000});return{status:"ok",action:f,browser:U(),message:(V.stdout||"Stopped browser CUA lane.").trim()}}default:throw Error(`Unsupported browser action: ${f}`)}}function E(f){switch(f.action){case"list":{if(!Array.isArray(f.pages)||f.pages.length===0)return"Browser CUA is ready, but no pages are open.";return["Open browser tabs:",...f.pages.map((L)=>L.url?`${L.target} ${L.title||"(untitled)"} ${L.url}`:`${L.target} ${L.title||"(untitled)"}`)].join(`
2
- `)}case"navigate":return[`Navigated browser tab ${f.tab} to ${f.url||"(unknown URL)"}`,f.title?`Title: ${f.title}`:""].filter(Boolean).join(`
3
- `);case"snapshot":return[`Browser snapshot for ${f.tab}`,f.snapshot||"(empty snapshot)"].join(`
4
- `);case"screenshot":return[`Saved browser screenshot for ${f.tab} at ${f.imagePath}`,f.title?`Title: ${f.title}`:"",f.url?`URL: ${f.url}`:""].filter(Boolean).join(`
5
- `);case"click":return`Clicked selector ${JSON.stringify(f.selector)} on browser tab ${f.tab}`;case"click_xy":return`Clicked browser tab ${f.tab} at CSS coordinates (${f.x}, ${f.y})`;case"type":return`Typed ${f.textLength} characters into browser tab ${f.tab}`;case"evaluate":return`Browser evaluate result for ${f.tab}: ${f.value}`;case"stop":return f.message||"Stopped browser CUA lane.";default:return JSON.stringify(f,null,2)}}var Xf=X.Object({action:X.Union([X.Literal("list"),X.Literal("navigate"),X.Literal("snapshot"),X.Literal("screenshot"),X.Literal("click"),X.Literal("click_xy"),X.Literal("type"),X.Literal("evaluate"),X.Literal("stop")],{description:"Browser action. Start with list, snapshot, or screenshot before mutating actions on unfamiliar pages."}),tab:X.Optional(X.String({description:"Optional target tab prefix from browser_cua(list). If omitted, the first live page in the dedicated profile is used."})),url:X.Optional(X.String({description:"URL for action=navigate."})),selector:X.Optional(X.String({description:"CSS selector for action=click."})),x:X.Optional(X.Number({description:"CSS x coordinate for action=click_xy."})),y:X.Optional(X.Number({description:"CSS y coordinate for action=click_xy."})),text:X.Optional(X.String({description:"Text for action=type."})),expression:X.Optional(X.String({description:"JavaScript expression for action=evaluate."}))},{additionalProperties:!1});function Zf(f){f.on("session_start",async(L,$)=>{let{launchScript:Q,cdpScript:Z}=w();if(!S(Q)||!S(Z))$.ui.notify("dm-cua: bundled browser helpers are missing. Rebuild DM so dm-cua and greedysearch-dm are staged together.","warning")}),f.registerTool({name:"browser_cua",label:"Browser CUA",description:"Drive a dedicated Chrome browser profile through DM's bundled browser CUA lane. Inspect first with list, snapshot, or screenshot. Fresh empty profiles auto-bootstrap a blank tab. Use navigate for deterministic URLs, click/click_xy/type for page interaction, evaluate for focused DOM reads, and stop to close the dedicated browser lane.",promptSnippet:"First-class browser CUA for Chrome. Inspect before acting. Screenshot complex pages before click/scroll loops.",parameters:Xf,async execute(L,$){try{let Q=await i($),Z=[{type:"text",text:E(Q)}];if(Q.imageBase64&&Q.mimeType)Z.push({type:"image",data:Q.imageBase64,mimeType:Q.mimeType});return{content:Z,details:Q}}catch(Q){let Z=Q instanceof Error?Q.message:String(Q);return{content:[{type:"text",text:`browser_cua failed: ${Z}`}],details:{status:"error",action:$?.action??null,message:Z}}}}})}export{Zf as default};
1
+ import{existsSync as R}from"node:fs";import{Type as Z}from"@sinclair/typebox";import{createHash as d}from"node:crypto";import{spawn as g}from"node:child_process";import{existsSync as G,mkdirSync as C,readFileSync as x}from"node:fs";import P from"node:http";import{homedir as i,platform as c,tmpdir as B}from"node:os";import{dirname as m,join as U,resolve as f}from"node:path";import{fileURLToPath as p}from"node:url";var F=m(p(import.meta.url)),K4=f(F,".."),v=9322,a=400,N=60000,l=/^(about:|https?:\/\/|file:|chrome-error:\/\/)/i,u={chrome:"Chrome",cloak:"CloakBrowser"};function h(L){let $=Number.parseInt(String(L??""),10);return Number.isInteger($)&&$>1024&&$<65535?$:null}function o(L){return d("sha256").update(String(L||"default")).digest("hex")}function n(L){let $=String(L||"chrome").trim().toLowerCase();if($==="cloakbrowser"||$==="cloak-browser")return"cloak";if($==="chromium"||$==="google-chrome"||$==="chrome")return"chrome";return $.replace(/[^a-z0-9_.-]/g,"-")||"chrome"}function s(L,$){let J=L.DM_CUA_BROWSER_PATH||($==="cloak"?L.DM_CUA_CLOAK_PATH||L.CLOAKBROWSER_BINARY_PATH:void 0)||L.CHROME_PATH;return J?f(String(J)):void 0}function t(L=process.env,$=process.cwd()){let J=L.DM_CUA_SCOPE||L.DM_CODING_AGENT_DIR||L.PI_CODING_AGENT_DIR||$,V=o(f(String(J||"."))),z=String(L.DM_CUA_INSTANCE_ID||V.slice(0,12)).replace(/[^a-zA-Z0-9_.-]/g,"-"),Y=n(L.DM_CUA_BROWSER||L.DM_BROWSER_CUA_BROWSER||L.DM_CUA_BROWSER_KIND),k=L.DM_CUA_BROWSER_DISPLAY_NAME||u[Y]||Y,M=s(L,Y),K=h(L.DM_CUA_PORT)??h(L.GREEDY_SEARCH_PORT)??v+Number.parseInt(V.slice(0,8),16)%a,Q=Y==="chrome"?"chrome":Y,X=f(L.DM_CUA_PROFILE_DIR||U(B(),`dm-cua-${Q}-profile-${z}`)),q=c()==="win32"?B():"/tmp";return{instanceId:z,browserKind:Y,browserDisplayName:k,browserExecutablePath:M,port:K,profileDir:X,pidFile:U(X,"chrome.pid"),modeFile:U(X,"chrome-mode"),pagesCache:U(X,"cdp-pages.json"),socketDir:U(q,`dm-cua-cdp-${z}`)}}var A=t(),r=A.port,f4=A.profileDir;function D(){return{kind:A.browserKind,displayName:A.browserDisplayName,executablePath:A.browserExecutablePath,profileDir:A.profileDir,port:A.port}}function e(){if(A.browserKind==="cloak"&&!A.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(A.browserExecutablePath&&!G(A.browserExecutablePath))throw Error(`${A.browserDisplayName} executable not found: ${A.browserExecutablePath}`)}function T(){let $=[f(F,"..","greedysearch-dm"),f(F,"..","..","greedysearch-dm")].map((J)=>({launchScript:U(J,"bin","launch.mjs"),cdpScript:U(J,"bin","cdp.mjs")}));for(let J of $)if(G(J.launchScript)&&G(J.cdpScript))return J;return $[0]}function L4(L=A,$=process.env){let J=$.GREEDY_SEARCH_VISIBLE??$.DM_CUA_VISIBLE??$.DM_BROWSER_CUA_VISIBLE;return{...$,...L.browserExecutablePath?{CHROME_PATH:L.browserExecutablePath}:{},CDP_PROFILE_DIR:L.profileDir.replace(/\\/g,"/"),CDP_PAGES_CACHE:L.pagesCache.replace(/\\/g,"/"),CDP_SOCKET_DIR:L.socketDir.replace(/\\/g,"/"),GREEDY_SEARCH_BROWSER_LABEL:L.browserDisplayName,GREEDY_SEARCH_PORT:String(L.port),GREEDY_SEARCH_PROFILE_DIR:L.profileDir.replace(/\\/g,"/"),GREEDY_SEARCH_PID_FILE:L.pidFile.replace(/\\/g,"/"),GREEDY_SEARCH_MODE_FILE:L.modeFile.replace(/\\/g,"/"),GREEDY_SEARCH_ALLOW_PORT_CLEANUP:$.GREEDY_SEARCH_ALLOW_PORT_CLEANUP??"0",...J===void 0?{}:{GREEDY_SEARCH_VISIBLE:J}}}function j(){return L4(A,process.env)}function $4(L){return l.test(L)}function J4(L){return L.split(/\r?\n/).map(($)=>$.trimEnd()).filter(Boolean).map(($)=>{let J=$.trim().split(/\s{2,}/).filter(Boolean);if(J.length===0)return null;let V=J[0];if(J.length===1)return{target:V,title:"",url:""};if(J.length===2&&$4(J[1]))return{target:V,title:"",url:J[1]};let z=J.at(-1)??"",Y=J.slice(1,-1).join(" ");return{target:V,title:Y,url:z}}).filter(Boolean)}function Q4(){let L=process.env.DM_CODING_AGENT_DIR||process.env.PI_CODING_AGENT_DIR;return L?f(L):U(i(),".dm","agent")}function V4(){let L=U(Q4(),"cua","screenshots");C(L,{recursive:!0});let $=new Date().toISOString().replace(/[:.]/g,"-");return U(L,`browser-${$}.png`)}function w(L,$,{env:J=process.env,timeoutMs:V=N}={}){return new Promise((z,Y)=>{let k=g("node",[L,...$],{env:J,stdio:["ignore","pipe","pipe"]}),M="",K="",Q=!1,X=setTimeout(()=>{if(Q)return;Q=!0,k.kill("SIGTERM"),Y(Error(`Timed out running ${L}`))},V);k.stdout.on("data",(q)=>{M+=q.toString()}),k.stderr.on("data",(q)=>{K+=q.toString()}),k.on("error",(q)=>{if(Q)return;Q=!0,clearTimeout(X),Y(q)}),k.on("close",(q)=>{if(Q)return;if(Q=!0,clearTimeout(X),q!==0){Y(Error((K||M||`exit ${q}`).trim()));return}z({stdout:M,stderr:K})})})}function X4(L,$){return new Promise((J,V)=>{let z=P.request({host:"127.0.0.1",port:r,path:$,method:L},(Y)=>{let k="";Y.on("data",(M)=>{k+=M.toString()}),Y.on("end",()=>{if((Y.statusCode??500)>=400){V(Error(`Browser CDP HTTP ${Y.statusCode}: ${k.trim()}`));return}try{J(k?JSON.parse(k):{})}catch(M){V(M)}})});z.on("error",V),z.end()})}async function Z4(L="about:blank"){return X4("PUT",`/json/new?${encodeURIComponent(L)}`)}async function H(L,$={}){let{cdpScript:J}=T();return w(J,L,{env:j(),timeoutMs:$.timeoutMs??N})}async function I(){let L=await H(["list"]);return J4(L.stdout)}function _(L,$){if(!$)return L[0]??null;let J=$.toUpperCase();return L.find((V)=>V.target.toUpperCase().startsWith(J))??null}async function O(){let{launchScript:L,cdpScript:$}=T();if(!G(L)||!G($))throw Error("Bundled browser helpers are missing. Rebuild DM so dm-cua and greedysearch-dm are staged together.");return{launchScript:L,cdpScript:$}}async function b(){e();let{launchScript:L}=await O();await w(L,[],{env:j(),timeoutMs:N});let $=await I();if($.length===0)await Z4("about:blank"),$=await I();return $}async function y(L){let $=await b(),J=_($,L);if(!J){if(L)throw Error(`No browser tab matching prefix "${L}". Run browser_cua(list) first.`);throw Error("No browser tab available after browser bootstrap.")}return{tab:J.target,page:J,pages:$}}async function S({action:L,tab:$,url:J,selector:V,x:z,y:Y,text:k,expression:M,outputPath:K}={}){switch(L){case"list":{let Q=await b();return{status:"ok",action:L,browser:D(),pages:Q}}case"navigate":{if(!J)throw Error("url is required for action=navigate");let Q=await y($),X=await H(["nav",Q.tab,J],{timeoutMs:90000}),q=await I(),W=_(q,Q.tab)??Q.page;return{status:"ok",action:L,browser:D(),tab:Q.tab,url:W?.url||J,title:W?.title||"",message:X.stdout.trim(),pages:q}}case"snapshot":{let Q=await y($),X=await H(["snap",Q.tab]);return{status:"ok",action:L,browser:D(),tab:Q.tab,title:Q.page?.title||"",url:Q.page?.url||"",snapshot:X.stdout.trim()}}case"screenshot":{let Q=await y($),X=K?f(K):V4();C(m(X),{recursive:!0});let q=await H(["shot",Q.tab,X],{timeoutMs:120000}),W=x(X).toString("base64");return{status:"ok",action:L,browser:D(),tab:Q.tab,title:Q.page?.title||"",url:Q.page?.url||"",imagePath:X,imageBase64:W,mimeType:"image/png",message:q.stdout.trim()}}case"click":{if(!V)throw Error("selector is required for action=click");let Q=await y($),X=await H(["click",Q.tab,V]);return{status:"ok",action:L,browser:D(),tab:Q.tab,selector:V,message:X.stdout.trim()}}case"click_xy":{if(typeof z!=="number"||Number.isNaN(z)||typeof Y!=="number"||Number.isNaN(Y))throw Error("x and y are required numbers for action=click_xy");let Q=await y($),X=await H(["clickxy",Q.tab,String(z),String(Y)]);return{status:"ok",action:L,browser:D(),tab:Q.tab,x:z,y:Y,message:X.stdout.trim()}}case"type":{if(k==null||k==="")throw Error("text is required for action=type");let Q=await y($),X=await H(["type",Q.tab,k]);return{status:"ok",action:L,browser:D(),tab:Q.tab,textLength:k.length,message:X.stdout.trim()}}case"evaluate":{if(!M)throw Error("expression is required for action=evaluate");let Q=await y($),X=await H(["eval",Q.tab,M]);return{status:"ok",action:L,browser:D(),tab:Q.tab,value:X.stdout.trim()}}case"stop":{let{launchScript:Q}=await O();try{await H(["stop"])}catch{}let X=await w(Q,["--kill"],{env:j(),timeoutMs:30000});return{status:"ok",action:L,browser:D(),message:(X.stdout||"Stopped browser CUA lane.").trim()}}default:throw Error(`Unsupported browser action: ${L}`)}}function E(L){switch(L.action){case"list":{if(!Array.isArray(L.pages)||L.pages.length===0)return"Browser CUA is ready, but no pages are open.";return["Open browser tabs:",...L.pages.map(($)=>$.url?`${$.target} ${$.title||"(untitled)"} ${$.url}`:`${$.target} ${$.title||"(untitled)"}`)].join(`
2
+ `)}case"navigate":return[`Navigated browser tab ${L.tab} to ${L.url||"(unknown URL)"}`,L.title?`Title: ${L.title}`:""].filter(Boolean).join(`
3
+ `);case"snapshot":return[`Browser snapshot for ${L.tab}`,L.snapshot||"(empty snapshot)"].join(`
4
+ `);case"screenshot":return[`Saved browser screenshot for ${L.tab} at ${L.imagePath}`,L.title?`Title: ${L.title}`:"",L.url?`URL: ${L.url}`:""].filter(Boolean).join(`
5
+ `);case"click":return`Clicked selector ${JSON.stringify(L.selector)} on browser tab ${L.tab}`;case"click_xy":return`Clicked browser tab ${L.tab} at CSS coordinates (${L.x}, ${L.y})`;case"type":return`Typed ${L.textLength} characters into browser tab ${L.tab}`;case"evaluate":return`Browser evaluate result for ${L.tab}: ${L.value}`;case"stop":return L.message||"Stopped browser CUA lane.";default:return JSON.stringify(L,null,2)}}var z4=Z.Object({action:Z.Union([Z.Literal("list"),Z.Literal("navigate"),Z.Literal("snapshot"),Z.Literal("screenshot"),Z.Literal("click"),Z.Literal("click_xy"),Z.Literal("type"),Z.Literal("evaluate"),Z.Literal("stop")],{description:"Browser action. Start with list, snapshot, or screenshot before mutating actions on unfamiliar pages."}),tab:Z.Optional(Z.String({description:"Optional target tab prefix from browser_cua(list). If omitted, the first live page in the dedicated profile is used."})),url:Z.Optional(Z.String({description:"URL for action=navigate."})),selector:Z.Optional(Z.String({description:"CSS selector for action=click."})),x:Z.Optional(Z.Number({description:"CSS x coordinate for action=click_xy."})),y:Z.Optional(Z.Number({description:"CSS y coordinate for action=click_xy."})),text:Z.Optional(Z.String({description:"Text for action=type."})),expression:Z.Optional(Z.String({description:"JavaScript expression for action=evaluate."}))},{additionalProperties:!1});function Y4(L){L.on("session_start",async($,J)=>{let{launchScript:V,cdpScript:z}=T();if(!R(V)||!R(z))J.ui.notify("dm-cua: bundled browser helpers are missing. Rebuild DM so dm-cua and greedysearch-dm are staged together.","warning")}),L.registerTool({name:"browser_cua",label:"Browser CUA",description:"Drive a dedicated Chrome browser profile through DM's bundled browser CUA lane. Inspect first with list, snapshot, or screenshot. Fresh empty profiles auto-bootstrap a blank tab. Use navigate for deterministic URLs, click/click_xy/type for page interaction, evaluate for focused DOM reads, and stop to close the dedicated browser lane.",promptSnippet:"First-class browser CUA for Chrome. Inspect before acting. Screenshot complex pages before click/scroll loops.",parameters:z4,async execute($,J){try{let V=await S(J),z=[{type:"text",text:E(V)}];if(V.imageBase64&&V.mimeType)z.push({type:"image",data:V.imageBase64,mimeType:V.mimeType});return{content:z,details:V}}catch(V){let z=V instanceof Error?V.message:String(V);return{content:[{type:"text",text:`browser_cua failed: ${z}`}],details:{status:"error",action:J?.action??null,message:z}}}}})}export{Y4 as default};
@@ -1,5 +1,5 @@
1
- import{createHash as P}from"node:crypto";import{spawn as O}from"node:child_process";import{existsSync as f,mkdirSync as w,readFileSync as b}from"node:fs";import R from"node:http";import{homedir as m,platform as S,tmpdir as y}from"node:os";import{dirname as C,join as M,resolve as U}from"node:path";import{fileURLToPath as v}from"node:url";var W=C(v(import.meta.url)),HQ=U(W,".."),x=9322,d=400,F=60000,p=/^(about:|https?:\/\/|file:|chrome-error:\/\/)/i,c={chrome:"Chrome",cloak:"CloakBrowser"};function B(Q){let V=Number.parseInt(String(Q??""),10);return Number.isInteger(V)&&V>1024&&V<65535?V:null}function u(Q){return P("sha256").update(String(Q||"default")).digest("hex")}function l(Q){let V=String(Q||"chrome").trim().toLowerCase();if(V==="cloakbrowser"||V==="cloak-browser")return"cloak";if(V==="chromium"||V==="google-chrome"||V==="chrome")return"chrome";return V.replace(/[^a-z0-9_.-]/g,"-")||"chrome"}function i(Q,V){let Z=Q.DM_CUA_BROWSER_PATH||(V==="cloak"?Q.DM_CUA_CLOAK_PATH||Q.CLOAKBROWSER_BINARY_PATH:void 0)||Q.CHROME_PATH;return Z?U(String(Z)):void 0}function n(Q=process.env,V=process.cwd()){let Z=Q.DM_CUA_SCOPE||Q.DM_CODING_AGENT_DIR||Q.PI_CODING_AGENT_DIR||V,Y=u(U(String(Z||"."))),H=String(Q.DM_CUA_INSTANCE_ID||Y.slice(0,12)).replace(/[^a-zA-Z0-9_.-]/g,"-"),J=l(Q.DM_CUA_BROWSER||Q.DM_BROWSER_CUA_BROWSER||Q.DM_CUA_BROWSER_KIND),q=Q.DM_CUA_BROWSER_DISPLAY_NAME||c[J]||J,L=i(Q,J),k=B(Q.DM_CUA_PORT)??B(Q.GREEDY_SEARCH_PORT)??x+Number.parseInt(Y.slice(0,8),16)%d,X=J==="chrome"?"chrome":J,$=U(Q.DM_CUA_PROFILE_DIR||M(y(),`dm-cua-${X}-profile-${H}`)),K=S()==="win32"?y():"/tmp";return{instanceId:H,browserKind:J,browserDisplayName:q,browserExecutablePath:L,port:k,profileDir:$,pidFile:M($,"chrome.pid"),modeFile:M($,"chrome-mode"),pagesCache:M($,"cdp-pages.json"),socketDir:M(K,`dm-cua-cdp-${H}`)}}var z=n(),a=z.port,KQ=z.profileDir;function A(){return{kind:z.browserKind,displayName:z.browserDisplayName,executablePath:z.browserExecutablePath,profileDir:z.profileDir,port:z.port}}function o(){if(z.browserKind==="cloak"&&!z.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(z.browserExecutablePath&&!f(z.browserExecutablePath))throw Error(`${z.browserDisplayName} executable not found: ${z.browserExecutablePath}`)}function E(){let V=[U(W,"..","greedysearch-dm"),U(W,"..","..","greedysearch-dm")].map((Z)=>({launchScript:M(Z,"bin","launch.mjs"),cdpScript:M(Z,"bin","cdp.mjs")}));for(let Z of V)if(f(Z.launchScript)&&f(Z.cdpScript))return Z;return V[0]}function N(){let Q=process.env.GREEDY_SEARCH_VISIBLE??process.env.DM_CUA_VISIBLE??process.env.DM_BROWSER_CUA_VISIBLE??"1";return{...process.env,...z.browserExecutablePath?{CHROME_PATH:z.browserExecutablePath}:{},CDP_PROFILE_DIR:z.profileDir.replace(/\\/g,"/"),CDP_PAGES_CACHE:z.pagesCache.replace(/\\/g,"/"),CDP_SOCKET_DIR:z.socketDir.replace(/\\/g,"/"),GREEDY_SEARCH_BROWSER_LABEL:z.browserDisplayName,GREEDY_SEARCH_PORT:String(z.port),GREEDY_SEARCH_PROFILE_DIR:z.profileDir.replace(/\\/g,"/"),GREEDY_SEARCH_PID_FILE:z.pidFile.replace(/\\/g,"/"),GREEDY_SEARCH_MODE_FILE:z.modeFile.replace(/\\/g,"/"),GREEDY_SEARCH_ALLOW_PORT_CLEANUP:process.env.GREEDY_SEARCH_ALLOW_PORT_CLEANUP??"0",GREEDY_SEARCH_VISIBLE:Q}}function s(Q){return p.test(Q)}function t(Q){return Q.split(/\r?\n/).map((V)=>V.trimEnd()).filter(Boolean).map((V)=>{let Z=V.trim().split(/\s{2,}/).filter(Boolean);if(Z.length===0)return null;let Y=Z[0];if(Z.length===1)return{target:Y,title:"",url:""};if(Z.length===2&&s(Z[1]))return{target:Y,title:"",url:Z[1]};let H=Z.at(-1)??"",J=Z.slice(1,-1).join(" ");return{target:Y,title:J,url:H}}).filter(Boolean)}function r(){let Q=process.env.DM_CODING_AGENT_DIR||process.env.PI_CODING_AGENT_DIR;return Q?U(Q):M(m(),".dm","agent")}function e(){let Q=M(r(),"cua","screenshots");w(Q,{recursive:!0});let V=new Date().toISOString().replace(/[:.]/g,"-");return M(Q,`browser-${V}.png`)}function I(Q,V,{env:Z=process.env,timeoutMs:Y=F}={}){return new Promise((H,J)=>{let q=O("node",[Q,...V],{env:Z,stdio:["ignore","pipe","pipe"]}),L="",k="",X=!1,$=setTimeout(()=>{if(X)return;X=!0,q.kill("SIGTERM"),J(Error(`Timed out running ${Q}`))},Y);q.stdout.on("data",(K)=>{L+=K.toString()}),q.stderr.on("data",(K)=>{k+=K.toString()}),q.on("error",(K)=>{if(X)return;X=!0,clearTimeout($),J(K)}),q.on("close",(K)=>{if(X)return;if(X=!0,clearTimeout($),K!==0){J(Error((k||L||`exit ${K}`).trim()));return}H({stdout:L,stderr:k})})})}function QQ(Q,V){return new Promise((Z,Y)=>{let H=R.request({host:"127.0.0.1",port:a,path:V,method:Q},(J)=>{let q="";J.on("data",(L)=>{q+=L.toString()}),J.on("end",()=>{if((J.statusCode??500)>=400){Y(Error(`Browser CDP HTTP ${J.statusCode}: ${q.trim()}`));return}try{Z(q?JSON.parse(q):{})}catch(L){Y(L)}})});H.on("error",Y),H.end()})}async function VQ(Q="about:blank"){return QQ("PUT",`/json/new?${encodeURIComponent(Q)}`)}async function D(Q,V={}){let{cdpScript:Z}=E();return I(Z,Q,{env:N(),timeoutMs:V.timeoutMs??F})}async function T(){let Q=await D(["list"]);return t(Q.stdout)}function _(Q,V){if(!V)return Q[0]??null;let Z=V.toUpperCase();return Q.find((Y)=>Y.target.toUpperCase().startsWith(Z))??null}async function h(){let{launchScript:Q,cdpScript:V}=E();if(!f(Q)||!f(V))throw Error("Bundled browser helpers are missing. Rebuild DM so dm-cua and greedysearch-dm are staged together.");return{launchScript:Q,cdpScript:V}}async function g(){o();let{launchScript:Q}=await h();await I(Q,[],{env:N(),timeoutMs:F});let V=await T();if(V.length===0)await VQ("about:blank"),V=await T();return V}async function j(Q){let V=await g(),Z=_(V,Q);if(!Z){if(Q)throw Error(`No browser tab matching prefix "${Q}". Run browser_cua(list) first.`);throw Error("No browser tab available after browser bootstrap.")}return{tab:Z.target,page:Z,pages:V}}async function LQ({action:Q,tab:V,url:Z,selector:Y,x:H,y:J,text:q,expression:L,outputPath:k}={}){switch(Q){case"list":{let X=await g();return{status:"ok",action:Q,browser:A(),pages:X}}case"navigate":{if(!Z)throw Error("url is required for action=navigate");let X=await j(V),$=await D(["nav",X.tab,Z],{timeoutMs:90000}),K=await T(),G=_(K,X.tab)??X.page;return{status:"ok",action:Q,browser:A(),tab:X.tab,url:G?.url||Z,title:G?.title||"",message:$.stdout.trim(),pages:K}}case"snapshot":{let X=await j(V),$=await D(["snap",X.tab]);return{status:"ok",action:Q,browser:A(),tab:X.tab,title:X.page?.title||"",url:X.page?.url||"",snapshot:$.stdout.trim()}}case"screenshot":{let X=await j(V),$=k?U(k):e();w(C($),{recursive:!0});let K=await D(["shot",X.tab,$],{timeoutMs:120000}),G=b($).toString("base64");return{status:"ok",action:Q,browser:A(),tab:X.tab,title:X.page?.title||"",url:X.page?.url||"",imagePath:$,imageBase64:G,mimeType:"image/png",message:K.stdout.trim()}}case"click":{if(!Y)throw Error("selector is required for action=click");let X=await j(V),$=await D(["click",X.tab,Y]);return{status:"ok",action:Q,browser:A(),tab:X.tab,selector:Y,message:$.stdout.trim()}}case"click_xy":{if(typeof H!=="number"||Number.isNaN(H)||typeof J!=="number"||Number.isNaN(J))throw Error("x and y are required numbers for action=click_xy");let X=await j(V),$=await D(["clickxy",X.tab,String(H),String(J)]);return{status:"ok",action:Q,browser:A(),tab:X.tab,x:H,y:J,message:$.stdout.trim()}}case"type":{if(q==null||q==="")throw Error("text is required for action=type");let X=await j(V),$=await D(["type",X.tab,q]);return{status:"ok",action:Q,browser:A(),tab:X.tab,textLength:q.length,message:$.stdout.trim()}}case"evaluate":{if(!L)throw Error("expression is required for action=evaluate");let X=await j(V),$=await D(["eval",X.tab,L]);return{status:"ok",action:Q,browser:A(),tab:X.tab,value:$.stdout.trim()}}case"stop":{let{launchScript:X}=await h();try{await D(["stop"])}catch{}let $=await I(X,["--kill"],{env:N(),timeoutMs:30000});return{status:"ok",action:Q,browser:A(),message:($.stdout||"Stopped browser CUA lane.").trim()}}default:throw Error(`Unsupported browser action: ${Q}`)}}function MQ(Q){switch(Q.action){case"list":{if(!Array.isArray(Q.pages)||Q.pages.length===0)return"Browser CUA is ready, but no pages are open.";return["Open browser tabs:",...Q.pages.map((V)=>V.url?`${V.target} ${V.title||"(untitled)"} ${V.url}`:`${V.target} ${V.title||"(untitled)"}`)].join(`
1
+ import{createHash as h}from"node:crypto";import{spawn as P}from"node:child_process";import{existsSync as W,mkdirSync as E,readFileSync as S}from"node:fs";import b from"node:http";import{homedir as m,platform as x,tmpdir as y}from"node:os";import{dirname as _,join as M,resolve as U}from"node:path";import{fileURLToPath as g}from"node:url";var j=_(g(import.meta.url)),KQ=U(j,".."),v=9322,d=400,I=60000,p=/^(about:|https?:\/\/|file:|chrome-error:\/\/)/i,u={chrome:"Chrome",cloak:"CloakBrowser"};function C(Q){let V=Number.parseInt(String(Q??""),10);return Number.isInteger(V)&&V>1024&&V<65535?V:null}function c(Q){return h("sha256").update(String(Q||"default")).digest("hex")}function l(Q){let V=String(Q||"chrome").trim().toLowerCase();if(V==="cloakbrowser"||V==="cloak-browser")return"cloak";if(V==="chromium"||V==="google-chrome"||V==="chrome")return"chrome";return V.replace(/[^a-z0-9_.-]/g,"-")||"chrome"}function i(Q,V){let X=Q.DM_CUA_BROWSER_PATH||(V==="cloak"?Q.DM_CUA_CLOAK_PATH||Q.CLOAKBROWSER_BINARY_PATH:void 0)||Q.CHROME_PATH;return X?U(String(X)):void 0}function a(Q=process.env,V=process.cwd()){let X=Q.DM_CUA_SCOPE||Q.DM_CODING_AGENT_DIR||Q.PI_CODING_AGENT_DIR||V,J=c(U(String(X||"."))),q=String(Q.DM_CUA_INSTANCE_ID||J.slice(0,12)).replace(/[^a-zA-Z0-9_.-]/g,"-"),z=l(Q.DM_CUA_BROWSER||Q.DM_BROWSER_CUA_BROWSER||Q.DM_CUA_BROWSER_KIND),Y=Q.DM_CUA_BROWSER_DISPLAY_NAME||u[z]||z,L=i(Q,z),k=C(Q.DM_CUA_PORT)??C(Q.GREEDY_SEARCH_PORT)??v+Number.parseInt(J.slice(0,8),16)%d,Z=z==="chrome"?"chrome":z,$=U(Q.DM_CUA_PROFILE_DIR||M(y(),`dm-cua-${Z}-profile-${q}`)),H=x()==="win32"?y():"/tmp";return{instanceId:q,browserKind:z,browserDisplayName:Y,browserExecutablePath:L,port:k,profileDir:$,pidFile:M($,"chrome.pid"),modeFile:M($,"chrome-mode"),pagesCache:M($,"cdp-pages.json"),socketDir:M(H,`dm-cua-cdp-${q}`)}}var K=a(),n=K.port,LQ=K.profileDir;function A(){return{kind:K.browserKind,displayName:K.browserDisplayName,executablePath:K.browserExecutablePath,profileDir:K.profileDir,port:K.port}}function o(){if(K.browserKind==="cloak"&&!K.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(K.browserExecutablePath&&!W(K.browserExecutablePath))throw Error(`${K.browserDisplayName} executable not found: ${K.browserExecutablePath}`)}function O(){let V=[U(j,"..","greedysearch-dm"),U(j,"..","..","greedysearch-dm")].map((X)=>({launchScript:M(X,"bin","launch.mjs"),cdpScript:M(X,"bin","cdp.mjs")}));for(let X of V)if(W(X.launchScript)&&W(X.cdpScript))return X;return V[0]}function s(Q=K,V=process.env){let X=V.GREEDY_SEARCH_VISIBLE??V.DM_CUA_VISIBLE??V.DM_BROWSER_CUA_VISIBLE;return{...V,...Q.browserExecutablePath?{CHROME_PATH:Q.browserExecutablePath}:{},CDP_PROFILE_DIR:Q.profileDir.replace(/\\/g,"/"),CDP_PAGES_CACHE:Q.pagesCache.replace(/\\/g,"/"),CDP_SOCKET_DIR:Q.socketDir.replace(/\\/g,"/"),GREEDY_SEARCH_BROWSER_LABEL:Q.browserDisplayName,GREEDY_SEARCH_PORT:String(Q.port),GREEDY_SEARCH_PROFILE_DIR:Q.profileDir.replace(/\\/g,"/"),GREEDY_SEARCH_PID_FILE:Q.pidFile.replace(/\\/g,"/"),GREEDY_SEARCH_MODE_FILE:Q.modeFile.replace(/\\/g,"/"),GREEDY_SEARCH_ALLOW_PORT_CLEANUP:V.GREEDY_SEARCH_ALLOW_PORT_CLEANUP??"0",...X===void 0?{}:{GREEDY_SEARCH_VISIBLE:X}}}function T(){return s(K,process.env)}function t(Q){return p.test(Q)}function r(Q){return Q.split(/\r?\n/).map((V)=>V.trimEnd()).filter(Boolean).map((V)=>{let X=V.trim().split(/\s{2,}/).filter(Boolean);if(X.length===0)return null;let J=X[0];if(X.length===1)return{target:J,title:"",url:""};if(X.length===2&&t(X[1]))return{target:J,title:"",url:X[1]};let q=X.at(-1)??"",z=X.slice(1,-1).join(" ");return{target:J,title:z,url:q}}).filter(Boolean)}function e(){let Q=process.env.DM_CODING_AGENT_DIR||process.env.PI_CODING_AGENT_DIR;return Q?U(Q):M(m(),".dm","agent")}function QQ(){let Q=M(e(),"cua","screenshots");E(Q,{recursive:!0});let V=new Date().toISOString().replace(/[:.]/g,"-");return M(Q,`browser-${V}.png`)}function B(Q,V,{env:X=process.env,timeoutMs:J=I}={}){return new Promise((q,z)=>{let Y=P("node",[Q,...V],{env:X,stdio:["ignore","pipe","pipe"]}),L="",k="",Z=!1,$=setTimeout(()=>{if(Z)return;Z=!0,Y.kill("SIGTERM"),z(Error(`Timed out running ${Q}`))},J);Y.stdout.on("data",(H)=>{L+=H.toString()}),Y.stderr.on("data",(H)=>{k+=H.toString()}),Y.on("error",(H)=>{if(Z)return;Z=!0,clearTimeout($),z(H)}),Y.on("close",(H)=>{if(Z)return;if(Z=!0,clearTimeout($),H!==0){z(Error((k||L||`exit ${H}`).trim()));return}q({stdout:L,stderr:k})})})}function VQ(Q,V){return new Promise((X,J)=>{let q=b.request({host:"127.0.0.1",port:n,path:V,method:Q},(z)=>{let Y="";z.on("data",(L)=>{Y+=L.toString()}),z.on("end",()=>{if((z.statusCode??500)>=400){J(Error(`Browser CDP HTTP ${z.statusCode}: ${Y.trim()}`));return}try{X(Y?JSON.parse(Y):{})}catch(L){J(L)}})});q.on("error",J),q.end()})}async function XQ(Q="about:blank"){return VQ("PUT",`/json/new?${encodeURIComponent(Q)}`)}async function D(Q,V={}){let{cdpScript:X}=O();return B(X,Q,{env:T(),timeoutMs:V.timeoutMs??I})}async function N(){let Q=await D(["list"]);return r(Q.stdout)}function w(Q,V){if(!V)return Q[0]??null;let X=V.toUpperCase();return Q.find((J)=>J.target.toUpperCase().startsWith(X))??null}async function R(){let{launchScript:Q,cdpScript:V}=O();if(!W(Q)||!W(V))throw Error("Bundled browser helpers are missing. Rebuild DM so dm-cua and greedysearch-dm are staged together.");return{launchScript:Q,cdpScript:V}}async function f(){o();let{launchScript:Q}=await R();await B(Q,[],{env:T(),timeoutMs:I});let V=await N();if(V.length===0)await XQ("about:blank"),V=await N();return V}async function G(Q){let V=await f(),X=w(V,Q);if(!X){if(Q)throw Error(`No browser tab matching prefix "${Q}". Run browser_cua(list) first.`);throw Error("No browser tab available after browser bootstrap.")}return{tab:X.target,page:X,pages:V}}async function MQ({action:Q,tab:V,url:X,selector:J,x:q,y:z,text:Y,expression:L,outputPath:k}={}){switch(Q){case"list":{let Z=await f();return{status:"ok",action:Q,browser:A(),pages:Z}}case"navigate":{if(!X)throw Error("url is required for action=navigate");let Z=await G(V),$=await D(["nav",Z.tab,X],{timeoutMs:90000}),H=await N(),F=w(H,Z.tab)??Z.page;return{status:"ok",action:Q,browser:A(),tab:Z.tab,url:F?.url||X,title:F?.title||"",message:$.stdout.trim(),pages:H}}case"snapshot":{let Z=await G(V),$=await D(["snap",Z.tab]);return{status:"ok",action:Q,browser:A(),tab:Z.tab,title:Z.page?.title||"",url:Z.page?.url||"",snapshot:$.stdout.trim()}}case"screenshot":{let Z=await G(V),$=k?U(k):QQ();E(_($),{recursive:!0});let H=await D(["shot",Z.tab,$],{timeoutMs:120000}),F=S($).toString("base64");return{status:"ok",action:Q,browser:A(),tab:Z.tab,title:Z.page?.title||"",url:Z.page?.url||"",imagePath:$,imageBase64:F,mimeType:"image/png",message:H.stdout.trim()}}case"click":{if(!J)throw Error("selector is required for action=click");let Z=await G(V),$=await D(["click",Z.tab,J]);return{status:"ok",action:Q,browser:A(),tab:Z.tab,selector:J,message:$.stdout.trim()}}case"click_xy":{if(typeof q!=="number"||Number.isNaN(q)||typeof z!=="number"||Number.isNaN(z))throw Error("x and y are required numbers for action=click_xy");let Z=await G(V),$=await D(["clickxy",Z.tab,String(q),String(z)]);return{status:"ok",action:Q,browser:A(),tab:Z.tab,x:q,y:z,message:$.stdout.trim()}}case"type":{if(Y==null||Y==="")throw Error("text is required for action=type");let Z=await G(V),$=await D(["type",Z.tab,Y]);return{status:"ok",action:Q,browser:A(),tab:Z.tab,textLength:Y.length,message:$.stdout.trim()}}case"evaluate":{if(!L)throw Error("expression is required for action=evaluate");let Z=await G(V),$=await D(["eval",Z.tab,L]);return{status:"ok",action:Q,browser:A(),tab:Z.tab,value:$.stdout.trim()}}case"stop":{let{launchScript:Z}=await R();try{await D(["stop"])}catch{}let $=await B(Z,["--kill"],{env:T(),timeoutMs:30000});return{status:"ok",action:Q,browser:A(),message:($.stdout||"Stopped browser CUA lane.").trim()}}default:throw Error(`Unsupported browser action: ${Q}`)}}function AQ(Q){switch(Q.action){case"list":{if(!Array.isArray(Q.pages)||Q.pages.length===0)return"Browser CUA is ready, but no pages are open.";return["Open browser tabs:",...Q.pages.map((V)=>V.url?`${V.target} ${V.title||"(untitled)"} ${V.url}`:`${V.target} ${V.title||"(untitled)"}`)].join(`
2
2
  `)}case"navigate":return[`Navigated browser tab ${Q.tab} to ${Q.url||"(unknown URL)"}`,Q.title?`Title: ${Q.title}`:""].filter(Boolean).join(`
3
3
  `);case"snapshot":return[`Browser snapshot for ${Q.tab}`,Q.snapshot||"(empty snapshot)"].join(`
4
4
  `);case"screenshot":return[`Saved browser screenshot for ${Q.tab} at ${Q.imagePath}`,Q.title?`Title: ${Q.title}`:"",Q.url?`URL: ${Q.url}`:""].filter(Boolean).join(`
5
- `);case"click":return`Clicked selector ${JSON.stringify(Q.selector)} on browser tab ${Q.tab}`;case"click_xy":return`Clicked browser tab ${Q.tab} at CSS coordinates (${Q.x}, ${Q.y})`;case"type":return`Typed ${Q.textLength} characters into browser tab ${Q.tab}`;case"evaluate":return`Browser evaluate result for ${Q.tab}: ${Q.value}`;case"stop":return Q.message||"Stopped browser CUA lane.";default:return JSON.stringify(Q,null,2)}}export{LQ as runBrowserAction,n as resolveBrowserRuntimeConfig,E as resolveBrowserHelperPaths,MQ as renderBrowserActionText,t as parseListOutput,g as ensureBrowserReady,KQ as BROWSER_PROFILE_DIR};
5
+ `);case"click":return`Clicked selector ${JSON.stringify(Q.selector)} on browser tab ${Q.tab}`;case"click_xy":return`Clicked browser tab ${Q.tab} at CSS coordinates (${Q.x}, ${Q.y})`;case"type":return`Typed ${Q.textLength} characters into browser tab ${Q.tab}`;case"evaluate":return`Browser evaluate result for ${Q.tab}: ${Q.value}`;case"stop":return Q.message||"Stopped browser CUA lane.";default:return JSON.stringify(Q,null,2)}}export{MQ as runBrowserAction,a as resolveBrowserRuntimeConfig,s as resolveBrowserProcessEnv,O as resolveBrowserHelperPaths,AQ as renderBrowserActionText,r as parseListOutput,f as ensureBrowserReady,LQ as BROWSER_PROFILE_DIR};
@@ -1,2 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import{spawn as K}from"node:child_process";import{existsSync as J,readFileSync as N}from"node:fs";var Q=GREEDY_PROFILE_DIR,G=ACTIVE_PORT_FILE,H=CHROME_MODE_FILE,v=process.argv.slice(2),k=null,z=v.indexOf("--mode");if(z!==-1)k=v[z+1]||null,v.splice(z,2);if(k&&!["visible","headless"].includes(k))console.error(`Invalid --mode ${k}. Use visible or headless.`),process.exit(2);if(!J(G))console.error(`GreedySearch Chrome is not running (missing ${G}). Launch with bin/visible.mjs or bin/launch.mjs.`),process.exit(1);if(k){let q=J(H)?N(H,"utf8").trim():"unknown";if(q!==k&&!(k==="visible"&&isExistingBrowserMode(q)))console.error(`GreedySearch Chrome is ${q}, not ${k}. Refusing to attach.`),process.exit(1)}var U=new URL("./cdp.mjs",import.meta.url).pathname.replace(/^\/([A-Z]:)/,"$1"),W=K(nodeRuntimeCommand(),[U,...v],{stdio:"inherit",env:{...process.env,CDP_PROFILE_DIR:Q}});W.on("close",(q)=>process.exit(q??0));
2
+ import{spawn as N}from"node:child_process";import{basename as g}from"node:path";function j(J=process.env,K=process.execPath){let q=J.GREEDY_SEARCH_NODE||J.NODE_BINARY||J.NODE;if(q?.trim())return q.trim();let $=g(K||"").toLowerCase();if($==="node"||$==="node.exe")return K;return"node"}import{existsSync as B,mkdirSync as C,readFileSync as A,writeFileSync as f}from"node:fs";import{homedir as h}from"node:os";import{join as u}from"node:path";import{tmpdir as p}from"node:os";function T(J,K=9222){let q=Number.parseInt(String(J??""),10);return Number.isInteger(q)&&q>1024&&q<65535?q:K}var m=p().replaceAll("\\","/"),i=T(process.env.GREEDY_SEARCH_PORT),Q=(process.env.GREEDY_SEARCH_PROFILE_DIR||process.env.CDP_PROFILE_DIR||`${m}/greedysearch-chrome-profile`).replaceAll("\\","/"),v=`${Q}/DevToolsActivePort`,r=process.env.GREEDY_SEARCH_PID_FILE||`${Q}/browser.pid`,s=process.env.CDP_PAGES_CACHE||`${Q}/cdp-pages.json`,x=process.env.GREEDY_SEARCH_MODE_FILE||`${Q}/browser-mode`,n=process.env.GREEDY_SEARCH_METADATA_FILE||`${Q}/browser-metadata.json`,o=process.env.GREEDY_SEARCH_LAUNCH_LOCK_FILE||`${Q}/browser-launch.lock`,I=process.env.GREEDY_SEARCH_ACTIVITY_FILE||`${Q}/browser-last-activity`,d=(process.env.CDP_SOCKET_DIR||`${Q}/cdp-sockets`).replaceAll("\\","/"),E=`${Q}/visible-recovery.jsonl`,w=u(h(),".dm"),W=u(w,"greedyconfig"),Z=["perplexity","google","chatgpt","gemini"],b="gemini";function P(){try{if(B(W)){let J=A(W,"utf8"),K=JSON.parse(J);if(Array.isArray(K.engines)&&K.engines.length>0&&K.engines.every((q)=>typeof q==="string")){let q=K.engines.filter((z)=>V[z]),$=K.engines.filter((z)=>!V[z]);if($.length>0)process.stderr.write(`[greedysearch] Warning: ignoring unknown engine(s) in ${W}: ${$.join(", ")}
3
+ [greedysearch] Available engines: ${Object.keys(V).join(", ")}
4
+ `);if(q.length>0)return q;process.stderr.write(`[greedysearch] Warning: no valid engines in ${W}, falling back to defaults: ${Z.join(", ")}
5
+ `)}}}catch{}return Z}function l(){try{if(!B(w))C(w,{recursive:!0});if(!B(W))f(W,JSON.stringify({engines:Z,synthesizer:b},null,2)+`
6
+ `,"utf8")}catch{}}l();var U=["gemini","chatgpt"];function L(){try{if(B(W)){let J=A(W,"utf8"),K=JSON.parse(J);if(typeof K.synthesizer==="string"){let q=K.synthesizer.toLowerCase();if(U.includes(q))return q;process.stderr.write(`[greedysearch] Warning: unknown synthesizer "${K.synthesizer}" in ${W}
7
+ [greedysearch] Available synthesizers: ${U.join(", ")}
8
+ [greedysearch] Falling back to default: ${b}
9
+ `)}}}catch{}return b}var V={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"},e=P();var qq=L(),Jq=Math.max(1,Number.parseInt(process.env.GREEDY_FETCH_CONCURRENCY||"5",10)||5);process.env.CDP_PROFILE_DIR=Q;import{existsSync as Y,readFileSync as O}from"node:fs";var G=Q,M=v,y=x,k=process.argv.slice(2),X=null,H=k.indexOf("--mode");if(H!==-1)X=k[H+1]||null,k.splice(H,2);if(X&&!["visible","headless"].includes(X))console.error(`Invalid --mode ${X}. Use visible or headless.`),process.exit(2);if(!Y(M))console.error(`GreedySearch Chrome is not running (missing ${M}). Launch with bin/visible.mjs or bin/launch.mjs.`),process.exit(1);if(X){let J=Y(y)?O(y,"utf8").trim():"unknown";if(J!==X)console.error(`GreedySearch Chrome is ${J}, not ${X}. Refusing to attach.`),process.exit(1)}var t=new URL("./cdp.mjs",import.meta.url).pathname.replace(/^\/([A-Z]:)/,"$1"),S=N(j(),[t,...k],{stdio:"inherit",env:{...process.env,CDP_PROFILE_DIR:G}});S.on("close",(J)=>process.exit(J??0));
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{spawn as K}from"node:child_process";import{basename as H}from"node:path";function F(q=process.env,z=process.execPath){let A=q.GREEDY_SEARCH_NODE||q.NODE_BINARY||q.NODE;if(A?.trim())return A.trim();let E=H(z||"").toLowerCase();if(E==="node"||E==="node.exe")return z;return"node"}var O=new URL("./cdp-greedy.mjs",import.meta.url).pathname.replace(/^\/([A-Z]:)/,"$1"),Q=K(F(),[O,"--mode","headless",...process.argv.slice(2)],{stdio:"inherit"});Q.on("close",(q)=>process.exit(q??0));
2
+ import{spawn as I}from"node:child_process";import{basename as H}from"node:path";function F(k=process.env,q=process.execPath){let z=k.GREEDY_SEARCH_NODE||k.NODE_BINARY||k.NODE;if(z?.trim())return z.trim();let E=H(q||"").toLowerCase();if(E==="node"||E==="node.exe")return q;return"node"}var J=new URL("./cdp-greedy.mjs",import.meta.url).pathname.replace(/^\/([A-Z]:)/,"$1"),K=I(F(),[J,"--mode","headless",...process.argv.slice(2)],{stdio:"inherit"});K.on("close",(k)=>process.exit(k??0));
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{spawn as K}from"node:child_process";import{basename as H}from"node:path";function F(q=process.env,z=process.execPath){let A=q.GREEDY_SEARCH_NODE||q.NODE_BINARY||q.NODE;if(A?.trim())return A.trim();let E=H(z||"").toLowerCase();if(E==="node"||E==="node.exe")return z;return"node"}var O=new URL("./cdp-greedy.mjs",import.meta.url).pathname.replace(/^\/([A-Z]:)/,"$1"),Q=K(F(),[O,"--mode","visible",...process.argv.slice(2)],{stdio:"inherit"});Q.on("close",(q)=>process.exit(q??0));
2
+ import{spawn as I}from"node:child_process";import{basename as H}from"node:path";function F(k=process.env,q=process.execPath){let z=k.GREEDY_SEARCH_NODE||k.NODE_BINARY||k.NODE;if(z?.trim())return z.trim();let E=H(q||"").toLowerCase();if(E==="node"||E==="node.exe")return q;return"node"}var J=new URL("./cdp-greedy.mjs",import.meta.url).pathname.replace(/^\/([A-Z]:)/,"$1"),K=I(F(),[J,"--mode","visible",...process.argv.slice(2)],{stdio:"inherit"});K.on("close",(k)=>process.exit(k??0));
@@ -1,35 +1,44 @@
1
1
  #!/usr/bin/env node
2
- import{spawn as s}from"node:child_process";import{existsSync as m,mkdirSync as t,readdirSync as e,readFileSync as w,unlinkSync as U,writeFileSync as p}from"node:fs";import l from"node:net";import{homedir as _,platform as F,tmpdir as c}from"node:os";import{join as H}from"node:path";import{basename as d}from"node:path";function I(z=process.env,$=process.execPath){let Q=z.GREEDY_SEARCH_NODE||z.NODE_BINARY||z.NODE;if(Q?.trim())return Q.trim();let Z=d($||"").toLowerCase();if(Z==="node"||Z==="node.exe")return $;return"node"}function D(z,$="localhost"){let Q=String(z||"").trim().split(/\r?\n/).map((K)=>K.trim()).filter(Boolean);if(Q.length===0)throw Error("empty DevToolsActivePort");let[Z,Y=""]=Q;if(/^wss?:\/\//i.test(Z))return Z;if(Z.includes(":"))return`ws://${Z}${Y}`;return`ws://${$}:${Z}${Y}`}var x=90000,i=30000,h=1200000,zz=20,Qz=300,v=8,a=c().replaceAll("\\","/"),C=process.env.CDP_PAGES_CACHE||`${a}/cdp-pages.json`,A=process.env.CDP_SOCKET_DIR||a;function o(z){if(F()==="win32")return`\\\\.\\pipe\\cdp-${z}`;return t(A,{recursive:!0}),`${A.replaceAll("\\","/")}/cdp-${z}.sock`}function Zz(){let z=F();if(z==="win32")return H(_(),"AppData","Local","Google","Chrome","User Data","DevToolsActivePort");if(z==="darwin")return H(_(),"Library","Application Support","Google","Chrome","DevToolsActivePort");return H(_(),".config","google-chrome","DevToolsActivePort")}function r(){for(let Q of["GREEDY_SEARCH_CDP_WS_URL","DM_CUA_CDP_WS_URL","CDP_WS_URL"])if(process.env[Q])return process.env[Q];let z=process.env.CDP_PROFILE_DIR;if(z){let Q=`${z.replaceAll("\\","/")}/DevToolsActivePort`;if(m(Q))return D(w(Q,"utf8"));throw Error(`GreedySearch DevToolsActivePort not found at ${Q}. Refusing to fall back to the main Chrome session.`)}let $=Zz();return D(w($,"utf8"))}var R=(z)=>new Promise(($)=>setTimeout($,z));function E(){if(F()==="win32")return[];let z=A;try{return e(z).filter(($)=>$.startsWith("cdp-")&&$.endsWith(".sock")).map(($)=>({targetId:$.slice(4,-5),socketPath:H(z,$)}))}catch{return[]}}function k(z,$,Q="target",Z=""){let Y=z.toUpperCase(),K=$.filter((X)=>X.toUpperCase().startsWith(Y));if(K.length===0){let X=Z?` ${Z}`:"";throw Error(`No ${Q} matching prefix "${z}".${X}`)}if(K.length>1)throw Error(`Ambiguous prefix "${z}" — matches ${K.length} ${Q}s. Use more characters.`);return K[0]}function $z(z){if(z.length===0)return v;let $=Math.max(...z.map((Q)=>Q.length));for(let Q=v;Q<=$;Q++)if(new Set(z.map((Y)=>Y.slice(0,Q).toUpperCase())).size===z.length)return Q;return $}class y{#z;#Y=0;#Q=new Map;#Z=new Map;#$=[];async connect(z){return new Promise(($,Q)=>{this.#z=new WebSocket(z),this.#z.onopen=()=>$(),this.#z.onerror=(Z)=>Q(Error(`WebSocket error: ${Z.message||Z.type}`)),this.#z.onclose=()=>this.#$.forEach((Z)=>Z()),this.#z.onmessage=(Z)=>{let Y=JSON.parse(Z.data);if(Y.id&&this.#Q.has(Y.id)){let{resolve:K,reject:X}=this.#Q.get(Y.id);if(this.#Q.delete(Y.id),Y.error)X(Error(Y.error.message));else K(Y.result)}else if(Y.method&&this.#Z.has(Y.method))for(let K of[...this.#Z.get(Y.method)])K(Y.params||{},Y)}})}send(z,$={},Q){let Z=++this.#Y;return new Promise((Y,K)=>{this.#Q.set(Z,{resolve:Y,reject:K});let X={id:Z,method:z,params:$};if(Q)X.sessionId=Q;this.#z.send(JSON.stringify(X)),setTimeout(()=>{if(this.#Q.has(Z))this.#Q.delete(Z),K(Error(`Timeout: ${z}`))},x)})}onEvent(z,$){if(!this.#Z.has(z))this.#Z.set(z,new Set);let Q=this.#Z.get(z);return Q.add($),()=>{if(Q.delete($),Q.size===0)this.#Z.delete(z)}}waitForEvent(z,$=x){let Q=!1,Z,Y;return{promise:new Promise((X,W)=>{Z=this.onEvent(z,(B)=>{if(Q)return;Q=!0,clearTimeout(Y),Z(),X(B)}),Y=setTimeout(()=>{if(Q)return;Q=!0,Z(),W(Error(`Timeout waiting for event: ${z}`))},$)}),cancel(){if(Q)return;Q=!0,clearTimeout(Y),Z?.()}}}onClose(z){this.#$.push(z)}close(){this.#z.close()}}async function T(z){let{targetInfos:$}=await z.send("Target.getTargets");return $.filter((Q)=>Q.type==="page"&&(!Q.url.startsWith("chrome://")||Q.url==="chrome://newtab/"))}function n(z){let $=$z(z.map((Q)=>Q.targetId));return z.map((Q)=>{let Z=Q.targetId.slice(0,$).padEnd($),Y=Q.title.substring(0,54).padEnd(54);return`${Z} ${Y} ${Q.url}`}).join(`
3
- `)}function Yz(z,$=!1){let Q=z.role?.value||"",Z=z.name?.value??"",Y=z.value?.value;if($&&Q==="InlineTextBox")return!1;return Q!=="none"&&Q!=="generic"&&!(Z===""&&(Y===""||Y==null))}function Kz(z,$){let Q=z.role?.value||"",Z=z.name?.value??"",Y=z.value?.value,X=`${" ".repeat(Math.min($,10))}[${Q}]`;if(Z!=="")X+=` ${Z}`;if(!(Y===""||Y==null))X+=` = ${JSON.stringify(Y)}`;return X}function Xz(z,$,Q){let Z=[],Y=new Set;for(let K of z.childIds||[]){let X=$.get(K);if(X&&!Y.has(X.nodeId))Y.add(X.nodeId),Z.push(X)}for(let K of Q.get(z.nodeId)||[])if(!Y.has(K.nodeId))Y.add(K.nodeId),Z.push(K);return Z}async function Vz(z,$,Q=!1){let{nodes:Z}=await z.send("Accessibility.getFullAXTree",{},$),Y=new Map(Z.map((V)=>[V.nodeId,V])),K=new Map;for(let V of Z){if(!V.parentId)continue;if(!K.has(V.parentId))K.set(V.parentId,[]);K.get(V.parentId).push(V)}let X=[],W=new Set;function B(V,q){if(!V||W.has(V.nodeId))return;if(W.add(V.nodeId),Yz(V,Q))X.push(Kz(V,q));for(let G of Xz(V,Y,K))B(G,q+1)}let J=Z.filter((V)=>!V.parentId||!Y.has(V.parentId));for(let V of J)B(V,0);for(let V of Z)B(V,0);return X.join(`
4
- `)}var N=new Map;async function g(z,$){let Q=[],Z=z.onEvent("Runtime.executionContextCreated",(W)=>{if(W?.context)Q.push(W.context)});await z.send("Runtime.enable",{},$),await R(100),Z(),await z.send("Runtime.disable",{},$).catch(()=>{});let Y=null;try{Y=(await z.send("Page.getFrameTree",{},$))?.frameTree?.frame?.id??null}catch{}let K=Q.filter((W)=>W.auxData?.isDefault&&W.auxData?.type==="default");return(Y&&K.find((W)=>W.auxData?.frameId===Y)||K[0]||null)?.id??null}async function L(z,$,Q){let Z=N.get($);if(Z==null){if(Z=await g(z,$),Z==null)throw Error("Failed to capture main execution context — is the page loaded?");N.set($,Z)}async function Y(K){let X=await z.send("Runtime.evaluate",{expression:Q,contextId:K,returnByValue:!0,awaitPromise:!0},$);if(X.exceptionDetails)throw Error(X.exceptionDetails.text||X.exceptionDetails.exception?.description);let W=X.result.value;return typeof W==="object"?JSON.stringify(W,null,2):String(W??"")}try{return await Y(Z)}catch(K){if(N.delete($),Z=await g(z,$),Z==null)throw Error("Failed to re-capture execution context after navigation");return N.set($,Z),Y(Z)}}async function Wz(z,$,Q){let Z=1;try{let W=await z.send("Page.getLayoutMetrics",{},$);Z=W.visualViewport?.clientWidth?W.cssVisualViewport?.clientWidth?Math.round(W.visualViewport.clientWidth/W.cssVisualViewport.clientWidth*100)/100:1:1;let{deviceScaleFactor:B}=await z.send("Emulation.getDeviceMetricsOverride",{},$).catch(()=>({}));if(B)Z=B}catch{}if(Z===1)try{let W=await L(z,$,"window.devicePixelRatio"),B=Number.parseFloat(W);if(B>0)Z=B}catch{}await z.send("Page.enable",{},$).catch(()=>{}),await z.send("Page.bringToFront",{},$).catch(()=>{});let{data:Y}=await z.send("Page.captureScreenshot",{format:"png"},$),K=Q||H(c(),"screenshot.png");p(K,Buffer.from(Y,"base64"));let X=[K];if(X.push(`Screenshot saved. Device pixel ratio (DPR): ${Z}`),X.push("Coordinate mapping:"),X.push(` Screenshot pixels → CSS pixels (for CDP Input events): divide by ${Z}`),X.push(` e.g. screenshot point (${Math.round(100*Z)}, ${Math.round(200*Z)}) → CSS (100, 200) → use clickxy <target> 100 200`),Z!==1)X.push(` On this ${Z}x display: CSS px = screenshot px / ${Z} ≈ screenshot px × ${Math.round(100/Z)/100}`);return X.join(`
5
- `)}async function qz(z,$,Q){let Z=Q?`document.querySelector(${JSON.stringify(Q)})?.outerHTML || 'Element not found'`:"document.documentElement.outerHTML";return L(z,$,Z)}async function Gz(z,$,Q=i){let Z=Date.now()+Q,Y="",K;while(Date.now()<Z){try{let X=await L(z,$,"document.readyState");if(Y=X,X==="complete")return}catch(X){K=X}await R(200)}if(Y)throw Error(`Timed out waiting for navigation to finish (last readyState: ${Y})`);if(K)throw Error(`Timed out waiting for navigation to finish (${K.message})`);throw Error("Timed out waiting for navigation to finish")}async function Jz(z,$,Q){await z.send("Page.enable",{},$);let Z=z.waitForEvent("Page.loadEventFired",i),Y=await z.send("Page.navigate",{url:Q},$);if(Y.errorText)throw Z.cancel(),Error(Y.errorText);if(Y.loaderId)await Z.promise;else Z.cancel();return await Gz(z,$,5000),`Navigated to ${Q}`}async function Bz(z,$){let Q=await L(z,$,`JSON.stringify(performance.getEntriesByType('resource').map(e => ({
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 => ({
6
15
  name: e.name.substring(0, 120), type: e.initiatorType,
7
16
  duration: Math.round(e.duration), size: e.transferSize
8
- })))`);return JSON.parse(Q).map((Z)=>`${String(Z.duration).padStart(5)}ms ${String(Z.size||"?").padStart(8)}B ${Z.type.padEnd(8)} ${Z.name}`).join(`
9
- `)}async function Lz(z,$,Q){if(!Q)throw Error("CSS selector required");let Z=`
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=`
10
19
  (function() {
11
- const el = document.querySelector(${JSON.stringify(Q)});
12
- if (!el) return { ok: false, error: 'Element not found: ' + ${JSON.stringify(Q)} };
20
+ const el = document.querySelector(${JSON.stringify($)});
21
+ if (!el) return { ok: false, error: 'Element not found: ' + ${JSON.stringify($)} };
13
22
  el.scrollIntoView({ block: 'center' });
14
23
  el.click();
15
24
  return { ok: true, tag: el.tagName, text: el.textContent.trim().substring(0, 80) };
16
25
  })()
17
- `,Y=await L(z,$,Z),K=JSON.parse(Y);if(!K.ok)throw Error(K.error);return`Clicked <${K.tag}> "${K.text}"`}async function Oz(z,$,Q,Z){let Y=Number.parseFloat(Q),K=Number.parseFloat(Z);if(Number.isNaN(Y)||Number.isNaN(K))throw Error("x and y must be numbers (CSS pixels)");let X={x:Y,y:K,button:"left",clickCount:1,modifiers:0};return await z.send("Input.dispatchMouseEvent",{...X,type:"mouseMoved"},$),await z.send("Input.dispatchMouseEvent",{...X,type:"mousePressed"},$),await R(50),await z.send("Input.dispatchMouseEvent",{...X,type:"mouseReleased"},$),`Clicked at CSS (${Y}, ${K})`}async function Hz(z,$,Q){if(Q==null||Q==="")throw Error("text required");return await z.send("Input.insertText",{text:Q},$),`Typed ${Q.length} characters`}async function Uz(z,$,Q,Z=1500){if(!Q)throw Error("CSS selector required");Z=Math.min(Math.max(Number.parseInt(Z,10)||1500,100),30000);let Y=0,K=Date.now()+300000;while(Date.now()<K){if(await L(z,$,`!!document.querySelector(${JSON.stringify(Q)})`)!=="true")break;let W=`
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=`
18
27
  (function() {
19
- const el = document.querySelector(${JSON.stringify(Q)});
28
+ const el = document.querySelector(${JSON.stringify($)});
20
29
  if (!el) return false;
21
30
  el.scrollIntoView({ block: 'center' });
22
31
  el.click();
23
32
  return true;
24
33
  })()
25
- `;if(await L(z,$,W)!=="true")break;Y++,await R(Z)}return`Clicked "${Q}" ${Y} time(s) until it disappeared`}async function bz(z,$,Q,Z){if(!Q)throw Error('CDP method required (e.g. "DOM.getDocument")');let Y={};if(Z)try{Y=JSON.parse(Z)}catch{throw Error(`Invalid JSON params: ${Z}`)}let K=await z.send(Q,Y,$);return JSON.stringify(K,null,2)}async function Rz(z,$,Q){if(!$)throw Error("Browser CDP method required");let Z={};if(Q)try{Z=JSON.parse(Q)}catch{throw Error(`Invalid JSON params: ${Q}`)}let Y=await z.send($,Z);return JSON.stringify(Y,null,2)}async function Nz(z){let $=o(z),Q=new y;try{await Q.connect(r())}catch(V){process.stderr.write(`Daemon: cannot connect to Chrome: ${V.message}
26
- `),process.exit(1)}let Z;try{Z=(await Q.send("Target.attachToTarget",{targetId:z,flatten:!0})).sessionId}catch(V){process.stderr.write(`Daemon: attach failed: ${V.message}
27
- `),Q.close(),process.exit(1)}let Y=!0;function K(){if(!Y)return;Y=!1,J.close();try{U($)}catch{}Q.close(),process.exit(0)}Q.onEvent("Target.targetDestroyed",(V)=>{if(V.targetId===z)K()}),Q.onEvent("Target.detachedFromTarget",(V)=>{if(V.sessionId===Z)K()}),Q.onClose(()=>K()),process.on("SIGTERM",K),process.on("SIGINT",K);let X=setTimeout(K,h);function W(){clearTimeout(X),X=setTimeout(K,h)}async function B({cmd:V,args:q}){W();try{let G;switch(V){case"list":{let O=await T(Q);G=n(O);break}case"list_raw":{let O=await T(Q);G=JSON.stringify(O);break}case"snap":case"snapshot":G=await Vz(Q,Z,!0);break;case"eval":G=await L(Q,Z,q[0]);break;case"shot":case"screenshot":G=await Wz(Q,Z,q[0]);break;case"html":G=await qz(Q,Z,q[0]);break;case"nav":case"navigate":G=await Jz(Q,Z,q[0]);break;case"net":case"network":G=await Bz(Q,Z);break;case"click":G=await Lz(Q,Z,q[0]);break;case"clickxy":G=await Oz(Q,Z,q[0],q[1]);break;case"type":G=await Hz(Q,Z,q[0]);break;case"loadall":G=await Uz(Q,Z,q[0],q[1]?Number.parseInt(q[1],10):1500);break;case"evalraw":G=await bz(Q,Z,q[0],q[1]);break;case"browse":case"browserraw":G=await Rz(Q,q[0],q[1]);break;case"stop":return{ok:!0,result:"",stopAfter:!0};default:return{ok:!1,error:`Unknown command: ${V}`}}return{ok:!0,result:G??""}}catch(G){return{ok:!1,error:G.message}}}let J=l.createServer((V)=>{let q="";V.on("data",(G)=>{q+=G.toString();let O=q.split(`
28
- `);q=O.pop();for(let S of O){if(!S.trim())continue;let M;try{M=JSON.parse(S)}catch{V.write(`${JSON.stringify({ok:!1,error:"Invalid JSON request",id:null})}
29
- `);continue}B(M).then((P)=>{let f=`${JSON.stringify({...P,id:M.id})}
30
- `;if(P.stopAfter)V.end(f,K);else V.write(f)})}})});try{U($)}catch{}J.listen($)}function b(z){return new Promise(($,Q)=>{let Z=l.connect(z);Z.on("connect",()=>$(Z)),Z.on("error",Q)})}async function jz(z){let $=o(z);try{return await b($)}catch{}try{U($)}catch{}s(I(),[process.argv[1],"_daemon",z],{detached:!0,stdio:"ignore"}).unref();for(let Z=0;Z<zz;Z++){await R(Qz);try{return await b($)}catch{}}throw Error("Daemon failed to start — did you click Allow in Chrome?")}function j(z,$){return new Promise((Q,Z)=>{let Y="",K=!1,X=()=>{z.off("data",W),z.off("error",B),z.off("end",J),z.off("close",V)},W=(q)=>{Y+=q.toString();let G=Y.indexOf(`
31
- `);if(G===-1)return;K=!0,X(),Q(JSON.parse(Y.slice(0,G))),z.end()},B=(q)=>{if(K)return;K=!0,X(),Z(q)},J=()=>{if(K)return;K=!0,X(),Z(Error("Connection closed before response"))},V=()=>{if(K)return;K=!0,X(),Z(Error("Connection closed before response"))};z.on("data",W),z.on("error",B),z.on("end",J),z.on("close",V),$.id=1,z.write(`${JSON.stringify($)}
32
- `)})}function Mz(){return E()[0]?.socketPath||null}async function Dz(z){let $=E();if(z){let Q=k(z,$.map((Y)=>Y.targetId),"daemon"),Z=$.find((Y)=>Y.targetId===Q);try{let Y=await b(Z.socketPath);await j(Y,{cmd:"stop"})}catch{try{U(Z.socketPath)}catch{}}return}for(let Q of $)try{let Z=await b(Q.socketPath);await j(Z,{cmd:"stop"})}catch{try{U(Q.socketPath)}catch{}}}var u=`cdp - lightweight Chrome DevTools Protocol CLI (no Puppeteer)
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)
33
42
 
34
43
  Usage: cdp <command> [args]
35
44
 
@@ -47,5 +56,5 @@ Usage: cdp <command> [args]
47
56
  evalraw <target> <method> [json] Send a raw CDP command; returns JSON result
48
57
  browse <target> <method> [json] Send a browser-level CDP command (no session)
49
58
  stop [target] Stop daemon(s)
50
- `,_z=new Set(["snap","snapshot","eval","shot","screenshot","html","nav","navigate","net","network","click","clickxy","type","loadall","evalraw","browse","browserraw"]);async function Cz(){let[z,...$]=process.argv.slice(2);if(z==="_daemon"){await Nz($[0]);return}if(!z||z==="help"||z==="--help"||z==="-h")console.log(u),process.exit(0);if(z==="list"||z==="ls"){let J,V=Mz();if(V)try{let q=await b(V),G=await j(q,{cmd:"list_raw"});if(G.ok)J=JSON.parse(G.result)}catch{}if(!J){let q=new y;await q.connect(r()),J=await T(q),q.close()}p(C,JSON.stringify(J)),console.log(n(J)),setTimeout(()=>process.exit(0),100);return}if(z==="stop"){await Dz($[0]);return}if(!_z.has(z))console.error(`Unknown command: ${z}
51
- `),console.log(u),process.exit(1);let Q=$[0];if(!Q)console.error('Error: target ID required. Run "cdp list" first.'),process.exit(1);let Z,Y=E().map((J)=>J.targetId);if(Y.filter((J)=>J.toUpperCase().startsWith(Q.toUpperCase())).length>0)Z=k(Q,Y,"daemon");else{if(!m(C))console.error('No page list cached. Run "cdp list" first.'),process.exit(1);let J=JSON.parse(w(C,"utf8"));Z=k(Q,J.map((V)=>V.targetId),"target",'Run "cdp list".')}let X=await jz(Z),W=$.slice(1);if(z==="eval"){let J=W.join(" ");if(!J)console.error("Error: expression required"),process.exit(1);W[0]=J}else if(z==="type"){let J=W.join(" ");if(W[0]==="--stdin")J=await new Promise((V)=>{let q="";if(process.stdin.setEncoding("utf8"),process.stdin.on("data",(G)=>q+=G),process.stdin.on("end",()=>V(q)),process.stdin.isTTY)V("")});if(!J)console.error("Error: text required"),process.exit(1);W[0]=J}else if(z==="evalraw"){if(!W[0])console.error("Error: CDP method required"),process.exit(1);if(W.length>2)W[1]=W.slice(1).join(" ")}else if(z==="browse"||z==="browserraw"){if(!W[0])console.error("Error: browser CDP method required"),process.exit(1);if(W.length>2)W[1]=W.slice(1).join(" ")}if((z==="nav"||z==="navigate")&&!W[0])console.error("Error: URL required"),process.exit(1);let B=await j(X,{cmd:z,args:W});if(B.ok){if(B.result)console.log(B.result)}else console.error("Error:",B.error),process.exitCode=1}Cz().catch((z)=>{console.error(z.message),process.exit(1)});
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)});