@duckmind/dm-darwin-arm64 0.52.3 → 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.
- package/dm +0 -0
- package/extensions/.dm-extensions.json +15 -8
- package/extensions/dm-cua/bin/browser-cua.mjs +6 -6
- package/extensions/dm-cua/index.js +5 -5
- package/extensions/dm-cua/src/browser-cua-lib.mjs +2 -2
- package/extensions/greedysearch-dm/bin/cdp-greedy.mjs +8 -1
- package/extensions/greedysearch-dm/bin/cdp-headless.mjs +1 -1
- package/extensions/greedysearch-dm/bin/cdp-visible.mjs +1 -1
- package/extensions/greedysearch-dm/bin/cdp.mjs +29 -20
- package/extensions/greedysearch-dm/bin/gschrome.mjs +1 -10
- package/extensions/greedysearch-dm/bin/kill-visible.mjs +1 -1
- package/extensions/greedysearch-dm/bin/launch-visible.mjs +1 -4
- package/extensions/greedysearch-dm/bin/launch.mjs +9 -5
- package/extensions/greedysearch-dm/bin/mcp.mjs +375 -0
- package/extensions/greedysearch-dm/bin/search.mjs +310 -156
- package/extensions/greedysearch-dm/bin/visible.mjs +1 -1
- package/extensions/greedysearch-dm/extractors/bing-copilot.mjs +205 -68
- package/extensions/greedysearch-dm/extractors/chatgpt.mjs +229 -140
- package/extensions/greedysearch-dm/extractors/common.mjs +160 -50
- package/extensions/greedysearch-dm/extractors/consensus.mjs +225 -92
- package/extensions/greedysearch-dm/extractors/consent.mjs +44 -17
- package/extensions/greedysearch-dm/extractors/gemini.mjs +275 -136
- package/extensions/greedysearch-dm/extractors/google-ai.mjs +172 -64
- package/extensions/greedysearch-dm/extractors/logically.mjs +183 -69
- package/extensions/greedysearch-dm/extractors/perplexity.mjs +331 -65
- package/extensions/greedysearch-dm/extractors/semantic-scholar.mjs +118 -41
- package/extensions/greedysearch-dm/index.js +20 -17
- package/extensions/greedysearch-dm/package.json +8 -3
- package/extensions/greedysearch-dm/skills/greedy-search/skill.md +8 -8
- package/extensions/greedysearch-dm/src/fetcher.mjs +2 -2
- package/extensions/greedysearch-dm/src/formatters/results.js +8 -6
- package/extensions/greedysearch-dm/src/github.mjs +7 -7
- package/extensions/greedysearch-dm/src/reddit.mjs +1 -1
- package/extensions/greedysearch-dm/src/search/browser-lifecycle.mjs +10 -9
- package/extensions/greedysearch-dm/src/search/challenge-detect.mjs +48 -0
- package/extensions/greedysearch-dm/src/search/chrome.mjs +129 -53
- package/extensions/greedysearch-dm/src/search/constants.mjs +8 -8
- package/extensions/greedysearch-dm/src/search/engines.mjs +9 -9
- package/extensions/greedysearch-dm/src/search/fetch-source.mjs +154 -77
- package/extensions/greedysearch-dm/src/search/minimize.mjs +1 -0
- package/extensions/greedysearch-dm/src/search/paths.mjs +1 -1
- package/extensions/greedysearch-dm/src/search/pdf.mjs +1 -1
- package/extensions/greedysearch-dm/src/search/port-pid.mjs +1 -0
- package/extensions/greedysearch-dm/src/search/progress.mjs +2 -0
- package/extensions/greedysearch-dm/src/search/recovery.mjs +1 -1
- package/extensions/greedysearch-dm/src/search/research.mjs +262 -167
- package/extensions/greedysearch-dm/src/search/scale-aware.mjs +11 -0
- package/extensions/greedysearch-dm/src/search/simple-research.mjs +827 -0
- package/extensions/greedysearch-dm/src/search/synthesis-runner.mjs +11 -11
- package/extensions/greedysearch-dm/src/search/synthesis.mjs +11 -11
- package/extensions/greedysearch-dm/src/tools/greedy-search-handler.js +23 -20
- package/extensions/greedysearch-dm/src/tools/shared.js +10 -9
- package/extensions/greedysearch-dm/src/utils/node-runtime.mjs +1 -1
- package/package.json +1 -1
- package/extensions/greedysearch-dm/src/search/cdp-endpoint.mjs +0 -3
- 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-
|
|
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,34 +379,38 @@
|
|
|
376
379
|
"src/github.mjs",
|
|
377
380
|
"src/reddit.mjs",
|
|
378
381
|
"src/search/browser-lifecycle.mjs",
|
|
379
|
-
"src/search/
|
|
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",
|
|
389
|
+
"src/search/minimize.mjs",
|
|
386
390
|
"src/search/output.mjs",
|
|
387
391
|
"src/search/paths.mjs",
|
|
388
392
|
"src/search/pdf.mjs",
|
|
393
|
+
"src/search/port-pid.mjs",
|
|
394
|
+
"src/search/progress.mjs",
|
|
389
395
|
"src/search/query.mjs",
|
|
390
396
|
"src/search/recovery.mjs",
|
|
391
397
|
"src/search/research.mjs",
|
|
398
|
+
"src/search/scale-aware.mjs",
|
|
399
|
+
"src/search/simple-research.mjs",
|
|
392
400
|
"src/search/sources.mjs",
|
|
393
401
|
"src/search/synthesis-runner.mjs",
|
|
394
402
|
"src/search/synthesis.mjs",
|
|
395
403
|
"src/tools/greedy-search-handler.js",
|
|
396
404
|
"src/tools/shared.js",
|
|
397
405
|
"src/types.js",
|
|
398
|
-
"src/utils/browser-executable.mjs",
|
|
399
406
|
"src/utils/content.mjs",
|
|
400
407
|
"src/utils/helpers.js",
|
|
401
408
|
"src/utils/node-runtime.mjs",
|
|
402
409
|
"src/utils/system-cmds.mjs"
|
|
403
410
|
],
|
|
404
|
-
"file_count":
|
|
405
|
-
"before_bytes":
|
|
406
|
-
"after_bytes":
|
|
411
|
+
"file_count": 58,
|
|
412
|
+
"before_bytes": 609953,
|
|
413
|
+
"after_bytes": 1166455,
|
|
407
414
|
"externals": [
|
|
408
415
|
"@duckmind/dm-agent-core",
|
|
409
416
|
"@duckmind/dm-ai",
|
|
@@ -628,8 +635,8 @@
|
|
|
628
635
|
"src/browser-cua-lib.mjs"
|
|
629
636
|
],
|
|
630
637
|
"file_count": 3,
|
|
631
|
-
"before_bytes":
|
|
632
|
-
"after_bytes":
|
|
638
|
+
"before_bytes": 22185,
|
|
639
|
+
"after_bytes": 32604,
|
|
633
640
|
"externals": [
|
|
634
641
|
"@duckmind/dm-agent-core",
|
|
635
642
|
"@duckmind/dm-ai",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{createHash as
|
|
3
|
-
`)}case"navigate":return[`Navigated browser tab ${
|
|
4
|
-
`);case"snapshot":return[`Browser snapshot for ${
|
|
5
|
-
`);case"screenshot":return[`Saved browser screenshot for ${
|
|
6
|
-
`);case"click":return`Clicked selector ${JSON.stringify(
|
|
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
|
|
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
|
|
2
|
-
`)}case"navigate":return[`Navigated browser tab ${
|
|
3
|
-
`);case"snapshot":return[`Browser snapshot for ${
|
|
4
|
-
`);case"screenshot":return[`Saved browser screenshot for ${
|
|
5
|
-
`);case"click":return`Clicked selector ${JSON.stringify(
|
|
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
|
|
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{
|
|
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
|
|
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
|
|
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
|
|
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
|
|
3
|
-
|
|
4
|
-
`)
|
|
5
|
-
`)}
|
|
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(
|
|
9
|
-
`)}async function
|
|
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(
|
|
12
|
-
if (!el) return { ok: false, error: 'Element not found: ' + ${JSON.stringify(
|
|
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
|
-
`,
|
|
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(
|
|
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
|
|
26
|
-
`),process.exit(1)}let
|
|
27
|
-
`)
|
|
28
|
-
`);
|
|
29
|
-
`);continue}
|
|
30
|
-
`;if(
|
|
31
|
-
`);if(
|
|
32
|
-
`)})}function
|
|
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
|
-
`,
|
|
51
|
-
`),console.log(
|
|
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)});
|