agent-device
Advanced tools
@@ -5,3 +5,3 @@ let e,t;import a from"node:crypto";import{isCancel as i,select as n}from"@clack/prompts";import{node_path as r,runCmdStreaming as o,promises as s,asAppError as l,fileURLToPath as c,runCmdBackground as u,node_fs as d,node_os as p,errors_AppError as f,runCmd as h,node_net as m,whichCmd as w}from"./861.js";async function g(e,t){let a=e,r=e=>e.toLowerCase().replace(/_/g," ").replace(/\s+/g," ").trim();if(t.platform&&(a=a.filter(e=>e.platform===t.platform)),t.udid){let e=a.find(e=>e.id===t.udid&&"ios"===e.platform);if(!e)throw new f("DEVICE_NOT_FOUND",`No iOS device with UDID ${t.udid}`);return e}if(t.serial){let e=a.find(e=>e.id===t.serial&&"android"===e.platform);if(!e)throw new f("DEVICE_NOT_FOUND",`No Android device with serial ${t.serial}`);return e}if(t.deviceName){let e=r(t.deviceName),i=a.find(t=>r(t.name)===e);if(!i)throw new f("DEVICE_NOT_FOUND",`No device named ${t.deviceName}`);return i}if(1===a.length)return a[0];if(0===a.length)throw new f("DEVICE_NOT_FOUND","No devices found",{selector:t});let o=a.filter(e=>e.booted);if(1===o.length)return o[0];if(!process.env.CI&&process.stdin.isTTY&&process.stdout.isTTY){let e=await n({message:"Multiple devices available. Choose a device to continue:",options:(o.length>0?o:a).map(e=>({label:`${e.name} (${e.platform}${e.kind?`, ${e.kind}`:""}${e.booted?", booted":""})`,value:e.id}))});if(i(e))throw new f("INVALID_ARGS","Device selection cancelled");if(e){let t=a.find(t=>t.id===e);if(t)return t}}return o[0]??a[0]}async function y(){if(!await w("adb"))throw new f("TOOL_MISSING","adb not found in PATH");let e=(await h("adb",["devices","-l"])).stdout.split("\n").map(e=>e.trim()),t=[];for(let a of e){if(!a||a.startsWith("List of devices"))continue;let e=a.split(/\s+/),i=e[0];if("device"!==e[1])continue;let n=(e.find(e=>e.startsWith("model:"))??"").replace("model:","").replace(/_/g," ").trim()||i;if(i.startsWith("emulator-")){let e=await h("adb",["-s",i,"emu","avd","name"],{allowFailure:!0}),t=e.stdout.trim();0===e.exitCode&&t&&(n=t.replace(/_/g," "))}let r=await v(i);t.push({platform:"android",id:i,name:n,kind:i.startsWith("emulator-")?"emulator":"device",booted:r})}return t}async function v(e){try{let t=await h("adb",["-s",e,"shell","getprop","sys.boot_completed"],{allowFailure:!0});return"1"===t.stdout.trim()}catch{return!1}}async function I(e,t=6e4){let a=Date.now();for(;Date.now()-a<t;){if(await v(e))return;await new Promise(e=>setTimeout(e,1e3))}throw new f("COMMAND_FAILED","Android device did not finish booting in time",{serial:e,timeoutMs:t})}async function N(e,t={}){let a,i=t.attempts??3,n=t.baseDelayMs??200,r=t.maxDelayMs??2e3,o=t.jitter??.2;for(let s=1;s<=i;s+=1)try{return await e()}catch(l){if(a=l,s>=i||t.shouldRetry&&!t.shouldRetry(l,s))break;let e=function(e,t,a,i){let n=Math.min(t,e*2**(i-1));return Math.max(0,n+n*a*(2*Math.random()-1))}(n,r,o,s);await function(e){return new Promise(t=>setTimeout(t,e))}(e)}if(a)throw a;throw new f("COMMAND_FAILED","retry failed")}let b={settings:{type:"intent",value:"android.settings.SETTINGS"}};function A(e,t){return["-s",e.id,...t]}async function S(e,t){let a=t.trim();if(a.includes("."))return{type:"package",value:a};let i=b[a.toLowerCase()];if(i)return i;let n=(await h("adb",A(e,["shell","pm","list","packages"]))).stdout.split("\n").map(e=>e.replace("package:","").trim()).filter(Boolean).filter(e=>e.toLowerCase().includes(a.toLowerCase()));if(1===n.length)return{type:"package",value:n[0]};if(n.length>1)throw new f("INVALID_ARGS",`Multiple packages matched "${t}"`,{matches:n});throw new f("APP_NOT_INSTALLED",`No package found matching "${t}"`)}async function D(e,t="launchable"){if("launchable"===t){let t=await h("adb",A(e,["shell","cmd","package","query-activities","--brief","-a","android.intent.action.MAIN","-c","android.intent.category.LAUNCHER"]),{allowFailure:!0});if(0===t.exitCode&&t.stdout.trim().length>0){let e=new Set;for(let a of t.stdout.split("\n")){let t=a.trim();if(!t)continue;let i=t.split(/\s+/)[0],n=i.includes("/")?i.split("/")[0]:i;n&&e.add(n)}if(e.size>0)return Array.from(e)}}return(await h("adb",A(e,"user-installed"===t?["shell","pm","list","packages","-3"]:["shell","pm","list","packages"]))).stdout.split("\n").map(e=>e.replace("package:","").trim()).filter(Boolean)}async function k(e,t="launchable"){let a=await D(e,t),i=new Set("launchable"===t?a:await D(e,"launchable"));return a.map(e=>({package:e,launchable:i.has(e)}))}async function P(e){let t=await O(e,[["shell","dumpsys","window","windows"],["shell","dumpsys","window"]]);if(t)return t;let a=await O(e,[["shell","dumpsys","activity","activities"],["shell","dumpsys","activity"]]);return a||{}}async function O(e,t){for(let a of t){let t=function(e){for(let t of[/mCurrentFocus=Window\{[^}]*\s([\w.]+)\/([\w.$]+)/,/mFocusedApp=AppWindowToken\{[^}]*\s([\w.]+)\/([\w.$]+)/,/mResumedActivity:.*?\s([\w.]+)\/([\w.$]+)/,/ResumedActivity:.*?\s([\w.]+)\/([\w.$]+)/]){let a=t.exec(e);if(a)return{package:a[1],activity:a[2]}}return null}((await h("adb",A(e,a),{allowFailure:!0})).stdout??"");if(t)return t}return null}async function _(e,t){e.booted||await I(e.id);let a=await S(e,t);"intent"===a.type?await h("adb",A(e,["shell","am","start","-a",a.value])):await h("adb",A(e,["shell","monkey","-p",a.value,"-c","android.intent.category.LAUNCHER","1"]))}async function x(e){e.booted||await I(e.id)}async function L(e,t){if("settings"===t.trim().toLowerCase())return void await h("adb",A(e,["shell","am","force-stop","com.android.settings"]));let a=await S(e,t);if("intent"===a.type)throw new f("INVALID_ARGS","Close requires a package name, not an intent");await h("adb",A(e,["shell","am","force-stop",a.value]))}async function E(e,t,a){await h("adb",A(e,["shell","input","tap",String(t),String(a)]))}async function R(e){await h("adb",A(e,["shell","input","keyevent","4"]))}async function C(e){await h("adb",A(e,["shell","input","keyevent","3"]))}async function M(e){await h("adb",A(e,["shell","input","keyevent","187"]))}async function T(e,t,a,i=800){await h("adb",A(e,["shell","input","swipe",String(t),String(a),String(t),String(a),String(i)]))}async function F(e,t){let a=t.replace(/ /g,"%s");await h("adb",A(e,["shell","input","text",a]))}async function B(e,t,a){await E(e,t,a)}async function $(e,t,a,i){await B(e,t,a),await F(e,i)}async function U(e,t,a=.6){let{width:i,height:n}=await W(e),r=Math.floor(i*a),o=Math.floor(n*a),s=Math.floor(i/2),l=Math.floor(n/2),c=s,u=l,d=s,p=l;switch(t){case"up":u=l-Math.floor(o/2),p=l+Math.floor(o/2);break;case"down":u=l+Math.floor(o/2),p=l-Math.floor(o/2);break;case"left":c=s-Math.floor(r/2),d=s+Math.floor(r/2);break;case"right":c=s+Math.floor(r/2),d=s-Math.floor(r/2);break;default:throw new f("INVALID_ARGS",`Unknown direction: ${t}`)}await h("adb",A(e,["shell","input","swipe",String(c),String(u),String(d),String(p),"300"]))}async function V(e,t){for(let a=0;a<8;a+=1){let a="";try{a=await X(e)}catch(t){let e=t instanceof Error?t.message:String(t);throw new f("UNSUPPORTED_OPERATION",`uiautomator dump failed: ${e}`)}if(function(e,t){let a=t.toLowerCase(),i=/<node[^>]+>/g,n=i.exec(e);for(;n;){let t=n[0],r=/text="([^"]*)"/.exec(t),o=/content-desc="([^"]*)"/.exec(t),s=(r?.[1]??"").toLowerCase(),l=(o?.[1]??"").toLowerCase();if(s.includes(a)||l.includes(a)){let e=/bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"/.exec(t);if(e){let t=Number(e[1]),a=Number(e[2]);return{x:Math.floor((t+Number(e[3]))/2),y:Math.floor((a+Number(e[4]))/2)}}return{x:0,y:0}}n=i.exec(e)}return null}(a,t))return;await U(e,"down",.5)}throw new f("COMMAND_FAILED",`Could not find element containing "${t}" after scrolling`)}async function j(e,t){let a=await h("adb",A(e,["exec-out","screencap","-p"]),{binaryStdout:!0});if(!a.stdoutBuffer)throw new f("COMMAND_FAILED","Failed to capture screenshot");await s.writeFile(t,a.stdoutBuffer)}async function G(e,t,a){let i=t.toLowerCase(),n=function(e){let t=e.toLowerCase();if("on"===t||"true"===t||"1"===t)return!0;if("off"===t||"false"===t||"0"===t)return!1;throw new f("INVALID_ARGS",`Invalid setting state: ${e}`)}(a);switch(i){case"wifi":return void await h("adb",A(e,["shell","svc","wifi",n?"enable":"disable"]));case"airplane":await h("adb",A(e,["shell","settings","put","global","airplane_mode_on",n?"1":"0"])),await h("adb",A(e,["shell","am","broadcast","-a","android.intent.action.AIRPLANE_MODE","--ez","state",n?"true":"false"]));return;case"location":return void await h("adb",A(e,["shell","settings","put","secure","location_mode",n?"3":"0"]));default:throw new f("INVALID_ARGS",`Unsupported setting: ${t}`)}}async function q(e,t={}){return function(e,t,a){let i=function(e){let t={type:null,label:null,value:null,identifier:null,depth:-1,children:[]},a=[t],i=/<node\b[^>]*>|<\/node>/g,n=i.exec(e);for(;n;){let t=n[0];if(t.startsWith("</node")){a.length>1&&a.pop(),n=i.exec(e);continue}let r=function(e){let t=t=>{let a=RegExp(`${t}="([^"]*)"`).exec(e);return a?a[1]:null},a=e=>{let a=t(e);if(null!==a)return"true"===a};return{text:t("text"),desc:t("content-desc"),resourceId:t("resource-id"),className:t("class"),bounds:t("bounds"),clickable:a("clickable"),enabled:a("enabled"),focusable:a("focusable")}}(t),o=function(e){if(!e)return;let t=/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/.exec(e);if(!t)return;let a=Number(t[1]),i=Number(t[2]);return{x:a,y:i,width:Math.max(0,Number(t[3])-a),height:Math.max(0,Number(t[4])-i)}}(r.bounds),s=a[a.length-1],l={type:r.className,label:r.text||r.desc,value:r.text,identifier:r.resourceId,rect:o,enabled:r.enabled,hittable:r.clickable??r.focusable,depth:s.depth+1,parentIndex:void 0,children:[]};s.children.push(l),t.endsWith("/>")||a.push(l),n=i.exec(e)}return t}(e),n=[],r=!1,o=a.depth??1/0,s=a.scope?function(e,t){let a=t.toLowerCase(),i=[...e.children];for(;i.length>0;){let e=i.shift(),t=e.label?.toLowerCase()??"",n=e.value?.toLowerCase()??"",r=e.identifier?.toLowerCase()??"";if(t.includes(a)||n.includes(a)||r.includes(a))return e;i.push(...e.children)}return null}(i,a.scope):null,l=s?[s]:i.children,c=(e,t)=>{if(n.length>=800){r=!0;return}if(!(t>o)){for(let i of((a.raw||function(e,t){if(t.interactiveOnly)return!!e.hittable;if(t.compact){let t=!!(e.label&&e.label.trim().length>0),a=!!(e.identifier&&e.identifier.trim().length>0);return t||a||!!e.hittable}return!0}(e,a))&&n.push({index:n.length,type:e.type??void 0,label:e.label??void 0,value:e.value??void 0,identifier:e.identifier??void 0,rect:e.rect,enabled:e.enabled,hittable:e.hittable,depth:t,parentIndex:e.parentIndex}),e.children))if(c(i,t+1),r)return}};for(let e of l)if(c(e,0),r)break;return r?{nodes:n,truncated:r}:{nodes:n}}(await X(e),800,t)}async function J(){if(!await w("adb"))throw new f("TOOL_MISSING","adb not found in PATH")}async function W(e){let t=(await h("adb",A(e,["shell","wm","size"]))).stdout.match(/Physical size:\s*(\d+)x(\d+)/);if(!t)throw new f("COMMAND_FAILED","Unable to read screen size");return{width:Number(t[1]),height:Number(t[2])}}async function X(e){return N(()=>z(e),{shouldRetry:H})}async function z(e){return await h("adb",A(e,["shell","uiautomator","dump","/sdcard/window_dump.xml"])),(await h("adb",A(e,["shell","cat","/sdcard/window_dump.xml"]))).stdout}function H(e){if(!(e instanceof f)||"COMMAND_FAILED"!==e.code)return!1;let t=`${e.details?.stderr??""}`.toLowerCase();return!!(t.includes("device offline")||t.includes("device not found")||t.includes("transport error")||t.includes("connection reset")||t.includes("broken pipe")||t.includes("timed out"))}async function Y(){if("darwin"!==process.platform)throw new f("UNSUPPORTED_PLATFORM","iOS tools are only available on macOS");if(!await w("xcrun"))throw new f("TOOL_MISSING","xcrun not found in PATH");let e=[],t=await h("xcrun",["simctl","list","devices","-j"]);try{let a=JSON.parse(t.stdout);for(let t of Object.values(a.devices))for(let a of t)a.isAvailable&&e.push({platform:"ios",id:a.udid,name:a.name,kind:"simulator",booted:"Booted"===a.state})}catch(e){throw new f("COMMAND_FAILED","Failed to parse simctl devices JSON",void 0,e)}if(await w("xcrun"))try{let t=await h("xcrun",["devicectl","list","devices","--json"]);for(let a of JSON.parse(t.stdout).devices??[])a.platform?.toLowerCase().includes("ios")&&e.push({platform:"ios",id:a.identifier,name:a.name,kind:"device",booted:!0})}catch{}return e}let Z={settings:"com.apple.Preferences"};async function K(e,t){let a=t.trim();if(a.includes("."))return a;let i=Z[a.toLowerCase()];if(i)return i;if("simulator"===e.kind){let i=(await ep(e)).filter(e=>e.name.toLowerCase()===a.toLowerCase());if(1===i.length)return i[0].bundleId;if(i.length>1)throw new f("INVALID_ARGS",`Multiple apps matched "${t}"`,{matches:i})}throw new f("APP_NOT_INSTALLED",`No app found matching "${t}"`)}async function Q(e,t){let a=await K(e,t);if("simulator"===e.kind){await ef(e),await h("open",["-a","Simulator"],{allowFailure:!0}),await h("xcrun",["simctl","launch",e.id,a]);return}await h("xcrun",["devicectl","device","process","launch","--device",e.id,a])}async function ee(e){"simulator"!==e.kind||"Booted"!==await eh(e.id)&&(await ef(e),await h("open",["-a","Simulator"],{allowFailure:!0}))}async function et(e,t){let a=await K(e,t);if("simulator"===e.kind){await ef(e);let t=await h("xcrun",["simctl","terminate",e.id,a],{allowFailure:!0});if(0!==t.exitCode){if(t.stderr.toLowerCase().includes("found nothing to terminate"))return;throw new f("COMMAND_FAILED",`xcrun exited with code ${t.exitCode}`,{cmd:"xcrun",args:["simctl","terminate",e.id,a],stdout:t.stdout,stderr:t.stderr,exitCode:t.exitCode})}return}await h("xcrun",["devicectl","device","process","terminate","--device",e.id,a])}async function ea(e,t,a){throw ed(e,"press"),new f("UNSUPPORTED_OPERATION","simctl io tap is not available; use the XCTest runner for input")}async function ei(e,t,a,i=800){throw ed(e,"long-press"),new f("UNSUPPORTED_OPERATION","long-press is not supported on iOS simulators without XCTest runner support")}async function en(e,t,a){await ea(e,t,a)}async function er(e,t){throw ed(e,"type"),new f("UNSUPPORTED_OPERATION","simctl io keyboard is not available; use the XCTest runner for input")}async function eo(e,t,a,i){await en(e,t,a),await er(e,i)}async function es(e,t,a=.6){throw ed(e,"scroll"),new f("UNSUPPORTED_OPERATION","simctl io swipe is not available; use the XCTest runner for input")}async function el(e){throw new f("UNSUPPORTED_OPERATION",`scrollintoview is not supported on iOS without UI automation (${e})`)}async function ec(e,t){if("simulator"===e.kind){await ef(e),await h("xcrun",["simctl","io",e.id,"screenshot",t]);return}await h("xcrun",["devicectl","device","screenshot","--device",e.id,t])}async function eu(e,t,a,i){ed(e,"settings"),await ef(e);let n=t.toLowerCase(),r=function(e){let t=e.toLowerCase();if("on"===t||"true"===t||"1"===t)return!0;if("off"===t||"false"===t||"0"===t)return!1;throw new f("INVALID_ARGS",`Invalid setting state: ${e}`)}(a);switch(n){case"wifi":return void await h("xcrun",["simctl","status_bar",e.id,"override","--wifiMode",r?"active":"failed"]);case"airplane":r?await h("xcrun",["simctl","status_bar",e.id,"override","--dataNetwork","hide","--wifiMode","failed","--wifiBars","0","--cellularMode","failed","--cellularBars","0","--operatorName",""]):await h("xcrun",["simctl","status_bar",e.id,"clear"]);return;case"location":if(!i)throw new f("INVALID_ARGS","location setting requires an active app in session");await h("xcrun",["simctl","privacy",e.id,r?"grant":"revoke","location",i]);return;default:throw new f("INVALID_ARGS",`Unsupported setting: ${t}`)}}function ed(e,t){if("simulator"!==e.kind)throw new f("UNSUPPORTED_OPERATION",`${t} is only supported on iOS simulators in v1`)}async function ep(e){let t=(await h("xcrun",["simctl","listapps",e.id],{allowFailure:!0})).stdout.trim();if(!t)return[];let a=null;if(t.startsWith("{"))try{a=JSON.parse(t)}catch{a=null}if(!a&&t.startsWith("{"))try{let e=await h("plutil",["-convert","json","-o","-","-"],{allowFailure:!0,stdin:t});0===e.exitCode&&e.stdout.trim().startsWith("{")&&(a=JSON.parse(e.stdout))}catch{a=null}return a?Object.entries(a).map(([e,t])=>({bundleId:e,name:t.CFBundleDisplayName??t.CFBundleName??e})):[]}async function ef(e){"simulator"!==e.kind||"Booted"!==await eh(e.id)&&(await h("xcrun",["simctl","boot",e.id],{allowFailure:!0}),await h("xcrun",["simctl","bootstatus",e.id,"-b"],{allowFailure:!0}))}async function eh(e){let t=await h("xcrun",["simctl","list","devices","-j"],{allowFailure:!0});if(0!==t.exitCode)return null;try{let a=JSON.parse(t.stdout);for(let t of Object.values(a.devices??{})){let a=t.find(t=>t.udid===e);if(a)return a.state}}catch{}return null}let em=new Map;async function ew(e,t,a={}){var i;return"snapshot"===(i=t.command)||"findText"===i||"listTappables"===i||"alert"===i?N(()=>eg(e,t,a),{shouldRetry:eS}):eg(e,t,a)}async function eg(e,t,a={}){if("simulator"!==e.kind)throw new f("UNSUPPORTED_OPERATION","iOS runner only supports simulators in v1");try{let i=await eI(e,a),n=await eD(e,i.port,t,a.logPath),r=await n.text(),o={};try{o=JSON.parse(r)}catch{throw new f("COMMAND_FAILED","Invalid runner response",{text:r})}if(!o.ok)throw new f("COMMAND_FAILED",o.error?.message??"Runner error",{runner:o,xcodebuild:{exitCode:1,stdout:"",stderr:""},logPath:a.logPath});return o.data??{}}catch(n){let i=n instanceof f?n:new f("COMMAND_FAILED",String(n));if("COMMAND_FAILED"===i.code&&"string"==typeof i.message&&i.message.includes("Runner did not accept connection")){await ey(e.id);let i=await eI(e,a),n=await eD(e,i.port,t,a.logPath),r=await n.text(),o={};try{o=JSON.parse(r)}catch{throw new f("COMMAND_FAILED","Invalid runner response",{text:r})}if(!o.ok)throw new f("COMMAND_FAILED",o.error?.message??"Runner error",{runner:o,xcodebuild:{exitCode:1,stdout:"",stderr:""},logPath:a.logPath});return o.data??{}}throw n}}async function ey(e){let t=em.get(e);if(t){try{await eD(t.device,t.port,{command:"shutdown"})}catch{}try{await t.testPromise}catch{}ex(t.xctestrunPath),ex(t.jsonPath),em.delete(e)}}async function ev(e){await h("xcrun",["simctl","bootstatus",e,"-b"],{allowFailure:!0})}async function eI(e,t){let a=em.get(e.id);if(a)return a;await ev(e.id);let i=await eN(e.id,t),n=await eP(),r=process.env.AGENT_DEVICE_RUNNER_TIMEOUT??"300",{xctestrunPath:s,jsonPath:l}=await e_(i,{AGENT_DEVICE_RUNNER_PORT:String(n),AGENT_DEVICE_RUNNER_TIMEOUT:r},`session-${e.id}-${n}`),c=o("xcodebuild",["test-without-building","-only-testing","AgentDeviceRunnerUITests/RunnerTests/testCommand","-parallel-testing-enabled","NO","-maximum-concurrent-test-simulator-destinations","1","-xctestrun",s,"-destination",`platform=iOS Simulator,id=${e.id}`],{onStdoutChunk:e=>{eA(e,t.logPath,t.traceLogPath,t.verbose)},onStderrChunk:e=>{eA(e,t.logPath,t.traceLogPath,t.verbose)},allowFailure:!0,env:{...process.env,AGENT_DEVICE_RUNNER_PORT:String(n),AGENT_DEVICE_RUNNER_TIMEOUT:r}}),u={device:e,deviceId:e.id,port:n,xctestrunPath:s,jsonPath:l,testPromise:c};return em.set(e.id,u),u}async function eN(e,t){let a,i=r.join(p.homedir(),".agent-device","ios-runner"),n=r.join(i,"derived");if((a=process.env.AGENT_DEVICE_IOS_CLEAN_DERIVED)&&["1","true","yes","on"].includes(a.toLowerCase()))try{d.rmSync(n,{recursive:!0,force:!0})}catch{}let s=eb(n);if(s)return s;let l=function(){let e=r.dirname(c(import.meta.url)),t=e;for(let e=0;e<6;e+=1){let e=r.join(t,"package.json");if(d.existsSync(e))return t;t=r.dirname(t)}return e}(),u=r.join(l,"ios-runner","AgentDeviceRunner","AgentDeviceRunner.xcodeproj");if(!d.existsSync(u))throw new f("COMMAND_FAILED","iOS runner project not found",{projectPath:u});try{await o("xcodebuild",["build-for-testing","-project",u,"-scheme","AgentDeviceRunner","-parallel-testing-enabled","NO","-maximum-concurrent-test-simulator-destinations","1","-destination",`platform=iOS Simulator,id=${e}`,"-derivedDataPath",n],{onStdoutChunk:e=>{eA(e,t.logPath,t.traceLogPath,t.verbose)},onStderrChunk:e=>{eA(e,t.logPath,t.traceLogPath,t.verbose)}})}catch(a){let e=a instanceof f?a:new f("COMMAND_FAILED",String(a));throw new f("COMMAND_FAILED","xcodebuild build-for-testing failed",{error:e.message,details:e.details,logPath:t.logPath})}let h=eb(n);if(!h)throw new f("COMMAND_FAILED","Failed to locate .xctestrun after build");return h}function eb(e){if(!d.existsSync(e))return null;let t=[],a=[e];for(;a.length>0;){let e=a.pop();for(let i of d.readdirSync(e,{withFileTypes:!0})){let n=r.join(e,i.name);if(i.isDirectory()){a.push(n);continue}if(i.isFile()&&i.name.endsWith(".xctestrun"))try{let e=d.statSync(n);t.push({path:n,mtimeMs:e.mtimeMs})}catch{}}}return 0===t.length?null:(t.sort((e,t)=>t.mtimeMs-e.mtimeMs),t[0]?.path??null)}function eA(e,t,a,i){t&&d.appendFileSync(t,e),a&&d.appendFileSync(a,e),i&&process.stderr.write(e)}function eS(e){if(!(e instanceof f)||"COMMAND_FAILED"!==e.code)return!1;let t=`${e.message??""}`.toLowerCase();return!!(t.includes("runner did not accept connection")||t.includes("fetch failed")||t.includes("econnrefused")||t.includes("socket hang up"))}async function eD(e,t,a,i){i&&await eO(i,4e3);let n=Date.now(),r=null;for(;Date.now()-n<8e3;)try{return await fetch(`http://127.0.0.1:${t}/command`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)})}catch(e){r=e,await new Promise(e=>setTimeout(e,100))}if("simulator"===e.kind){let i=await ek(e.id,t,a);return new Response(i.body,{status:i.status})}let o=i?function(e){try{if(!d.existsSync(e))return null;let t=d.readFileSync(e,"utf8").match(/AGENT_DEVICE_RUNNER_PORT=(\d+)/);if(t)return Number(t[1])}catch{}return null}(i):null;if(o&&o!==t)try{return await fetch(`http://127.0.0.1:${o}/command`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)})}catch(e){r=e}throw new f("COMMAND_FAILED","Runner did not accept connection",{port:t,fallbackPort:o,logPath:i,lastError:r?String(r):void 0})}async function ek(e,t,a){let i=JSON.stringify(a),n=await h("xcrun",["simctl","spawn",e,"/usr/bin/curl","-s","-X","POST","-H","Content-Type: application/json","--data",i,`http://127.0.0.1:${t}/command`],{allowFailure:!0}),r=n.stdout;if(0!==n.exitCode)throw new f("COMMAND_FAILED","Runner did not accept connection (simctl spawn)",{port:t,stdout:n.stdout,stderr:n.stderr,exitCode:n.exitCode});return{status:200,body:r}}async function eP(){return await new Promise((e,t)=>{let a=m.createServer();a.listen(0,"127.0.0.1",()=>{let i=a.address();a.close(),"object"==typeof i&&i?.port?e(i.port):t(new f("COMMAND_FAILED","Failed to allocate port"))}),a.on("error",t)})}async function eO(e,t){if(!d.existsSync(e))return;let a=Date.now(),i=0;for(;Date.now()-a<t;){if(!d.existsSync(e))return;let t=d.statSync(e);if(t.size>i){let a=d.openSync(e,"r"),n=Buffer.alloc(t.size-i);d.readSync(a,n,0,n.length,i),d.closeSync(a),i=t.size;let r=n.toString("utf8");if(r.includes("AGENT_DEVICE_RUNNER_LISTENER_READY")||r.includes("AGENT_DEVICE_RUNNER_PORT="))return}await new Promise(e=>setTimeout(e,100))}}async function e_(e,t,a){let i,n=r.dirname(e),o=a.replace(/[^a-zA-Z0-9._-]/g,"_"),s=r.join(n,`AgentDeviceRunner.env.${o}.json`),l=r.join(n,`AgentDeviceRunner.env.${o}.xctestrun`),c=await h("plutil",["-convert","json","-o","-",e],{allowFailure:!0});if(0!==c.exitCode||!c.stdout.trim())throw new f("COMMAND_FAILED","Failed to read xctestrun plist",{xctestrunPath:e,stderr:c.stderr});try{i=JSON.parse(c.stdout)}catch(t){throw new f("COMMAND_FAILED","Failed to parse xctestrun JSON",{xctestrunPath:e,error:String(t)})}let u=e=>{e.EnvironmentVariables={...e.EnvironmentVariables??{},...t},e.UITestEnvironmentVariables={...e.UITestEnvironmentVariables??{},...t},e.UITargetAppEnvironmentVariables={...e.UITargetAppEnvironmentVariables??{},...t},e.TestingEnvironmentVariables={...e.TestingEnvironmentVariables??{},...t}},p=i.TestConfigurations;if(Array.isArray(p))for(let e of p){if(!e||"object"!=typeof e)continue;let t=e.TestTargets;if(Array.isArray(t))for(let e of t)e&&"object"==typeof e&&u(e)}for(let[e,t]of Object.entries(i))t&&"object"==typeof t&&t.TestBundlePath&&(u(t),i[e]=t);d.writeFileSync(s,JSON.stringify(i,null,2));let m=await h("plutil",["-convert","xml1","-o",l,s],{allowFailure:!0});if(0!==m.exitCode)throw new f("COMMAND_FAILED","Failed to write xctestrun plist",{tmpXctestrunPath:l,stderr:m.stderr});return{xctestrunPath:l,jsonPath:s}}function ex(e){try{d.existsSync(e)&&d.unlinkSync(e)}catch{}}async function eL(e,t={}){let a,i;if("ios"!==e.platform||"simulator"!==e.kind)throw new f("UNSUPPORTED_OPERATION","AX snapshot is only supported on iOS simulators");let n=await eE(),r=await N(async()=>{var e,a;let i,r,o,s=await h(n,[],{allowFailure:!0});if(t.traceLogPath&&(e=t.traceLogPath,i=((a=s).stdout??"").toString(),r=(a.stderr??"").toString(),o=` | ||
| `),0!==a.exitCode&&i.length>0&&d.appendFileSync(e,`${i} | ||
| `))),0!==s.exitCode){let e,t,a=(s.stderr??"").toString(),i=(e=a.toLowerCase()).includes("accessibility permission")?" Enable Accessibility for your terminal in System Settings > Privacy & Security > Accessibility, or use --backend xctest (slower snapshots via XCTest).":e.includes("could not find ios app content")?" AX snapshot sometimes caches empty content. Try restarting the Simulator app.":"",n=!!((t=a.toLowerCase()).includes("could not find ios app content")||t.includes("timeout"));throw new f("COMMAND_FAILED","AX snapshot failed",{stderr:`${a}${i}`,stdout:s.stdout,retryable:n})}return s},{shouldRetry:e=>{var t;return(t=e)instanceof f&&"COMMAND_FAILED"===t.code&&t.details?.retryable===!0}});try{let e=JSON.parse(r.stdout);if(e&&"object"==typeof e&&"root"in e){if(!e.root)throw Error("AX snapshot missing root");a=e.root,i=e.windowFrame??void 0}else a=e}catch(e){throw new f("COMMAND_FAILED","Invalid AX snapshot JSON",{error:String(e)})}let o=a.frame??i,s=[],l=[],c=(e,t)=>{e.frame&&s.push(e.frame);let a=e.frame&&o?{x:e.frame.x-o.x,y:e.frame.y-o.y,width:e.frame.width,height:e.frame.height}:e.frame;for(let i of(l.push({...e,frame:a,children:void 0,depth:t}),e.children??[]))c(i,t+1)};return c(a,0),{nodes:(function(e,t,a){if(!t||0===a.length)return e;let i=1/0,n=1/0;for(let e of a)e.x<i&&(i=e.x),e.y<n&&(n=e.y);return i<=5&&n<=5?e.map(e=>({...e,frame:e.frame?{x:e.frame.x+t.x,y:e.frame.y+t.y,width:e.frame.width,height:e.frame.height}:void 0})):e})(l,o,s).map((e,t)=>({index:t,type:e.subrole??e.role,label:e.label,value:e.value,identifier:e.identifier,rect:e.frame?{x:e.frame.x,y:e.frame.y,width:e.frame.width,height:e.frame.height}:void 0,depth:e.depth}))}}async function eE(){let e=function(){let e=process.cwd();for(let t=0;t<6;t+=1){let t=r.join(e,"package.json");if(d.existsSync(t))return e;e=r.dirname(e)}return process.cwd()}(),t=r.join(e,"ios-runner","AXSnapshot"),a=process.env.AGENT_DEVICE_AX_BINARY;if(a&&d.existsSync(a))return a;let i=r.join(e,"dist","bin","axsnapshot");if(d.existsSync(i))return i;let n=r.join(t,".build","release","axsnapshot");if(d.existsSync(n))return n;let o=await h("swift",["build","-c","release"],{cwd:t,allowFailure:!0});if(0!==o.exitCode||!d.existsSync(n))throw new f("COMMAND_FAILED","Failed to build AX snapshot tool",{stderr:o.stderr,stdout:o.stdout});return n}async function eR(e){let t={platform:e.platform,deviceName:e.device,udid:e.udid,serial:e.serial};if("android"===t.platform){await J();let e=await y();return await g(e,t)}if("ios"===t.platform){let e=await Y();return await g(e,t)}let a=[];try{a.push(...await y())}catch{}try{a.push(...await Y())}catch{}return await g(a,t)}async function eC(e,t,a,i,n){let r=function(e){switch(e.platform){case"android":return{open:t=>_(e,t),openDevice:()=>x(e),close:t=>L(e,t),tap:(t,a)=>E(e,t,a),longPress:(t,a,i)=>T(e,t,a,i),focus:(t,a)=>B(e,t,a),type:t=>F(e,t),fill:(t,a,i)=>$(e,t,a,i),scroll:(t,a)=>U(e,t,a),scrollIntoView:t=>V(e,t),screenshot:t=>j(e,t)};case"ios":return{open:t=>Q(e,t),openDevice:()=>ee(e),close:t=>et(e,t),tap:(t,a)=>ea(e,t,a),longPress:(t,a,i)=>ei(e,t,a,i),focus:(t,a)=>en(e,t,a),type:t=>er(e,t),fill:(t,a,i)=>eo(e,t,a,i),scroll:(t,a)=>es(e,t,a),scrollIntoView:e=>el(e),screenshot:t=>ec(e,t)};default:throw new f("UNSUPPORTED_PLATFORM",`Unsupported platform: ${e.platform}`)}}(e);switch(t){case"open":{let e=a[0];if(!e)return await r.openDevice(),{app:null};return await r.open(e),{app:e}}case"close":{let e=a[0];if(!e)return{closed:"session"};return await r.close(e),{app:e}}case"press":{let[t,i]=a.map(Number);if(Number.isNaN(t)||Number.isNaN(i))throw new f("INVALID_ARGS","press requires x y");return"ios"===e.platform&&"simulator"===e.kind?await ew(e,{command:"tap",x:t,y:i,appBundleId:n?.appBundleId},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath}):await r.tap(t,i),{x:t,y:i}}case"long-press":{let e=Number(a[0]),t=Number(a[1]),i=a[2]?Number(a[2]):void 0;if(Number.isNaN(e)||Number.isNaN(t))throw new f("INVALID_ARGS","long-press requires x y [durationMs]");return await r.longPress(e,t,i),{x:e,y:t,durationMs:i}}case"focus":{let[t,i]=a.map(Number);if(Number.isNaN(t)||Number.isNaN(i))throw new f("INVALID_ARGS","focus requires x y");return"ios"===e.platform&&"simulator"===e.kind?await ew(e,{command:"tap",x:t,y:i,appBundleId:n?.appBundleId},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath}):await r.focus(t,i),{x:t,y:i}}case"type":{let t=a.join(" ");if(!t)throw new f("INVALID_ARGS","type requires text");return"ios"===e.platform&&"simulator"===e.kind?await ew(e,{command:"type",text:t,appBundleId:n?.appBundleId},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath}):await r.type(t),{text:t}}case"fill":{let t=Number(a[0]),i=Number(a[1]),o=a.slice(2).join(" ");if(Number.isNaN(t)||Number.isNaN(i)||!o)throw new f("INVALID_ARGS","fill requires x y text");return"ios"===e.platform&&"simulator"===e.kind?(await ew(e,{command:"tap",x:t,y:i,appBundleId:n?.appBundleId},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath}),await ew(e,{command:"type",text:o,appBundleId:n?.appBundleId},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath})):await r.fill(t,i,o),{x:t,y:i,text:o}}case"scroll":{let t=a[0],i=a[1]?Number(a[1]):void 0;if(!t)throw new f("INVALID_ARGS","scroll requires direction");if("ios"===e.platform&&"simulator"===e.kind){if(!["up","down","left","right"].includes(t))throw new f("INVALID_ARGS",`Unknown direction: ${t}`);let a=function(e){switch(e){case"up":return"down";case"down":return"up";case"left":return"right";case"right":return"left"}}(t);await ew(e,{command:"swipe",direction:a,appBundleId:n?.appBundleId},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath})}else await r.scroll(t,i);return{direction:t,amount:i}}case"scrollintoview":{let t=a.join(" ").trim();if(!t)throw new f("INVALID_ARGS","scrollintoview requires text");if("ios"===e.platform&&"simulator"===e.kind){for(let a=0;a<8;a+=1){let i=await ew(e,{command:"findText",text:t,appBundleId:n?.appBundleId},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath});if(i?.found)return{text:t,attempts:a+1};await ew(e,{command:"swipe",direction:"up",appBundleId:n?.appBundleId},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath}),await new Promise(e=>setTimeout(e,300))}throw new f("COMMAND_FAILED",`scrollintoview could not find text: ${t}`)}return await r.scrollIntoView(t),{text:t}}case"screenshot":{let e=i??`./screenshot-${Date.now()}.png`;return await r.screenshot(e),{path:e}}case"back":if("ios"===e.platform){if("simulator"!==e.kind)throw new f("UNSUPPORTED_OPERATION","back is only supported on iOS simulators in v1");return await ew(e,{command:"back",appBundleId:n?.appBundleId},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath}),{action:"back"}}return await R(e),{action:"back"};case"home":if("ios"===e.platform){if("simulator"!==e.kind)throw new f("UNSUPPORTED_OPERATION","home is only supported on iOS simulators in v1");return await ew(e,{command:"home",appBundleId:n?.appBundleId},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath}),{action:"home"}}return await C(e),{action:"home"};case"app-switcher":if("ios"===e.platform){if("simulator"!==e.kind)throw new f("UNSUPPORTED_OPERATION","app-switcher is only supported on iOS simulators in v1");return await ew(e,{command:"appSwitcher",appBundleId:n?.appBundleId},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath}),{action:"app-switcher"}}return await M(e),{action:"app-switcher"};case"settings":{let[t,i,r]=a;if("ios"===e.platform)return await eu(e,t,i,r??n?.appBundleId),{setting:t,state:i};return await G(e,t,i),{setting:t,state:i}}case"snapshot":{let t=n?.snapshotBackend??"hybrid";if("ios"===e.platform){if("simulator"!==e.kind)throw new f("UNSUPPORTED_OPERATION","snapshot is only supported on iOS simulators in v1");if("ax"===t)return{nodes:(await eL(e,{traceLogPath:n?.traceLogPath})).nodes??[],truncated:!1,backend:"ax"};if("hybrid"===t){let t=(await eL(e,{traceLogPath:n?.traceLogPath})).nodes??[],a=function(e){let t=[];for(let a=0;a<e.length;a+=1){let i=e[a],n=i.depth??0;if((e[a+1]?.depth??-1)>n)continue;let r=eF(i.type);eM.has(r)&&t.push({index:a,depth:n,label:i.label,identifier:i.identifier,type:i.type})}return t}(t);if(0===a.length)return{nodes:t,truncated:!1,backend:"hybrid"};let i=await eT(e,t,a,{appBundleId:n?.appBundleId,interactiveOnly:n?.snapshotInteractiveOnly,compact:n?.snapshotCompact,depth:n?.snapshotDepth,raw:n?.snapshotRaw,verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath});return{nodes:i.nodes,truncated:i.truncated,backend:"hybrid"}}let a=await ew(e,{command:"snapshot",appBundleId:n?.appBundleId,interactiveOnly:n?.snapshotInteractiveOnly,compact:n?.snapshotCompact,depth:n?.snapshotDepth,scope:n?.snapshotScope,raw:n?.snapshotRaw},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath});return{nodes:a.nodes??[],truncated:a.truncated??!1,backend:"xctest"}}let a=await q(e,{interactiveOnly:n?.snapshotInteractiveOnly,compact:n?.snapshotCompact,depth:n?.snapshotDepth,scope:n?.snapshotScope,raw:n?.snapshotRaw});return{nodes:a.nodes??[],truncated:a.truncated??!1,backend:"android"}}default:throw new f("INVALID_ARGS",`Unknown command: ${t}`)}}let eM=new Set(["tabbar","toolbar","group"]);async function eT(e,t,a,i){let n=[...t],r=!1,o=0;for(let t of a){let a=function(e){for(let t of[e.label,e.identifier]){if(!t)continue;let e=t.trim();if(e)return e}return null}(t);if(!a)continue;let s=await ew(e,{command:"snapshot",appBundleId:i.appBundleId,interactiveOnly:i.interactiveOnly,compact:i.compact,depth:i.depth,scope:a,raw:i.raw},{verbose:i.verbose,logPath:i.logPath,traceLogPath:i.traceLogPath});s.truncated&&(r=!0);let l=(s.nodes??[]).filter(e=>{let t=eF(e.type);return"application"!==t&&"window"!==t});if(0===l.length)continue;let c=function(e,t){let a=1/0;for(let t of e){let e=t.depth??0;e<a&&(a=e)}return Number.isFinite(a)||(a=0),e.map(e=>({...e,depth:t+(e.depth??0)-a}))}(l,t.depth+1);n.splice(t.index+1+o,0,...c),o+=c.length}return{nodes:n=n.map((e,t)=>({...e,index:t})),truncated:r}}function eF(e){if(!e)return"";let t=e.replace(/XCUIElementType/gi,"").toLowerCase();return t.startsWith("ax")&&(t=t.replace(/^ax/,"")),t}function eB(e){return e.map((e,t)=>({...e,ref:`e${t+1}`}))}function e$(e){let t=e.trim();return t.startsWith("@")?t.slice(1)||null:t.startsWith("e")?t:null}function eU(e,t){return e.find(e=>e.ref===t)??null}function eV(e){return{x:Math.round(e.x+e.width/2),y:Math.round(e.y+e.height/2)}}function ej(e,t,a,i={}){let n=eq(a);if(!n)return null;let r=null;for(let a of e){if(i.requireRect&&!a.rect)continue;let e=function(e,t,a){switch(t){case"role":return function(e,t){let a=function(e){let t=e.trim();return t?((t=(t.split(".").pop()??t).replace(/XCUIElementType/gi,"").toLowerCase()).startsWith("ax")&&(t=t.replace(/^ax/,"")),t):""}(e??"");return a?a===t?2:+!!a.includes(t):0}(e.type,a);case"label":return eG(e.label,a);case"value":return eG(e.value,a);case"id":return eG(e.identifier,a);default:return Math.max(eG(e.label,a),eG(e.value,a),eG(e.identifier,a))}}(a,t,n);if(!(e<=0)&&(!r||e>r.score)&&(r={node:a,score:e},e>=2))break}return r?.node??null}function eG(e,t){let a=eq(e??"");return a?a===t?2:+!!a.includes(t):0}function eq(e){return e.trim().toLowerCase().replace(/\s+/g," ")}let eJ=new Map,eW=r.join(p.homedir(),".agent-device"),eX=r.join(eW,"daemon.json"),ez=r.join(eW,"daemon.log"),eH=r.join(eW,"sessions"),eY=function(){try{let e=function(){let e=r.dirname(c(import.meta.url)),t=e;for(let e=0;e<6;e+=1){let e=r.join(t,"package.json");if(d.existsSync(e))return t;t=r.dirname(t)}return e}();return JSON.parse(d.readFileSync(r.join(e,"package.json"),"utf8")).version??"0.0.0"}catch{return"0.0.0"}}(),eZ=a.randomBytes(24).toString("hex");function eK(e,t,a){return{appBundleId:t,verbose:e?.verbose,logPath:ez,traceLogPath:a,snapshotInteractiveOnly:e?.snapshotInteractiveOnly,snapshotCompact:e?.snapshotCompact,snapshotDepth:e?.snapshotDepth,snapshotScope:e?.snapshotScope,snapshotRaw:e?.snapshotRaw,snapshotBackend:e?.snapshotBackend}}async function eQ(e){if(e.token!==eZ)return{ok:!1,error:{code:"UNAUTHORIZED",message:"Invalid token"}};let t=e.command,a=e.session||"default";if("session_list"===t)return{ok:!0,data:{sessions:Array.from(eJ.values()).map(e=>({name:e.name,platform:e.device.platform,device:e.device.name,id:e.device.id,createdAt:e.createdAt}))}};if("devices"===t)try{let t=[];if(e.flags?.platform==="android"){let{listAndroidDevices:e}=await Promise.resolve().then(()=>({listAndroidDevices:y}));t.push(...await e())}else if(e.flags?.platform==="ios"){let{listIosDevices:e}=await Promise.resolve().then(()=>({listIosDevices:Y}));t.push(...await e())}else{let{listAndroidDevices:e}=await Promise.resolve().then(()=>({listAndroidDevices:y})),{listIosDevices:a}=await Promise.resolve().then(()=>({listIosDevices:Y}));try{t.push(...await e())}catch{}try{t.push(...await a())}catch{}}return{ok:!0,data:{devices:t}}}catch(t){let e=l(t);return{ok:!1,error:{code:e.code,message:e.message,details:e.details}}}if("apps"===t){let t=eJ.get(a),i=e.flags??{};if(!t&&!i.platform&&!i.device&&!i.udid&&!i.serial)return{ok:!1,error:{code:"INVALID_ARGS",message:"apps requires an active session or an explicit device selector (e.g. --platform ios)."}};let n=t?.device??await eR(i);if(await e9(n),"ios"===n.platform){if("simulator"!==n.kind)return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"apps list is only supported on iOS simulators"}};let{listSimulatorApps:t}=await Promise.resolve().then(()=>({listSimulatorApps:ep})),a=await t(n);return e.flags?.appsMetadata?{ok:!0,data:{apps:a}}:{ok:!0,data:{apps:a.map(e=>e.name&&e.name!==e.bundleId?`${e.name} (${e.bundleId})`:e.bundleId)}}}let{listAndroidApps:r,listAndroidAppsMetadata:o}=await Promise.resolve().then(()=>({listAndroidApps:D,listAndroidAppsMetadata:k}));return e.flags?.appsMetadata?{ok:!0,data:{apps:await o(n,e.flags?.appsFilter)}}:{ok:!0,data:{apps:await r(n,e.flags?.appsFilter)}}}if("appstate"===t){let t=eJ.get(a),i=e.flags??{},n=t?.device??await eR(i);if(await e9(n),"ios"===n.platform){if(t?.appBundleId)return{ok:!0,data:{platform:"ios",appBundleId:t.appBundleId,appName:t.appName??t.appBundleId,source:"session"}};let a=await e1(n,t?.trace?.outPath,e.flags);return{ok:!0,data:{platform:"ios",appName:a.appName,appBundleId:a.appBundleId,source:a.source}}}let{getAndroidAppState:r}=await Promise.resolve().then(()=>({getAndroidAppState:P})),o=await r(n);return{ok:!0,data:{platform:"android",package:o.package,activity:o.activity}}}if("open"===t){let i;if(eJ.has(a))return{ok:!1,error:{code:"INVALID_ARGS",message:"Session already active. Close it first or pass a new --session name."}};let n=await eR(e.flags??{});await e9(n);let r=Array.from(eJ.values()).find(e=>e.device.id===n.id);if(r)return{ok:!1,error:{code:"DEVICE_IN_USE",message:`Device is already in use by session "${r.name}".`,details:{session:r.name,deviceId:n.id,deviceName:n.name}}};let o=e.positionals?.[0];if("ios"===n.platform)try{let{resolveIosApp:t}=await Promise.resolve().then(()=>({resolveIosApp:K}));i=await t(n,e.positionals?.[0]??"")}catch{i=void 0}await eC(n,"open",e.positionals??[],e.flags?.out,{...eK(e.flags,i)});let s={name:a,device:n,createdAt:Date.now(),appBundleId:i,appName:o,actions:[]};return e0(s,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{session:a}}),eJ.set(a,s),{ok:!0,data:{session:a}}}if("replay"===t){let t=e.positionals?.[0];if(!t)return{ok:!1,error:{code:"INVALID_ARGS",message:"replay requires a path"}};try{let e=e8(t),i=JSON.parse(d.readFileSync(e,"utf8")),n=i.optimizedActions??i.actions??[];for(let e of n)e&&"replay"!==e.command&&await eQ({token:eZ,session:a,command:e.command,positionals:e.positionals??[],flags:e.flags??{}});return{ok:!0,data:{replayed:n.length,session:a}}}catch(t){let e=l(t);return{ok:!1,error:{code:e.code,message:e.message}}}}if("close"===t){let i=eJ.get(a);return i?(e.positionals&&e.positionals.length>0&&await eC(i.device,"close",e.positionals??[],e.flags?.out,{...eK(e.flags,i.appBundleId,i.trace?.outPath)}),"ios"===i.device.platform&&"simulator"===i.device.kind&&await ey(i.device.id),e0(i,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{session:a}}),e3(i),eJ.delete(a),{ok:!0,data:{session:a}}):{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session"}}}if("snapshot"===t){let i=eJ.get(a),n=i?.device??await eR(e.flags??{});i||await e9(n);let r=i?.appBundleId,o=e.flags?.snapshotScope;if(o&&o.trim().startsWith("@")){if(!i?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"Ref scope requires an existing snapshot in session."}};let e=e$(o.trim());if(!e)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref scope: ${o}`}};let t=eU(i.snapshot.nodes,e),a=t?e7(t,i.snapshot.nodes):void 0;if(!a)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${o} not found or has no label`}};o=a}let s=await eC(n,"snapshot",[],e.flags?.out,{...eK({...e.flags,snapshotScope:o},r,i?.trace?.outPath)}),l=s?.nodes??[],c=eB(e.flags?.snapshotRaw?l:te(l)),u={nodes:c,truncated:s?.truncated,createdAt:Date.now(),backend:s?.backend},d={name:a,device:n,createdAt:i?.createdAt??Date.now(),appBundleId:i?.appBundleId??r,snapshot:u,actions:i?.actions??[],appName:i?.appName};return e0(d,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{nodes:c.length,truncated:s?.truncated??!1}}),eJ.set(a,d),{ok:!0,data:{nodes:c,truncated:s?.truncated??!1,appName:d.appBundleId?d.appName??d.appBundleId:void 0,appBundleId:d.appBundleId}}}if("wait"===t){let i=eJ.get(a),n=i?.device??await eR(e.flags??{});i||await e9(n);let r=e.positionals??[];if(0===r.length)return{ok:!1,error:{code:"INVALID_ARGS",message:"wait requires a duration or text"}};let o=e=>{if(!e)return null;let t=Number(e);return Number.isFinite(t)?t:null},s=o(r[0]);if(null!==s)return await new Promise(e=>setTimeout(e,s)),i&&e0(i,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{waitedMs:s}}),{ok:!0,data:{waitedMs:s}};let l="",c=null;if("text"===r[0])l=null!==(c=o(r[r.length-1]))?r.slice(1,-1).join(" "):r.slice(1).join(" ");else if(r[0].startsWith("@")){if(!i?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"Ref wait requires an existing snapshot in session."}};let e=e$(r[0]);if(!e)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref: ${r[0]}`}};let t=eU(i.snapshot.nodes,e),a=t?e7(t,i.snapshot.nodes):void 0;if(!a)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${r[0]} not found or has no label`}};c=o(r[r.length-1]),l=a}else l=null!==(c=o(r[r.length-1]))?r.slice(0,-1).join(" "):r.join(" ");if(!(l=l.trim()))return{ok:!1,error:{code:"INVALID_ARGS",message:"wait requires text"}};let u=c??1e4,d=Date.now();for(;Date.now()-d<u;){if("ios"===n.platform&&"simulator"===n.kind){let a=await ew(n,{command:"findText",text:l,appBundleId:i?.appBundleId},{verbose:e.flags?.verbose,logPath:ez,traceLogPath:i?.trace?.outPath});if(a?.found)return i&&e0(i,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{text:l,waitedMs:Date.now()-d}}),{ok:!0,data:{text:l,waitedMs:Date.now()-d}}}else if("android"!==n.platform)return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"wait is not supported on this device"}};else if(e6(eB((await q(n,{scope:l})).nodes??[]),l))return i&&e0(i,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{text:l,waitedMs:Date.now()-d}}),{ok:!0,data:{text:l,waitedMs:Date.now()-d}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:`wait timed out for text: ${l}`}}}if("alert"===t){let i=eJ.get(a),n=i?.device??await eR(e.flags??{});i||await e9(n);let r=(e.positionals?.[0]??"get").toLowerCase();if("ios"!==n.platform||"simulator"!==n.kind)return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"alert is only supported on iOS simulators in v1"}};if("wait"===r){let a=(e=>{if(!e)return null;let t=Number(e);return Number.isFinite(t)?t:null})(e.positionals?.[1])??1e4,r=Date.now();for(;Date.now()-r<a;){try{let a=await ew(n,{command:"alert",action:"get",appBundleId:i?.appBundleId},{verbose:e.flags?.verbose,logPath:ez,traceLogPath:i?.trace?.outPath});return i&&e0(i,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:a}),{ok:!0,data:a}}catch{}await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:"alert wait timed out"}}}let o=await ew(n,{command:"alert",action:"accept"===r||"dismiss"===r?r:"get",appBundleId:i?.appBundleId},{verbose:e.flags?.verbose,logPath:ez,traceLogPath:i?.trace?.outPath});return i&&e0(i,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:o}),{ok:!0,data:o}}if("record"===t){let i=(e.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(i))return{ok:!1,error:{code:"INVALID_ARGS",message:"record requires start|stop"}};let n=eJ.get(a),o=n?.device??await eR(e.flags??{});n||await e9(o);let s=n??{name:a,device:o,createdAt:Date.now(),actions:[]};if("start"===i){if(s.recording)return{ok:!1,error:{code:"INVALID_ARGS",message:"recording already in progress"}};let i=e.positionals?.[1]??`./recording-${Date.now()}.mp4`,n=r.resolve(i),l=r.dirname(n);if(d.existsSync(l)||d.mkdirSync(l,{recursive:!0}),"ios"===o.platform){if("simulator"!==o.kind)return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"record is only supported on iOS simulators in v1"}};let{child:e,wait:t}=u("xcrun",["simctl","io",o.id,"recordVideo",n],{allowFailure:!0});s.recording={platform:"ios",outPath:n,child:e,wait:t}}else{let e=`/sdcard/agent-device-recording-${Date.now()}.mp4`,{child:t,wait:a}=u("adb",["-s",o.id,"shell","screenrecord",e],{allowFailure:!0});s.recording={platform:"android",outPath:n,remotePath:e,child:t,wait:a}}return eJ.set(a,s),e0(s,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{action:"start"}}),{ok:!0,data:{recording:"started",outPath:i}}}if(!s.recording)return{ok:!1,error:{code:"INVALID_ARGS",message:"no active recording"}};let l=s.recording;l.child.kill("SIGINT");try{await l.wait}catch{}if("android"===l.platform&&l.remotePath)try{await h("adb",["-s",o.id,"pull",l.remotePath,l.outPath],{allowFailure:!0}),await h("adb",["-s",o.id,"shell","rm","-f",l.remotePath],{allowFailure:!0})}catch{}return s.recording=void 0,e0(s,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{action:"stop",outPath:l.outPath}}),{ok:!0,data:{recording:"stopped",outPath:l.outPath}}}if("trace"===t){let i=(e.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(i))return{ok:!1,error:{code:"INVALID_ARGS",message:"trace requires start|stop"}};let n=eJ.get(a);if(!n)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session"}};if("start"===i){let a,i;if(n.trace)return{ok:!1,error:{code:"INVALID_ARGS",message:"trace already in progress"}};let o=e8(e.positionals?.[1]??(a=n.name.replace(/[^a-zA-Z0-9._-]/g,"_"),i=new Date().toISOString().replace(/[:.]/g,"-"),r.join(eH,`${a}-${i}.trace.log`)));return d.mkdirSync(r.dirname(o),{recursive:!0}),d.appendFileSync(o,""),n.trace={outPath:o,startedAt:Date.now()},e0(n,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{action:"start",outPath:o}}),{ok:!0,data:{trace:"started",outPath:o}}}if(!n.trace)return{ok:!1,error:{code:"INVALID_ARGS",message:"no active trace"}};let o=n.trace.outPath;if(e.positionals?.[1]){let t=e8(e.positionals[1]);d.mkdirSync(r.dirname(t),{recursive:!0}),d.existsSync(o)?d.renameSync(o,t):d.appendFileSync(t,""),o=t}return n.trace=void 0,e0(n,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{action:"stop",outPath:o}}),{ok:!0,data:{trace:"stopped",outPath:o}}}if("settings"===t){let i=e.positionals?.[0],n=e.positionals?.[1];if(!i||!n)return{ok:!1,error:{code:"INVALID_ARGS",message:"settings requires <wifi|airplane|location> <on|off>"}};let r=eJ.get(a),o=r?.device??await eR(e.flags??{});r||await e9(o);let s=r?.appBundleId,l=await eC(o,"settings",[i,n,s??""],e.flags?.out,{...eK(e.flags,s,r?.trace?.outPath)});return r&&e0(r,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:l??{setting:i,state:n}}),{ok:!0,data:l??{setting:i,state:n}}}if("find"===t){let n=e.positionals??[];if(0===n.length)return{ok:!1,error:{code:"INVALID_ARGS",message:"find requires a locator or text"}};let{locator:r,query:o,action:s,value:l,timeoutMs:c}=function(e){let t="any",a=0;["text","label","value","role","id"].includes(e[0])&&(t=e[0],a=1);let i=e[a]??"",n=e.slice(a+1);if(0===n.length)return{locator:t,query:i,action:"click"};let r=n[0].toLowerCase();if("get"===r){let e=n[1]?.toLowerCase();if("text"===e)return{locator:t,query:i,action:"get_text"};if("attrs"===e)return{locator:t,query:i,action:"get_attrs"};throw new f("INVALID_ARGS","find get only supports text or attrs")}if("wait"===r)return{locator:t,query:i,action:"wait",timeoutMs:function(e){if(!e)return null;let t=Number(e);return Number.isFinite(t)?t:null}(n[1])??void 0};if("exists"===r)return{locator:t,query:i,action:"exists"};if("click"===r)return{locator:t,query:i,action:"click"};if("focus"===r)return{locator:t,query:i,action:"focus"};if("fill"===r)return{locator:t,query:i,action:"fill",value:n.slice(1).join(" ")};if("type"===r)return{locator:t,query:i,action:"type",value:n.slice(1).join(" ")};throw new f("INVALID_ARGS",`Unsupported find action: ${n[0]}`)}(n);if(!o)return{ok:!1,error:{code:"INVALID_ARGS",message:"find requires a value"}};let u=eJ.get(a);if(!u&&"exists"!==s&&"wait"!==s&&"get_text"!==s&&"get_attrs"!==s)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let d=u?.device??await eR(e.flags??{});u||await e9(d);let p=u?.appBundleId,h="role"!==r?o:void 0,m="click"===s||"focus"===s||"fill"===s||"type"===s,w=0,g=null,y=async()=>{let t=Date.now();if(g&&t-w<750)return{nodes:g};let i=await eC(d,"snapshot",[],e.flags?.out,{...eK({...e.flags,snapshotScope:h,snapshotInteractiveOnly:m,snapshotCompact:m},p,u?.trace?.outPath)}),n=i?.nodes??[],r=eB(e.flags?.snapshotRaw?n:te(n));return w=t,g=r,u&&(u.snapshot={nodes:r,truncated:i?.truncated,createdAt:Date.now(),backend:i?.backend},eJ.set(a,u)),{nodes:r,truncated:i?.truncated,backend:i?.backend}};if("wait"===s){let a=c??1e4,i=Date.now();for(;Date.now()-i<a;){let{nodes:a}=await y();if(ej(a,r,o,{requireRect:!1}))return u&&e0(u,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{found:!0,waitedMs:Date.now()-i}}),{ok:!0,data:{found:!0,waitedMs:Date.now()-i}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:"find wait timed out"}}}let{nodes:v}=await y(),I=ej(v,r,o,{requireRect:m});if(!I)return{ok:!1,error:{code:"COMMAND_FAILED",message:"find did not match any element"}};let N=`@${I.ref}`,b={...e.flags??{},noRecord:!0};if("exists"===s)return u&&e0(u,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{found:!0}}),{ok:!0,data:{found:!0}};if("get_text"===s){var i;let a=[(i=I).label,i.value,i.identifier].map(e=>"string"==typeof e?e.trim():"").filter(e=>e.length>0)[0]??"";return u&&e0(u,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{ref:N,action:"get text",text:a}}),{ok:!0,data:{ref:N,text:a,node:I}}}if("get_attrs"===s)return u&&e0(u,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{ref:N,action:"get attrs"}}),{ok:!0,data:{ref:N,node:I}};if("click"===s){let i=await eQ({token:eZ,session:a,command:"click",positionals:[N],flags:b});return i.ok&&u&&e0(u,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{ref:N,action:"click"}}),i}if("fill"===s){if(!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"find fill requires text"}};let i=await eQ({token:eZ,session:a,command:"fill",positionals:[N,l],flags:b});return i.ok&&u&&e0(u,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{ref:N,action:"fill"}}),i}if("focus"===s){let a=I.rect?eV(I.rect):null;if(!a)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};let i=await eC(d,"focus",[String(a.x),String(a.y)],e.flags?.out,{...eK(e.flags,u?.appBundleId,u?.trace?.outPath)});return u&&e0(u,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{ref:N,action:"focus"}}),{ok:!0,data:i??{ref:N}}}if("type"===s){if(!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"find type requires text"}};let a=I.rect?eV(I.rect):null;if(!a)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};await eC(d,"focus",[String(a.x),String(a.y)],e.flags?.out,{...eK(e.flags,u?.appBundleId,u?.trace?.outPath)});let i=await eC(d,"type",[l],e.flags?.out,{...eK(e.flags,u?.appBundleId,u?.trace?.outPath)});return u&&e0(u,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{ref:N,action:"type"}}),{ok:!0,data:i??{ref:N}}}}if("click"===t){let i=eJ.get(a);if(!i?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}};let n=e.positionals?.[0]??"",r=e$(n);if(!r)return{ok:!1,error:{code:"INVALID_ARGS",message:"click requires a ref like @e2"}};let o=eU(i.snapshot.nodes,r);if(!o?.rect&&e.positionals.length>1){let t=e.positionals.slice(1).join(" ").trim();t.length>0&&(o=e6(i.snapshot.nodes,t))}if(!o?.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${n} not found or has no bounds`}};let s=e7(o,i.snapshot.nodes),l=o.label?.trim();if("ios"===i.device.platform&&"simulator"===i.device.kind&&l&&function(e,t){let a=t.trim().toLowerCase();if(!a)return!1;let i=0;for(let t of e)if((t.label??"").trim().toLowerCase()===a&&(i+=1)>1)return!1;return 1===i}(i.snapshot.nodes,l))return await ew(i.device,{command:"tap",text:l,appBundleId:i.appBundleId},{verbose:e.flags?.verbose,logPath:ez,traceLogPath:i?.trace?.outPath}),e0(i,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{ref:r,refLabel:l,mode:"text"}}),{ok:!0,data:{ref:r,mode:"text"}};let{x:c,y:u}=eV(o.rect);return await eC(i.device,"press",[String(c),String(u)],e.flags?.out,{...eK(e.flags,i.appBundleId,i.trace?.outPath)}),e0(i,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{ref:r,x:c,y:u,refLabel:s}}),{ok:!0,data:{ref:r,x:c,y:u}}}if("fill"===t){let i=eJ.get(a);if(e.positionals?.[0]?.startsWith("@")){if(!i?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}};let a=e$(e.positionals[0]);if(!a)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires a ref like @e2"}};let n=e.positionals.length>=3?e.positionals[1]:"",r=e.positionals.length>=3?e.positionals.slice(2).join(" "):e.positionals.slice(1).join(" ");if(!r)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after ref"}};let o=eU(i.snapshot.nodes,a);if(!o?.rect&&n&&(o=e6(i.snapshot.nodes,n)),!o?.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${e.positionals[0]} not found or has no bounds`}};let s=e7(o,i.snapshot.nodes),{x:l,y:c}=eV(o.rect),u=await eC(i.device,"fill",[String(l),String(c),r],e.flags?.out,{...eK(e.flags,i.appBundleId,i.trace?.outPath)});return e0(i,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:u??{ref:a,x:l,y:c,refLabel:s}}),{ok:!0,data:u??{ref:a,x:l,y:c}}}}if("get"===t){let i=e.positionals?.[0],n=e.positionals?.[1];if("text"!==i&&"attrs"!==i)return{ok:!1,error:{code:"INVALID_ARGS",message:"get only supports text or attrs"}};let r=eJ.get(a);if(!r?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}};let o=e$(n??"");if(!o)return{ok:!1,error:{code:"INVALID_ARGS",message:"get text requires a ref like @e2"}};let s=eU(r.snapshot.nodes,o);if(!s&&e.positionals.length>2){let t=e.positionals.slice(2).join(" ").trim();t.length>0&&(s=e6(r.snapshot.nodes,t))}if(!s)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${n} not found`}};if("attrs"===i)return e0(r,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{ref:o}}),{ok:!0,data:{ref:o,node:s}};let l=[s.label,s.value,s.identifier].map(e=>"string"==typeof e?e.trim():"").filter(e=>e.length>0)[0]??"";return e0(r,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{ref:o,text:l,refLabel:l||void 0}}),{ok:!0,data:{ref:o,text:l,node:s}}}let n=eJ.get(a);if(!n)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let o=await eC(n.device,t,e.positionals??[],e.flags?.out,{...eK(e.flags,n.appBundleId,n.trace?.outPath)});return e0(n,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:o??{}}),{ok:!0,data:o??{}}}function e0(e,t){t.flags?.noRecord||e.actions.push({ts:Date.now(),command:t.command,positionals:t.positionals,flags:function(e){if(!e)return{};let{platform:t,device:a,udid:i,serial:n,out:r,verbose:o,snapshotInteractiveOnly:s,snapshotCompact:l,snapshotDepth:c,snapshotScope:u,snapshotRaw:d,snapshotBackend:p,appsMetadata:f,noRecord:h,recordJson:m}=e;return{platform:t,device:a,udid:i,serial:n,out:r,verbose:o,snapshotInteractiveOnly:s,snapshotCompact:l,snapshotDepth:c,snapshotScope:u,snapshotRaw:d,snapshotBackend:p,appsMetadata:f,noRecord:h,recordJson:m}}(t.flags),result:t.result})}async function e1(e,t,a){let i=e2(await eC(e,"snapshot",[],a?.out,{...eK({...a,snapshotDepth:1,snapshotCompact:!0,snapshotBackend:"ax"},void 0,t)}));if(i?.appName||i?.appBundleId)return{appName:i.appName??i.appBundleId??"unknown",appBundleId:i.appBundleId,source:"snapshot-ax"};let n=e2(await eC(e,"snapshot",[],a?.out,{...eK({...a,snapshotDepth:1,snapshotCompact:!0,snapshotBackend:"xctest"},void 0,t)}));return{appName:n?.appName??n?.appBundleId??"unknown",appBundleId:n?.appBundleId,source:"snapshot-xctest"}}function e2(e){let t=eB(e?.nodes??[]),a=t.find(e=>"application"===tt(e.type??""))??t[0];if(!a)return null;let i=a.label?.trim(),n=a.identifier?.trim();return i||n?{appName:i||void 0,appBundleId:n||void 0}:null}function e3(e){try{d.existsSync(eH)||d.mkdirSync(eH,{recursive:!0});let t=e.name.replace(/[^a-zA-Z0-9._-]/g,"_"),a=new Date(e.createdAt).toISOString().replace(/[:.]/g,"-"),i=r.join(eH,`${t}-${a}.ad`),n=r.join(eH,`${t}-${a}.json`),o={name:e.name,device:e.device,createdAt:e.createdAt,appBundleId:e.appBundleId,actions:e.actions,optimizedActions:function(e){let t=[];for(let a of e.actions)if("snapshot"!==a.command){if("click"===a.command||"fill"===a.command||"get"===a.command){let i=a.result?.refLabel;"string"==typeof i&&i.trim().length>0&&t.push({ts:a.ts,command:"snapshot",positionals:[],flags:{platform:e.device.platform,snapshotInteractiveOnly:!0,snapshotCompact:!0,snapshotScope:i.trim()},result:{scope:i.trim()}})}t.push(a)}return t}(e)},s=function(e,t){let a=[],i=e.device.name.replace(/"/g,'\\"'),n=e.device.kind?` kind=${e.device.kind}`:"";for(let r of(a.push(`context platform=${e.device.platform} device="${i}"${n} theme=unknown`),t))r.flags?.noRecord||a.push(function(e){let t=[e.command];if("click"===e.command){let a=e.positionals?.[0];if(a){t.push(e4(a));let i=e.result?.refLabel;return"string"==typeof i&&i.trim().length>0&&t.push(e4(i)),t.join(" ")}}if("fill"===e.command){let a=e.positionals?.[0];if(a&&a.startsWith("@")){t.push(e4(a));let i=e.result?.refLabel,n=e.positionals.slice(1).join(" ");return"string"==typeof i&&i.trim().length>0&&t.push(e4(i)),n&&t.push(e4(n)),t.join(" ")}}if("get"===e.command){let a=e.positionals?.[0],i=e.positionals?.[1];if(a&&i){t.push(e4(a)),t.push(e4(i));let n=e.result?.refLabel;return"string"==typeof n&&n.trim().length>0&&t.push(e4(n)),t.join(" ")}}if("snapshot"===e.command)return e.flags?.snapshotInteractiveOnly&&t.push("-i"),e.flags?.snapshotCompact&&t.push("-c"),"number"==typeof e.flags?.snapshotDepth&&t.push("-d",String(e.flags.snapshotDepth)),e.flags?.snapshotScope&&t.push("-s",e4(e.flags.snapshotScope)),e.flags?.snapshotRaw&&t.push("--raw"),e.flags?.snapshotBackend&&t.push("--backend",e.flags.snapshotBackend),t.join(" ");for(let a of e.positionals??[])t.push(e4(a));return t.join(" ")}(r));return`${a.join("\n")} | ||
| `))),0!==s.exitCode){let e,t,a=(s.stderr??"").toString(),i=(e=a.toLowerCase()).includes("accessibility permission")?" Enable Accessibility for your terminal in System Settings > Privacy & Security > Accessibility, or use --backend xctest (slower snapshots via XCTest).":e.includes("could not find ios app content")?" AX snapshot sometimes caches empty content. Try restarting the Simulator app.":"",n=!!((t=a.toLowerCase()).includes("could not find ios app content")||t.includes("timeout"));throw new f("COMMAND_FAILED","AX snapshot failed",{stderr:`${a}${i}`,stdout:s.stdout,retryable:n})}return s},{shouldRetry:e=>{var t;return(t=e)instanceof f&&"COMMAND_FAILED"===t.code&&t.details?.retryable===!0}});try{let e=JSON.parse(r.stdout);if(e&&"object"==typeof e&&"root"in e){if(!e.root)throw Error("AX snapshot missing root");a=e.root,i=e.windowFrame??void 0}else a=e}catch(e){throw new f("COMMAND_FAILED","Invalid AX snapshot JSON",{error:String(e)})}let o=a.frame??i,s=[],l=[],c=(e,t)=>{e.frame&&s.push(e.frame);let a=e.frame&&o?{x:e.frame.x-o.x,y:e.frame.y-o.y,width:e.frame.width,height:e.frame.height}:e.frame;for(let i of(l.push({...e,frame:a,children:void 0,depth:t}),e.children??[]))c(i,t+1)};return c(a,0),{nodes:(function(e,t,a){if(!t||0===a.length)return e;let i=1/0,n=1/0;for(let e of a)e.x<i&&(i=e.x),e.y<n&&(n=e.y);return i<=5&&n<=5?e.map(e=>({...e,frame:e.frame?{x:e.frame.x+t.x,y:e.frame.y+t.y,width:e.frame.width,height:e.frame.height}:void 0})):e})(l,o,s).map((e,t)=>({index:t,type:e.subrole??e.role,label:e.label,value:e.value,identifier:e.identifier,rect:e.frame?{x:e.frame.x,y:e.frame.y,width:e.frame.width,height:e.frame.height}:void 0,depth:e.depth}))}}async function eE(){let e=function(){let e=r.dirname(c(import.meta.url));for(let t=0;t<6;t+=1){let t=r.join(e,"package.json");if(d.existsSync(t))return e;e=r.dirname(e)}return process.cwd()}(),t=r.join(e,"ios-runner","AXSnapshot"),a=process.env.AGENT_DEVICE_AX_BINARY;if(a&&d.existsSync(a))return a;let i=r.join(e,"dist","bin","axsnapshot");if(d.existsSync(i))return i;let n=r.join(t,".build","release","axsnapshot");if(d.existsSync(n))return n;let o=await h("swift",["build","-c","release"],{cwd:t,allowFailure:!0});if(0!==o.exitCode||!d.existsSync(n))throw new f("COMMAND_FAILED","Failed to build AX snapshot tool",{stderr:o.stderr,stdout:o.stdout});return n}async function eR(e){let t={platform:e.platform,deviceName:e.device,udid:e.udid,serial:e.serial};if("android"===t.platform){await J();let e=await y();return await g(e,t)}if("ios"===t.platform){let e=await Y();return await g(e,t)}let a=[];try{a.push(...await y())}catch{}try{a.push(...await Y())}catch{}return await g(a,t)}async function eC(e,t,a,i,n){let r=function(e){switch(e.platform){case"android":return{open:t=>_(e,t),openDevice:()=>x(e),close:t=>L(e,t),tap:(t,a)=>E(e,t,a),longPress:(t,a,i)=>T(e,t,a,i),focus:(t,a)=>B(e,t,a),type:t=>F(e,t),fill:(t,a,i)=>$(e,t,a,i),scroll:(t,a)=>U(e,t,a),scrollIntoView:t=>V(e,t),screenshot:t=>j(e,t)};case"ios":return{open:t=>Q(e,t),openDevice:()=>ee(e),close:t=>et(e,t),tap:(t,a)=>ea(e,t,a),longPress:(t,a,i)=>ei(e,t,a,i),focus:(t,a)=>en(e,t,a),type:t=>er(e,t),fill:(t,a,i)=>eo(e,t,a,i),scroll:(t,a)=>es(e,t,a),scrollIntoView:e=>el(e),screenshot:t=>ec(e,t)};default:throw new f("UNSUPPORTED_PLATFORM",`Unsupported platform: ${e.platform}`)}}(e);switch(t){case"open":{let e=a[0];if(!e)return await r.openDevice(),{app:null};return await r.open(e),{app:e}}case"close":{let e=a[0];if(!e)return{closed:"session"};return await r.close(e),{app:e}}case"press":{let[t,i]=a.map(Number);if(Number.isNaN(t)||Number.isNaN(i))throw new f("INVALID_ARGS","press requires x y");return"ios"===e.platform&&"simulator"===e.kind?await ew(e,{command:"tap",x:t,y:i,appBundleId:n?.appBundleId},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath}):await r.tap(t,i),{x:t,y:i}}case"long-press":{let e=Number(a[0]),t=Number(a[1]),i=a[2]?Number(a[2]):void 0;if(Number.isNaN(e)||Number.isNaN(t))throw new f("INVALID_ARGS","long-press requires x y [durationMs]");return await r.longPress(e,t,i),{x:e,y:t,durationMs:i}}case"focus":{let[t,i]=a.map(Number);if(Number.isNaN(t)||Number.isNaN(i))throw new f("INVALID_ARGS","focus requires x y");return"ios"===e.platform&&"simulator"===e.kind?await ew(e,{command:"tap",x:t,y:i,appBundleId:n?.appBundleId},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath}):await r.focus(t,i),{x:t,y:i}}case"type":{let t=a.join(" ");if(!t)throw new f("INVALID_ARGS","type requires text");return"ios"===e.platform&&"simulator"===e.kind?await ew(e,{command:"type",text:t,appBundleId:n?.appBundleId},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath}):await r.type(t),{text:t}}case"fill":{let t=Number(a[0]),i=Number(a[1]),o=a.slice(2).join(" ");if(Number.isNaN(t)||Number.isNaN(i)||!o)throw new f("INVALID_ARGS","fill requires x y text");return"ios"===e.platform&&"simulator"===e.kind?(await ew(e,{command:"tap",x:t,y:i,appBundleId:n?.appBundleId},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath}),await ew(e,{command:"type",text:o,appBundleId:n?.appBundleId},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath})):await r.fill(t,i,o),{x:t,y:i,text:o}}case"scroll":{let t=a[0],i=a[1]?Number(a[1]):void 0;if(!t)throw new f("INVALID_ARGS","scroll requires direction");if("ios"===e.platform&&"simulator"===e.kind){if(!["up","down","left","right"].includes(t))throw new f("INVALID_ARGS",`Unknown direction: ${t}`);let a=function(e){switch(e){case"up":return"down";case"down":return"up";case"left":return"right";case"right":return"left"}}(t);await ew(e,{command:"swipe",direction:a,appBundleId:n?.appBundleId},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath})}else await r.scroll(t,i);return{direction:t,amount:i}}case"scrollintoview":{let t=a.join(" ").trim();if(!t)throw new f("INVALID_ARGS","scrollintoview requires text");if("ios"===e.platform&&"simulator"===e.kind){for(let a=0;a<8;a+=1){let i=await ew(e,{command:"findText",text:t,appBundleId:n?.appBundleId},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath});if(i?.found)return{text:t,attempts:a+1};await ew(e,{command:"swipe",direction:"up",appBundleId:n?.appBundleId},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath}),await new Promise(e=>setTimeout(e,300))}throw new f("COMMAND_FAILED",`scrollintoview could not find text: ${t}`)}return await r.scrollIntoView(t),{text:t}}case"screenshot":{let e=i??`./screenshot-${Date.now()}.png`;return await r.screenshot(e),{path:e}}case"back":if("ios"===e.platform){if("simulator"!==e.kind)throw new f("UNSUPPORTED_OPERATION","back is only supported on iOS simulators in v1");return await ew(e,{command:"back",appBundleId:n?.appBundleId},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath}),{action:"back"}}return await R(e),{action:"back"};case"home":if("ios"===e.platform){if("simulator"!==e.kind)throw new f("UNSUPPORTED_OPERATION","home is only supported on iOS simulators in v1");return await ew(e,{command:"home",appBundleId:n?.appBundleId},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath}),{action:"home"}}return await C(e),{action:"home"};case"app-switcher":if("ios"===e.platform){if("simulator"!==e.kind)throw new f("UNSUPPORTED_OPERATION","app-switcher is only supported on iOS simulators in v1");return await ew(e,{command:"appSwitcher",appBundleId:n?.appBundleId},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath}),{action:"app-switcher"}}return await M(e),{action:"app-switcher"};case"settings":{let[t,i,r]=a;if("ios"===e.platform)return await eu(e,t,i,r??n?.appBundleId),{setting:t,state:i};return await G(e,t,i),{setting:t,state:i}}case"snapshot":{let t=n?.snapshotBackend??"hybrid";if("ios"===e.platform){if("simulator"!==e.kind)throw new f("UNSUPPORTED_OPERATION","snapshot is only supported on iOS simulators in v1");if("ax"===t)return{nodes:(await eL(e,{traceLogPath:n?.traceLogPath})).nodes??[],truncated:!1,backend:"ax"};if("hybrid"===t){let t=(await eL(e,{traceLogPath:n?.traceLogPath})).nodes??[],a=function(e){let t=[];for(let a=0;a<e.length;a+=1){let i=e[a],n=i.depth??0;if((e[a+1]?.depth??-1)>n)continue;let r=eF(i.type);eM.has(r)&&t.push({index:a,depth:n,label:i.label,identifier:i.identifier,type:i.type})}return t}(t);if(0===a.length)return{nodes:t,truncated:!1,backend:"hybrid"};let i=await eT(e,t,a,{appBundleId:n?.appBundleId,interactiveOnly:n?.snapshotInteractiveOnly,compact:n?.snapshotCompact,depth:n?.snapshotDepth,raw:n?.snapshotRaw,verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath});return{nodes:i.nodes,truncated:i.truncated,backend:"hybrid"}}let a=await ew(e,{command:"snapshot",appBundleId:n?.appBundleId,interactiveOnly:n?.snapshotInteractiveOnly,compact:n?.snapshotCompact,depth:n?.snapshotDepth,scope:n?.snapshotScope,raw:n?.snapshotRaw},{verbose:n?.verbose,logPath:n?.logPath,traceLogPath:n?.traceLogPath});return{nodes:a.nodes??[],truncated:a.truncated??!1,backend:"xctest"}}let a=await q(e,{interactiveOnly:n?.snapshotInteractiveOnly,compact:n?.snapshotCompact,depth:n?.snapshotDepth,scope:n?.snapshotScope,raw:n?.snapshotRaw});return{nodes:a.nodes??[],truncated:a.truncated??!1,backend:"android"}}default:throw new f("INVALID_ARGS",`Unknown command: ${t}`)}}let eM=new Set(["tabbar","toolbar","group"]);async function eT(e,t,a,i){let n=[...t],r=!1,o=0;for(let t of a){let a=function(e){for(let t of[e.label,e.identifier]){if(!t)continue;let e=t.trim();if(e)return e}return null}(t);if(!a)continue;let s=await ew(e,{command:"snapshot",appBundleId:i.appBundleId,interactiveOnly:i.interactiveOnly,compact:i.compact,depth:i.depth,scope:a,raw:i.raw},{verbose:i.verbose,logPath:i.logPath,traceLogPath:i.traceLogPath});s.truncated&&(r=!0);let l=(s.nodes??[]).filter(e=>{let t=eF(e.type);return"application"!==t&&"window"!==t});if(0===l.length)continue;let c=function(e,t){let a=1/0;for(let t of e){let e=t.depth??0;e<a&&(a=e)}return Number.isFinite(a)||(a=0),e.map(e=>({...e,depth:t+(e.depth??0)-a}))}(l,t.depth+1);n.splice(t.index+1+o,0,...c),o+=c.length}return{nodes:n=n.map((e,t)=>({...e,index:t})),truncated:r}}function eF(e){if(!e)return"";let t=e.replace(/XCUIElementType/gi,"").toLowerCase();return t.startsWith("ax")&&(t=t.replace(/^ax/,"")),t}function eB(e){return e.map((e,t)=>({...e,ref:`e${t+1}`}))}function e$(e){let t=e.trim();return t.startsWith("@")?t.slice(1)||null:t.startsWith("e")?t:null}function eU(e,t){return e.find(e=>e.ref===t)??null}function eV(e){return{x:Math.round(e.x+e.width/2),y:Math.round(e.y+e.height/2)}}function ej(e,t,a,i={}){let n=eq(a);if(!n)return null;let r=null;for(let a of e){if(i.requireRect&&!a.rect)continue;let e=function(e,t,a){switch(t){case"role":return function(e,t){let a=function(e){let t=e.trim();return t?((t=(t.split(".").pop()??t).replace(/XCUIElementType/gi,"").toLowerCase()).startsWith("ax")&&(t=t.replace(/^ax/,"")),t):""}(e??"");return a?a===t?2:+!!a.includes(t):0}(e.type,a);case"label":return eG(e.label,a);case"value":return eG(e.value,a);case"id":return eG(e.identifier,a);default:return Math.max(eG(e.label,a),eG(e.value,a),eG(e.identifier,a))}}(a,t,n);if(!(e<=0)&&(!r||e>r.score)&&(r={node:a,score:e},e>=2))break}return r?.node??null}function eG(e,t){let a=eq(e??"");return a?a===t?2:+!!a.includes(t):0}function eq(e){return e.trim().toLowerCase().replace(/\s+/g," ")}let eJ=new Map,eW=r.join(p.homedir(),".agent-device"),eX=r.join(eW,"daemon.json"),ez=r.join(eW,"daemon.log"),eH=r.join(eW,"sessions"),eY=function(){try{let e=function(){let e=r.dirname(c(import.meta.url)),t=e;for(let e=0;e<6;e+=1){let e=r.join(t,"package.json");if(d.existsSync(e))return t;t=r.dirname(t)}return e}();return JSON.parse(d.readFileSync(r.join(e,"package.json"),"utf8")).version??"0.0.0"}catch{return"0.0.0"}}(),eZ=a.randomBytes(24).toString("hex");function eK(e,t,a){return{appBundleId:t,verbose:e?.verbose,logPath:ez,traceLogPath:a,snapshotInteractiveOnly:e?.snapshotInteractiveOnly,snapshotCompact:e?.snapshotCompact,snapshotDepth:e?.snapshotDepth,snapshotScope:e?.snapshotScope,snapshotRaw:e?.snapshotRaw,snapshotBackend:e?.snapshotBackend}}async function eQ(e){if(e.token!==eZ)return{ok:!1,error:{code:"UNAUTHORIZED",message:"Invalid token"}};let t=e.command,a=e.session||"default";if("session_list"===t)return{ok:!0,data:{sessions:Array.from(eJ.values()).map(e=>({name:e.name,platform:e.device.platform,device:e.device.name,id:e.device.id,createdAt:e.createdAt}))}};if("devices"===t)try{let t=[];if(e.flags?.platform==="android"){let{listAndroidDevices:e}=await Promise.resolve().then(()=>({listAndroidDevices:y}));t.push(...await e())}else if(e.flags?.platform==="ios"){let{listIosDevices:e}=await Promise.resolve().then(()=>({listIosDevices:Y}));t.push(...await e())}else{let{listAndroidDevices:e}=await Promise.resolve().then(()=>({listAndroidDevices:y})),{listIosDevices:a}=await Promise.resolve().then(()=>({listIosDevices:Y}));try{t.push(...await e())}catch{}try{t.push(...await a())}catch{}}return{ok:!0,data:{devices:t}}}catch(t){let e=l(t);return{ok:!1,error:{code:e.code,message:e.message,details:e.details}}}if("apps"===t){let t=eJ.get(a),i=e.flags??{};if(!t&&!i.platform&&!i.device&&!i.udid&&!i.serial)return{ok:!1,error:{code:"INVALID_ARGS",message:"apps requires an active session or an explicit device selector (e.g. --platform ios)."}};let n=t?.device??await eR(i);if(await e9(n),"ios"===n.platform){if("simulator"!==n.kind)return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"apps list is only supported on iOS simulators"}};let{listSimulatorApps:t}=await Promise.resolve().then(()=>({listSimulatorApps:ep})),a=await t(n);return e.flags?.appsMetadata?{ok:!0,data:{apps:a}}:{ok:!0,data:{apps:a.map(e=>e.name&&e.name!==e.bundleId?`${e.name} (${e.bundleId})`:e.bundleId)}}}let{listAndroidApps:r,listAndroidAppsMetadata:o}=await Promise.resolve().then(()=>({listAndroidApps:D,listAndroidAppsMetadata:k}));return e.flags?.appsMetadata?{ok:!0,data:{apps:await o(n,e.flags?.appsFilter)}}:{ok:!0,data:{apps:await r(n,e.flags?.appsFilter)}}}if("appstate"===t){let t=eJ.get(a),i=e.flags??{},n=t?.device??await eR(i);if(await e9(n),"ios"===n.platform){if(t?.appBundleId)return{ok:!0,data:{platform:"ios",appBundleId:t.appBundleId,appName:t.appName??t.appBundleId,source:"session"}};let a=await e1(n,t?.trace?.outPath,e.flags);return{ok:!0,data:{platform:"ios",appName:a.appName,appBundleId:a.appBundleId,source:a.source}}}let{getAndroidAppState:r}=await Promise.resolve().then(()=>({getAndroidAppState:P})),o=await r(n);return{ok:!0,data:{platform:"android",package:o.package,activity:o.activity}}}if("open"===t){let i;if(eJ.has(a))return{ok:!1,error:{code:"INVALID_ARGS",message:"Session already active. Close it first or pass a new --session name."}};let n=await eR(e.flags??{});await e9(n);let r=Array.from(eJ.values()).find(e=>e.device.id===n.id);if(r)return{ok:!1,error:{code:"DEVICE_IN_USE",message:`Device is already in use by session "${r.name}".`,details:{session:r.name,deviceId:n.id,deviceName:n.name}}};let o=e.positionals?.[0];if("ios"===n.platform)try{let{resolveIosApp:t}=await Promise.resolve().then(()=>({resolveIosApp:K}));i=await t(n,e.positionals?.[0]??"")}catch{i=void 0}await eC(n,"open",e.positionals??[],e.flags?.out,{...eK(e.flags,i)});let s={name:a,device:n,createdAt:Date.now(),appBundleId:i,appName:o,actions:[]};return e0(s,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{session:a}}),eJ.set(a,s),{ok:!0,data:{session:a}}}if("replay"===t){let t=e.positionals?.[0];if(!t)return{ok:!1,error:{code:"INVALID_ARGS",message:"replay requires a path"}};try{let e=e8(t),i=JSON.parse(d.readFileSync(e,"utf8")),n=i.optimizedActions??i.actions??[];for(let e of n)e&&"replay"!==e.command&&await eQ({token:eZ,session:a,command:e.command,positionals:e.positionals??[],flags:e.flags??{}});return{ok:!0,data:{replayed:n.length,session:a}}}catch(t){let e=l(t);return{ok:!1,error:{code:e.code,message:e.message}}}}if("close"===t){let i=eJ.get(a);return i?(e.positionals&&e.positionals.length>0&&await eC(i.device,"close",e.positionals??[],e.flags?.out,{...eK(e.flags,i.appBundleId,i.trace?.outPath)}),"ios"===i.device.platform&&"simulator"===i.device.kind&&await ey(i.device.id),e0(i,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{session:a}}),e3(i),eJ.delete(a),{ok:!0,data:{session:a}}):{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session"}}}if("snapshot"===t){let i=eJ.get(a),n=i?.device??await eR(e.flags??{});i||await e9(n);let r=i?.appBundleId,o=e.flags?.snapshotScope;if(o&&o.trim().startsWith("@")){if(!i?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"Ref scope requires an existing snapshot in session."}};let e=e$(o.trim());if(!e)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref scope: ${o}`}};let t=eU(i.snapshot.nodes,e),a=t?e7(t,i.snapshot.nodes):void 0;if(!a)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${o} not found or has no label`}};o=a}let s=await eC(n,"snapshot",[],e.flags?.out,{...eK({...e.flags,snapshotScope:o},r,i?.trace?.outPath)}),l=s?.nodes??[],c=eB(e.flags?.snapshotRaw?l:te(l)),u={nodes:c,truncated:s?.truncated,createdAt:Date.now(),backend:s?.backend},d={name:a,device:n,createdAt:i?.createdAt??Date.now(),appBundleId:i?.appBundleId??r,snapshot:u,actions:i?.actions??[],appName:i?.appName};return e0(d,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{nodes:c.length,truncated:s?.truncated??!1}}),eJ.set(a,d),{ok:!0,data:{nodes:c,truncated:s?.truncated??!1,appName:d.appBundleId?d.appName??d.appBundleId:void 0,appBundleId:d.appBundleId}}}if("wait"===t){let i=eJ.get(a),n=i?.device??await eR(e.flags??{});i||await e9(n);let r=e.positionals??[];if(0===r.length)return{ok:!1,error:{code:"INVALID_ARGS",message:"wait requires a duration or text"}};let o=e=>{if(!e)return null;let t=Number(e);return Number.isFinite(t)?t:null},s=o(r[0]);if(null!==s)return await new Promise(e=>setTimeout(e,s)),i&&e0(i,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{waitedMs:s}}),{ok:!0,data:{waitedMs:s}};let l="",c=null;if("text"===r[0])l=null!==(c=o(r[r.length-1]))?r.slice(1,-1).join(" "):r.slice(1).join(" ");else if(r[0].startsWith("@")){if(!i?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"Ref wait requires an existing snapshot in session."}};let e=e$(r[0]);if(!e)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref: ${r[0]}`}};let t=eU(i.snapshot.nodes,e),a=t?e7(t,i.snapshot.nodes):void 0;if(!a)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${r[0]} not found or has no label`}};c=o(r[r.length-1]),l=a}else l=null!==(c=o(r[r.length-1]))?r.slice(0,-1).join(" "):r.join(" ");if(!(l=l.trim()))return{ok:!1,error:{code:"INVALID_ARGS",message:"wait requires text"}};let u=c??1e4,d=Date.now();for(;Date.now()-d<u;){if("ios"===n.platform&&"simulator"===n.kind){let a=await ew(n,{command:"findText",text:l,appBundleId:i?.appBundleId},{verbose:e.flags?.verbose,logPath:ez,traceLogPath:i?.trace?.outPath});if(a?.found)return i&&e0(i,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{text:l,waitedMs:Date.now()-d}}),{ok:!0,data:{text:l,waitedMs:Date.now()-d}}}else if("android"!==n.platform)return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"wait is not supported on this device"}};else if(e6(eB((await q(n,{scope:l})).nodes??[]),l))return i&&e0(i,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{text:l,waitedMs:Date.now()-d}}),{ok:!0,data:{text:l,waitedMs:Date.now()-d}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:`wait timed out for text: ${l}`}}}if("alert"===t){let i=eJ.get(a),n=i?.device??await eR(e.flags??{});i||await e9(n);let r=(e.positionals?.[0]??"get").toLowerCase();if("ios"!==n.platform||"simulator"!==n.kind)return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"alert is only supported on iOS simulators in v1"}};if("wait"===r){let a=(e=>{if(!e)return null;let t=Number(e);return Number.isFinite(t)?t:null})(e.positionals?.[1])??1e4,r=Date.now();for(;Date.now()-r<a;){try{let a=await ew(n,{command:"alert",action:"get",appBundleId:i?.appBundleId},{verbose:e.flags?.verbose,logPath:ez,traceLogPath:i?.trace?.outPath});return i&&e0(i,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:a}),{ok:!0,data:a}}catch{}await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:"alert wait timed out"}}}let o=await ew(n,{command:"alert",action:"accept"===r||"dismiss"===r?r:"get",appBundleId:i?.appBundleId},{verbose:e.flags?.verbose,logPath:ez,traceLogPath:i?.trace?.outPath});return i&&e0(i,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:o}),{ok:!0,data:o}}if("record"===t){let i=(e.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(i))return{ok:!1,error:{code:"INVALID_ARGS",message:"record requires start|stop"}};let n=eJ.get(a),o=n?.device??await eR(e.flags??{});n||await e9(o);let s=n??{name:a,device:o,createdAt:Date.now(),actions:[]};if("start"===i){if(s.recording)return{ok:!1,error:{code:"INVALID_ARGS",message:"recording already in progress"}};let i=e.positionals?.[1]??`./recording-${Date.now()}.mp4`,n=r.resolve(i),l=r.dirname(n);if(d.existsSync(l)||d.mkdirSync(l,{recursive:!0}),"ios"===o.platform){if("simulator"!==o.kind)return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"record is only supported on iOS simulators in v1"}};let{child:e,wait:t}=u("xcrun",["simctl","io",o.id,"recordVideo",n],{allowFailure:!0});s.recording={platform:"ios",outPath:n,child:e,wait:t}}else{let e=`/sdcard/agent-device-recording-${Date.now()}.mp4`,{child:t,wait:a}=u("adb",["-s",o.id,"shell","screenrecord",e],{allowFailure:!0});s.recording={platform:"android",outPath:n,remotePath:e,child:t,wait:a}}return eJ.set(a,s),e0(s,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{action:"start"}}),{ok:!0,data:{recording:"started",outPath:i}}}if(!s.recording)return{ok:!1,error:{code:"INVALID_ARGS",message:"no active recording"}};let l=s.recording;l.child.kill("SIGINT");try{await l.wait}catch{}if("android"===l.platform&&l.remotePath)try{await h("adb",["-s",o.id,"pull",l.remotePath,l.outPath],{allowFailure:!0}),await h("adb",["-s",o.id,"shell","rm","-f",l.remotePath],{allowFailure:!0})}catch{}return s.recording=void 0,e0(s,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{action:"stop",outPath:l.outPath}}),{ok:!0,data:{recording:"stopped",outPath:l.outPath}}}if("trace"===t){let i=(e.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(i))return{ok:!1,error:{code:"INVALID_ARGS",message:"trace requires start|stop"}};let n=eJ.get(a);if(!n)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session"}};if("start"===i){let a,i;if(n.trace)return{ok:!1,error:{code:"INVALID_ARGS",message:"trace already in progress"}};let o=e8(e.positionals?.[1]??(a=n.name.replace(/[^a-zA-Z0-9._-]/g,"_"),i=new Date().toISOString().replace(/[:.]/g,"-"),r.join(eH,`${a}-${i}.trace.log`)));return d.mkdirSync(r.dirname(o),{recursive:!0}),d.appendFileSync(o,""),n.trace={outPath:o,startedAt:Date.now()},e0(n,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{action:"start",outPath:o}}),{ok:!0,data:{trace:"started",outPath:o}}}if(!n.trace)return{ok:!1,error:{code:"INVALID_ARGS",message:"no active trace"}};let o=n.trace.outPath;if(e.positionals?.[1]){let t=e8(e.positionals[1]);d.mkdirSync(r.dirname(t),{recursive:!0}),d.existsSync(o)?d.renameSync(o,t):d.appendFileSync(t,""),o=t}return n.trace=void 0,e0(n,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{action:"stop",outPath:o}}),{ok:!0,data:{trace:"stopped",outPath:o}}}if("settings"===t){let i=e.positionals?.[0],n=e.positionals?.[1];if(!i||!n)return{ok:!1,error:{code:"INVALID_ARGS",message:"settings requires <wifi|airplane|location> <on|off>"}};let r=eJ.get(a),o=r?.device??await eR(e.flags??{});r||await e9(o);let s=r?.appBundleId,l=await eC(o,"settings",[i,n,s??""],e.flags?.out,{...eK(e.flags,s,r?.trace?.outPath)});return r&&e0(r,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:l??{setting:i,state:n}}),{ok:!0,data:l??{setting:i,state:n}}}if("find"===t){let n=e.positionals??[];if(0===n.length)return{ok:!1,error:{code:"INVALID_ARGS",message:"find requires a locator or text"}};let{locator:r,query:o,action:s,value:l,timeoutMs:c}=function(e){let t="any",a=0;["text","label","value","role","id"].includes(e[0])&&(t=e[0],a=1);let i=e[a]??"",n=e.slice(a+1);if(0===n.length)return{locator:t,query:i,action:"click"};let r=n[0].toLowerCase();if("get"===r){let e=n[1]?.toLowerCase();if("text"===e)return{locator:t,query:i,action:"get_text"};if("attrs"===e)return{locator:t,query:i,action:"get_attrs"};throw new f("INVALID_ARGS","find get only supports text or attrs")}if("wait"===r)return{locator:t,query:i,action:"wait",timeoutMs:function(e){if(!e)return null;let t=Number(e);return Number.isFinite(t)?t:null}(n[1])??void 0};if("exists"===r)return{locator:t,query:i,action:"exists"};if("click"===r)return{locator:t,query:i,action:"click"};if("focus"===r)return{locator:t,query:i,action:"focus"};if("fill"===r)return{locator:t,query:i,action:"fill",value:n.slice(1).join(" ")};if("type"===r)return{locator:t,query:i,action:"type",value:n.slice(1).join(" ")};throw new f("INVALID_ARGS",`Unsupported find action: ${n[0]}`)}(n);if(!o)return{ok:!1,error:{code:"INVALID_ARGS",message:"find requires a value"}};let u=eJ.get(a);if(!u&&"exists"!==s&&"wait"!==s&&"get_text"!==s&&"get_attrs"!==s)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let d=u?.device??await eR(e.flags??{});u||await e9(d);let p=u?.appBundleId,h="role"!==r?o:void 0,m="click"===s||"focus"===s||"fill"===s||"type"===s,w=0,g=null,y=async()=>{let t=Date.now();if(g&&t-w<750)return{nodes:g};let i=await eC(d,"snapshot",[],e.flags?.out,{...eK({...e.flags,snapshotScope:h,snapshotInteractiveOnly:m,snapshotCompact:m},p,u?.trace?.outPath)}),n=i?.nodes??[],r=eB(e.flags?.snapshotRaw?n:te(n));return w=t,g=r,u&&(u.snapshot={nodes:r,truncated:i?.truncated,createdAt:Date.now(),backend:i?.backend},eJ.set(a,u)),{nodes:r,truncated:i?.truncated,backend:i?.backend}};if("wait"===s){let a=c??1e4,i=Date.now();for(;Date.now()-i<a;){let{nodes:a}=await y();if(ej(a,r,o,{requireRect:!1}))return u&&e0(u,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{found:!0,waitedMs:Date.now()-i}}),{ok:!0,data:{found:!0,waitedMs:Date.now()-i}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:"find wait timed out"}}}let{nodes:v}=await y(),I=ej(v,r,o,{requireRect:m});if(!I)return{ok:!1,error:{code:"COMMAND_FAILED",message:"find did not match any element"}};let N=`@${I.ref}`,b={...e.flags??{},noRecord:!0};if("exists"===s)return u&&e0(u,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{found:!0}}),{ok:!0,data:{found:!0}};if("get_text"===s){var i;let a=[(i=I).label,i.value,i.identifier].map(e=>"string"==typeof e?e.trim():"").filter(e=>e.length>0)[0]??"";return u&&e0(u,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{ref:N,action:"get text",text:a}}),{ok:!0,data:{ref:N,text:a,node:I}}}if("get_attrs"===s)return u&&e0(u,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{ref:N,action:"get attrs"}}),{ok:!0,data:{ref:N,node:I}};if("click"===s){let i=await eQ({token:eZ,session:a,command:"click",positionals:[N],flags:b});return i.ok&&u&&e0(u,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{ref:N,action:"click"}}),i}if("fill"===s){if(!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"find fill requires text"}};let i=await eQ({token:eZ,session:a,command:"fill",positionals:[N,l],flags:b});return i.ok&&u&&e0(u,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{ref:N,action:"fill"}}),i}if("focus"===s){let a=I.rect?eV(I.rect):null;if(!a)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};let i=await eC(d,"focus",[String(a.x),String(a.y)],e.flags?.out,{...eK(e.flags,u?.appBundleId,u?.trace?.outPath)});return u&&e0(u,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{ref:N,action:"focus"}}),{ok:!0,data:i??{ref:N}}}if("type"===s){if(!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"find type requires text"}};let a=I.rect?eV(I.rect):null;if(!a)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};await eC(d,"focus",[String(a.x),String(a.y)],e.flags?.out,{...eK(e.flags,u?.appBundleId,u?.trace?.outPath)});let i=await eC(d,"type",[l],e.flags?.out,{...eK(e.flags,u?.appBundleId,u?.trace?.outPath)});return u&&e0(u,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{ref:N,action:"type"}}),{ok:!0,data:i??{ref:N}}}}if("click"===t){let i=eJ.get(a);if(!i?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}};let n=e.positionals?.[0]??"",r=e$(n);if(!r)return{ok:!1,error:{code:"INVALID_ARGS",message:"click requires a ref like @e2"}};let o=eU(i.snapshot.nodes,r);if(!o?.rect&&e.positionals.length>1){let t=e.positionals.slice(1).join(" ").trim();t.length>0&&(o=e6(i.snapshot.nodes,t))}if(!o?.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${n} not found or has no bounds`}};let s=e7(o,i.snapshot.nodes),l=o.label?.trim();if("ios"===i.device.platform&&"simulator"===i.device.kind&&l&&function(e,t){let a=t.trim().toLowerCase();if(!a)return!1;let i=0;for(let t of e)if((t.label??"").trim().toLowerCase()===a&&(i+=1)>1)return!1;return 1===i}(i.snapshot.nodes,l))return await ew(i.device,{command:"tap",text:l,appBundleId:i.appBundleId},{verbose:e.flags?.verbose,logPath:ez,traceLogPath:i?.trace?.outPath}),e0(i,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{ref:r,refLabel:l,mode:"text"}}),{ok:!0,data:{ref:r,mode:"text"}};let{x:c,y:u}=eV(o.rect);return await eC(i.device,"press",[String(c),String(u)],e.flags?.out,{...eK(e.flags,i.appBundleId,i.trace?.outPath)}),e0(i,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{ref:r,x:c,y:u,refLabel:s}}),{ok:!0,data:{ref:r,x:c,y:u}}}if("fill"===t){let i=eJ.get(a);if(e.positionals?.[0]?.startsWith("@")){if(!i?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}};let a=e$(e.positionals[0]);if(!a)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires a ref like @e2"}};let n=e.positionals.length>=3?e.positionals[1]:"",r=e.positionals.length>=3?e.positionals.slice(2).join(" "):e.positionals.slice(1).join(" ");if(!r)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after ref"}};let o=eU(i.snapshot.nodes,a);if(!o?.rect&&n&&(o=e6(i.snapshot.nodes,n)),!o?.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${e.positionals[0]} not found or has no bounds`}};let s=e7(o,i.snapshot.nodes),{x:l,y:c}=eV(o.rect),u=await eC(i.device,"fill",[String(l),String(c),r],e.flags?.out,{...eK(e.flags,i.appBundleId,i.trace?.outPath)});return e0(i,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:u??{ref:a,x:l,y:c,refLabel:s}}),{ok:!0,data:u??{ref:a,x:l,y:c}}}}if("get"===t){let i=e.positionals?.[0],n=e.positionals?.[1];if("text"!==i&&"attrs"!==i)return{ok:!1,error:{code:"INVALID_ARGS",message:"get only supports text or attrs"}};let r=eJ.get(a);if(!r?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}};let o=e$(n??"");if(!o)return{ok:!1,error:{code:"INVALID_ARGS",message:"get text requires a ref like @e2"}};let s=eU(r.snapshot.nodes,o);if(!s&&e.positionals.length>2){let t=e.positionals.slice(2).join(" ").trim();t.length>0&&(s=e6(r.snapshot.nodes,t))}if(!s)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${n} not found`}};if("attrs"===i)return e0(r,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{ref:o}}),{ok:!0,data:{ref:o,node:s}};let l=[s.label,s.value,s.identifier].map(e=>"string"==typeof e?e.trim():"").filter(e=>e.length>0)[0]??"";return e0(r,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:{ref:o,text:l,refLabel:l||void 0}}),{ok:!0,data:{ref:o,text:l,node:s}}}let n=eJ.get(a);if(!n)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let o=await eC(n.device,t,e.positionals??[],e.flags?.out,{...eK(e.flags,n.appBundleId,n.trace?.outPath)});return e0(n,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:o??{}}),{ok:!0,data:o??{}}}function e0(e,t){t.flags?.noRecord||e.actions.push({ts:Date.now(),command:t.command,positionals:t.positionals,flags:function(e){if(!e)return{};let{platform:t,device:a,udid:i,serial:n,out:r,verbose:o,snapshotInteractiveOnly:s,snapshotCompact:l,snapshotDepth:c,snapshotScope:u,snapshotRaw:d,snapshotBackend:p,appsMetadata:f,noRecord:h,recordJson:m}=e;return{platform:t,device:a,udid:i,serial:n,out:r,verbose:o,snapshotInteractiveOnly:s,snapshotCompact:l,snapshotDepth:c,snapshotScope:u,snapshotRaw:d,snapshotBackend:p,appsMetadata:f,noRecord:h,recordJson:m}}(t.flags),result:t.result})}async function e1(e,t,a){let i=e2(await eC(e,"snapshot",[],a?.out,{...eK({...a,snapshotDepth:1,snapshotCompact:!0,snapshotBackend:"ax"},void 0,t)}));if(i?.appName||i?.appBundleId)return{appName:i.appName??i.appBundleId??"unknown",appBundleId:i.appBundleId,source:"snapshot-ax"};let n=e2(await eC(e,"snapshot",[],a?.out,{...eK({...a,snapshotDepth:1,snapshotCompact:!0,snapshotBackend:"xctest"},void 0,t)}));return{appName:n?.appName??n?.appBundleId??"unknown",appBundleId:n?.appBundleId,source:"snapshot-xctest"}}function e2(e){let t=eB(e?.nodes??[]),a=t.find(e=>"application"===tt(e.type??""))??t[0];if(!a)return null;let i=a.label?.trim(),n=a.identifier?.trim();return i||n?{appName:i||void 0,appBundleId:n||void 0}:null}function e3(e){try{d.existsSync(eH)||d.mkdirSync(eH,{recursive:!0});let t=e.name.replace(/[^a-zA-Z0-9._-]/g,"_"),a=new Date(e.createdAt).toISOString().replace(/[:.]/g,"-"),i=r.join(eH,`${t}-${a}.ad`),n=r.join(eH,`${t}-${a}.json`),o={name:e.name,device:e.device,createdAt:e.createdAt,appBundleId:e.appBundleId,actions:e.actions,optimizedActions:function(e){let t=[];for(let a of e.actions)if("snapshot"!==a.command){if("click"===a.command||"fill"===a.command||"get"===a.command){let i=a.result?.refLabel;"string"==typeof i&&i.trim().length>0&&t.push({ts:a.ts,command:"snapshot",positionals:[],flags:{platform:e.device.platform,snapshotInteractiveOnly:!0,snapshotCompact:!0,snapshotScope:i.trim()},result:{scope:i.trim()}})}t.push(a)}return t}(e)},s=function(e,t){let a=[],i=e.device.name.replace(/"/g,'\\"'),n=e.device.kind?` kind=${e.device.kind}`:"";for(let r of(a.push(`context platform=${e.device.platform} device="${i}"${n} theme=unknown`),t))r.flags?.noRecord||a.push(function(e){let t=[e.command];if("click"===e.command){let a=e.positionals?.[0];if(a){t.push(e4(a));let i=e.result?.refLabel;return"string"==typeof i&&i.trim().length>0&&t.push(e4(i)),t.join(" ")}}if("fill"===e.command){let a=e.positionals?.[0];if(a&&a.startsWith("@")){t.push(e4(a));let i=e.result?.refLabel,n=e.positionals.slice(1).join(" ");return"string"==typeof i&&i.trim().length>0&&t.push(e4(i)),n&&t.push(e4(n)),t.join(" ")}}if("get"===e.command){let a=e.positionals?.[0],i=e.positionals?.[1];if(a&&i){t.push(e4(a)),t.push(e4(i));let n=e.result?.refLabel;return"string"==typeof n&&n.trim().length>0&&t.push(e4(n)),t.join(" ")}}if("snapshot"===e.command)return e.flags?.snapshotInteractiveOnly&&t.push("-i"),e.flags?.snapshotCompact&&t.push("-c"),"number"==typeof e.flags?.snapshotDepth&&t.push("-d",String(e.flags.snapshotDepth)),e.flags?.snapshotScope&&t.push("-s",e4(e.flags.snapshotScope)),e.flags?.snapshotRaw&&t.push("--raw"),e.flags?.snapshotBackend&&t.push("--backend",e.flags.snapshotBackend),t.join(" ");for(let a of e.positionals??[])t.push(e4(a));return t.join(" ")}(r));return`${a.join("\n")} | ||
| `}(e,o.optimizedActions);d.writeFileSync(i,s),e.actions.some(e=>e.flags?.recordJson)&&d.writeFileSync(n,JSON.stringify(o,null,2))}catch{}}function e8(e){return e.startsWith("~/")?r.join(p.homedir(),e.slice(2)):r.resolve(e)}function e4(e){let t=e.trim();return t.startsWith("@")||/^-?\d+(\.\d+)?$/.test(t)?t:JSON.stringify(t)}function e6(e,t){let a=t.toLowerCase();return e.find(e=>{let t=(e.label??"").toLowerCase(),i=(e.value??"").toLowerCase(),n=(e.identifier??"").toLowerCase();return t.includes(a)||i.includes(a)||n.includes(a)})??null}function e7(e,t){let a=[e.label,e.value,e.identifier].map(e=>"string"==typeof e?e.trim():"").find(e=>e&&e.length>0);return a&&e5(a)?a:function(e,t){if(!e.rect)return;let a=e.rect.y+e.rect.height/2,i=null;for(let e of t){if(!e.rect)continue;let t=[e.label,e.value,e.identifier].map(e=>"string"==typeof e?e.trim():"").find(e=>e&&e.length>0);if(!t||!e5(t))continue;let n=Math.abs(e.rect.y+e.rect.height/2-a);(!i||n<i.distance)&&(i={label:t,distance:n})}return i?.label}(e,t)??(a&&e5(a)?a:void 0)}function e5(e){let t=e.trim();return!(!t||/^(true|false)$/i.test(t)||/^\d+$/.test(t))}async function e9(e){if("ios"===e.platform&&"simulator"===e.kind){let{ensureBootedSimulator:t}=await Promise.resolve().then(()=>({ensureBootedSimulator:ef}));await t(e);return}if("android"===e.platform){let{waitForAndroidBoot:t}=await Promise.resolve().then(()=>({waitForAndroidBoot:I}));await t(e.id)}}function te(e){let t=[],a=[];for(let i of e){let e=i.depth??0;for(;t.length>0&&e<=t[t.length-1];)t.pop();let n=tt(i.type??""),r=[i.label,i.value,i.identifier].map(e=>"string"==typeof e?e.trim():"").find(e=>e&&e.length>0),o=!!r&&e5(r);if(("group"===n||"ioscontentgroup"===n)&&!o){t.push(e);continue}let s=Math.max(0,e-t.length);a.push({...i,depth:s})}return a}function tt(e){let t=e.replace(/XCUIElementType/gi,"").toLowerCase();return t.startsWith("ax")&&(t=t.replace(/^ax/,"")),t}(e=m.createServer(e=>{let t="";e.setEncoding("utf8"),e.on("data",async a=>{let i=(t+=a).indexOf("\n");for(;-1!==i;){let a,n=t.slice(0,i).trim();if(t=t.slice(i+1),0===n.length){i=t.indexOf("\n");continue}try{let e=JSON.parse(n);a=await eQ(e)}catch(t){let e=l(t);a={ok:!1,error:{code:e.code,message:e.message,details:e.details}}}e.write(`${JSON.stringify(a)} | ||
@@ -8,0 +8,0 @@ `),i=t.indexOf("\n")}})})).listen(0,"127.0.0.1",()=>{let t=e.address();if("object"==typeof t&&t?.port){var a;a=t.port,d.existsSync(eW)||d.mkdirSync(eW,{recursive:!0}),d.writeFileSync(ez,""),d.writeFileSync(eX,JSON.stringify({port:a,token:eZ,pid:process.pid,version:eY},null,2),{mode:384}),process.stdout.write(`AGENT_DEVICE_DAEMON_PORT=${t.port} |
+1
-1
| { | ||
| "name": "agent-device", | ||
| "version": "0.1.5", | ||
| "version": "0.1.6", | ||
| "description": "Unified control plane for physical and virtual devices via an agent-driven CLI.", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
+1
-0
@@ -117,2 +117,3 @@ # agent-device | ||
| - `alert` and `scrollintoview` use the XCTest runner and are simulator-only in v1. | ||
| - Real device support (including snapshots) is on the roadmap for iOS. | ||
@@ -119,0 +120,0 @@ ## Testing |
| import path from 'node:path'; | ||
| import fs from 'node:fs'; | ||
| import { fileURLToPath } from 'node:url'; | ||
| import { AppError } from '../../utils/errors.ts'; | ||
@@ -199,3 +200,3 @@ import { runCmd } from '../../utils/exec.ts'; | ||
| function findProjectRoot(): string { | ||
| let current = process.cwd(); | ||
| let current = path.dirname(fileURLToPath(import.meta.url)); | ||
| for (let i = 0; i < 6; i += 1) { | ||
@@ -202,0 +203,0 @@ const pkgPath = path.join(current, 'package.json'); |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 7 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 7 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
737576
0.02%5559
0.02%145
0.69%