everclaw 0.3.16 → 0.3.18

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.
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+ // @bun
3
+ import{createServer as R}from"net";import{unlink as U,chmod as v,existsSync as N}from"fs";import q from"ws";var F={x:0,y:0};function Z(){return{...F}}function $(u){F={...u}}function T(u,n){return u+Math.random()*(n-u)}function x(u,n){return Math.floor(T(u,n+1))}function B(u=0,n=1){let f=Math.random(),t=Math.random();return Math.sqrt(-2*Math.log(f))*Math.cos(2*Math.PI*t)*n+u}function j(u,n,f){let t=f??x(8,15),o=n.x-u.x,h=n.y-u.y,b=Math.sqrt(o*o+h*h),i=-h/(b||1),M=o/(b||1),p=b*0.3,y=B(0,p),P=B(0,p),c=T(0.2,0.4),w=T(0.6,0.8),C={x:u.x+o*c+i*y,y:u.y+h*c+M*y},E={x:u.x+o*w+i*P,y:u.y+h*w+M*P},z=[];for(let L=0;L<=t;L++){let D=L/t,S=I(u,C,E,n,D);z.push(S)}return z}function I(u,n,f,t,o){let h=o*o,b=h*o,i=1-o,M=i*i,p=M*i;return{x:p*u.x+3*M*o*n.x+3*i*h*f.x+b*t.x,y:p*u.y+3*M*o*n.y+3*i*h*f.y+b*t.y}}function A(u,n){let f=B(0,n/2),t=B(0,n/2);return{x:u.x+f,y:u.y+t}}function O(u){let n=u.x+u.width/2,f=u.y+u.height/2,t=u.width/6,o=u.height/6,h=B(0,t),b=B(0,o),i=u.width/2-2,M=u.height/2-2;return h=Math.max(-i,Math.min(i,h)),b=Math.max(-M,Math.min(M,b)),{x:n+h,y:f+b}}function _(u,n){let f=[],t=[],o=Z();if(!(n?.skipCursorMovement??!1)&&(o.x!==u.x||o.y!==u.y)){let p=j(o,u);for(let y of p){let P=A(y,1);f.push({type:"mouseMoved",x:P.x,y:P.y}),t.push(x(5,15))}}let b=A(u,2),i=x(30,80);f.push({type:"mousePressed",x:b.x,y:b.y,button:"left",clickCount:1}),t.push(i);let M=x(50,150);return f.push({type:"mouseReleased",x:b.x,y:b.y,button:"left",clickCount:1}),t.push(M),$(b),{events:f,delays:t}}function X(u,n){let f=x(40,120);if(u===" ")f=x(20,60);else if(u==="."||u===","||u==="!"||u==="?")f=x(100,200);else if(u===`
4
+ `)f=x(150,300);if(n!==null){let t=(C)=>C===C.toUpperCase()&&C!==C.toLowerCase(),o=t(n),h=t(u);if(o!==h)f+=x(30,80);let b=new Set(["q","w","e","r","t","a","s","d","f","g","z","x","c","v","b"]),i=new Set(["y","u","i","o","p","h","j","k","l","n","m"]),M=n.toLowerCase(),p=u.toLowerCase(),y=b.has(M),P=i.has(p),c=i.has(M),w=b.has(p);if(y&&P||c&&w)f=Math.max(30,f-x(10,30))}if(Math.random()<0.03)f+=x(200,500);return f}function V(u,n){if(n<5)return!1;if(u<2||u>=n-2)return!1;return Math.random()<0.02}function G(u,n){let f=n?.speed??"normal",t=f==="slow"?1.5:f==="fast"?0.5:1,o=[],h=[];for(let b=0;b<u.length;b++){let i=u.charAt(b),M=b>0?u.charAt(b-1):null;if(V(b,u.length)){let p={a:["q","s","z","w"],b:["v","n","h","g"],c:["x","v","d","f"],d:["s","f","e","r","c"],e:["w","r","d","s"],f:["d","g","r","t","v"],g:["f","h","t","y","b"],h:["g","j","y","u","n"],i:["u","o","j","k"],j:["h","k","u","i","m"],k:["j","l","i","o",","],l:["k",";","o","p","."],m:["n",",","j","k"],n:["b","m","h","j"],o:["i","p","k","l"],p:["o","[","l",";"],q:["w","a","s"],r:["e","t","f","d"],s:["a","d","w","e","x","z"],t:["r","y","g","f"],u:["y","i","h","j"],v:["c","b","f","g"],w:["q","e","a","s"],x:["z","c","s","d"],y:["t","u","g","h"],z:["a","x","s"]},y=i.toLowerCase(),P=p[y];if(P&&P.length>0){let c=P[Math.floor(Math.random()*P.length)],w=i===y?c:c.toUpperCase();o.push(w),h.push(Math.round(X(w,M)*t)),o.push("\b"),h.push(x(80,150)),o.push(i),h.push(Math.round(X(i,w)*t))}else o.push(i),h.push(Math.round(X(i,M)*t))}else o.push(i),h.push(Math.round(X(i,M)*t))}return{chars:o,delays:h}}function K(u){return Math.round(u*(0.7+Math.random()*0.6))}function W(){let u=process.argv.slice(2),n="",f="",t="",o=0;for(let h=0;h<u.length;h+=2){let b=u[h],i=u[h+1];switch(b){case"--target":n=i??"";break;case"--ws-url":f=i??"";break;case"--socket":t=i??"";break;case"--idle-timeout":o=parseInt(i??"0",10);break}}if(!n||!f||!t||!o)console.error("Missing required arguments"),console.error("Usage: daemon-worker.ts --target <id> --ws-url <url> --socket <path> --idle-timeout <ms>"),process.exit(1);return{targetId:n,wsUrl:f,socketPath:t,idleTimeoutMs:o}}class J{args;ws=null;server=null;clientSocket=null;cdpCommandId=1;pendingCdpCommands=new Map;idleTimer=null;ndjsonBuffer="";running=!1;cleanupHandlers=[];stealthConfig=null;stealthScriptsInjected=!1;constructor(u){this.args=u}async start(){await this.connectToChrome(),await this.attachToTarget(),await this.createSocketServer(),this.resetIdleTimer(),this.setupSignalHandlers(),this.setupCdpListeners(),this.running=!0,console.log("READY")}async connectToChrome(){return new Promise((u,n)=>{this.ws=new q(this.args.wsUrl),this.ws.on("open",()=>{u()}),this.ws.on("message",(f)=>{let t=Array.isArray(f)?Buffer.concat(f).toString("utf8"):f.toString("utf8");this.handleCdpMessage(t)}),this.ws.on("close",()=>{this.exit("Chrome WebSocket closed")}),this.ws.on("error",(f)=>{if(!this.running)n(new Error(`Failed to connect to Chrome: ${f.message}`));else this.exit(`Chrome WebSocket error: ${f.message}`)})})}async attachToTarget(){await this.sendCdpCommand("Target.attachToTarget",{targetId:this.args.targetId,flatten:!0})}async createSocketServer(){return new Promise((u,n)=>{this.server=R((f)=>{if(this.clientSocket){f.destroy();return}this.clientSocket=f,f.on("data",(t)=>{this.handleSocketData(t),this.resetIdleTimer()}),f.on("close",()=>{this.clientSocket=null}),f.on("error",()=>{this.clientSocket=null})}),this.server.on("error",n),this.server.listen(this.args.socketPath,()=>{if(process.platform!=="win32")try{k(this.args.socketPath,384)}catch{}u()})})}setupSignalHandlers(){let u=()=>{this.exit("Received termination signal")};process.on("SIGTERM",u),process.on("SIGINT",u),this.cleanupHandlers.push(()=>{process.off("SIGTERM",u),process.off("SIGINT",u)})}setupCdpListeners(){}handleCdpMessage(u){let n;try{n=JSON.parse(u)}catch{return}if(n.method==="Target.targetDestroyed"){if(n.params?.targetId===this.args.targetId){this.exit("Target destroyed");return}}if(typeof n.id==="number"){let f=this.pendingCdpCommands.get(n.id);if(f)if(this.pendingCdpCommands.delete(n.id),n.error)f.reject(new Error(`CDP error ${n.error.code}: ${n.error.message}`));else f.resolve(n.result??{})}}handleSocketData(u){this.ndjsonBuffer+=u.toString("utf8");let n=this.ndjsonBuffer.split(`
5
+ `);this.ndjsonBuffer=n.pop()??"";for(let f of n){let t=f.trim();if(!t)continue;try{let o=JSON.parse(t);this.handleCommand(o)}catch{}}}async handleCommand(u){try{let n=await this.executeCommand(u.cmd,u.args??{});this.sendResponse({id:u.id,ok:!0,result:n})}catch(n){let f=n instanceof Error?n.message:String(n);this.sendResponse({id:u.id,ok:!1,error:f})}}async executeCommand(u,n){switch(u){case"cdp":return this.sendCdpCommand(n.method,n.params);case"ping":return{pong:!0};case"snap":return this.handleSnap(n);case"shot":return this.handleShot(n);case"eval":return this.handleEval(n);case"html":return this.handleHtml(n);case"nav":return this.handleNav(n);case"net":return this.handleNet();case"click":return this.handleClick(n);case"clickxy":return this.handleClickxy(n);case"type":return this.handleType(n);case"loadall":return this.handleLoadall(n);case"evalraw":return this.handleEvalraw(n);case"setStealthConfig":return this.handleSetStealthConfig(n);default:throw new Error(`Unknown command: ${u}`)}}async handleSnap(u){let n=u.depth;await this.sendCdpCommand("Accessibility.enable",{});let f=await this.sendCdpCommand("Accessibility.getFullAXTree",{depth:n});await this.sendCdpCommand("Accessibility.disable",{});let t=f.nodes;return{tree:this.buildCompactAXTree(t),nodeCount:t.length}}buildCompactAXTree(u){let n=new Map;for(let o of u)n.set(o.nodeId,o);let f=new Set(["button","link","textbox","searchbox","checkbox","radio","combobox","listbox","menu","menuitem","tab","tabpanel","dialog","alertdialog","heading","paragraph","document","list","listitem","tree","treeitem","grid","gridcell","table","row","cell","form","group","region","article","main","navigation","banner","contentinfo","complementary","search","img","figure","image","text","statictext","textfield","popUpButton","checkBox","radioButton"]),t=[];for(let o of u){if(o.ignored===!0)continue;let h=o.role?.value;if(!h)continue;if(!f.has(h.toLowerCase())){if(!o.name?.value?.trim())continue}let b=o.name?.value,i=o.value?.value,M=o.description?.value,p={role:h,name:b?.trim()||void 0,value:i!==void 0?String(i):void 0,nodeId:o.nodeId};if(M?.trim())p.description=M.trim();let y=o.properties;if(y)for(let P of y){let c=P.name;if(["focusable","focused","disabled","checked","selected","expanded"].includes(c))p[c]=P.value?.value}t.push(p)}return t}async handleShot(u){let{path:n,selector:f}=u,o=(await this.sendCdpCommand("Runtime.evaluate",{expression:"window.devicePixelRatio",returnByValue:!0})).result?.value??1,b=(await this.sendCdpCommand("Page.getLayoutMetrics",{})).layoutViewport??{},i;if(f){let y=(await this.sendCdpCommand("DOM.getDocument",{})).root,P=await this.sendCdpCommand("DOM.querySelector",{nodeId:y.nodeId,selector:f});if(!P.nodeId)throw new Error(`Element not found: ${f}`);let C=(await this.sendCdpCommand("DOM.getBoxModel",{nodeId:P.nodeId})).model.content,E=C[0]??0,z=C[1]??0,L=C[2]??0,D=C[3]??0,S=C[4]??0,m=C[5]??0,Y=C[6]??0,H=C[7]??0,Q={x:Math.min(E,L,S,Y),y:Math.min(z,D,m,H),width:Math.max(E,L,S,Y)-Math.min(E,L,S,Y),height:Math.max(z,D,m,H)-Math.min(z,D,m,H),scale:1};i=await this.sendCdpCommand("Page.captureScreenshot",{format:"png",clip:Q})}else i=await this.sendCdpCommand("Page.captureScreenshot",{format:"png"});return{data:i.data,path:n,dpr:o,viewport:{width:b.clientWidth??0,height:b.clientHeight??0},message:n?`Screenshot saved to ${n}`:"Screenshot captured"}}async handleEval(u){let n=u.expression,f=u.awaitPromise??!0;if(!n)throw new Error("Missing required 'expression' parameter");try{let t=await this.sendCdpCommand("Runtime.evaluate",{expression:n,returnByValue:!0,awaitPromise:f});if(t.exceptionDetails){let i=t.exceptionDetails;return{ok:!1,error:i.text??"Evaluation threw exception",exceptionDetails:i}}let o=t.result?.value,h=t.result?.type,b=t.result?.subtype;return{ok:!0,result:o,type:h,subtype:b}}catch(t){return{ok:!1,error:t instanceof Error?t.message:String(t)}}}async handleHtml(u){let n=u.selector,f=u.outer??!0;if(n){let o=await this.sendCdpCommand("Runtime.evaluate",{expression:`
6
+ (function() {
7
+ const el = document.querySelector(${JSON.stringify(n)});
8
+ if (!el) return null;
9
+ return ${f?"el.outerHTML":"el.innerHTML"};
10
+ })()
11
+ `,returnByValue:!0});if(o.result?.value===null)throw new Error(`Element not found: ${n}`);return{html:o.result?.value,selector:n}}return{html:(await this.sendCdpCommand("Runtime.evaluate",{expression:f?"document.documentElement.outerHTML":"document.documentElement.innerHTML",returnByValue:!0})).result?.value}}async handleNav(u){let n=u.url,f=u.timeout??30000;if(!n)throw new Error("Missing required 'url' parameter");let t;try{t=new URL(n)}catch{throw new Error(`Invalid URL: ${n}`)}if(!["http:","https:"].includes(t.protocol))throw new Error(`Invalid protocol '${t.protocol}'. Only http and https are allowed.`);let o=this.ws;if(!o)throw new Error("WebSocket not connected");let h=new Promise((M)=>{let p=(y)=>{let P=Array.isArray(y)?Buffer.concat(y).toString("utf8"):y.toString("utf8");try{let c=JSON.parse(P);if(c.method==="Page.loadEventFired")o.removeListener("message",p),M(c.params??{})}catch{}};o.on("message",p)}),b=await this.sendCdpCommand("Page.navigate",{url:n}),i=new Promise((M,p)=>{setTimeout(()=>p(new Error(`Navigation timeout after ${f}ms`)),f)});return await Promise.race([h,i]),await this.sendCdpCommand("Runtime.evaluate",{expression:"new Promise(r => document.readyState === 'complete' ? r(void 0) : window.addEventListener('load', () => r(void 0)))",awaitPromise:!0}),{ok:!0,url:n,frameId:b.frameId,loaderId:b.loaderId,message:`Navigated to ${n}`}}async handleNet(){let u=await this.sendCdpCommand("Runtime.evaluate",{expression:`JSON.stringify(
12
+ performance.getEntriesByType('resource').map(e => ({
13
+ name: e.name,
14
+ type: e.initiatorType,
15
+ duration: Math.round(e.duration),
16
+ startTime: Math.round(e.startTime),
17
+ transferSize: e.transferSize,
18
+ encodedBodySize: e.encodedBodySize,
19
+ decodedBodySize: e.decodedBodySize,
20
+ }))
21
+ )`,returnByValue:!0}),n=JSON.parse(u.result?.value??"[]");return{entries:n,count:n.length}}async handleClick(u){let n=u.selector;if(!n)throw new Error("Missing required 'selector' parameter");let t=(await this.sendCdpCommand("DOM.getDocument",{})).root,o=await this.sendCdpCommand("DOM.querySelector",{nodeId:t.nodeId,selector:n});if(!o.nodeId)throw new Error(`Element not found: ${n}`);let h=o.nodeId,p=(await this.sendCdpCommand("DOM.getOuterHTML",{nodeId:h})).outerHTML.match(/^<(\w+)/)?.[1]?.toLowerCase()??"unknown",P=(await this.sendCdpCommand("Runtime.evaluate",{expression:`
22
+ (function() {
23
+ const el = document.querySelector(${JSON.stringify(n)});
24
+ if (!el) return '';
25
+ const text = el.textContent?.trim() || el.getAttribute('aria-label') || el.getAttribute('title') || '';
26
+ return text.substring(0, 100);
27
+ })()
28
+ `,returnByValue:!0})).result?.value??"";await this.sendCdpCommand("DOM.scrollIntoViewIfNeeded",{nodeId:h});let C=(await this.sendCdpCommand("DOM.getBoxModel",{nodeId:h})).model.content,E=C[0]??0,z=C[1]??0,L=C[4]??0,D=C[5]??0;if(this.stealthConfig?.enabled&&this.stealthConfig.cursorMovement){let S={x:Math.min(E,L),y:Math.min(z,D),width:Math.abs(L-E),height:Math.abs(D-z)},m=O(S);await this.performHumanizedClick(m)}else{let S=(E+L)/2,m=(z+D)/2;await this.sendCdpCommand("Input.dispatchMouseEvent",{type:"mousePressed",x:S,y:m,button:"left",clickCount:1}),await this.sendCdpCommand("Input.dispatchMouseEvent",{type:"mouseReleased",x:S,y:m,button:"left",clickCount:1})}return{tag:p,text:P.substring(0,50),message:`Clicked <${p}> element`}}async handleClickxy(u){let{x:n,y:f}=u;if(typeof n!=="number"||typeof f!=="number")throw new Error("Missing required 'x' and 'y' parameters");if(this.stealthConfig?.enabled&&this.stealthConfig.cursorMovement)await this.performHumanizedClick({x:n,y:f});else await this.sendCdpCommand("Input.dispatchMouseEvent",{type:"mousePressed",x:n,y:f,button:"left",clickCount:1}),await this.sendCdpCommand("Input.dispatchMouseEvent",{type:"mouseReleased",x:n,y:f,button:"left",clickCount:1});return{x:n,y:f,message:`Clicked at (${n}, ${f})`}}async handleType(u){let n=u.text;if(!n)throw new Error("Missing required 'text' parameter");if(this.stealthConfig?.enabled&&this.stealthConfig.humanizedTyping){let{chars:f,delays:t}=G(n);for(let o=0;o<f.length;o++){let h=f[o];if(h==="\b")await this.sendCdpCommand("Input.dispatchKeyEvent",{type:"keyDown",key:"Backspace",code:"Backspace",windowsVirtualKeyCode:8}),await this.sleep(t[o]),await this.sendCdpCommand("Input.dispatchKeyEvent",{type:"keyUp",key:"Backspace",code:"Backspace",windowsVirtualKeyCode:8});else await this.sendCdpCommand("Input.dispatchKeyEvent",{type:"char",text:h}),await this.sleep(t[o])}}else await this.sendCdpCommand("Input.insertText",{text:n});return{text:n,length:n.length,message:`Inserted ${n.length} character(s)`}}async handleLoadall(u){let n=u.selector,f=u.interval??500,t=u.timeout??300000;if(!n)throw new Error("Missing required 'selector' parameter");let o=Date.now(),h=0,b=!1;while(Date.now()-o<t){if(!(await this.sendCdpCommand("Runtime.evaluate",{expression:`
29
+ (function() {
30
+ const el = document.querySelector(${JSON.stringify(n)});
31
+ return el !== null;
32
+ })()
33
+ `,returnByValue:!0})).result?.value)break;try{await this.handleClick({selector:n}),h++}catch{break}let p=this.stealthConfig?.enabled?K(f):f;await new Promise((y)=>setTimeout(y,p))}if(Date.now()-o>=t)b=!0;return{clickCount:h,timedOut:b,message:b?`Clicked ${h} time(s) before timeout`:`Clicked ${h} time(s) until element disappeared`}}async handleEvalraw(u){let n=u.method,f=u.params??{};if(!n)throw new Error("Missing required 'method' parameter");return{result:await this.sendCdpCommand(n,f)}}async handleSetStealthConfig(u){let n=u.stealth;if(n&&typeof n==="object"){if(this.stealthConfig={enabled:n.enabled??!1,cursorMovement:n.cursorMovement??!0,humanizedTyping:n.humanizedTyping??!0,interStepDelay:n.interStepDelay??[300,1500]},this.stealthConfig.enabled&&!this.stealthScriptsInjected)await this.injectStealthScripts(),this.stealthScriptsInjected=!0}return{stealthConfig:this.stealthConfig,message:"Stealth config updated"}}async performHumanizedClick(u){let n=_(u);for(let f=0;f<n.events.length;f++){let t=n.events[f],o=n.delays[f]??10;if(await this.sendCdpCommand("Input.dispatchMouseEvent",{type:t.type,x:t.x,y:t.y,button:t.button??"left",clickCount:t.clickCount??1}),f<n.events.length-1)await this.sleep(o)}}async injectStealthScripts(){await this.sendCdpCommand("Page.addScriptToEvaluateOnNewDocument",{source:`
34
+ Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
35
+ delete navigator.__proto__.webdriver;
36
+
37
+ for (const key of Object.keys(window)) {
38
+ if (key.match(/^cdc_/)) delete window[key];
39
+ }
40
+
41
+ const origQuery = window.Permissions.prototype.query;
42
+ window.Permissions.prototype.query = function(params) {
43
+ if (params.name === 'notifications') {
44
+ return Promise.resolve({ state: Notification.permission });
45
+ }
46
+ return origQuery.call(this, params);
47
+ };
48
+ `})}sleep(u){return new Promise((n)=>setTimeout(n,u))}async sendCdpCommand(u,n={}){return new Promise((f,t)=>{if(!this.ws||this.ws.readyState!==q.OPEN){t(new Error("WebSocket not connected"));return}let o=this.cdpCommandId++;this.pendingCdpCommands.set(o,{resolve:f,reject:t});try{this.ws.send(JSON.stringify({id:o,method:u,params:n}))}catch(h){this.pendingCdpCommands.delete(o),t(h)}setTimeout(()=>{if(this.pendingCdpCommands.has(o))this.pendingCdpCommands.delete(o),t(new Error(`CDP command ${o} timed out`))},30000)})}sendResponse(u){if(this.clientSocket&&!this.clientSocket.destroyed)this.clientSocket.write(JSON.stringify(u)+`
49
+ `)}resetIdleTimer(){if(this.idleTimer)clearTimeout(this.idleTimer);this.idleTimer=setTimeout(()=>{this.exit("Idle timeout")},this.args.idleTimeoutMs),this.idleTimer.unref()}exit(u){if(!this.running)return;if(this.running=!1,this.idleTimer)clearTimeout(this.idleTimer),this.idleTimer=null;for(let n of this.cleanupHandlers)n();if(this.cleanupHandlers=[],this.clientSocket)this.clientSocket.destroy(),this.clientSocket=null;if(this.server)this.server.close(),this.server=null;if(this.ws)this.ws.close(),this.ws=null;if(process.platform!=="win32"&&N(this.args.socketPath))U(this.args.socketPath,()=>{});process.exit(0)}}function k(u,n){v(u,n,(f)=>{if(f)console.error(`Failed to chmod ${u}:`,f.message)})}if(import.meta.url===`file://${process.argv[1]?.replace(/\\/g,"/")}`||process.argv[1]?.endsWith("daemon-worker.ts")||process.argv[1]?.endsWith("daemon-worker.js")){let u=W();new J(u).start().catch((f)=>{console.error("Daemon startup failed:",f.message),process.exit(1)})}