agent-device 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -12
- package/dist/src/bin.js +32 -32
- package/dist/src/daemon.js +18 -14
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +8 -2
- package/package.json +1 -1
- package/skills/agent-device/SKILL.md +19 -13
- package/skills/agent-device/references/permissions.md +7 -2
- package/skills/agent-device/references/session-management.md +3 -1
- package/src/__tests__/cli-close.test.ts +155 -0
- package/src/cli.ts +32 -16
- package/src/core/__tests__/capabilities.test.ts +2 -1
- package/src/core/__tests__/dispatch-open.test.ts +25 -0
- package/src/core/__tests__/open-target.test.ts +40 -1
- package/src/core/capabilities.ts +1 -1
- package/src/core/dispatch.ts +22 -0
- package/src/core/open-target.ts +14 -0
- package/src/daemon/__tests__/device-ready.test.ts +52 -0
- package/src/daemon/device-ready.ts +146 -4
- package/src/daemon/handlers/__tests__/session.test.ts +477 -0
- package/src/daemon/handlers/session.ts +196 -91
- package/src/daemon/session-store.ts +0 -2
- package/src/daemon-client.ts +118 -18
- package/src/platforms/android/__tests__/index.test.ts +118 -1
- package/src/platforms/android/index.ts +77 -47
- package/src/platforms/ios/__tests__/index.test.ts +292 -4
- package/src/platforms/ios/__tests__/runner-client.test.ts +42 -0
- package/src/platforms/ios/apps.ts +358 -0
- package/src/platforms/ios/config.ts +28 -0
- package/src/platforms/ios/devicectl.ts +134 -0
- package/src/platforms/ios/devices.ts +15 -2
- package/src/platforms/ios/index.ts +20 -455
- package/src/platforms/ios/runner-client.ts +72 -16
- package/src/platforms/ios/simulator.ts +164 -0
- package/src/utils/__tests__/args.test.ts +20 -2
- package/src/utils/__tests__/daemon-client.test.ts +21 -4
- package/src/utils/args.ts +6 -1
- package/src/utils/command-schema.ts +7 -14
- package/src/utils/interactors.ts +2 -2
- package/src/utils/timeouts.ts +9 -0
- package/src/daemon/__tests__/app-state.test.ts +0 -138
- package/src/daemon/app-state.ts +0 -65
package/dist/src/daemon.js
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
import e from"node:crypto";import{isCancel as t,select as i}from"@clack/prompts";import{node_path as r,runCmdStreaming as n,fileURLToPath as a,isAgentDeviceDaemonProcess as o,runCmdBackground as s,readVersion as l,runCmd as c,promises as
|
|
2
|
-
`)}})}catch(
|
|
3
|
-
${r.stderr}`.toLowerCase();if(!e.includes("unknown package")&&!e.includes("not installed"))throw new
|
|
4
|
-
${r}`,a=/dumped to:\s*(\S+)/i.exec(n),a?.[1]??t),
|
|
5
|
-
${t}`,r=i.indexOf("<?xml"),n=r>=0?r:i.indexOf("<hierarchy");if(n<0)return null;let a=i.lastIndexOf("</hierarchy>");if(a<0||a<n)return null;let o=i.slice(n,a+12).trim();return o.length>0?o:null}function
|
|
6
|
-
${
|
|
7
|
-
${t.stderr}`.toLowerCase(),o
|
|
8
|
-
`)}})}catch(
|
|
9
|
-
${
|
|
1
|
+
import e from"node:crypto";import{isCancel as t,select as i}from"@clack/prompts";import{node_path as r,runCmdStreaming as n,fileURLToPath as a,isAgentDeviceDaemonProcess as o,runCmdBackground as s,readVersion as l,runCmd as c,promises as d,asAppError as u,isProcessAlive as p,AppError as f,node_fs as m,node_os as h,readProcessStartTime as w,node_net as g,whichCmd as v}from"./797.js";async function I(e,r){let n=e,a=e=>e.toLowerCase().replace(/_/g," ").replace(/\s+/g," ").trim();if(r.platform&&(n=n.filter(e=>e.platform===r.platform)),r.udid){let e=n.find(e=>e.id===r.udid&&"ios"===e.platform);if(!e)throw new f("DEVICE_NOT_FOUND",`No iOS device with UDID ${r.udid}`);return e}if(r.serial){let e=n.find(e=>e.id===r.serial&&"android"===e.platform);if(!e)throw new f("DEVICE_NOT_FOUND",`No Android device with serial ${r.serial}`);return e}if(r.deviceName){let e=a(r.deviceName),t=n.find(t=>a(t.name)===e);if(!t)throw new f("DEVICE_NOT_FOUND",`No device named ${r.deviceName}`);return t}if(1===n.length)return n[0];if(0===n.length)throw new f("DEVICE_NOT_FOUND","No devices found",{selector:r});let o=n.filter(e=>e.booted);if(1===o.length)return o[0];if(!process.env.CI&&process.stdin.isTTY&&process.stdout.isTTY){let e=await i({message:"Multiple devices available. Choose a device to continue:",options:(o.length>0?o:n).map(e=>({label:`${e.name} (${e.platform}${e.kind?`, ${e.kind}`:""}${e.booted?", booted":""})`,value:e.id}))});if(t(e))throw new f("INVALID_ARGS","Device selection cancelled");if(e){let t=n.find(t=>t.id===e);if(t)return t}}return o[0]??n[0]}function A(e,t,i){return t in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}function y(e){return["1","true","yes","on"].includes((e??"").toLowerCase())}let N=2e4,S=12e4,D=1e4;class _{static fromTimeoutMs(e,t=Date.now()){return new _(t,e)}remainingMs(e=Date.now()){return Math.max(0,this.expiresAtMs-e)}elapsedMs(e=Date.now()){return Math.max(0,e-this.startedAtMs)}isExpired(e=Date.now()){return 0>=this.remainingMs(e)}constructor(e,t){A(this,"startedAtMs",void 0),A(this,"expiresAtMs",void 0),this.startedAtMs=e,this.expiresAtMs=e+Math.max(0,t)}}async function b(e,t={},i={}){let r,n={maxAttempts:t.maxAttempts??3,baseDelayMs:t.baseDelayMs??200,maxDelayMs:t.maxDelayMs??2e3,jitter:t.jitter??.2,shouldRetry:t.shouldRetry};for(let t=1;t<=n.maxAttempts&&(!i.deadline?.isExpired()||!(t>1));t+=1)try{let r=await e({attempt:t,maxAttempts:n.maxAttempts,deadline:i.deadline});return i.onEvent?.({phase:i.phase,event:"succeeded",attempt:t,maxAttempts:n.maxAttempts,elapsedMs:i.deadline?.elapsedMs(),remainingMs:i.deadline?.remainingMs()}),r}catch(s){r=s;let e=i.classifyReason?.(s);if(i.onEvent?.({phase:i.phase,event:"attempt_failed",attempt:t,maxAttempts:n.maxAttempts,elapsedMs:i.deadline?.elapsedMs(),remainingMs:i.deadline?.remainingMs(),reason:e}),t>=n.maxAttempts||n.shouldRetry&&!n.shouldRetry(s,t))break;let a=function(e,t,i,r){let n=Math.min(t,e*2**(r-1));return Math.max(0,n+n*i*(2*Math.random()-1))}(n.baseDelayMs,n.maxDelayMs,n.jitter,t),o=i.deadline?Math.min(a,i.deadline.remainingMs()):a;if(o<=0)break;i.onEvent?.({phase:i.phase,event:"retry_scheduled",attempt:t,maxAttempts:n.maxAttempts,delayMs:o,elapsedMs:i.deadline?.elapsedMs(),remainingMs:i.deadline?.remainingMs(),reason:e}),await function(e){return new Promise(t=>setTimeout(t,e))}(o)}if(i.onEvent?.({phase:i.phase,event:"exhausted",attempt:n.maxAttempts,maxAttempts:n.maxAttempts,elapsedMs:i.deadline?.elapsedMs(),remainingMs:i.deadline?.remainingMs(),reason:i.classifyReason?.(r)}),r)throw r;throw new f("COMMAND_FAILED","retry failed")}async function O(e,t={}){return b(()=>e(),{maxAttempts:t.attempts,baseDelayMs:t.baseDelayMs,maxDelayMs:t.maxDelayMs,jitter:t.jitter,shouldRetry:t.shouldRetry})}function E(e){let t=e.error?u(e.error):null,i=e.context?.platform,r=e.context?.phase;if(t?.code==="TOOL_MISSING")return"android"===i?"ADB_TRANSPORT_UNAVAILABLE":"IOS_TOOL_MISSING";let n=t?.details??{},a="string"==typeof n.message?n.message:void 0,o="string"==typeof n.stdout?n.stdout:void 0,s="string"==typeof n.stderr?n.stderr:void 0,l=n.boot&&"object"==typeof n.boot?n.boot:null,c=n.bootstatus&&"object"==typeof n.bootstatus?n.bootstatus:null,d=[e.message,t?.message,e.stdout,e.stderr,a,o,s,"string"==typeof l?.stdout?l.stdout:void 0,"string"==typeof l?.stderr?l.stderr:void 0,"string"==typeof c?.stdout?c.stdout:void 0,"string"==typeof c?.stderr?c.stderr:void 0].filter(Boolean).join("\n").toLowerCase();return"ios"===i&&(d.includes("runner did not accept connection")||"connect"===r&&(d.includes("timed out")||d.includes("timeout")||d.includes("econnrefused")||d.includes("connection refused")||d.includes("fetch failed")||d.includes("socket hang up")))?"IOS_RUNNER_CONNECT_TIMEOUT":"ios"===i&&"boot"===r&&(d.includes("timed out")||d.includes("timeout"))?"IOS_BOOT_TIMEOUT":"android"===i&&"boot"===r&&(d.includes("timed out")||d.includes("timeout"))?"ANDROID_BOOT_TIMEOUT":d.includes("resource temporarily unavailable")||d.includes("killed: 9")||d.includes("cannot allocate memory")||d.includes("system is low on memory")?"CI_RESOURCE_STARVATION_SUSPECTED":"android"===i&&(d.includes("device not found")||d.includes("no devices")||d.includes("device offline")||d.includes("offline")||d.includes("unauthorized")||d.includes("not authorized")||d.includes("unable to locate device")||d.includes("invalid device"))?"ADB_TRANSPORT_UNAVAILABLE":t?.code==="COMMAND_FAILED"||d.length>0?"BOOT_COMMAND_FAILED":"UNKNOWN"}function x(e){switch(e){case"IOS_BOOT_TIMEOUT":return"Retry simulator boot and inspect simctl bootstatus logs; in CI consider increasing AGENT_DEVICE_IOS_BOOT_TIMEOUT_MS.";case"IOS_RUNNER_CONNECT_TIMEOUT":return"Retry runner startup, inspect xcodebuild logs, and verify simulator responsiveness before command execution.";case"ANDROID_BOOT_TIMEOUT":return"Retry emulator startup and verify sys.boot_completed reaches 1; consider increasing startup budget in CI.";case"ADB_TRANSPORT_UNAVAILABLE":return"Check adb server/device transport (adb devices -l), restart adb, and ensure the target device is online and authorized.";case"CI_RESOURCE_STARVATION_SUSPECTED":return"CI machine may be resource constrained; reduce parallel jobs or use a larger runner.";case"IOS_TOOL_MISSING":return"Xcode command-line tools are missing or not in PATH; run xcode-select --install and verify xcrun works.";case"BOOT_COMMAND_FAILED":return"Inspect command stderr/stdout for the failing boot phase and retry after environment validation.";default:return"Retry once and inspect verbose logs for the failing phase."}}let k=y(process.env.AGENT_DEVICE_RETRY_LOGS);function M(e){return e.startsWith("emulator-")}async function L(e,t=D){return c("adb",["-s",e,"shell","getprop","sys.boot_completed"],{allowFailure:!0,timeoutMs:t})}async function R(e,t){let i=t.replace(/_/g," ").trim();if(!M(e))return i||e;let r=await c("adb",["-s",e,"emu","avd","name"],{allowFailure:!0,timeoutMs:D}),n=r.stdout.trim();return 0===r.exitCode&&n?n.replace(/_/g," "):i||e}async function C(){if(!await v("adb"))throw new f("TOOL_MISSING","adb not found in PATH");let e=(await c("adb",["devices","-l"],{timeoutMs:D})).stdout.split("\n").map(e=>e.trim()).filter(e=>e.length>0&&!e.startsWith("List of devices")).map(e=>e.split(/\s+/)).filter(e=>"device"===e[1]).map(e=>({serial:e[0],rawModel:(e.find(e=>e.startsWith("model:"))??"").replace("model:","")}));return await Promise.all(e.map(async({serial:e,rawModel:t})=>{let[i,r]=await Promise.all([R(e,t),T(e)]);return{platform:"android",id:e,name:i,kind:M(e)?"emulator":"device",booted:r}}))}async function T(e){try{let t=await L(e);return"1"===t.stdout.trim()}catch{return!1}}async function P(e,t=6e4){let i,r=_.fromTimeoutMs(t),n=Math.max(1,Math.ceil(t/1e3)),a=!1;try{await b(async({deadline:n})=>{if(n?.isExpired())throw a=!0,new f("COMMAND_FAILED","Android boot deadline exceeded",{serial:e,timeoutMs:t,elapsedMs:r.elapsedMs(),message:"timeout"});let o=Math.max(1e3,n?.remainingMs()??t),s=await L(e,Math.min(o,D));if(i=s,"1"!==s.stdout.trim())throw new f("COMMAND_FAILED","Android device is still booting",{serial:e,stdout:s.stdout,stderr:s.stderr,exitCode:s.exitCode})},{maxAttempts:n,baseDelayMs:1e3,maxDelayMs:1e3,jitter:0,shouldRetry:e=>{let t=E({error:e,stdout:i?.stdout,stderr:i?.stderr,context:{platform:"android",phase:"boot"}});return"ADB_TRANSPORT_UNAVAILABLE"!==t&&"ANDROID_BOOT_TIMEOUT"!==t}},{deadline:r,phase:"boot",classifyReason:e=>E({error:e,stdout:i?.stdout,stderr:i?.stderr,context:{platform:"android",phase:"boot"}}),onEvent:e=>{k&&process.stderr.write(`[agent-device][retry] ${JSON.stringify(e)}
|
|
2
|
+
`)}})}catch(p){let n=u(p),o=i?.stdout,s=i?.stderr,l=i?.exitCode,c=E({error:p,stdout:o,stderr:s,context:{platform:"android",phase:"boot"}});"BOOT_COMMAND_FAILED"===c&&"Android device is still booting"===n.message&&(c="ANDROID_BOOT_TIMEOUT");let d={serial:e,timeoutMs:t,elapsedMs:r.elapsedMs(),reason:c,hint:x(c),stdout:o,stderr:s,exitCode:l};if(a||"ANDROID_BOOT_TIMEOUT"===c)throw new f("COMMAND_FAILED","Android device did not finish booting in time",d);if("TOOL_MISSING"===n.code)throw new f("TOOL_MISSING",n.message,{...d,...n.details??{}});if("ADB_TRANSPORT_UNAVAILABLE"===c)throw new f("COMMAND_FAILED",n.message,{...d,...n.details??{}});throw new f(n.code,n.message,{...d,...n.details??{}},n.cause)}}function $(e){let t=e.trim();if(!t||/\s/.test(t))return!1;let i=/^([A-Za-z][A-Za-z0-9+.-]*):(.+)$/.exec(t);if(!i)return!1;let r=i[1]?.toLowerCase(),n=i[2]??"";return"http"!==r&&"https"!==r&&"ws"!==r&&"wss"!==r&&"ftp"!==r&&"ftps"!==r||n.startsWith("//")}function F(e,t){let i,r=e?.trim();return r?r:"http"===(i=t.trim().split(":")[0]?.toLowerCase())||"https"===i?"com.apple.mobilesafari":void 0}function V(e){let t=U(e),i=e=>{let i=G(t,e);if(null!==i)return"true"===i};return{text:G(t,"text"),desc:G(t,"content-desc"),resourceId:G(t,"resource-id"),className:G(t,"class"),bounds:G(t,"bounds"),clickable:i("clickable"),enabled:i("enabled"),focusable:i("focusable"),focused:i("focused")}}function U(e){let t=new Map,i=e.indexOf(" "),r=e.lastIndexOf(">");if(i<0||r<=i)return t;let n=/([^\s=/>]+)\s*=\s*(["'])([\s\S]*?)\2/y,a=i;for(;a<r;){for(;a<r;){let t=e[a];if(" "!==t&&"\n"!==t&&"\r"!==t&&" "!==t)break;a+=1}if(a>=r)break;let i=e[a];if("/"===i||">"===i)break;n.lastIndex=a;let o=n.exec(e);if(!o)break;t.set(o[1],o[3]),a=n.lastIndex}return t}function G(e,t){return e.get(t)??null}function B(e){if(!e)return;let t=/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/.exec(e);if(!t)return;let i=Number(t[1]),r=Number(t[2]);return{x:i,y:r,width:Math.max(0,Number(t[3])-i),height:Math.max(0,Number(t[4])-r)}}function j(e){return e?e.toLowerCase():""}function q(e){let t=e.trim();return!!t&&/^[\w.]+:id\/[\w.-]+$/i.test(t)}let W={settings:{type:"intent",value:"android.settings.SETTINGS"}};function J(e,t){return["-s",e.id,...t]}async function X(e,t){let i=t.trim();if(i.includes("."))return{type:"package",value:i};let r=W[i.toLowerCase()];if(r)return r;let n=(await c("adb",J(e,["shell","pm","list","packages"]))).stdout.split("\n").map(e=>e.replace("package:","").trim()).filter(Boolean).filter(e=>e.toLowerCase().includes(i.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 H(e,t="all"){let i=await z(e);return("user-installed"===t?(await Y(e)).filter(e=>i.has(e)):Array.from(i)).sort((e,t)=>e.localeCompare(t)).map(e=>({package:e,name:function(e){let t=new Set(["com","android","google","app","apps","service","services","mobile","client"]),i=e.split(".").flatMap(e=>e.split(/[_-]+/)).map(e=>e.trim().toLowerCase()).filter(e=>e.length>0),r=i[i.length-1]??e;for(let e=i.length-1;e>=0;e-=1){let n=i[e];if(!t.has(n)){r=n;break}}return r.split(/[^a-z0-9]+/i).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}(e)}))}async function z(e){let t=await c("adb",J(e,["shell","cmd","package","query-activities","--brief","-a","android.intent.action.MAIN","-c","android.intent.category.LAUNCHER"]),{allowFailure:!0});if(0!==t.exitCode||0===t.stdout.trim().length)return new Set;let i=new Set;for(let e of t.stdout.split("\n")){let t=e.trim();if(!t)continue;let r=t.split(/\s+/)[0],n=r.includes("/")?r.split("/")[0]:r;n&&i.add(n)}return i}async function Y(e){return(await c("adb",J(e,["shell","pm","list","packages","-3"]))).stdout.split("\n").map(e=>e.replace("package:","").trim()).filter(Boolean)}async function K(e){let t=await Z(e,[["shell","dumpsys","window","windows"],["shell","dumpsys","window"]]);if(t)return t;let i=await Z(e,[["shell","dumpsys","activity","activities"],["shell","dumpsys","activity"]]);return i||{}}async function Z(e,t){for(let i 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 i=t.exec(e);if(i)return{package:i[1],activity:i[2]}}return null}((await c("adb",J(e,i),{allowFailure:!0})).stdout??"");if(t)return t}return null}async function Q(e,t,i){e.booted||await P(e.id);let r=t.trim();if($(r)){if(i)throw new f("INVALID_ARGS","Activity override is not supported when opening a deep link URL");await c("adb",J(e,["shell","am","start","-W","-a","android.intent.action.VIEW","-d",r]));return}let n=await X(e,t);if("intent"===n.type){if(i)throw new f("INVALID_ARGS","Activity override requires a package name, not an intent");await c("adb",J(e,["shell","am","start","-W","-a",n.value]));return}if(i){let t=i.includes("/")?i:`${n.value}/${i.startsWith(".")?i:`.${i}`}`;await c("adb",J(e,["shell","am","start","-W","-a","android.intent.action.MAIN","-c","android.intent.category.DEFAULT","-c","android.intent.category.LAUNCHER","-n",t]));return}try{await c("adb",J(e,["shell","am","start","-W","-a","android.intent.action.MAIN","-c","android.intent.category.DEFAULT","-c","android.intent.category.LAUNCHER","-p",n.value]));return}catch(i){let t=await ee(e,n.value);if(!t)throw i;await c("adb",J(e,["shell","am","start","-W","-a","android.intent.action.MAIN","-c","android.intent.category.DEFAULT","-c","android.intent.category.LAUNCHER","-n",t]))}}async function ee(e,t){let i=await c("adb",J(e,["shell","cmd","package","resolve-activity","--brief",t]),{allowFailure:!0});return 0!==i.exitCode?null:function(e){let t=e.split("\n").map(e=>e.trim()).filter(Boolean);for(let e=t.length-1;e>=0;e-=1){let i=t[e];if(i.includes("/"))return i.split(/\s+/)[0]}return null}(i.stdout)}async function et(e){e.booted||await P(e.id)}async function ei(e,t){if("settings"===t.trim().toLowerCase())return void await c("adb",J(e,["shell","am","force-stop","com.android.settings"]));let i=await X(e,t);if("intent"===i.type)throw new f("INVALID_ARGS","Close requires a package name, not an intent");await c("adb",J(e,["shell","am","force-stop",i.value]))}async function er(e,t){let i=await X(e,t);if("intent"===i.type)throw new f("INVALID_ARGS","reinstall requires a package name, not an intent");let r=await c("adb",J(e,["uninstall",i.value]),{allowFailure:!0});if(0!==r.exitCode){let e=`${r.stdout}
|
|
3
|
+
${r.stderr}`.toLowerCase();if(!e.includes("unknown package")&&!e.includes("not installed"))throw new f("COMMAND_FAILED",`adb uninstall failed for ${i.value}`,{stdout:r.stdout,stderr:r.stderr,exitCode:r.exitCode})}return{package:i.value}}async function en(e,t){await c("adb",J(e,["install",t]))}async function ea(e,t,i){e.booted||await P(e.id);let{package:r}=await er(e,t);return await en(e,i),{package:r}}async function eo(e,t,i){await c("adb",J(e,["shell","input","tap",String(t),String(i)]))}async function es(e,t,i,r,n,a=250){await c("adb",J(e,["shell","input","swipe",String(t),String(i),String(r),String(n),String(a)]))}async function el(e){await c("adb",J(e,["shell","input","keyevent","4"]))}async function ec(e){await c("adb",J(e,["shell","input","keyevent","3"]))}async function ed(e){await c("adb",J(e,["shell","input","keyevent","187"]))}async function eu(e,t,i,r=800){await c("adb",J(e,["shell","input","swipe",String(t),String(i),String(t),String(i),String(r)]))}async function ep(e,t){let i=t.replace(/ /g,"%s");await c("adb",J(e,["shell","input","text",i]))}async function ef(e,t,i){await eo(e,t,i)}async function em(e,t,i,r){await ef(e,t,i);let n=null;for(let s of[{clearPadding:12,minClear:8,maxClear:48,chunkSize:4,delayMs:0},{clearPadding:24,minClear:16,maxClear:96,chunkSize:1,delayMs:15}]){var a,o;let l=(a=r.length+s.clearPadding,o=s.minClear,Math.max(o,Math.min(s.maxClear,a)));if(await eO(e,l),await eb(e,r,s.chunkSize,s.delayMs),(n=await eE(e,t,i))===r)return}throw new f("COMMAND_FAILED","Android fill verification failed",{expected:r,actual:n??null})}async function eh(e,t,i=.6){let{width:r,height:n}=await ey(e),a=Math.floor(r*i),o=Math.floor(n*i),s=Math.floor(r/2),l=Math.floor(n/2),d=s,u=l,p=s,m=l;switch(t){case"up":u=l-Math.floor(o/2),m=l+Math.floor(o/2);break;case"down":u=l+Math.floor(o/2),m=l-Math.floor(o/2);break;case"left":d=s-Math.floor(a/2),p=s+Math.floor(a/2);break;case"right":d=s+Math.floor(a/2),p=s-Math.floor(a/2);break;default:throw new f("INVALID_ARGS",`Unknown direction: ${t}`)}await c("adb",J(e,["shell","input","swipe",String(d),String(u),String(p),String(m),"300"]))}async function ew(e,t){for(let i=0;i<8;i+=1){let i="";try{i=await eN(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 i=t.toLowerCase(),r=/<node[^>]+>/g,n=r.exec(e);for(;n;){let t=U(n[0]),a=(G(t,"text")??"").toLowerCase(),o=(G(t,"content-desc")??"").toLowerCase();if(a.includes(i)||o.includes(i)){let e=B(G(t,"bounds"));if(e)return{x:Math.floor(e.x+e.width/2),y:Math.floor(e.y+e.height/2)};return{x:0,y:0}}n=r.exec(e)}return null}(i,t))return;await eh(e,"down",.5)}throw new f("COMMAND_FAILED",`Could not find element containing "${t}" after scrolling`)}async function eg(e,t){let i=await c("adb",J(e,["exec-out","screencap","-p"]),{binaryStdout:!0});if(!i.stdoutBuffer)throw new f("COMMAND_FAILED","Failed to capture screenshot");await d.writeFile(t,i.stdoutBuffer)}async function ev(e,t,i){let r=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}`)}(i);switch(r){case"wifi":return void await c("adb",J(e,["shell","svc","wifi",n?"enable":"disable"]));case"airplane":await c("adb",J(e,["shell","settings","put","global","airplane_mode_on",n?"1":"0"])),await c("adb",J(e,["shell","am","broadcast","-a","android.intent.action.AIRPLANE_MODE","--ez","state",n?"true":"false"]));return;case"location":return void await c("adb",J(e,["shell","settings","put","secure","location_mode",n?"3":"0"]));default:throw new f("INVALID_ARGS",`Unsupported setting: ${t}`)}}async function eI(e,t={}){return function(e,t,i){let r=function(e){let t={type:null,label:null,value:null,identifier:null,depth:-1,children:[]},i=[t],r=/<node\b[^>]*>|<\/node>/g,n=r.exec(e);for(;n;){let t=n[0];if(t.startsWith("</node")){i.length>1&&i.pop(),n=r.exec(e);continue}let a=V(t),o=B(a.bounds),s=i[i.length-1],l={type:a.className,label:a.text||a.desc,value:a.text,identifier:a.resourceId,rect:o,enabled:a.enabled,hittable:a.clickable??a.focusable,depth:s.depth+1,parentIndex:void 0,children:[]};s.children.push(l),t.endsWith("/>")||i.push(l),n=r.exec(e)}return t}(e),n=[],a=!1,o=i.depth??1/0,s=i.scope?function(e,t){let i=t.toLowerCase(),r=[...e.children];for(;r.length>0;){let e=r.shift(),t=e.label?.toLowerCase()??"",n=e.value?.toLowerCase()??"",a=e.identifier?.toLowerCase()??"";if(t.includes(i)||n.includes(i)||a.includes(i))return e;r.push(...e.children)}return null}(r,i.scope):null,l=s?[s]:r.children,c=new Map,d=e=>{let t=c.get(e);if(void 0!==t)return t;for(let t of e.children)if(t.hittable||d(t))return c.set(e,!0),!0;return c.set(e,!1),!1},u=(e,t,r,s=!1,l=!1)=>{var c,p,f,m,h,w;let g,v,I,A,y,N,S,D;if(n.length>=800){a=!0;return}if(t>o)return;let _=!!i.raw||(c=e,p=i,f=s,m=d(e),h=l,v=j(c.type),I=!!(c.label&&c.label.trim().length>0),A=!!(c.identifier&&c.identifier.trim().length>0),y=I&&!q(c.label??""),N=A&&!q(c.identifier??""),S=(g=(w=v).split(".").pop()??w).includes("layout")||"viewgroup"===g||"view"===g,D="imageview"===v||"imagebutton"===v,p.interactiveOnly?!!c.hittable||!!(y||N)&&!D&&(!S||!!h)&&(f||m||h):p.compact?y||N||!!c.hittable:!S&&!D||!!c.hittable||!!y||!!N&&!!m||m),b=r;_&&(b=n.length,n.push({index:b,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:r}));let O=s||!!e.hittable,E=l||function(e){if(!e)return!1;let t=j(e);return t.includes("recyclerview")||t.includes("listview")||t.includes("gridview")}(e.type);for(let i of e.children)if(u(i,t+1,b,O,E),a)return};for(let e of l)if(u(e,0,void 0,!1,!1),a)break;return a?{nodes:n,truncated:a}:{nodes:n}}(await eN(e),0,t)}async function eA(){if(!await v("adb"))throw new f("TOOL_MISSING","adb not found in PATH")}async function ey(e){let t=(await c("adb",J(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 eN(e){return O(()=>eS(e),{shouldRetry:e_})}async function eS(e){var t,i,r;let n,a,o=await c("adb",J(e,["exec-out","uiautomator","dump","/dev/tty"]),{allowFailure:!0});if(0===o.exitCode){let e=eD(o.stdout,o.stderr);if(e)return e}let s="/sdcard/window_dump.xml",l=await c("adb",J(e,["shell","uiautomator","dump",s])),d=(t=s,i=l.stdout,r=l.stderr,n=`${i}
|
|
4
|
+
${r}`,a=/dumped to:\s*(\S+)/i.exec(n),a?.[1]??t),u=await c("adb",J(e,["shell","cat",d])),p=eD(u.stdout,u.stderr);if(!p)throw new f("COMMAND_FAILED","uiautomator dump did not return XML",{stdout:u.stdout,stderr:u.stderr});return p}function eD(e,t){let i=`${e}
|
|
5
|
+
${t}`,r=i.indexOf("<?xml"),n=r>=0?r:i.indexOf("<hierarchy");if(n<0)return null;let a=i.lastIndexOf("</hierarchy>");if(a<0||a<n)return null;let o=i.slice(n,a+12).trim();return o.length>0?o:null}function e_(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")||t.includes("no such file or directory"))}async function eb(e,t,i,r){let n=Math.max(1,Math.floor(i));for(let i=0;i<t.length;i+=n){let a=t.slice(i,i+n);await ep(e,a),r>0&&i+n<t.length&&await ek(r)}}async function eO(e,t){let i=Math.max(0,t);await c("adb",J(e,["shell","input","keyevent","KEYCODE_MOVE_END"]),{allowFailure:!0});for(let t=0;t<i;t+=24){let r=Math.min(24,i-t);await c("adb",J(e,["shell","input","keyevent",...Array(r).fill("KEYCODE_DEL")]),{allowFailure:!0})}}async function eE(e,t,i){let r,n=await eN(e),a=/<node\b[^>]*>/g,o=null,s=null,l=null;for(;null!==(r=a.exec(n));){let e=V(r[0]),n=B(e.bounds);if(!n)continue;let a=e.className??"",c=(e.text??"").replace(/"/g,'"').replace(/'/g,"'").replace(/</g,"<").replace(/>/g,">").replace(/&/g,"&"),d=e.focused??!1;if(!c)continue;let u=Math.max(1,n.width*n.height),p=t>=n.x&&t<=n.x+n.width&&i>=n.y&&i<=n.y+n.height;if(d&&ex(a)){(!o||u<=o.area)&&(o={text:c,area:u});continue}if(p&&ex(a)){(!s||u<=s.area)&&(s={text:c,area:u});continue}p&&(!l||u<=l.area)&&(l={text:c,area:u})}return o?.text??s?.text??l?.text??null}function ex(e){let t=e.toLowerCase();return t.includes("edittext")||t.includes("textfield")}async function ek(e){await new Promise(t=>setTimeout(t,e))}function eM(e,t,i){if(!e)return t;let r=Number(e);return Number.isFinite(r)?Math.max(i,Math.floor(r)):t}let eL=eM(process.env.AGENT_DEVICE_IOS_DEVICECTL_LIST_TIMEOUT_MS,8e3,500);async function eR(){if("darwin"!==process.platform)throw new f("UNSUPPORTED_PLATFORM","iOS tools are only available on macOS");if(!await v("xcrun"))throw new f("TOOL_MISSING","xcrun not found in PATH");let e=[],t=await c("xcrun",["simctl","list","devices","-j"]);try{let i=JSON.parse(t.stdout);for(let t of Object.values(i.devices))for(let i of t)i.isAvailable&&e.push({platform:"ios",id:i.udid,name:i.name,kind:"simulator",booted:"Booted"===i.state})}catch(e){throw new f("COMMAND_FAILED","Failed to parse simctl devices JSON",void 0,e)}let i=null;try{i=r.join(h.tmpdir(),`agent-device-devicectl-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);let t=await c("xcrun",["devicectl","list","devices","--json-output",i],{allowFailure:!0,timeoutMs:eL});if(0!==t.exitCode)return e;let n=await d.readFile(i,"utf8"),a=JSON.parse(n);for(let t of a.result?.devices??[])if((t.hardwareProperties?.platform??"").toLowerCase().includes("ios")){let i=t.hardwareProperties?.udid??t.identifier??"",r=t.name??t.deviceProperties?.name??i;if(!i)continue;e.push({platform:"ios",id:i,name:r,kind:"device",booted:!0})}}catch{}finally{i&&await d.rm(i,{force:!0}).catch(()=>{})}return e}let eC=eM(process.env.AGENT_DEVICE_IOS_BOOT_TIMEOUT_MS,S,5e3),eT=eM(process.env.AGENT_DEVICE_IOS_SIMCTL_LIST_TIMEOUT_MS,N,1e3),eP=eM(process.env.AGENT_DEVICE_IOS_APP_LAUNCH_TIMEOUT_MS,3e4,5e3),e$=eM(process.env.AGENT_DEVICE_IOS_DEVICECTL_TIMEOUT_MS,2e4,1e3),eF=y(process.env.AGENT_DEVICE_RETRY_LOGS);async function eV(e,t){let i=["devicectl",...e],r=await c("xcrun",i,{allowFailure:!0,timeoutMs:e$});if(0===r.exitCode)return;let n=String(r.stdout??""),a=String(r.stderr??"");throw new f("COMMAND_FAILED",`Failed to ${t.action}`,{cmd:"xcrun",args:i,exitCode:r.exitCode,stdout:n,stderr:a,deviceId:t.deviceId,hint:eB(n,a)??eG})}async function eU(e,t){let i=r.join(h.tmpdir(),`agent-device-ios-apps-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`),n=["devicectl","device","info","apps","--device",e.id,"--include-all-apps","--json-output",i],a=await c("xcrun",n,{allowFailure:!0,timeoutMs:e$});try{var o,s;if(0!==a.exitCode){let t=String(a.stdout??""),i=String(a.stderr??"");throw new f("COMMAND_FAILED","Failed to list iOS apps",{cmd:"xcrun",args:n,exitCode:a.exitCode,stdout:t,stderr:i,deviceId:e.id,hint:eB(t,i)??eG})}let r=await d.readFile(i,"utf8");return o=function(e){let t=e?.result?.apps;if(!Array.isArray(t))return[];let i=[];for(let e of t){if(!e||"object"!=typeof e)continue;let t="string"==typeof e.bundleIdentifier?e.bundleIdentifier.trim():"";if(!t)continue;let r="string"==typeof e.name&&e.name.trim().length>0?e.name.trim():t;i.push({bundleId:t,name:r})}return i}(JSON.parse(r)),s=t,"user-installed"===s?o.filter(e=>!e.bundleId.startsWith("com.apple.")):o}catch(t){if(t instanceof f)throw t;throw new f("COMMAND_FAILED","Failed to parse iOS apps list",{deviceId:e.id,cause:String(t)})}finally{await d.unlink(i).catch(()=>{})}}let eG="Ensure the iOS device is unlocked, trusted, and available in Xcode > Devices, then retry.";function eB(e,t){let i=`${e}
|
|
6
|
+
${t}`.toLowerCase();return i.includes("device is busy")&&i.includes("connecting")?"iOS device is still connecting. Keep it unlocked and connected by cable until it is fully available in Xcode Devices, then retry.":i.includes("coredeviceservice")&&i.includes("timed out")?"CoreDevice service timed out. Reconnect the device and retry; if it persists restart Xcode and the iOS device.":null}function ej(e,t){if("simulator"!==e.kind)throw new f("UNSUPPORTED_OPERATION",`${t} is only supported on iOS simulators`)}async function eq(e){let t,i;if("simulator"!==e.kind||"Booted"===await eW(e.id))return;let r=_.fromTimeoutMs(eC);try{await b(async({deadline:r})=>{if(r?.isExpired())throw new f("COMMAND_FAILED","iOS simulator boot deadline exceeded",{timeoutMs:eC});let n=Math.max(1e3,r?.remainingMs()??eC),a=await c("xcrun",["simctl","boot",e.id],{allowFailure:!0,timeoutMs:n});t={stdout:String(a.stdout??""),stderr:String(a.stderr??""),exitCode:a.exitCode};let o=`${t.stdout}
|
|
7
|
+
${t.stderr}`.toLowerCase(),s=o.includes("already booted")||o.includes("current state: booted");if(0!==t.exitCode&&!s)throw new f("COMMAND_FAILED","simctl boot failed",{stdout:t.stdout,stderr:t.stderr,exitCode:t.exitCode});let l=await c("xcrun",["simctl","bootstatus",e.id,"-b"],{allowFailure:!0,timeoutMs:n});if(i={stdout:String(l.stdout??""),stderr:String(l.stderr??""),exitCode:l.exitCode},0!==i.exitCode)throw new f("COMMAND_FAILED","simctl bootstatus failed",{stdout:i.stdout,stderr:i.stderr,exitCode:i.exitCode});let d=await eW(e.id);if("Booted"!==d)throw new f("COMMAND_FAILED","Simulator is still booting",{state:d})},{maxAttempts:3,baseDelayMs:500,maxDelayMs:2e3,jitter:.2,shouldRetry:e=>{let r=E({error:e,stdout:i?.stdout??t?.stdout,stderr:i?.stderr??t?.stderr,context:{platform:"ios",phase:"boot"}});return"IOS_BOOT_TIMEOUT"!==r&&"CI_RESOURCE_STARVATION_SUSPECTED"!==r}},{deadline:r,phase:"boot",classifyReason:e=>E({error:e,stdout:i?.stdout??t?.stdout,stderr:i?.stderr??t?.stderr,context:{platform:"ios",phase:"boot"}}),onEvent:e=>{eF&&process.stderr.write(`[agent-device][retry] ${JSON.stringify(e)}
|
|
8
|
+
`)}})}catch(a){let n=E({error:a,stdout:i?.stdout??t?.stdout,stderr:i?.stderr??t?.stderr,context:{platform:"ios",phase:"boot"}});throw new f("COMMAND_FAILED","iOS simulator failed to boot",{platform:"ios",deviceId:e.id,timeoutMs:eC,elapsedMs:r.elapsedMs(),reason:n,hint:x(n),boot:t,bootstatus:i})}}async function eW(e){let t=await c("xcrun",["simctl","list","devices","-j"],{allowFailure:!0,timeoutMs:eT});if(0!==t.exitCode)return null;try{let i=JSON.parse(String(t.stdout??""));for(let t of Object.values(i.devices??{})){let i=t.find(t=>t.udid===e);if(i)return i.state}return null}catch{return null}}let eJ={settings:"com.apple.Preferences"};async function eX(e,t){let i=t.trim();if(i.includes("."))return i;let r=eJ[i.toLowerCase()];if(r)return r;let n=("simulator"===e.kind?await e3(e):await eU(e,"all")).filter(e=>e.name.toLowerCase()===i.toLowerCase());if(1===n.length)return n[0].bundleId;if(n.length>1)throw new f("INVALID_ARGS",`Multiple apps matched "${t}"`,{matches:n});throw new f("APP_NOT_INSTALLED",`No app found matching "${t}"`)}async function eH(e,t,i){let r=i?.url?.trim();if(r){if(!$(r))throw new f("INVALID_ARGS","open <app> <url> requires a valid URL target");if("simulator"===e.kind){await eq(e),await c("open",["-a","Simulator"],{allowFailure:!0}),await c("xcrun",["simctl","openurl",e.id,r]);return}let n=F(i?.appBundleId??await eX(e,t),r);if(!n)throw new f("INVALID_ARGS","Deep link open on iOS devices requires an active app bundle ID. Open the app first, then open the URL.");await e8(e,n,{payloadUrl:r});return}let n=t.trim();if($(n)){if("simulator"===e.kind){await eq(e),await c("open",["-a","Simulator"],{allowFailure:!0}),await c("xcrun",["simctl","openurl",e.id,n]);return}let t=F(i?.appBundleId,n);if(!t)throw new f("INVALID_ARGS","Deep link open on iOS devices requires an active app bundle ID. Open the app first, then open the URL.");await e8(e,t,{payloadUrl:n});return}let a=i?.appBundleId??await eX(e,t);"simulator"===e.kind?await e5(e,a):await e8(e,a)}async function ez(e){"simulator"!==e.kind||"Booted"!==await eW(e.id)&&(await eq(e),await c("open",["-a","Simulator"],{allowFailure:!0}))}async function eY(e,t){let i=await eX(e,t);if("simulator"===e.kind){await eq(e);let t=await c("xcrun",["simctl","terminate",e.id,i],{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,i],stdout:t.stdout,stderr:t.stderr,exitCode:t.exitCode})}return}await eV(["device","process","terminate","--device",e.id,i],{action:"terminate iOS app",deviceId:e.id})}async function eK(e,t){ej(e,"reinstall");let i=await eX(e,t);await eq(e);let r=await c("xcrun",["simctl","uninstall",e.id,i],{allowFailure:!0});if(0!==r.exitCode){let e=`${r.stdout}
|
|
9
|
+
${r.stderr}`.toLowerCase();if(!e.includes("not installed")&&!e.includes("not found")&&!e.includes("no such file"))throw new f("COMMAND_FAILED",`simctl uninstall failed for ${i}`,{stdout:r.stdout,stderr:r.stderr,exitCode:r.exitCode})}return{bundleId:i}}async function eZ(e,t){ej(e,"reinstall"),await eq(e),await c("xcrun",["simctl","install",e.id,t])}async function eQ(e,t,i){let{bundleId:r}=await eK(e,t);return await eZ(e,i),{bundleId:r}}async function e0(e,t){if("simulator"===e.kind){await eq(e),await c("xcrun",["simctl","io",e.id,"screenshot",t]);return}await eV(["device","screenshot","--device",e.id,t],{action:"capture iOS screenshot",deviceId:e.id})}async function e1(e,t,i,r){ej(e,"settings"),await eq(e);let n=t.toLowerCase(),a=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}`)}(i);switch(n){case"wifi":return void await c("xcrun",["simctl","status_bar",e.id,"override","--wifiMode",a?"active":"failed"]);case"airplane":a?await c("xcrun",["simctl","status_bar",e.id,"override","--dataNetwork","hide","--wifiMode","failed","--wifiBars","0","--cellularMode","failed","--cellularBars","0","--operatorName",""]):await c("xcrun",["simctl","status_bar",e.id,"clear"]);return;case"location":if(!r)throw new f("INVALID_ARGS","location setting requires an active app in session");await c("xcrun",["simctl","privacy",e.id,a?"grant":"revoke","location",r]);return;default:throw new f("INVALID_ARGS",`Unsupported setting: ${t}`)}}async function e2(e,t="all"){var i;return"simulator"===e.kind?(i=await e3(e),"user-installed"===t?i.filter(e=>!e.bundleId.startsWith("com.apple.")):i):await eU(e,t)}async function e3(e){let t=(await c("xcrun",["simctl","listapps",e.id],{allowFailure:!0})).stdout.trim();if(!t)return[];let i=null;if(t.startsWith("{"))try{i=JSON.parse(t)}catch{i=null}if(!i&&t.startsWith("{"))try{let e=await c("plutil",["-convert","json","-o","-","-"],{allowFailure:!0,stdin:t});0===e.exitCode&&e.stdout.trim().startsWith("{")&&(i=JSON.parse(e.stdout))}catch{i=null}return i?Object.entries(i).map(([e,t])=>({bundleId:e,name:t.CFBundleDisplayName??t.CFBundleName??e})):[]}function e4(e){if(!(e instanceof f)||"COMMAND_FAILED"!==e.code)return!1;let t=e.details??{};if(4!==t.exitCode)return!1;let i=String(t.stderr??"").toLowerCase();return i.includes("fbsopenapplicationserviceerrordomain")&&i.includes("the request to open")}async function e5(e,t){await eq(e),await c("open",["-a","Simulator"],{allowFailure:!0});let i=_.fromTimeoutMs(eP);await b(async({deadline:i})=>{if(i?.isExpired())throw new f("COMMAND_FAILED","App launch deadline exceeded",{timeoutMs:eP});let r=await c("xcrun",["simctl","launch",e.id,t],{allowFailure:!0});if(0!==r.exitCode)throw new f("COMMAND_FAILED",`xcrun exited with code ${r.exitCode}`,{cmd:"xcrun",args:["simctl","launch",e.id,t],stdout:r.stdout,stderr:r.stderr,exitCode:r.exitCode})},{maxAttempts:30,baseDelayMs:1e3,maxDelayMs:5e3,jitter:.2,shouldRetry:e4},{deadline:i})}async function e8(e,t,i){let r=["device","process","launch","--device",e.id,t];i?.payloadUrl&&r.push("--payload-url",i.payloadUrl),await eV(r,{action:"launch iOS app",deviceId:e.id})}async function e6(e,t,i){let r=(e.get(t)??Promise.resolve()).catch(()=>{}).then(i);return e.set(t,r),r.finally(()=>{e.get(t)===r&&e.delete(t)})}let e7=new Map,e9=new Map,te=eM(process.env.AGENT_DEVICE_RUNNER_STARTUP_TIMEOUT_MS,45e3,5e3),tt=eM(process.env.AGENT_DEVICE_RUNNER_COMMAND_TIMEOUT_MS,15e3,1e3),ti=eM(process.env.AGENT_DEVICE_RUNNER_CONNECT_ATTEMPT_INTERVAL_MS,250,50),tr=eM(process.env.AGENT_DEVICE_RUNNER_CONNECT_RETRY_BASE_DELAY_MS,300,10),tn=eM(process.env.AGENT_DEVICE_RUNNER_CONNECT_RETRY_MAX_DELAY_MS,2e3,10),ta=eM(process.env.AGENT_DEVICE_RUNNER_CONNECT_REQUEST_TIMEOUT_MS,5e3,250),to=eM(process.env.AGENT_DEVICE_IOS_DEVICE_INFO_TIMEOUT_MS,1e4,500),ts=eM(process.env.AGENT_DEVICE_RUNNER_DESTINATION_TIMEOUT_SECONDS,20,5),tl=r.join(h.homedir(),".agent-device","ios-runner");async function tc(e,t,i={}){var r;return(function(e){if("ios"!==e.platform)throw new f("UNSUPPORTED_PLATFORM",`Unsupported platform for iOS runner: ${e.platform}`);if("simulator"!==e.kind&&"device"!==e.kind)throw new f("UNSUPPORTED_OPERATION",`Unsupported iOS device kind for runner: ${e.kind}`)}(e),"snapshot"===(r=t.command)||"findText"===r||"listTappables"===r||"alert"===r)?O(()=>td(e,t,i),{shouldRetry:tD}):td(e,t,i)}async function td(e,t,i={}){let r;try{let n=(r=await tv(e,i)).ready?tt:te;return await tu(e,r,t,i.logPath,n)}catch(a){let n=a instanceof f?a:new f("COMMAND_FAILED",String(a));if("COMMAND_FAILED"===n.code&&"string"==typeof n.message&&n.message.includes("Runner did not accept connection")&&tb(n)&&r?.ready){r?await th(r):await tf(e.id),r=await tv(e,i);let n=await tE(r.device,r.port,t,i.logPath,te);return await tp(n,r,i.logPath)}throw a}}async function tu(e,t,i,r,n){let a=await tE(e,t.port,i,r,n,t);return await tp(a,t,r)}async function tp(e,t,i){let r=await e.text(),n={};try{n=JSON.parse(r)}catch{throw new f("COMMAND_FAILED","Invalid runner response",{text:r})}if(!n.ok)throw new f("COMMAND_FAILED",n.error?.message??"Runner error",{runner:n,xcodebuild:{exitCode:1,stdout:"",stderr:""},logPath:i});return t.ready=!0,n.data??{}}async function tf(e){await e6(e9,e,async()=>{await tw(e)})}async function tm(){let e=Array.from(e7.keys());await Promise.allSettled(e.map(async e=>{await tf(e)}))}async function th(e){await e6(e9,e.deviceId,async()=>{await tw(e.deviceId,e)})}async function tw(e,t){let i=t??e7.get(e);if(i){try{await tE(i.device,i.port,{command:"shutdown"},void 0,15e3)}catch{await tI(i.child.pid,"SIGTERM")}try{await Promise.race([i.testPromise,new Promise(e=>setTimeout(e,1e4))])}catch{}await tI(i.child.pid,"SIGKILL"),tT(i.xctestrunPath),tT(i.jsonPath),e7.get(e)===i&&e7.delete(e)}}async function tg(e){await c("xcrun",["simctl","bootstatus",e,"-b"],{allowFailure:!0,timeoutMs:te})}async function tv(e,t){return await e6(e9,e.id,async()=>{var i,r;let n=e7.get(e.id);if(n){if((i=n.child.pid)&&p(i))return n;await tw(e.id,n)}await ("simulator"!==(r=e).kind?Promise.resolve():tg(r.id));let a=await tA(e,t),o=await tR(),{xctestrunPath:l,jsonPath:c}=await tC(a,{AGENT_DEVICE_RUNNER_PORT:String(o)},`session-${e.id}-${o}`),{child:d,wait:u}=s("xcodebuild",["test-without-building","-only-testing","AgentDeviceRunnerUITests/RunnerTests/testCommand","-parallel-testing-enabled","NO","-test-timeouts-enabled","NO",ty(e),"1","-destination-timeout",String(ts),"-xctestrun",l,"-destination",function(e){if("ios"!==e.platform)throw new f("UNSUPPORTED_PLATFORM",`Unsupported platform for iOS runner: ${e.platform}`);return"simulator"===e.kind?`platform=iOS Simulator,id=${e.id}`:`platform=iOS,id=${e.id}`}(e)],{allowFailure:!0,env:{...process.env,AGENT_DEVICE_RUNNER_PORT:String(o)}});d.stdout?.on("data",e=>{tS(e,t.logPath,t.traceLogPath,t.verbose)}),d.stderr?.on("data",e=>{tS(e,t.logPath,t.traceLogPath,t.verbose)});let m={device:e,deviceId:e.id,port:o,xctestrunPath:l,jsonPath:c,testPromise:u,child:d,ready:!1};return e7.set(e.id,m),m})}async function tI(e,t){if(!e||e<=0)return;try{process.kill(e,t)}catch{}let i="SIGTERM"===t?"TERM":"KILL";try{await c("pkill",[`-${i}`,"-P",String(e)],{allowFailure:!0})}catch{}}async function tA(e,t){var i,o;let s,l=(i=e.kind,(s=process.env.AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH?.trim())?r.resolve(s):"simulator"===i?r.join(tl,"derived"):r.join(tl,"derived",i));if(y(process.env.AGENT_DEVICE_IOS_CLEAN_DERIVED)){!function(e,t=process.env){if(t.AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH?.trim()&&!function(e=process.env){return y(e.AGENT_DEVICE_IOS_ALLOW_OVERRIDE_DERIVED_CLEAN)}(t))throw new f("COMMAND_FAILED","Refusing to clean AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH automatically",{derivedPath:e,hint:"Unset AGENT_DEVICE_IOS_CLEAN_DERIVED, or set AGENT_DEVICE_IOS_ALLOW_OVERRIDE_DERIVED_CLEAN=1 if you trust this path."})}(l);try{m.rmSync(l,{recursive:!0,force:!0})}catch{}}let c=tN(l);if(c)return c;let d=function(){let e=r.dirname(a(import.meta.url)),t=e;for(let e=0;e<6;e+=1){let e=r.join(t,"package.json");if(m.existsSync(e))return t;t=r.dirname(t)}return e}(),u=r.join(d,"ios-runner","AgentDeviceRunner","AgentDeviceRunner.xcodeproj");if(!m.existsSync(u))throw new f("COMMAND_FAILED","iOS runner project not found",{projectPath:u});let p=function(e=process.env,t=!1){if(!t)return[];let i=e.AGENT_DEVICE_IOS_TEAM_ID?.trim()||"",r=e.AGENT_DEVICE_IOS_SIGNING_IDENTITY?.trim()||"",n=e.AGENT_DEVICE_IOS_PROVISIONING_PROFILE?.trim()||"",a=["CODE_SIGN_STYLE=Automatic"];return i&&a.push(`DEVELOPMENT_TEAM=${i}`),r&&a.push(`CODE_SIGN_IDENTITY=${r}`),n&&a.push(`PROVISIONING_PROFILE_SPECIFIER=${n}`),a}(process.env,"device"===e.kind),h="device"===e.kind?["-allowProvisioningUpdates"]:[];try{await n("xcodebuild",["build-for-testing","-project",u,"-scheme","AgentDeviceRunner","-parallel-testing-enabled","NO",ty(e),"1","-destination",function(e){if("ios"!==e.platform)throw new f("UNSUPPORTED_PLATFORM",`Unsupported platform for iOS runner: ${e.platform}`);return"simulator"===e.kind?`platform=iOS Simulator,id=${e.id}`:"generic/platform=iOS"}(e),"-derivedDataPath",l,...h,...p],{onStdoutChunk:e=>{tS(e,t.logPath,t.traceLogPath,t.verbose)},onStderrChunk:e=>{tS(e,t.logPath,t.traceLogPath,t.verbose)}})}catch(a){let e,i,r=a instanceof f?a:new f("COMMAND_FAILED",String(a)),n=(e=(o=r).details?JSON.stringify(o.details):"",(i=`${o.message}
|
|
10
|
+
${e}`.toLowerCase()).includes("requires a development team")?"Configure signing in Xcode or set AGENT_DEVICE_IOS_TEAM_ID for physical-device runs.":i.includes("no profiles for")||i.includes("provisioning profile")?"Install/select a valid iOS provisioning profile, or set AGENT_DEVICE_IOS_PROVISIONING_PROFILE.":i.includes("code signing")?"Enable Automatic Signing in Xcode or provide AGENT_DEVICE_IOS_TEAM_ID and optional AGENT_DEVICE_IOS_SIGNING_IDENTITY.":void 0);throw new f("COMMAND_FAILED","xcodebuild build-for-testing failed",{error:r.message,details:r.details,logPath:t.logPath,hint:n})}let w=tN(l);if(!w)throw new f("COMMAND_FAILED","Failed to locate .xctestrun after build");return w}function ty(e){return"device"===e.kind?"-maximum-concurrent-test-device-destinations":"-maximum-concurrent-test-simulator-destinations"}function tN(e){if(!m.existsSync(e))return null;let t=[],i=[e];for(;i.length>0;){let e=i.pop();for(let n of m.readdirSync(e,{withFileTypes:!0})){let a=r.join(e,n.name);if(n.isDirectory()){i.push(a);continue}if(n.isFile()&&n.name.endsWith(".xctestrun"))try{let e=m.statSync(a);t.push({path:a,mtimeMs:e.mtimeMs})}catch{}}}return 0===t.length?null:(t.sort((e,t)=>t.mtimeMs-e.mtimeMs),t[0]?.path??null)}function tS(e,t,i,r){t&&m.appendFileSync(t,e),i&&m.appendFileSync(i,e),r&&process.stderr.write(e)}function tD(e){if(!(e instanceof f)||"COMMAND_FAILED"!==e.code)return!1;let t=`${e.message??""}`.toLowerCase();return!(t.includes("xcodebuild exited early")||t.includes("device is busy")&&t.includes("connecting"))&&!!(t.includes("runner did not accept connection")||t.includes("fetch failed")||t.includes("econnrefused")||t.includes("socket hang up"))}function t_(e){let{port:t,endpoints:i,logPath:r,lastError:n}=e,a="Runner did not accept connection";return new f("COMMAND_FAILED",a,{port:t,endpoints:i,logPath:r,lastError:n?String(n):void 0,reason:E({error:n,message:a,context:{platform:"ios",phase:"connect"}}),hint:x("IOS_RUNNER_CONNECT_TIMEOUT")})}function tb(e){return!(e instanceof f)||"COMMAND_FAILED"!==e.code||!String(e.message??"").toLowerCase().includes("xcodebuild exited early")}async function tO(e){var t,i;let r,{session:n,port:a,logPath:o}=e,s=await n.testPromise,l="Runner did not accept connection (xcodebuild exited early)",c=E({message:l,stdout:s.stdout,stderr:s.stderr,context:{platform:"ios",phase:"connect"}});return new f("COMMAND_FAILED",l,{port:a,logPath:o,xcodebuild:{exitCode:s.exitCode,stdout:s.stdout,stderr:s.stderr},reason:c,hint:(t=s.stdout,i=s.stderr,(r=`${l}
|
|
11
|
+
${t}
|
|
12
|
+
${i}`.toLowerCase()).includes("device is busy")&&r.includes("connecting")?"Target iOS device is still connecting. Keep it unlocked, wait for device trust/connection to settle, then retry.":x("IOS_RUNNER_CONNECT_TIMEOUT"))})}async function tE(e,t,i,r,n=te,a){let o=_.fromTimeoutMs(n),s=await tx(e,t,o.remainingMs()),l=null,c=Math.max(1,Math.ceil(n/ti));try{return await b(async({deadline:o})=>{if(o?.isExpired())throw new f("COMMAND_FAILED","Runner connection deadline exceeded",{port:t,timeoutMs:n});if(a&&null!==a.child.exitCode&&void 0!==a.child.exitCode)throw await tO({session:a,port:t,logPath:r});for(let r of("device"===e.kind&&(s=await tx(e,t,o?.remainingMs())),s))try{let e=o?.remainingMs()??n;if(e<=0)throw new f("COMMAND_FAILED","Runner connection deadline exceeded",{port:t,timeoutMs:n});return await tk(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)},Math.min(ta,e))}catch(e){l=e}throw new f("COMMAND_FAILED","Runner endpoint probe failed",{port:t,endpoints:s,lastError:l?String(l):void 0})},{maxAttempts:c,baseDelayMs:tr,maxDelayMs:tn,jitter:.2,shouldRetry:tb},{deadline:o,phase:"ios_runner_connect"})}catch(e){l||(l=e)}if("simulator"===e.kind){let n=o.remainingMs();if(n<=0)throw t_({port:t,endpoints:s,logPath:r,lastError:l});let a=await tL(e.id,t,i,n);return new Response(a.body,{status:a.status})}throw t_({port:t,endpoints:s,logPath:r,lastError:l})}async function tx(e,t,i){let r=[`http://127.0.0.1:${t}/command`];if("device"!==e.kind)return r;let n=await tM(e.id,i);return n&&r.unshift(`http://[${n}]:${t}/command`),r}async function tk(e,t,i){let r=new AbortController,n=setTimeout(()=>r.abort(),i);try{return await fetch(e,{...t,signal:r.signal})}finally{clearTimeout(n)}}async function tM(e,t){if("number"==typeof t&&t<=0)return null;let i="number"==typeof t?Math.max(1,Math.min(to,t)):to,n=r.join(h.tmpdir(),`agent-device-devicectl-info-${process.pid}-${Date.now()}.json`);try{let t=Math.max(1,Math.ceil(i/1e3)),r=await c("xcrun",["devicectl","device","info","details","--device",e,"--json-output",n,"--timeout",String(t)],{allowFailure:!0,timeoutMs:i});if(0!==r.exitCode||!m.existsSync(n))return null;let a=JSON.parse(m.readFileSync(n,"utf8"));if(a.info?.outcome&&"success"!==a.info.outcome)return null;let o=(a.result?.connectionProperties?.tunnelIPAddress??a.result?.device?.connectionProperties?.tunnelIPAddress)?.trim();return o&&o.length>0?o:null}catch{return null}finally{tT(n)}}async function tL(e,t,i,r){let n=JSON.stringify(i),a=await c("xcrun",["simctl","spawn",e,"/usr/bin/curl","-s","-X","POST","-H","Content-Type: application/json","--data",n,`http://127.0.0.1:${t}/command`],{allowFailure:!0,timeoutMs:r}),o=a.stdout;if(0!==a.exitCode){let e=E({message:"Runner did not accept connection (simctl spawn)",stdout:a.stdout,stderr:a.stderr,context:{platform:"ios",phase:"connect"}});throw new f("COMMAND_FAILED","Runner did not accept connection (simctl spawn)",{port:t,stdout:a.stdout,stderr:a.stderr,exitCode:a.exitCode,reason:e,hint:x(e)})}return{status:200,body:o}}async function tR(){return await new Promise((e,t)=>{let i=g.createServer();i.listen(0,"127.0.0.1",()=>{let r=i.address();i.close(),"object"==typeof r&&r?.port?e(r.port):t(new f("COMMAND_FAILED","Failed to allocate port"))}),i.on("error",t)})}async function tC(e,t,i){let n,a=r.dirname(e),o=i.replace(/[^a-zA-Z0-9._-]/g,"_"),s=r.join(a,`AgentDeviceRunner.env.${o}.json`),l=r.join(a,`AgentDeviceRunner.env.${o}.xctestrun`),d=await c("plutil",["-convert","json","-o","-",e],{allowFailure:!0});if(0!==d.exitCode||!d.stdout.trim())throw new f("COMMAND_FAILED","Failed to read xctestrun plist",{xctestrunPath:e,stderr:d.stderr});try{n=JSON.parse(d.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=n.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(n))t&&"object"==typeof t&&t.TestBundlePath&&(u(t),n[e]=t);m.writeFileSync(s,JSON.stringify(n,null,2));let h=await c("plutil",["-convert","xml1","-o",l,s],{allowFailure:!0});if(0!==h.exitCode)throw new f("COMMAND_FAILED","Failed to write xctestrun plist",{tmpXctestrunPath:l,stderr:h.stderr});return{xctestrunPath:l,jsonPath:s}}function tT(e){try{m.existsSync(e)&&m.unlinkSync(e)}catch{}}async function tP(e,t={}){let i,r;if("ios"!==e.platform||"simulator"!==e.kind)throw new f("UNSUPPORTED_OPERATION","AX snapshot is only supported on iOS simulators");let n=await t$(),a=await O(async()=>{var e,i;let r,a,o,s=await c(n,[],{allowFailure:!0});if(t.traceLogPath&&(e=t.traceLogPath,r=((i=s).stdout??"").toString(),a=(i.stderr??"").toString(),o=`
|
|
10
13
|
[axsnapshot] exit=${i.exitCode} stdoutBytes=${r.length} stderrBytes=${a.length}
|
|
11
14
|
`,m.appendFileSync(e,o),(0!==i.exitCode||a.length>0)&&(a.length>0&&m.appendFileSync(e,`${a}
|
|
12
15
|
`),0!==i.exitCode&&r.length>0&&m.appendFileSync(e,`${r}
|
|
13
|
-
`))),0!==s.exitCode){let e,t,i=(s.stderr??"").toString(),r=(e=i.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=i.toLowerCase()).includes("could not find ios app content")||t.includes("timeout"));throw new p("COMMAND_FAILED","AX snapshot failed",{stderr:`${i}${r}`,stdout:s.stdout,retryable:n})}return s},{shouldRetry:e=>{var t;return(t=e)instanceof p&&"COMMAND_FAILED"===t.code&&t.details?.retryable===!0}});try{let e=JSON.parse(a.stdout);if(e&&"object"==typeof e&&"root"in e){if(!e.root)throw Error("AX snapshot missing root");i=e.root,r=e.windowFrame??void 0}else i=e}catch(e){throw new p("COMMAND_FAILED","Invalid AX snapshot JSON",{error:String(e)})}let o=i.frame??r,s=[],l=[],u=(e,t)=>{e.frame&&s.push(e.frame);let i=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 r of(l.push({...e,frame:i,children:void 0,depth:t}),e.children??[]))u(r,t+1)};return u(i,0),{nodes:(function(e,t,i){if(!t||0===i.length)return e;let r=1/0,n=1/0;for(let e of i)e.x<r&&(r=e.x),e.y<n&&(n=e.y);return r<=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 tb(){let e=function(){let e=r.dirname(a(import.meta.url));for(let t=0;t<6;t+=1){let t=r.join(e,"package.json");if(m.existsSync(t))return e;e=r.dirname(e)}return process.cwd()}(),t=r.join(e,"ios-runner","AXSnapshot"),i=process.env.AGENT_DEVICE_AX_BINARY;if(i&&m.existsSync(i))return i;let n=r.join(e,"dist","bin","axsnapshot");if(m.existsSync(n))return n;let o=r.join(t,".build","release","axsnapshot");if(m.existsSync(o))return o;let s=await c("swift",["build","-c","release"],{cwd:t,allowFailure:!0});if(0!==s.exitCode||!m.existsSync(o))throw new p("COMMAND_FAILED","Failed to build AX snapshot tool",{stderr:s.stderr,stdout:s.stdout});return o}async function t_(e){let t={platform:e.platform,deviceName:e.device,udid:e.udid,serial:e.serial};if("android"===t.platform){await eA();let e=await C();return await I(e,t)}if("ios"===t.platform){let e=await ek();return await I(e,t)}let i=[];try{i.push(...await C())}catch{}try{i.push(...await ek())}catch{}return await I(i,t)}async function tD(e,t,i,n,a){let o=function(e,t){switch(e.platform){case"android":return{open:(t,i)=>K(e,t,i?.activity),openDevice:()=>Q(e),close:t=>ee(e,t),tap:(t,i)=>en(e,t,i),swipe:(t,i,r,n,a)=>ea(e,t,i,r,n,a),longPress:(t,i,r)=>ec(e,t,i,r),focus:(t,i)=>ed(e,t,i),type:t=>eu(e,t),fill:(t,i,r)=>ef(e,t,i,r),scroll:(t,i)=>ep(e,t,i),scrollIntoView:t=>em(e,t),screenshot:t=>eh(e,t)};case"ios":var i,r;let n;return{open:(t,i)=>eP(e,t,{appBundleId:i?.appBundleId}),openDevice:()=>e$(e),close:t=>eF(e,t),screenshot:t=>eB(e,t),...(i=e,n={verbose:(r=t).verbose,logPath:r.logPath,traceLogPath:r.traceLogPath},{tap:async(e,t)=>{await e7(i,{command:"tap",x:e,y:t,appBundleId:r.appBundleId},n)},swipe:async(e,t,a,o,s)=>{await e7(i,{command:"drag",x:e,y:t,x2:a,y2:o,durationMs:s,appBundleId:r.appBundleId},n)},longPress:async(e,t,a)=>{await e7(i,{command:"longPress",x:e,y:t,durationMs:a,appBundleId:r.appBundleId},n)},focus:async(e,t)=>{await e7(i,{command:"tap",x:e,y:t,appBundleId:r.appBundleId},n)},type:async e=>{await e7(i,{command:"type",text:e,appBundleId:r.appBundleId},n)},fill:async(e,t,a)=>{await e7(i,{command:"tap",x:e,y:t,appBundleId:r.appBundleId},n),await e7(i,{command:"type",text:a,clearFirst:!0,appBundleId:r.appBundleId},n)},scroll:async(e,t)=>{if(!["up","down","left","right"].includes(e))throw new p("INVALID_ARGS",`Unknown direction: ${e}`);let a=function(e){switch(e){case"up":return"down";case"down":return"up";case"left":return"right";case"right":return"left"}}(e);await e7(i,{command:"swipe",direction:a,appBundleId:r.appBundleId},n)},scrollIntoView:async e=>{for(let t=0;t<8;t+=1){let a=await e7(i,{command:"findText",text:e,appBundleId:r.appBundleId},n);if(a?.found)return{attempts:t+1};await e7(i,{command:"swipe",direction:"up",appBundleId:r.appBundleId},n),await new Promise(e=>setTimeout(e,300))}throw new p("COMMAND_FAILED",`scrollintoview could not find text: ${e}`)}})};default:throw new p("UNSUPPORTED_PLATFORM",`Unsupported platform: ${e.platform}`)}}(e,{appBundleId:a?.appBundleId,verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath});switch(t){case"open":{let e=i[0];if(!e)return await o.openDevice(),{app:null};return await o.open(e,{activity:a?.activity,appBundleId:a?.appBundleId}),{app:e}}case"close":{let e=i[0];if(!e)return{closed:"session"};return await o.close(e),{app:e}}case"press":{let[e,t]=i.map(Number);if(Number.isNaN(e)||Number.isNaN(t))throw new p("INVALID_ARGS","press requires x y");let r=tO(a?.count??1,"count",1,200),n=tO(a?.intervalMs??0,"interval-ms",0,1e4),s=tO(a?.holdMs??0,"hold-ms",0,1e4),l=tO(a?.jitterPx??0,"jitter-px",0,100);for(let i=0;i<r;i+=1){let[a,c]=function(e,t){if(t<=0)return[0,0];let[i,r]=tx[e%tx.length];return[i*t,r*t]}(i,l),u=e+a,d=t+c;s>0?await o.longPress(u,d,s):await o.tap(u,d),i<r-1&&n>0&&await tk(n)}return{x:e,y:t,count:r,intervalMs:n,holdMs:s,jitterPx:l}}case"swipe":{let t=Number(i[0]),r=Number(i[1]),n=Number(i[2]),s=Number(i[3]);if([t,r,n,s].some(Number.isNaN))throw new p("INVALID_ARGS","swipe requires x1 y1 x2 y2 [durationMs]");let l=tO(i[4]?Number(i[4]):250,"durationMs",16,1e4),c="ios"===e.platform?60:l,u=tO(a?.count??1,"count",1,200),d=tO(a?.pauseMs??0,"pause-ms",0,1e4),f=a?.pattern??"one-way";if("one-way"!==f&&"ping-pong"!==f)throw new p("INVALID_ARGS",`Invalid pattern: ${f}`);for(let e=0;e<u;e+=1)"ping-pong"===f&&e%2==1?await o.swipe(n,s,t,r,c):await o.swipe(t,r,n,s,c),e<u-1&&d>0&&await tk(d);return{x1:t,y1:r,x2:n,y2:s,durationMs:l,effectiveDurationMs:c,timingMode:"ios"===e.platform?"safe-normalized":"direct",count:u,pauseMs:d,pattern:f}}case"long-press":{let e=Number(i[0]),t=Number(i[1]),r=i[2]?Number(i[2]):void 0;if(Number.isNaN(e)||Number.isNaN(t))throw new p("INVALID_ARGS","long-press requires x y [durationMs]");return await o.longPress(e,t,r),{x:e,y:t,durationMs:r}}case"focus":{let[e,t]=i.map(Number);if(Number.isNaN(e)||Number.isNaN(t))throw new p("INVALID_ARGS","focus requires x y");return await o.focus(e,t),{x:e,y:t}}case"type":{let e=i.join(" ");if(!e)throw new p("INVALID_ARGS","type requires text");return await o.type(e),{text:e}}case"fill":{let e=Number(i[0]),t=Number(i[1]),r=i.slice(2).join(" ");if(Number.isNaN(e)||Number.isNaN(t)||!r)throw new p("INVALID_ARGS","fill requires x y text");return await o.fill(e,t,r),{x:e,y:t,text:r}}case"scroll":{let e=i[0],t=i[1]?Number(i[1]):void 0;if(!e)throw new p("INVALID_ARGS","scroll requires direction");return await o.scroll(e,t),{direction:e,amount:t}}case"scrollintoview":{let e=i.join(" ").trim();if(!e)throw new p("INVALID_ARGS","scrollintoview requires text");let t=await o.scrollIntoView(e);if(t?.attempts)return{text:e,attempts:t.attempts};return{text:e}}case"pinch":{if("android"===e.platform)throw new p("UNSUPPORTED_OPERATION","Android pinch is not supported in current adb backend; requires instrumentation-based backend.");let t=Number(i[0]),r=i[1]?Number(i[1]):void 0,n=i[2]?Number(i[2]):void 0;if(Number.isNaN(t)||t<=0)throw new p("INVALID_ARGS","pinch requires scale > 0");return await e7(e,{command:"pinch",scale:t,x:r,y:n,appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{scale:t,x:r,y:n}}case"screenshot":{let e=i[0]??n??`./screenshot-${Date.now()}.png`;return await u.mkdir(r.dirname(e),{recursive:!0}),await o.screenshot(e),{path:e}}case"back":if("ios"===e.platform)return await e7(e,{command:"back",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{action:"back"};return await eo(e),{action:"back"};case"home":if("ios"===e.platform)return await e7(e,{command:"home",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{action:"home"};return await es(e),{action:"home"};case"app-switcher":if("ios"===e.platform)return await e7(e,{command:"appSwitcher",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{action:"app-switcher"};return await el(e),{action:"app-switcher"};case"settings":{let[t,r,n]=i;if("ios"===e.platform)return await ej(e,t,r,n??a?.appBundleId),{setting:t,state:r};return await ew(e,t,r),{setting:t,state:r}}case"snapshot":{let t=a?.snapshotBackend??"xctest";if("ios"===e.platform){if("ax"===t&&"simulator"!==e.kind)throw new p("UNSUPPORTED_OPERATION","AX snapshot backend is not supported on iOS physical devices; use --backend xctest");if("ax"===t)return{nodes:(await tS(e,{traceLogPath:a?.traceLogPath})).nodes??[],truncated:!1,backend:"ax"};let i=await e7(e,{command:"snapshot",appBundleId:a?.appBundleId,interactiveOnly:a?.snapshotInteractiveOnly,compact:a?.snapshotCompact,depth:a?.snapshotDepth,scope:a?.snapshotScope,raw:a?.snapshotRaw},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),r=i.nodes??[];if(0===r.length&&"simulator"===e.kind)throw new p("COMMAND_FAILED","XCTest snapshot returned 0 nodes on iOS simulator. You can try --backend ax for diagnostics, but AX snapshots are not recommended.");return{nodes:r,truncated:i.truncated??!1,backend:"xctest"}}let i=await eg(e,{interactiveOnly:a?.snapshotInteractiveOnly,compact:a?.snapshotCompact,depth:a?.snapshotDepth,scope:a?.snapshotScope,raw:a?.snapshotRaw});return{nodes:i.nodes??[],truncated:i.truncated??!1,backend:"android"}}default:throw new p("INVALID_ARGS",`Unknown command: ${t}`)}}let tx=[[0,0],[1,0],[0,1],[-1,0],[0,-1],[1,1],[-1,1],[1,-1],[-1,-1]];function tO(e,t,i,r){if(!Number.isFinite(e)||!Number.isInteger(e)||e<i||e>r)throw new p("INVALID_ARGS",`${t} must be an integer between ${i} and ${r}`);return e}async function tk(e){await new Promise(t=>setTimeout(t,e))}let tE={alert:{ios:{simulator:!0},android:{}},pinch:{ios:{simulator:!0},android:{}},"app-switcher":{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},apps:{ios:{simulator:!0},android:{emulator:!0,device:!0,unknown:!0}},back:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},boot:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},click:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},close:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},fill:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},find:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},focus:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},get:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},is:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},home:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},"long-press":{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},open:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},reinstall:{ios:{simulator:!0},android:{emulator:!0,device:!0,unknown:!0}},press:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},record:{ios:{simulator:!0},android:{emulator:!0,device:!0,unknown:!0}},screenshot:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},scroll:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},scrollintoview:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},swipe:{ios:{simulator:!0},android:{emulator:!0,device:!0,unknown:!0}},settings:{ios:{simulator:!0},android:{emulator:!0,device:!0,unknown:!0}},snapshot:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},type:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},wait:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}}};function tM(e,t){let i=tE[e];if(!i)return!0;let r=i[t.platform];return!!r&&!0===r[t.kind??"unknown"]}function tR(e){let t=e.result?.text;if("string"==typeof t&&t.trim().length>0)return t;let i=e.positionals??[];return 0===i.length?"":i[0].startsWith("@")?i.length>=3?i.slice(2).join(" ").trim():i.slice(1).join(" ").trim():!(i.length>=3)||Number.isNaN(Number(i[0]))||Number.isNaN(Number(i[1]))?i.slice(1).join(" ").trim():i.slice(2).join(" ").trim()}function tL(e){let t=new Set,i=[];for(let r of e)t.has(r)||(t.add(r),i.push(r));return i}function tC(e,t,i){return t in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}class tT{get(e){return this.sessions.get(e)}has(e){return this.sessions.has(e)}set(e,t){this.sessions.set(e,t)}delete(e){return this.sessions.delete(e)}values(){return this.sessions.values()}toArray(){return Array.from(this.sessions.values())}recordAction(e,t){t.flags?.noRecord||(t.flags?.saveScript&&(e.recordSession=!0,"string"==typeof t.flags.saveScript&&(e.saveScriptPath=tT.expandHome(t.flags.saveScript))),e.actions.push({ts:Date.now(),command:t.command,positionals:t.positionals,flags:function(e){if(!e)return{};let{platform:t,device:i,udid:r,serial:n,out:a,verbose:o,snapshotInteractiveOnly:s,snapshotCompact:l,snapshotDepth:c,snapshotScope:u,snapshotRaw:d,snapshotBackend:f,appsMetadata:p,relaunch:m,saveScript:h,noRecord:w}=e;return{platform:t,device:i,udid:r,serial:n,out:a,verbose:o,snapshotInteractiveOnly:s,snapshotCompact:l,snapshotDepth:c,snapshotScope:u,snapshotRaw:d,snapshotBackend:f,appsMetadata:p,relaunch:m,saveScript:h,noRecord:w}}(t.flags),result:t.result}))}writeSessionLog(e){try{if(!e.recordSession)return;let t=this.resolveScriptPath(e),i=r.dirname(t);m.existsSync(i)||m.mkdirSync(i,{recursive:!0});let n=function(e,t){let i=[],r=e.device.name.replace(/"/g,'\\"'),n=e.device.kind?` kind=${e.device.kind}`:"";for(let a of(i.push(`context platform=${e.device.platform} device="${r}"${n} theme=unknown`),t))a.flags?.noRecord||i.push(function(e){let t=[e.command];if("click"===e.command){let i=e.positionals?.[0];if(i){if(t.push(tP(i)),i.startsWith("@")){let i=e.result?.refLabel;"string"==typeof i&&i.trim().length>0&&t.push(tP(i))}return t.join(" ")}}if("fill"===e.command){let i=e.positionals?.[0];if(i&&i.startsWith("@")){t.push(tP(i));let r=e.result?.refLabel,n=e.positionals.slice(1).join(" ");return"string"==typeof r&&r.trim().length>0&&t.push(tP(r)),n&&t.push(tP(n)),t.join(" ")}}if("get"===e.command){let i=e.positionals?.[0],r=e.positionals?.[1];if(i&&r){if(t.push(tP(i)),t.push(tP(r)),r.startsWith("@")){let i=e.result?.refLabel;"string"==typeof i&&i.trim().length>0&&t.push(tP(i))}return 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",tP(e.flags.snapshotScope)),e.flags?.snapshotRaw&&t.push("--raw"),e.flags?.snapshotBackend&&t.push("--backend",e.flags.snapshotBackend),t.join(" ");if("open"===e.command){for(let i of e.positionals??[])t.push(tP(i));return e.flags?.relaunch&&t.push("--relaunch"),t.join(" ")}for(let i of e.positionals??[])t.push(tP(i));return t.join(" ")}(a));return`${i.join("\n")}
|
|
14
|
-
`}(e,this.buildOptimizedActions(e));m.writeFileSync(t,n)}catch{}}defaultTracePath(e){let t=e.name.replace(/[^a-zA-Z0-9._-]/g,"_"),i=new Date().toISOString().replace(/[:.]/g,"-");return r.join(this.sessionsDir,`${t}-${i}.trace.log`)}static expandHome(e){return e.startsWith("~/")?r.join(h.homedir(),e.slice(2)):r.resolve(e)}resolveScriptPath(e){if(e.saveScriptPath)return tT.expandHome(e.saveScriptPath);m.existsSync(this.sessionsDir)||m.mkdirSync(this.sessionsDir,{recursive:!0});let t=e.name.replace(/[^a-zA-Z0-9._-]/g,"_"),i=new Date(e.createdAt).toISOString().replace(/[:.]/g,"-");return r.join(this.sessionsDir,`${t}-${i}.ad`)}buildOptimizedActions(e){let t=[];for(let i of e.actions){if("snapshot"===i.command)continue;let r=Array.isArray(i.result?.selectorChain)&&i.result?.selectorChain.every(e=>"string"==typeof e)?i.result.selectorChain:[];if(r.length>0&&("click"===i.command||"fill"===i.command||"get"===i.command)){let e=r.join(" || ");if("click"===i.command){t.push({...i,positionals:[e]});continue}if("fill"===i.command){let r=tR(i);if(r.length>0){t.push({...i,positionals:[e,r]});continue}}if("get"===i.command){let r=i.positionals?.[0];if("text"===r||"attrs"===r){t.push({...i,positionals:[r,e]});continue}}}if("click"===i.command||"fill"===i.command||"get"===i.command){let r=i.result?.refLabel;"string"==typeof r&&r.trim().length>0&&t.push({ts:i.ts,command:"snapshot",positionals:[],flags:{platform:e.device.platform,snapshotInteractiveOnly:!0,snapshotCompact:!0,snapshotScope:r.trim()},result:{scope:r.trim()}})}t.push(i)}return t}constructor(e){tC(this,"sessions",new Map),tC(this,"sessionsDir",void 0),this.sessionsDir=e}}function tP(e){let t=e.trim();return t.startsWith("@")||/^-?\d+(\.\d+)?$/.test(t)?t:JSON.stringify(t)}function t$(e,t,i,r){return{appBundleId:i,activity:t?.activity,verbose:t?.verbose,logPath:e,traceLogPath:r,snapshotInteractiveOnly:t?.snapshotInteractiveOnly,snapshotCompact:t?.snapshotCompact,snapshotDepth:t?.snapshotDepth,snapshotScope:t?.snapshotScope,snapshotRaw:t?.snapshotRaw,snapshotBackend:t?.snapshotBackend,count:t?.count,intervalMs:t?.intervalMs,holdMs:t?.holdMs,jitterPx:t?.jitterPx,pauseMs:t?.pauseMs,pattern:t?.pattern}}async function tF(e){if("ios"===e.platform&&"simulator"===e.kind){let{ensureBootedSimulator:t}=await Promise.resolve().then(()=>({ensureBootedSimulator:eX}));await t(e);return}if("android"===e.platform){let{waitForAndroidBoot:t}=await Promise.resolve().then(()=>({waitForAndroidBoot:P}));await t(e.id)}}function tV(e){return e.map((e,t)=>({...e,ref:`e${t+1}`}))}function tU(e){let t=e.trim();return t.startsWith("@")?t.slice(1)||null:t.startsWith("e")?t:null}function tG(e,t){return e.find(e=>e.ref===t)??null}function tB(e){return{x:Math.round(e.x+e.width/2),y:Math.round(e.y+e.height/2)}}function tj(e,t){let i=t.toLowerCase();return e.find(e=>{let t=(e.label??"").toLowerCase(),r=(e.value??"").toLowerCase(),n=(e.identifier??"").toLowerCase();return t.includes(i)||r.includes(i)||n.includes(i)})??null}function tq(e,t){let i=[e.label,e.value,e.identifier].map(e=>"string"==typeof e?e.trim():"").find(e=>e&&e.length>0);return i&&tW(i)?i:function(e,t){if(!e.rect)return;let i=e.rect.y+e.rect.height/2,r=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||!tW(t))continue;let n=Math.abs(e.rect.y+e.rect.height/2-i);(!r||n<r.distance)&&(r={label:t,distance:n})}return r?.label}(e,t)??(i&&tW(i)?i:void 0)}function tW(e){let t=e.trim();return!(!t||/^(true|false)$/i.test(t)||/^\d+$/.test(t))}function tJ(e){let t=[],i=[];for(let r of e){let e=r.depth??0;for(;t.length>0&&e<=t[t.length-1];)t.pop();let n=tX(r.type??""),a=[r.label,r.value,r.identifier].map(e=>"string"==typeof e?e.trim():"").find(e=>e&&e.length>0),o=!!a&&tW(a);if(("group"===n||"ioscontentgroup"===n)&&!o){t.push(e);continue}let s=Math.max(0,e-t.length);i.push({...r,depth:s})}return i}function tX(e){let t=e.trim().replace(/XCUIElementType/gi,"").toLowerCase();t.startsWith("ax")&&(t=t.replace(/^ax/,""));let i=Math.max(t.lastIndexOf("."),t.lastIndexOf("/"));return -1!==i&&(t=t.slice(i+1)),t}function tH(e,t){let i=tX(e);return!i||("android"===t?i.includes("edittext")||i.includes("autocompletetextview"):i.includes("textfield")||i.includes("securetextfield")||i.includes("searchfield")||i.includes("textview")||i.includes("textarea")||"search"===i)}function tz(e){return[e.label,e.value,e.identifier].map(e=>"string"==typeof e?e.trim():"").filter(e=>e.length>0)[0]??""}async function tY(e,t,i,r,n=tD){let a;try{a=await n(e,"snapshot",[],r?.out,{...t$(t,{...r,snapshotDepth:1,snapshotCompact:!0,snapshotBackend:"xctest"},void 0,i)})}catch(e){throw new p("COMMAND_FAILED","Unable to resolve iOS app state from XCTest snapshot. You can try snapshot --backend ax for diagnostics, but AX snapshots are not recommended.",{cause:e instanceof Error?e.message:String(e)})}let o=function(e){let t=tV(e?.nodes??[]),i=t.find(e=>"application"===tX(e.type??""))??t[0];if(!i)return null;let r=i.label?.trim(),n=i.identifier?.trim();return r||n?{appName:r||void 0,appBundleId:n||void 0}:null}(a);if(o?.appName||o?.appBundleId)return{appName:o.appName??o.appBundleId??"unknown",appBundleId:o.appBundleId,source:"snapshot-xctest"};throw new p("COMMAND_FAILED","Unable to resolve iOS app state from XCTest snapshot (0 nodes or missing application node). You can try snapshot --backend ax for diagnostics, but AX snapshots are not recommended.")}let tK=new Set(["id","role","text","label","value"]),tZ=new Set(["visible","hidden","editable","selected","enabled","hittable"]),tQ=new Set([...tK,...tZ]);function t0(e){let t=e.trim();if(!t)throw new p("INVALID_ARGS","Selector expression cannot be empty");let i=function(e){let t=[],i="",r=null;for(let n=0;n<e.length;n+=1){let a=e[n];if(('"'===a||"'"===a)&&!ic(e,n)){r?r===a&&(r=null):r=a,i+=a;continue}if(!r&&"|"===a&&"|"===e[n+1]){let r=i.trim();if(!r)throw new p("INVALID_ARGS",`Invalid selector fallback expression: ${e}`);t.push(r),i="",n+=1;continue}i+=a}let n=i.trim();if(!n)throw new p("INVALID_ARGS",`Invalid selector fallback expression: ${e}`);return t.push(n),t}(t);if(0===i.length)throw new p("INVALID_ARGS","Selector expression cannot be empty");return{raw:t,selectors:i.map(e=>(function(e){let t=e.trim();if(!t)throw new p("INVALID_ARGS","Selector segment cannot be empty");let i=function(e){let t=[],i="",r=null;for(let n=0;n<e.length;n+=1){let a=e[n];if(('"'===a||"'"===a)&&!ic(e,n)){r?r===a&&(r=null):r=a,i+=a;continue}if(!r&&/\s/.test(a)){i.trim().length>0&&t.push(i.trim()),i="";continue}i+=a}if(r)throw new p("INVALID_ARGS",`Unclosed quote in selector: ${e}`);return i.trim().length>0&&t.push(i.trim()),t}(t);if(0===i.length)throw new p("INVALID_ARGS",`Invalid selector segment: ${e}`);return{raw:t,terms:i.map(ie)}})(e))}}function t1(e){try{return t0(e)}catch{return null}}function t2(e,t,i){let r=i.requireRect??!1,n=i.requireUnique??!0,a=i.disambiguateAmbiguous??!1,o=[];for(let s=0;s<t.selectors.length;s+=1){let l=t.selectors[s],c=function(e,t,i){let r=0,n=null,a=null,o=!1;for(let s of e){if(i.requireRect&&!s.rect||!it(s,t,i.platform))continue;if(r+=1,n||(n=s),!a){a=s;continue}let e=function(e,t){let i=e.depth??0,r=t.depth??0;if(i!==r)return i>r?1:-1;let n=il(e),a=il(t);return n!==a?n<a?1:-1:0}(s,a);if(e>0){a=s,o=!1;continue}0===e&&(o=!0)}return{count:r,firstNode:n,disambiguated:o?null:a}}(e,l,{platform:i.platform,requireRect:r});if(o.push({selector:l.raw,matches:c.count}),0!==c.count&&c.firstNode){if(n&&1!==c.count){if(!a)continue;let e=c.disambiguated;if(!e)continue;return{node:e,selector:l,selectorIndex:s,matches:c.count,diagnostics:o}}return{node:c.firstNode,selector:l,selectorIndex:s,matches:c.count,diagnostics:o}}}return null}function t3(e,t,i){let r=i.requireRect??!1,n=[];for(let a=0;a<t.selectors.length;a+=1){let o=t.selectors[a],s=function(e,t,i){let r=0;for(let n of e)(!i.requireRect||n.rect)&&it(n,t,i.platform)&&(r+=1);return r}(e,o,{platform:i.platform,requireRect:r});if(n.push({selector:o.raw,matches:s}),s>0)return{selectorIndex:a,selector:o,matches:s,diagnostics:n}}return null}function t4(e,t,i){let r=i.unique??!0;if(0===t.length)return`Selector did not match: ${e.raw}`;let n=t.map(e=>`${e.selector} -> ${e.matches}`).join(", ");return r?`Selector did not resolve uniquely (${n})`:`Selector did not match (${n})`}function t5(e,t={}){if(0===e.length)return null;let i=t.preferTrailingValue??!1,r=0,n=[];for(;r<e.length&&function(e){let t=e.trim();if(!t)return!1;if("||"===t)return!0;let i=t.indexOf("=");if(-1!==i){let e=t.slice(0,i).trim().toLowerCase();return tQ.has(e)}return tQ.has(t.toLowerCase())}(e[r]);){r+=1;let t=e.slice(0,r).join(" ").trim();t&&t1(t)&&n.push(r)}if(0===n.length)return null;let a=n[n.length-1];if(i){for(let t=n.length-1;t>=0;t-=1)if(n[t]<e.length){a=n[t];break}}let o=e.slice(0,a).join(" ").trim();return o?{selectorExpression:o,rest:e.slice(a)}:null}function t8(e){let t=e[0]??"",i=t5(e.slice(1),{preferTrailingValue:"text"===t});return{predicate:t,split:i}}function t6(e){return!0===e.hittable||!!e.rect&&e.rect.width>0&&e.rect.height>0}function t7(e,t){return tH(e.type??"",t)&&!1!==e.enabled}function t9(e,t,i={}){let r=[],n=tX(e.type??""),a=is(e.identifier),o=is(e.label),s=is(e.value),l=is(tz(e)),c="fill"===i.action;a&&r.push(`id=${io(a)}`),n&&o&&r.push(c?`role=${io(n)} label=${io(o)} editable=true`:`role=${io(n)} label=${io(o)}`),o&&r.push(c?`label=${io(o)} editable=true`:`label=${io(o)}`),s&&r.push(c?`value=${io(s)} editable=true`:`value=${io(s)}`),l&&l!==o&&l!==s&&r.push(c?`text=${io(l)} editable=true`:`text=${io(l)}`),n&&c&&!r.some(e=>e.includes("editable=true"))&&r.push(`role=${io(n)} editable=true`);let u=tL(r);return 0===u.length&&n&&u.push(c?`role=${io(n)} editable=true`:`role=${io(n)}`),0===u.length&&t6(e)&&u.push("visible=true"),u}function ie(e){let t=e.trim();if(!t)throw new p("INVALID_ARGS","Empty selector term");let i=t.indexOf("=");if(-1===i){let i=t.toLowerCase();if(!tZ.has(i))throw new p("INVALID_ARGS",`Invalid selector term "${e}", expected key=value`);return{key:i,value:!0}}let r=t.slice(0,i).trim().toLowerCase(),n=t.slice(i+1).trim();if(!tQ.has(r))throw new p("INVALID_ARGS",`Unknown selector key: ${r}`);if(!n)throw new p("INVALID_ARGS",`Missing selector value for key: ${r}`);if(tZ.has(r)){let e,t="true"===(e=ii(n).toLowerCase())||"false"!==e&&null;if(null===t)throw new p("INVALID_ARGS",`Invalid boolean value for ${r}: ${n}`);return{key:r,value:t}}return{key:r,value:ii(n)}}function it(e,t,i){return t.terms.every(t=>(function(e,t,i){switch(t.key){case"id":return ir(e.identifier,String(t.value));case"role":var r,n;return r=e.type,n=String(t.value),function(e){return tX(e)}(r??"")===function(e){return tX(e)}(n);case"label":return ir(e.label,String(t.value));case"value":return ir(e.value,String(t.value));case"text":{let i=ia(String(t.value));return ia(tz(e))===i}case"visible":return t6(e)===!!t.value;case"hidden":return!t6(e)==!!t.value;case"editable":return t7(e,i)===!!t.value;case"selected":return!0===e.selected==!!t.value;case"enabled":return!1!==e.enabled==!!t.value;case"hittable":return!0===e.hittable==!!t.value;default:return!1}})(e,t,i))}function ii(e){let t=e.trim();return t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1).replace(/\\(["'])/g,"$1"):t}function ir(e,t){return ia(e??"")===ia(t)}function ia(e){return e.trim().toLowerCase().replace(/\s+/g," ")}function io(e){return JSON.stringify(e)}function is(e){if(!e)return null;let t=e.trim();return t||null}function il(e){return e.rect?e.rect.width*e.rect.height:1/0}function ic(e,t){let i=0;for(let r=t-1;r>=0&&"\\"===e[r];r-=1)i+=1;return i%2==1}function iu(e){return!!(e?.platform||e?.device||e?.udid||e?.serial)}async function id(e){let t=e.session?.device??await t_(e.flags??{});return!1!==e.ensureReady&&await e.ensureReadyFn(t),t}let ip={ios:async(e,t,i)=>{let{reinstallIosApp:r}=await Promise.resolve().then(()=>({reinstallIosApp:eG}));return await r(e,t,i)},android:async(e,t,i)=>{let{reinstallAndroidApp:r}=await Promise.resolve().then(()=>({reinstallAndroidApp:er}));return await r(e,t,i)}};async function im(e,t){if(!("ios"!==e.platform||!t||$(t)))try{let{resolveIosApp:i}=await Promise.resolve().then(()=>({resolveIosApp:eT}));return await i(e,t)}catch{return}}async function ih(e){let{req:t,sessionName:i,logPath:r,sessionStore:n,invoke:a,dispatch:o,ensureReady:s,reinstallOps:l=ip}=e,c=o??tD,u=s??tF,f=t.command;if("session_list"===f)return{ok:!0,data:{sessions:n.toArray().map(e=>({name:e.name,platform:e.device.platform,device:e.device.name,id:e.device.id,createdAt:e.createdAt}))}};if("devices"===f)try{let e=[];if(t.flags?.platform==="android"){let{listAndroidDevices:t}=await Promise.resolve().then(()=>({listAndroidDevices:C}));e.push(...await t())}else if(t.flags?.platform==="ios"){let{listIosDevices:t}=await Promise.resolve().then(()=>({listIosDevices:ek}));e.push(...await t())}else{let{listAndroidDevices:t}=await Promise.resolve().then(()=>({listAndroidDevices:C})),{listIosDevices:i}=await Promise.resolve().then(()=>({listIosDevices:ek}));try{e.push(...await t())}catch{}try{e.push(...await i())}catch{}}return{ok:!0,data:{devices:e}}}catch(t){let e=d(t);return{ok:!1,error:{code:e.code,message:e.message,details:e.details}}}if("apps"===f){let e=n.get(i),r=t.flags??{};if(!e&&!iu(r))return{ok:!1,error:{code:"INVALID_ARGS",message:"apps requires an active session or an explicit device selector (e.g. --platform ios)."}};let a=await id({session:e,flags:r,ensureReadyFn:u,ensureReady:!0});if(!tM("apps",a))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"apps is not supported on this device"}};if("ios"===a.platform){let{listSimulatorApps:e}=await Promise.resolve().then(()=>({listSimulatorApps:eJ})),i=await e(a);return t.flags?.appsMetadata?{ok:!0,data:{apps:i}}:{ok:!0,data:{apps:i.map(e=>e.name&&e.name!==e.bundleId?`${e.name} (${e.bundleId})`:e.bundleId)}}}let{listAndroidApps:o,listAndroidAppsMetadata:s}=await Promise.resolve().then(()=>({listAndroidApps:X,listAndroidAppsMetadata:H}));return t.flags?.appsMetadata?{ok:!0,data:{apps:await s(a,t.flags?.appsFilter)}}:{ok:!0,data:{apps:await o(a,t.flags?.appsFilter)}}}if("boot"===f){let e=n.get(i),r=t.flags??{};if(!e&&!iu(r))return{ok:!1,error:{code:"INVALID_ARGS",message:"boot requires an active session or an explicit device selector (e.g. --platform ios)."}};let a=e?.device??await t_(r);return tM("boot",a)?(await u(a),{ok:!0,data:{platform:a.platform,device:a.name,id:a.id,kind:a.kind,booted:!0}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"boot is not supported on this device"}}}if("appstate"===f){let e=n.get(i),a=t.flags??{},o=await id({session:e,flags:a,ensureReadyFn:u,ensureReady:!0});if("ios"===o.platform){if(e?.appBundleId)return{ok:!0,data:{platform:"ios",appBundleId:e.appBundleId,appName:e.appName??e.appBundleId,source:"session"}};let i=await tY(o,r,e?.trace?.outPath,t.flags);return{ok:!0,data:{platform:"ios",appName:i.appName,appBundleId:i.appBundleId,source:i.source}}}let{getAndroidAppState:s}=await Promise.resolve().then(()=>({getAndroidAppState:z})),l=await s(o);return{ok:!0,data:{platform:"android",package:l.package,activity:l.activity}}}if("reinstall"===f){let e,r=n.get(i),a=t.flags??{};if(!r&&!iu(a))return{ok:!1,error:{code:"INVALID_ARGS",message:"reinstall requires an active session or an explicit device selector (e.g. --platform ios)."}};let o=t.positionals?.[0]?.trim(),s=t.positionals?.[1]?.trim();if(!o||!s)return{ok:!1,error:{code:"INVALID_ARGS",message:"reinstall requires: reinstall <app> <path-to-app-binary>"}};let c=tT.expandHome(s);if(!m.existsSync(c))return{ok:!1,error:{code:"INVALID_ARGS",message:`App binary not found: ${c}`}};let d=await id({session:r,flags:a,ensureReadyFn:u,ensureReady:!1});if(!tM("reinstall",d))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"reinstall is not supported on this device"}};if("ios"===d.platform){let t=await l.ios(d,o,c);e={platform:"ios",appId:t.bundleId,bundleId:t.bundleId}}else{let t=await l.android(d,o,c);e={platform:"android",appId:t.package,package:t.package}}let p={app:o,appPath:c,...e};return r&&n.recordAction(r,{command:f,positionals:t.positionals??[],flags:t.flags??{},result:p}),{ok:!0,data:p}}if("open"===f){let e=t.flags?.relaunch===!0;if(n.has(i)){let a=n.get(i),o=t.positionals?.[0],s=o??(e?a?.appName:void 0);if(!a||!s)return e?{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch requires an app name or an active session app."}}:{ok:!1,error:{code:"INVALID_ARGS",message:"Session already active. Close it first or pass a new --session name."}};if(e&&$(s))return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch does not support URL targets."}};let l=await im(a.device,s),u=o?t.positionals??[]:[s];if(e){let e=l??s;await c(a.device,"close",[e],t.flags?.out,{...t$(r,t.flags,l??a.appBundleId,a.trace?.outPath)})}await c(a.device,"open",u,t.flags?.out,{...t$(r,t.flags,l)});let d={...a,appBundleId:l,appName:s,recordSession:a.recordSession||!!t.flags?.saveScript,snapshot:void 0};return n.recordAction(d,{command:f,positionals:u,flags:t.flags??{},result:{session:i,appName:s,appBundleId:l}}),n.set(i,d),{ok:!0,data:{session:i,appName:s,appBundleId:l}}}let a=t.positionals?.[0];if(e&&!a)return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch requires an app argument."}};if(e&&a&&$(a))return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch does not support URL targets."}};let o=await t_(t.flags??{}),s=n.toArray().find(e=>e.device.id===o.id);if(s)return{ok:!1,error:{code:"DEVICE_IN_USE",message:`Device is already in use by session "${s.name}".`,details:{session:s.name,deviceId:o.id,deviceName:o.name}}};let l=await im(o,a);if(e&&a){let e=l??a;await c(o,"close",[e],t.flags?.out,{...t$(r,t.flags,l)})}await c(o,"open",t.positionals??[],t.flags?.out,{...t$(r,t.flags,l)});let u={name:i,device:o,createdAt:Date.now(),appBundleId:l,appName:a,recordSession:!!t.flags?.saveScript,actions:[]};return n.recordAction(u,{command:f,positionals:t.positionals??[],flags:t.flags??{},result:{session:i}}),n.set(i,u),{ok:!0,data:{session:i}}}if("replay"===f){let e=t.positionals?.[0];if(!e)return{ok:!1,error:{code:"INVALID_ARGS",message:"replay requires a path"}};try{let o=tT.expandHome(e),s=m.readFileSync(o,"utf8"),l=s.trimStart()[0];if("{"===l||"["===l)return{ok:!1,error:{code:"INVALID_ARGS",message:"replay accepts .ad script files. JSON replay payloads are no longer supported."}};let u=function(e){let t=[];for(let i of e.split(/\r?\n/)){let e=function(e){let t=e.trim();if(0===t.length||t.startsWith("#"))return null;let i=function(e){let t=[],i=0;for(;i<e.length;){for(;i<e.length&&/\s/.test(e[i]);)i+=1;if(i>=e.length)break;if('"'===e[i]){let r=i+1,n=!1;for(;r<e.length;){let t=e[r];if('"'===t&&!n)break;n="\\"===t&&!n,"\\"!==t&&(n=!1),r+=1}if(r>=e.length)throw new p("INVALID_ARGS",`Invalid replay script line: ${e}`);let a=e.slice(i,r+1);t.push(JSON.parse(a)),i=r+1;continue}let r=i;for(;r<e.length&&!/\s/.test(e[r]);)r+=1;t.push(e.slice(i,r)),i=r}return t}(t);if(0===i.length)return null;let[r,...n]=i;if("context"===r)return null;let a={ts:Date.now(),command:r,positionals:[],flags:{}};if("snapshot"===r){a.positionals=[];for(let e=0;e<n.length;e+=1){let t=n[e];if("-i"===t){a.flags.snapshotInteractiveOnly=!0;continue}if("-c"===t){a.flags.snapshotCompact=!0;continue}if("--raw"===t){a.flags.snapshotRaw=!0;continue}if(("-d"===t||"--depth"===t)&&e+1<n.length){let t=Number(n[e+1]);Number.isFinite(t)&&t>=0&&(a.flags.snapshotDepth=Math.floor(t)),e+=1;continue}if(("-s"===t||"--scope"===t)&&e+1<n.length){a.flags.snapshotScope=n[e+1],e+=1;continue}if("--backend"===t&&e+1<n.length){let t=n[e+1];("ax"===t||"xctest"===t)&&(a.flags.snapshotBackend=t),e+=1}}return a}if("open"===r){a.positionals=[];for(let e=0;e<n.length;e+=1){let t=n[e];if("--relaunch"===t){a.flags.relaunch=!0;continue}a.positionals.push(t)}return a}if("click"===r){if(0===n.length)return a;let e=n[0];return e.startsWith("@")?(a.positionals=[e],n[1]&&(a.result={refLabel:n[1]})):a.positionals=[n.join(" ")],a}if("fill"===r){if(n.length<2)return a.positionals=n,a;let e=n[0];return e.startsWith("@")?(n.length>=3?(a.positionals=[e,n.slice(2).join(" ")],a.result={refLabel:n[1]}):a.positionals=[e,n[1]],a):(a.positionals=[e,n.slice(1).join(" ")],a)}if("get"===r){if(n.length<2)return a.positionals=n,a;let e=n[0],t=n[1];return t.startsWith("@")?(a.positionals=[e,t],n[2]&&(a.result={refLabel:n[2]})):a.positionals=[e,n.slice(1).join(" ")],a}return a.positionals=n,a}(i);e&&t.push(e)}return t}(s),d=t.flags?.replayUpdate===!0,f=0;for(let e=0;e<u.length;e+=1){let s=u[e];if(!s||"replay"===s.command)continue;let l=await a({token:t.token,session:i,command:s.command,positionals:s.positionals??[],flags:s.flags??{}});if(l.ok)continue;if(!d)return iw(l,s,e,o);let p=await ig({action:s,sessionName:i,logPath:r,sessionStore:n,dispatch:c});if(!p)return iw(l,s,e,o);if(u[e]=p,!(l=await a({token:t.token,session:i,command:p.command,positionals:p.positionals??[],flags:p.flags??{}})).ok)return iw(l,p,e,o);f+=1}if(d&&f>0){let e=n.get(i);!function(e,t,i){let r=[];if(i){let e=i.device.name.replace(/"/g,'\\"'),t=i.device.kind?` kind=${i.device.kind}`:"";r.push(`context platform=${i.device.platform} device="${e}"${t} theme=unknown`)}for(let e of t)r.push(function(e){let t=[e.command];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",iv(e.flags.snapshotScope)),e.flags?.snapshotRaw&&t.push("--raw"),e.flags?.snapshotBackend&&t.push("--backend",e.flags.snapshotBackend),t.join(" ");if("open"===e.command){for(let i of e.positionals??[])t.push(iv(i));return e.flags?.relaunch&&t.push("--relaunch"),t.join(" ")}for(let i of e.positionals??[])t.push(iv(i));return t.join(" ")}(e));let n=`${r.join("\n")}
|
|
15
|
-
`,a=`${e}.tmp-${process.pid}-${Date.now()}`;m.writeFileSync(a,n),m.renameSync(a,e)}(o,u,e)}return{ok:!0,data:{replayed:u.length,healed:f,session:i}}}catch(t){let e=d(t);return{ok:!1,error:{code:e.code,message:e.message}}}}if("close"===f){let e=n.get(i);return e?(t.positionals&&t.positionals.length>0&&await c(e.device,"close",t.positionals??[],t.flags?.out,{...t$(r,t.flags,e.appBundleId,e.trace?.outPath)}),"ios"===e.device.platform&&await ti(e.device.id),n.recordAction(e,{command:f,positionals:t.positionals??[],flags:t.flags??{},result:{session:i}}),t.flags?.saveScript&&(e.recordSession=!0),n.writeSessionLog(e),n.delete(i),{ok:!0,data:{session:i}}):{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session"}}}return null}function iw(e,t,i,r){var n;let a;if(e.ok)return e;let o=i+1,s=(a=((n=t).positionals??[]).map(e=>{let t=e.trim();return/^-?\d+(\.\d+)?$/.test(t)||t.startsWith("@")?t:JSON.stringify(t)}),[n.command,...a].join(" ")),l={...e.error.details??{},replayPath:r,step:o,action:t.command,positionals:t.positionals??[]};return{ok:!1,error:{code:e.error.code,message:`Replay failed at step ${o} (${s}): ${e.error.message}`,details:l}}}async function ig(e){let{action:t,sessionName:i,logPath:r,sessionStore:n,dispatch:a}=e;if(!["click","fill","get","is","wait"].includes(t.command))return null;let o=n.get(i);if(!o)return null;let s="click"===t.command||"fill"===t.command,l="click"===t.command||"fill"===t.command||"get"===t.command&&t.positionals?.[0]==="text",c=await iA(o,t,r,s,a,n);for(let e of function(e){let t=[],i=Array.isArray(e.result?.selectorChain)&&e.result?.selectorChain.every(e=>"string"==typeof e)?e.result.selectorChain:[];if(t.push(...i),"click"===e.command){let i=e.positionals?.[0]??"";i&&!i.startsWith("@")&&t.push(e.positionals.join(" "))}if("fill"===e.command){let i=e.positionals?.[0]??"";i&&!i.startsWith("@")&&Number.isNaN(Number(i))&&t.push(i)}if("get"===e.command){let i=e.positionals?.[1]??"";i&&!i.startsWith("@")&&t.push(e.positionals.slice(1).join(" "))}if("is"===e.command){let{split:i}=t8(e.positionals);i&&t.push(i.selectorExpression)}if("wait"===e.command){let{selectorExpression:i}=iI(e.positionals??[]);i&&t.push(i)}let r="string"==typeof e.result?.refLabel?e.result.refLabel.trim():"";if(r.length>0){let i=JSON.stringify(r);"fill"===e.command?(t.push(`id=${i} editable=true`),t.push(`label=${i} editable=true`),t.push(`text=${i} editable=true`),t.push(`value=${i} editable=true`)):(t.push(`id=${i}`),t.push(`label=${i}`),t.push(`text=${i}`),t.push(`value=${i}`))}return tL(t).filter(e=>e.trim().length>0)}(t)){let i=t1(e);if(!i)continue;let r=t2(c.nodes,i,{platform:o.device.platform,requireRect:s,requireUnique:!0,disambiguateAmbiguous:l});if(!r)continue;let n=t9(r.node,o.device.platform,{action:"click"===t.command?"click":"fill"===t.command?"fill":"get"}).join(" || ");if("click"===t.command)return{...t,positionals:[n]};if("fill"===t.command){let e=tR(t);if(!e)continue;return{...t,positionals:[n,e]}}if("get"===t.command){let e=t.positionals?.[0];if("text"!==e&&"attrs"!==e)continue;return{...t,positionals:[e,n]}}if("is"===t.command){let{predicate:e,split:i}=t8(t.positionals);if(!e)continue;let r=i?.rest.join(" ").trim()??"",a=[e,n];return"text"===e&&r.length>0&&a.push(r),{...t,positionals:a}}if("wait"===t.command){let{selectorTimeout:e}=iI(t.positionals??[]),i=[n];return e&&i.push(e),{...t,positionals:i}}}let u=function(e,t,i){if("get"!==e.command||e.positionals?.[0]!=="text")return null;let r=e.positionals?.[1];if(!r)return null;let n=t1(r);if(!n)return null;let a=new Set,o=!1;for(let e of n.selectors)for(let t of e.terms)"role"===t.key&&"string"==typeof t.value&&a.add(tX(t.value)),("text"===t.key||"label"===t.key||"value"===t.key)&&"string"==typeof t.value&&/^\d+$/.test(t.value.trim())&&(o=!0);if(!o)return null;let s=t.nodes.filter(e=>{let t=tz(e).trim();return!!/^\d+$/.test(t)&&(0===a.size||a.has(tX(e.type??"")))});if(0===s.length||1!==tL(s.map(e=>tz(e).trim())).length)return null;let l=s[0];if(!l)return null;let c=t9(l,i.device.platform,{action:"get"});return 0===c.length?null:{...e,positionals:["text",c.join(" || ")]}}(t,c,o);return u||null}async function iA(e,t,i,r,n,a){let o=await n(e.device,"snapshot",[],t.flags?.out,{...t$(i,{...t.flags??{},snapshotInteractiveOnly:r,snapshotCompact:r},e.appBundleId,e.trace?.outPath)}),s=o?.nodes??[],l={nodes:tV(t.flags?.snapshotRaw?s:tJ(s)),truncated:o?.truncated,createdAt:Date.now(),backend:o?.backend};return e.snapshot=l,a.set(e.name,e),l}function iI(e){if(0===e.length)return{selectorExpression:null,selectorTimeout:null};let t=e[e.length-1],i=/^\d+$/.test(t??""),r=t5(i?e.slice(0,-1):e.slice());return!r||r.rest.length>0?{selectorExpression:null,selectorTimeout:null}:{selectorExpression:r.selectorExpression,selectorTimeout:i?t:null}}function iv(e){let t=e.trim();return t.startsWith("@")||/^-?\d+(\.\d+)?$/.test(t)?t:JSON.stringify(t)}function iy(e){if(!e)return null;let t=Number(e);return Number.isFinite(t)?t:null}async function iN(e){let{req:t,sessionName:i,logPath:r,sessionStore:n}=e,a=t.command;if("snapshot"===a){let{session:e,device:a}=await iS(n,i,t.flags);if(!tM("snapshot",a))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"snapshot is not supported on this device"}};if("ios"===a.platform&&"device"===a.kind&&t.flags?.snapshotBackend==="ax")return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"AX snapshot backend is not supported on iOS physical devices; use --backend xctest"}};let o=e?.appBundleId,s=t.flags?.snapshotScope;if(s&&s.trim().startsWith("@")){if(!e?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"Ref scope requires an existing snapshot in session."}};let t=tU(s.trim());if(!t)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref scope: ${s}`}};let i=tG(e.snapshot.nodes,t),r=i?tq(i,e.snapshot.nodes):void 0;if(!r)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${s} not found or has no label`}};s=r}return await ib(e,a,async()=>{let l=await tD(a,"snapshot",[],t.flags?.out,{...t$(r,{...t.flags,snapshotScope:s},o,e?.trace?.outPath)}),c=l?.nodes??[],u=tV(t.flags?.snapshotRaw?c:tJ(c)),d={nodes:u,truncated:l?.truncated,createdAt:Date.now(),backend:l?.backend},f=e?{...e,snapshot:d}:{name:i,device:a,createdAt:Date.now(),appBundleId:o,snapshot:d,actions:[]};return i_(n,f,t,{nodes:u.length,truncated:l?.truncated??!1}),n.set(i,f),{ok:!0,data:{nodes:u,truncated:l?.truncated??!1,appName:f.appBundleId?f.appName??f.appBundleId:void 0,appBundleId:f.appBundleId}}})}if("wait"===a){let{session:e,device:a}=await iS(n,i,t.flags),o=function(e){if(0===e.length)return null;let t=iy(e[0]);if(null!==t)return{kind:"sleep",durationMs:t};if("text"===e[0]){let t=iy(e[e.length-1]);return{kind:"text",text:(null!==t?e.slice(1,-1).join(" "):e.slice(1).join(" ")).trim(),timeoutMs:t}}if(e[0].startsWith("@")){let t=iy(e[e.length-1]);return{kind:"ref",rawRef:e[0],timeoutMs:t}}let i=iy(e[e.length-1]),r=t5(null!==i?e.slice(0,-1):e.slice());if(r&&0===r.rest.length){let e=t1(r.selectorExpression);if(e)return{kind:"selector",selector:e,selectorExpression:r.selectorExpression,timeoutMs:i}}return{kind:"text",text:(null!==i?e.slice(0,-1).join(" "):e.join(" ")).trim(),timeoutMs:i}}(t.positionals??[]);return o?"sleep"===o.kind?(await new Promise(e=>setTimeout(e,o.durationMs)),i_(n,e,t,{waitedMs:o.durationMs}),{ok:!0,data:{waitedMs:o.durationMs}}):tM("wait",a)?await ib(e,a,async()=>{let s,l;if("selector"===o.kind){let s=o.timeoutMs??1e4,l=Date.now();for(;Date.now()-l<s;){let s=await tD(a,"snapshot",[],t.flags?.out,{...t$(r,{...t.flags,snapshotInteractiveOnly:!1,snapshotCompact:!1},e?.appBundleId,e?.trace?.outPath)}),c=s?.nodes??[],u=tV(t.flags?.snapshotRaw?c:tJ(c));e&&(e.snapshot={nodes:u,truncated:s?.truncated,createdAt:Date.now(),backend:s?.backend},n.set(i,e));let d=t3(u,o.selector,{platform:a.platform});if(d)return i_(n,e,t,{selector:d.selector.raw,waitedMs:Date.now()-l}),{ok:!0,data:{selector:d.selector.raw,waitedMs:Date.now()-l}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:`wait timed out for selector: ${o.selectorExpression}`}}}if("ref"===o.kind){if(!e?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"Ref wait requires an existing snapshot in session."}};let t=tU(o.rawRef);if(!t)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref: ${o.rawRef}`}};let i=tG(e.snapshot.nodes,t),r=i?tq(i,e.snapshot.nodes):void 0;if(!r)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${o.rawRef} not found or has no label`}};s=r,l=o.timeoutMs}else s=o.text,l=o.timeoutMs;if(!s)return{ok:!1,error:{code:"INVALID_ARGS",message:"wait requires text"}};let c=l??1e4,u=Date.now();for(;Date.now()-u<c;){if("ios"===a.platform){let i=await e7(a,{command:"findText",text:s,appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:r,traceLogPath:e?.trace?.outPath});if(i?.found)return i_(n,e,t,{text:s,waitedMs:Date.now()-u}),{ok:!0,data:{text:s,waitedMs:Date.now()-u}}}else if("android"===a.platform&&tj(tV((await eg(a,{scope:s})).nodes??[]),s))return i_(n,e,t,{text:s,waitedMs:Date.now()-u}),{ok:!0,data:{text:s,waitedMs:Date.now()-u}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:`wait timed out for text: ${s}`}}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"wait is not supported on this device"}}:{ok:!1,error:{code:"INVALID_ARGS",message:"wait requires a duration or text"}}}if("alert"===a){let{session:e,device:a}=await iS(n,i,t.flags),o=(t.positionals?.[0]??"get").toLowerCase();return tM("alert",a)?await ib(e,a,async()=>{if("wait"===o){let i=iy(t.positionals?.[1])??1e4,o=Date.now();for(;Date.now()-o<i;){try{let i=await e7(a,{command:"alert",action:"get",appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:r,traceLogPath:e?.trace?.outPath});return i_(n,e,t,i),{ok:!0,data:i}}catch{}await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:"alert wait timed out"}}}let i=await e7(a,{command:"alert",action:"accept"===o||"dismiss"===o?o:"get",appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:r,traceLogPath:e?.trace?.outPath});return i_(n,e,t,i),{ok:!0,data:i}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"alert is only supported on iOS simulators"}}}if("settings"===a){let e=t.positionals?.[0],a=t.positionals?.[1];if(!e||!a)return{ok:!1,error:{code:"INVALID_ARGS",message:"settings requires <wifi|airplane|location> <on|off>"}};let{session:o,device:s}=await iS(n,i,t.flags);return tM("settings",s)?await ib(o,s,async()=>{let i=o?.appBundleId,l=await tD(s,"settings",[e,a,i??""],t.flags?.out,{...t$(r,t.flags,i,o?.trace?.outPath)});return i_(n,o,t,l??{setting:e,state:a}),{ok:!0,data:l??{setting:e,state:a}}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"settings is not supported on this device"}}}return null}async function iS(e,t,i){let r=e.get(t),n=r?.device??await t_(i??{});return r||await tF(n),{session:r,device:n}}async function ib(e,t,i){let r=!e&&"ios"===t.platform;try{return await i()}finally{r&&await ti(t.id)}}function i_(e,t,i,r){t&&e.recordAction(t,{command:i.command,positionals:i.positionals??[],flags:i.flags??{},result:r})}function iD(e,t,i,r={}){let n=iO(i);if(!n)return{matches:[],score:0};let a=0,o=[];for(let i of e){if(r.requireRect&&!i.rect)continue;let e=function(e,t,i){switch(t){case"role":return function(e,t){let i=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 i?i===t?2:+!!i.includes(t):0}(e.type,i);case"label":return ix(e.label,i);case"value":return ix(e.value,i);case"id":return ix(e.identifier,i);default:return Math.max(ix(e.label,i),ix(e.value,i),ix(e.identifier,i))}}(i,t,n);if(!(e<=0)){if(e>a){a=e,o.length=0,o.push(i);continue}e===a&&o.push(i)}}return{matches:o,score:a}}function ix(e,t){let i=iO(e??"");return i?i===t?2:+!!i.includes(t):0}function iO(e){return e.trim().toLowerCase().replace(/\s+/g," ")}async function ik(e){let{req:t,sessionName:i,logPath:r,sessionStore:n,invoke:a}=e,o=t.command;if("find"!==o)return null;let s=t.positionals??[];if(0===s.length)return{ok:!1,error:{code:"INVALID_ARGS",message:"find requires a locator or text"}};let{locator:l,query:c,action:u,value:d,timeoutMs:f}=function(e){let t="any",i=0;["text","label","value","role","id"].includes(e[0])&&(t=e[0],i=1);let r=e[i]??"",n=e.slice(i+1);if(0===n.length)return{locator:t,query:r,action:"click"};let a=n[0].toLowerCase();if("get"===a){let e=n[1]?.toLowerCase();if("text"===e)return{locator:t,query:r,action:"get_text"};if("attrs"===e)return{locator:t,query:r,action:"get_attrs"};throw new p("INVALID_ARGS","find get only supports text or attrs")}if("wait"===a)return{locator:t,query:r,action:"wait",timeoutMs:iy(n[1])??void 0};if("exists"===a)return{locator:t,query:r,action:"exists"};if("click"===a)return{locator:t,query:r,action:"click"};if("focus"===a)return{locator:t,query:r,action:"focus"};if("fill"===a)return{locator:t,query:r,action:"fill",value:n.slice(1).join(" ")};if("type"===a)return{locator:t,query:r,action:"type",value:n.slice(1).join(" ")};throw new p("INVALID_ARGS",`Unsupported find action: ${n[0]}`)}(s);if(!c)return{ok:!1,error:{code:"INVALID_ARGS",message:"find requires a value"}};let m=n.get(i);if(!m&&"exists"!==u&&"wait"!==u&&"get_text"!==u&&"get_attrs"!==u)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let h=m?.device??await t_(t.flags??{});m||await tF(h);let w=m?.appBundleId,g="role"!==l?c:void 0,A="click"===u||"focus"===u||"fill"===u||"type"===u,I=0,v=null,y=async()=>{let e=Date.now();if(v&&e-I<750)return{nodes:v};let a=await tD(h,"snapshot",[],t.flags?.out,{...t$(r,{...t.flags,snapshotScope:g,snapshotInteractiveOnly:A,snapshotCompact:A},w,m?.trace?.outPath)}),o=a?.nodes??[],s=tV(t.flags?.snapshotRaw?o:tJ(o));return I=e,v=s,m&&(m.snapshot={nodes:s,truncated:a?.truncated,createdAt:Date.now(),backend:a?.backend},n.set(i,m)),{nodes:s,truncated:a?.truncated,backend:a?.backend}};if("wait"===u){let e=f??1e4,i=Date.now();for(;Date.now()-i<e;){let{nodes:e}=await y();if(iD(e,l,c,{requireRect:!1}).matches[0])return m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.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:N}=await y(),S=iD(N,l,c,{requireRect:A});if(A&&S.matches.length>1){let e=S.matches.slice(0,8).map(e=>{let t=tz(e)||e.label||e.identifier||e.type||"";return`@${e.ref}${t?`(${t})`:""}`});return{ok:!1,error:{code:"AMBIGUOUS_MATCH",message:`find matched ${S.matches.length} elements for ${l} "${c}". Use a more specific locator or selector.`,details:{locator:l,query:c,matches:S.matches.length,candidates:e}}}}let b=S.matches[0]??null;if(!b)return{ok:!1,error:{code:"COMMAND_FAILED",message:"find did not match any element"}};let _="click"===u||"focus"===u||"fill"===u||"type"===u?function(e,t){if(t.hittable)return t;let i=t,r=new Set;for(;void 0!==i.parentIndex&&!r.has(i.ref);){r.add(i.ref);let t=e[i.parentIndex];if(!t)break;if(t.hittable)return t;i=t}return null}(N,b)??b:b,D=`@${_.ref}`,x={...t.flags??{},noRecord:!0};if("exists"===u)return m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{found:!0}}),{ok:!0,data:{found:!0}};if("get_text"===u){let e=tz(b);return m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"get text",text:e}}),{ok:!0,data:{ref:D,text:e,node:b}}}if("get_attrs"===u)return m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"get attrs"}}),{ok:!0,data:{ref:D,node:b}};if("click"===u){let e=await a({token:t.token,session:i,command:"click",positionals:[D],flags:x});return e.ok&&m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"click"}}),e}if("fill"===u){if(!d)return{ok:!1,error:{code:"INVALID_ARGS",message:"find fill requires text"}};let e=await a({token:t.token,session:i,command:"fill",positionals:[D,d],flags:x});return e.ok&&m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"fill"}}),e}if("focus"===u){let e=b.rect?tB(b.rect):null;if(!e)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};let i=await tD(h,"focus",[String(e.x),String(e.y)],t.flags?.out,{...t$(r,t.flags,m?.appBundleId,m?.trace?.outPath)});return m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"focus"}}),{ok:!0,data:i??{ref:D}}}if("type"===u){if(!d)return{ok:!1,error:{code:"INVALID_ARGS",message:"find type requires text"}};let e=b.rect?tB(b.rect):null;if(!e)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};await tD(h,"focus",[String(e.x),String(e.y)],t.flags?.out,{...t$(r,t.flags,m?.appBundleId,m?.trace?.outPath)});let i=await tD(h,"type",[d],t.flags?.out,{...t$(r,t.flags,m?.appBundleId,m?.trace?.outPath)});return m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"type"}}),{ok:!0,data:i??{ref:D}}}return null}async function iE(e){let{req:t,sessionName:i,sessionStore:n}=e,a=t.command;if("record"===a){let e=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"record requires start|stop"}};let o=n.get(i),l=o?.device??await t_(t.flags??{});o||await tF(l);let u=o??{name:i,device:l,createdAt:Date.now(),actions:[]};if("start"===e){if(u.recording)return{ok:!1,error:{code:"INVALID_ARGS",message:"recording already in progress"}};let e=t.positionals?.[1]??`./recording-${Date.now()}.mp4`,o=r.resolve(e),c=r.dirname(o);if(m.existsSync(c)||m.mkdirSync(c,{recursive:!0}),!tM("record",l))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"record is only supported on iOS simulators"}};if("ios"===l.platform){let{child:e,wait:t}=s("xcrun",["simctl","io",l.id,"recordVideo",o],{allowFailure:!0});u.recording={platform:"ios",outPath:o,child:e,wait:t}}else{let e=`/sdcard/agent-device-recording-${Date.now()}.mp4`,{child:t,wait:i}=s("adb",["-s",l.id,"shell","screenrecord",e],{allowFailure:!0});u.recording={platform:"android",outPath:o,remotePath:e,child:t,wait:i}}return n.set(i,u),n.recordAction(u,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{action:"start"}}),{ok:!0,data:{recording:"started",outPath:e}}}if(!u.recording)return{ok:!1,error:{code:"INVALID_ARGS",message:"no active recording"}};let d=u.recording;d.child.kill("SIGINT");try{await d.wait}catch{}if("android"===d.platform&&d.remotePath)try{await c("adb",["-s",l.id,"pull",d.remotePath,d.outPath],{allowFailure:!0}),await c("adb",["-s",l.id,"shell","rm","-f",d.remotePath],{allowFailure:!0})}catch{}return u.recording=void 0,n.recordAction(u,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:d.outPath}}),{ok:!0,data:{recording:"stopped",outPath:d.outPath}}}if("trace"===a){let e=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"trace requires start|stop"}};let o=n.get(i);if(!o)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session"}};if("start"===e){if(o.trace)return{ok:!1,error:{code:"INVALID_ARGS",message:"trace already in progress"}};let e=t.positionals?.[1]??n.defaultTracePath(o),i=tT.expandHome(e);return m.mkdirSync(r.dirname(i),{recursive:!0}),m.appendFileSync(i,""),o.trace={outPath:i,startedAt:Date.now()},n.recordAction(o,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{action:"start",outPath:i}}),{ok:!0,data:{trace:"started",outPath:i}}}if(!o.trace)return{ok:!1,error:{code:"INVALID_ARGS",message:"no active trace"}};let s=o.trace.outPath;if(t.positionals?.[1]){let e=tT.expandHome(t.positionals[1]);m.mkdirSync(r.dirname(e),{recursive:!0}),m.existsSync(s)?m.renameSync(s,e):m.appendFileSync(e,""),s=e}return o.trace=void 0,n.recordAction(o,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:s}}),{ok:!0,data:{trace:"stopped",outPath:s}}}return null}async function iM(e){let{req:t,sessionName:i,sessionStore:r,contextFromFlags:n}=e,a=t.command;if("click"===a){let e=r.get(i);if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let o=t.positionals?.[0]??"";if(o.startsWith("@")){let i=iC("click",t.flags);if(i)return i;if(!e.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}};let s=tU(o);if(!s)return{ok:!1,error:{code:"INVALID_ARGS",message:"click requires a ref like @e2"}};let l=tG(e.snapshot.nodes,s);if(!l?.rect&&t.positionals.length>1){let i=t.positionals.slice(1).join(" ").trim();i.length>0&&(l=tj(e.snapshot.nodes,i))}if(!l?.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${o} not found or has no bounds`}};let c=tq(l,e.snapshot.nodes),u=t9(l,e.device.platform,{action:"click"}),{x:d,y:f}=tB(l.rect);return await tD(e.device,"press",[String(d),String(f)],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)}),r.recordAction(e,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{ref:s,x:d,y:f,refLabel:c,selectorChain:u}}),{ok:!0,data:{ref:s,x:d,y:f}}}let s=(t.positionals??[]).join(" ").trim();if(!s)return{ok:!1,error:{code:"INVALID_ARGS",message:"click requires @ref or selector expression"}};let l=t0(s),c=await iR(e,t.flags,r,n,{interactiveOnly:!0}),u=t2(c.nodes,l,{platform:e.device.platform,requireRect:!0,requireUnique:!0,disambiguateAmbiguous:!0});if(!u||!u.node.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:t4(l,u?.diagnostics??[],{unique:!0})}};let{x:d,y:f}=tB(u.node.rect);await tD(e.device,"press",[String(d),String(f)],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)});let p=t9(u.node,e.device.platform,{action:"click"}),m=tq(u.node,c.nodes);return r.recordAction(e,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{x:d,y:f,selector:u.selector.raw,selectorChain:p,refLabel:m}}),{ok:!0,data:{selector:u.selector.raw,x:d,y:f}}}if("fill"===a){let e=r.get(i);if(t.positionals?.[0]?.startsWith("@")){let i=iC("fill",t.flags);if(i)return i;if(!e?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}};let o=tU(t.positionals[0]);if(!o)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires a ref like @e2"}};let s=t.positionals.length>=3?t.positionals[1]:"",l=t.positionals.length>=3?t.positionals.slice(2).join(" "):t.positionals.slice(1).join(" ");if(!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after ref"}};let c=tG(e.snapshot.nodes,o);if(!c?.rect&&s&&(c=tj(e.snapshot.nodes,s)),!c?.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${t.positionals[0]} not found or has no bounds`}};let u=c.type??"",d=u&&!tH(u,e.device.platform)?`fill target ${t.positionals[0]} resolved to "${u}", attempting fill anyway.`:void 0,f=tq(c,e.snapshot.nodes),p=t9(c,e.device.platform,{action:"fill"}),{x:m,y:h}=tB(c.rect),w={...await tD(e.device,"fill",[String(m),String(h),l],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)})??{ref:o,x:m,y:h}};return d&&(w.warning=d),r.recordAction(e,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{...w,refLabel:f,selectorChain:p}}),{ok:!0,data:w}}if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let o=t5(t.positionals??[],{preferTrailingValue:!0});if(o){if(0===o.rest.length)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after selector"}};let i=o.rest.join(" ").trim();if(!i)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after selector"}};let s=t0(o.selectorExpression),l=await iR(e,t.flags,r,n,{interactiveOnly:!0}),c=t2(l.nodes,s,{platform:e.device.platform,requireRect:!0,requireUnique:!0,disambiguateAmbiguous:!0});if(!c||!c.node.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:t4(s,c?.diagnostics??[],{unique:!0})}};let u=c.node,d=u.type??"",f=d&&!tH(d,e.device.platform)?`fill target ${c.selector.raw} resolved to "${d}", attempting fill anyway.`:void 0,{x:p,y:m}=tB(c.node.rect),h=await tD(e.device,"fill",[String(p),String(m),i],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)}),w=t9(u,e.device.platform,{action:"fill"}),g={...h??{x:p,y:m,text:i},selector:c.selector.raw,selectorChain:w,refLabel:tq(u,l.nodes)};return f&&(g.warning=f),r.recordAction(e,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:g}),{ok:!0,data:g}}return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires x y text, @ref text, or selector text"}}}if("get"===a){let e=t.positionals?.[0];if("text"!==e&&"attrs"!==e)return{ok:!1,error:{code:"INVALID_ARGS",message:"get only supports text or attrs"}};let o=r.get(i);if(!o)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let s=t.positionals?.[1]??"";if(s.startsWith("@")){let i=iC("get",t.flags);if(i)return i;if(!o.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}};let n=tU(s??"");if(!n)return{ok:!1,error:{code:"INVALID_ARGS",message:"get text requires a ref like @e2"}};let l=tG(o.snapshot.nodes,n);if(!l&&t.positionals.length>2){let e=t.positionals.slice(2).join(" ").trim();e.length>0&&(l=tj(o.snapshot.nodes,e))}if(!l)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${s} not found`}};let c=t9(l,o.device.platform,{action:"get"});if("attrs"===e)return r.recordAction(o,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{ref:n,selectorChain:c}}),{ok:!0,data:{ref:n,node:l}};let u=tz(l);return r.recordAction(o,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{ref:n,text:u,refLabel:u||void 0,selectorChain:c}}),{ok:!0,data:{ref:n,text:u,node:l}}}let l=t.positionals.slice(1).join(" ").trim();if(!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"get requires @ref or selector expression"}};let c=t0(l),u=t2((await iR(o,t.flags,r,n,{interactiveOnly:!1})).nodes,c,{platform:o.device.platform,requireRect:!1,requireUnique:!0,disambiguateAmbiguous:"text"===e});if(!u)return{ok:!1,error:{code:"COMMAND_FAILED",message:t4(c,[],{unique:!0})}};let d=u.node,f=t9(d,o.device.platform,{action:"get"});if("attrs"===e)return r.recordAction(o,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{selector:u.selector.raw,selectorChain:f}}),{ok:!0,data:{selector:u.selector.raw,node:d}};let p=tz(d);return r.recordAction(o,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{text:p,refLabel:p||void 0,selector:u.selector.raw,selectorChain:f}}),{ok:!0,data:{selector:u.selector.raw,text:p,node:d}}}if("is"===a){let e=(t.positionals?.[0]??"").toLowerCase();if(!["visible","hidden","exists","editable","selected","text"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"is requires predicate: visible|hidden|exists|editable|selected|text"}};let o=r.get(i);if(!o)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};if(!tM("is",o.device))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"is is not supported on this device"}};let{split:s}=t8(t.positionals);if(!s)return{ok:!1,error:{code:"INVALID_ARGS",message:"is requires a selector expression"}};let l=s.rest.join(" ").trim();if("text"===e&&!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"is text requires expected text value"}};if("text"!==e&&s.rest.length>0)return{ok:!1,error:{code:"INVALID_ARGS",message:`is ${e} does not accept trailing values`}};let c=t0(s.selectorExpression),u=await iR(o,t.flags,r,n,{interactiveOnly:!1});if("exists"===e){let i=t3(u.nodes,c,{platform:o.device.platform});return i?(r.recordAction(o,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{predicate:e,selector:i.selector.raw,selectorChain:c.selectors.map(e=>e.raw),pass:!0,matches:i.matches}}),{ok:!0,data:{predicate:e,pass:!0,selector:i.selector.raw,matches:i.matches}}):{ok:!1,error:{code:"COMMAND_FAILED",message:t4(c,[],{unique:!1})}}}let d=t2(u.nodes,c,{platform:o.device.platform,requireUnique:!0});if(!d)return{ok:!1,error:{code:"COMMAND_FAILED",message:t4(c,[],{unique:!0})}};let f=function(e){let{predicate:t,node:i,expectedText:r,platform:n}=e,a=tz(i),o=!1;switch(t){case"visible":o=t6(i);break;case"hidden":o=!t6(i);break;case"editable":o=t7(i,n);break;case"selected":o=!0===i.selected;break;case"text":o=a===(r??"")}let s="text"===t?`expected="${r??""}" actual="${a}"`:`actual=${JSON.stringify({visible:t6(i),editable:t7(i,n),selected:!0===i.selected})}`;return{pass:o,actualText:a,details:s}}({predicate:e,node:d.node,expectedText:l,platform:o.device.platform});return f.pass?(r.recordAction(o,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{predicate:e,selector:d.selector.raw,selectorChain:c.selectors.map(e=>e.raw),pass:!0,text:"text"===e?f.actualText:void 0}}),{ok:!0,data:{predicate:e,pass:!0,selector:d.selector.raw}}):{ok:!1,error:{code:"COMMAND_FAILED",message:`is ${e} failed for selector ${d.selector.raw}: ${f.details}`}}}return null}async function iR(e,t,i,r,n){let a=await tD(e.device,"snapshot",[],t?.out,{...r({...t??{},snapshotInteractiveOnly:n.interactiveOnly,snapshotCompact:n.interactiveOnly},e.appBundleId,e.trace?.outPath)}),o=a?.nodes??[];return e.snapshot={nodes:tV(t?.snapshotRaw?o:tJ(o)),truncated:a?.truncated,createdAt:Date.now(),backend:a?.backend},i.set(e.name,e),e.snapshot}let iL=[["snapshotDepth","--depth"],["snapshotScope","--scope"],["snapshotRaw","--raw"],["snapshotBackend","--backend"]];function iC(e,t){let i=function(e){if(!e)return[];let t=[];for(let[i,r]of iL)void 0!==e[i]&&t.push(r);return t}(t);return 0===i.length?null:{ok:!1,error:{code:"INVALID_ARGS",message:`${e} @ref does not support ${i.join(", ")}.`}}}let iT=r.join(h.homedir(),".agent-device"),iP=r.join(iT,"daemon.json"),i$=r.join(iT,"daemon.lock"),iF=r.join(iT,"daemon.log"),iV=new tT(r.join(iT,"sessions")),iU=l(),iG=e.randomBytes(24).toString("hex"),iB=new Set(["session_list","devices"]),ij=w(process.pid)??void 0;function iq(e,t,i){return t$(iF,e,t,i)}async function iW(e){if(e.token!==iG)return{ok:!1,error:{code:"UNAUTHORIZED",message:"Invalid token"}};let t=e.command,i=function(e,t){var i;let r,n=e.session||"default";if(i=e,"string"==typeof(r=i.flags?.session)&&r.trim().length>0||"default"!==n||t.has(n))return n;let a=t.toArray();return 1===a.length?a[0].name:n}(e,iV),r=iV.get(i);r&&!iB.has(t)&&function(e,t){if(!t)return;let i=[],r=e.device;if(t.platform&&t.platform!==r.platform&&i.push(`--platform=${t.platform}`),t.udid&&("ios"!==r.platform||t.udid!==r.id)&&i.push(`--udid=${t.udid}`),t.serial&&("android"!==r.platform||t.serial!==r.id)&&i.push(`--serial=${t.serial}`),0!==i.length){var n;let t,r,a;throw new p("INVALID_ARGS",`Session "${e.name}" is bound to ${(t=(n=e).device.platform,r=n.device.name.trim(),a=n.device.id,`${t} device "${r}" (${a})`)} and cannot be used with ${i.join(", ")}. Use a different --session name or close this session first.`)}}(r,e.flags);let n=await ih({req:e,sessionName:i,logPath:iF,sessionStore:iV,invoke:iW});if(n)return n;let a=await iN({req:e,sessionName:i,logPath:iF,sessionStore:iV});if(a)return a;let o=await iE({req:e,sessionName:i,sessionStore:iV});if(o)return o;let s=await ik({req:e,sessionName:i,logPath:iF,sessionStore:iV,invoke:iW});if(s)return s;let l=await iM({req:e,sessionName:i,sessionStore:iV,contextFromFlags:iq});if(l)return l;let c=iV.get(i);if(!c)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};if(!tM(t,c.device))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:`${t} is not supported on this device`}};let u=await tD(c.device,t,e.positionals??[],e.flags?.out,{...iq(e.flags,c.appBundleId,c.trace?.outPath)});return iV.recordAction(c,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:u??{}}),{ok:!0,data:u??{}}}function iJ(){if(!m.existsSync(i$))return null;try{let e=JSON.parse(m.readFileSync(i$,"utf8"));if(!Number.isInteger(e.pid)||e.pid<=0)return null;return e}catch{return null}}!function(){if(!function(){m.existsSync(iT)||m.mkdirSync(iT,{recursive:!0});let e=JSON.stringify({pid:process.pid,version:iU,startedAt:Date.now(),processStartTime:ij},null,2),t=()=>{try{return m.writeFileSync(i$,e,{flag:"wx",mode:384}),!0}catch(e){if("EEXIST"===e.code)return!1;throw e}};if(t())return!0;let i=iJ();if(i?.pid&&i.pid!==process.pid&&o(i.pid,i.processStartTime))return!1;try{m.unlinkSync(i$)}catch{}return t()}()){process.stderr.write("Daemon lock is held by another process; exiting.\n"),process.exit(0);return}let e=g.createServer(e=>{let t="";e.setEncoding("utf8"),e.on("data",async i=>{let r=(t+=i).indexOf("\n");for(;-1!==r;){let i,n=t.slice(0,r).trim();if(t=t.slice(r+1),0===n.length){r=t.indexOf("\n");continue}try{let e=JSON.parse(n);i=await iW(e)}catch(t){let e=d(t);i={ok:!1,error:{code:e.code,message:e.message,details:e.details}}}e.write(`${JSON.stringify(i)}
|
|
16
|
-
`),r=t.indexOf("\n")}})});e.listen(0,"127.0.0.1",()=>{let t=e.address();if("object"==typeof t&&t?.port){var i;i=t.port,m.existsSync(iT)||m.mkdirSync(iT,{recursive:!0}),m.writeFileSync(iF,""),m.writeFileSync(iP,JSON.stringify({port:i,token:iG,pid:process.pid,version:iU,processStartTime:ij},null,2),{mode:384}),process.stdout.write(`AGENT_DEVICE_DAEMON_PORT=${t.port}
|
|
17
|
-
`)
|
|
16
|
+
`))),0!==s.exitCode){let e,t,i=(s.stderr??"").toString(),r=(e=i.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=i.toLowerCase()).includes("could not find ios app content")||t.includes("timeout"));throw new f("COMMAND_FAILED","AX snapshot failed",{stderr:`${i}${r}`,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(a.stdout);if(e&&"object"==typeof e&&"root"in e){if(!e.root)throw Error("AX snapshot missing root");i=e.root,r=e.windowFrame??void 0}else i=e}catch(e){throw new f("COMMAND_FAILED","Invalid AX snapshot JSON",{error:String(e)})}let o=i.frame??r,s=[],l=[],d=(e,t)=>{e.frame&&s.push(e.frame);let i=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 r of(l.push({...e,frame:i,children:void 0,depth:t}),e.children??[]))d(r,t+1)};return d(i,0),{nodes:(function(e,t,i){if(!t||0===i.length)return e;let r=1/0,n=1/0;for(let e of i)e.x<r&&(r=e.x),e.y<n&&(n=e.y);return r<=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 t$(){let e=function(){let e=r.dirname(a(import.meta.url));for(let t=0;t<6;t+=1){let t=r.join(e,"package.json");if(m.existsSync(t))return e;e=r.dirname(e)}return process.cwd()}(),t=r.join(e,"ios-runner","AXSnapshot"),i=process.env.AGENT_DEVICE_AX_BINARY;if(i&&m.existsSync(i))return i;let n=r.join(e,"dist","bin","axsnapshot");if(m.existsSync(n))return n;let o=r.join(t,".build","release","axsnapshot");if(m.existsSync(o))return o;let s=await c("swift",["build","-c","release"],{cwd:t,allowFailure:!0});if(0!==s.exitCode||!m.existsSync(o))throw new f("COMMAND_FAILED","Failed to build AX snapshot tool",{stderr:s.stderr,stdout:s.stdout});return o}async function tF(e){let t={platform:e.platform,deviceName:e.device,udid:e.udid,serial:e.serial};if("android"===t.platform){await eA();let e=await C();return await I(e,t)}if("ios"===t.platform){let e=await eR();return await I(e,t)}let i=[];try{i.push(...await C())}catch{}try{i.push(...await eR())}catch{}return await I(i,t)}async function tV(e,t,i,n,a){let o=function(e,t){switch(e.platform){case"android":return{open:(t,i)=>Q(e,t,i?.activity),openDevice:()=>et(e),close:t=>ei(e,t),tap:(t,i)=>eo(e,t,i),swipe:(t,i,r,n,a)=>es(e,t,i,r,n,a),longPress:(t,i,r)=>eu(e,t,i,r),focus:(t,i)=>ef(e,t,i),type:t=>ep(e,t),fill:(t,i,r)=>em(e,t,i,r),scroll:(t,i)=>eh(e,t,i),scrollIntoView:t=>ew(e,t),screenshot:t=>eg(e,t)};case"ios":var i,r;let n;return{open:(t,i)=>eH(e,t,{appBundleId:i?.appBundleId,url:i?.url}),openDevice:()=>ez(e),close:t=>eY(e,t),screenshot:t=>e0(e,t),...(i=e,n={verbose:(r=t).verbose,logPath:r.logPath,traceLogPath:r.traceLogPath},{tap:async(e,t)=>{await tc(i,{command:"tap",x:e,y:t,appBundleId:r.appBundleId},n)},swipe:async(e,t,a,o,s)=>{await tc(i,{command:"drag",x:e,y:t,x2:a,y2:o,durationMs:s,appBundleId:r.appBundleId},n)},longPress:async(e,t,a)=>{await tc(i,{command:"longPress",x:e,y:t,durationMs:a,appBundleId:r.appBundleId},n)},focus:async(e,t)=>{await tc(i,{command:"tap",x:e,y:t,appBundleId:r.appBundleId},n)},type:async e=>{await tc(i,{command:"type",text:e,appBundleId:r.appBundleId},n)},fill:async(e,t,a)=>{await tc(i,{command:"tap",x:e,y:t,appBundleId:r.appBundleId},n),await tc(i,{command:"type",text:a,clearFirst:!0,appBundleId:r.appBundleId},n)},scroll:async(e,t)=>{if(!["up","down","left","right"].includes(e))throw new f("INVALID_ARGS",`Unknown direction: ${e}`);let a=function(e){switch(e){case"up":return"down";case"down":return"up";case"left":return"right";case"right":return"left"}}(e);await tc(i,{command:"swipe",direction:a,appBundleId:r.appBundleId},n)},scrollIntoView:async e=>{for(let t=0;t<8;t+=1){let a=await tc(i,{command:"findText",text:e,appBundleId:r.appBundleId},n);if(a?.found)return{attempts:t+1};await tc(i,{command:"swipe",direction:"up",appBundleId:r.appBundleId},n),await new Promise(e=>setTimeout(e,300))}throw new f("COMMAND_FAILED",`scrollintoview could not find text: ${e}`)}})};default:throw new f("UNSUPPORTED_PLATFORM",`Unsupported platform: ${e.platform}`)}}(e,{appBundleId:a?.appBundleId,verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath});switch(t){case"open":{let t=i[0],r=i[1];if(i.length>2)throw new f("INVALID_ARGS","open accepts at most two arguments: <app|url> [url]");if(!t)return await o.openDevice(),{app:null};if(void 0!==r){if("ios"!==e.platform)throw new f("INVALID_ARGS","open <app> <url> is supported only on iOS");if($(t))throw new f("INVALID_ARGS","open <app> <url> requires an app target as the first argument");if(!$(r))throw new f("INVALID_ARGS","open <app> <url> requires a valid URL target");return await o.open(t,{activity:a?.activity,appBundleId:a?.appBundleId,url:r}),{app:t,url:r}}return await o.open(t,{activity:a?.activity,appBundleId:a?.appBundleId}),{app:t}}case"close":{let e=i[0];if(!e)return{closed:"session"};return await o.close(e),{app:e}}case"press":{let[e,t]=i.map(Number);if(Number.isNaN(e)||Number.isNaN(t))throw new f("INVALID_ARGS","press requires x y");let r=tG(a?.count??1,"count",1,200),n=tG(a?.intervalMs??0,"interval-ms",0,1e4),s=tG(a?.holdMs??0,"hold-ms",0,1e4),l=tG(a?.jitterPx??0,"jitter-px",0,100);for(let i=0;i<r;i+=1){let[a,c]=function(e,t){if(t<=0)return[0,0];let[i,r]=tU[e%tU.length];return[i*t,r*t]}(i,l),d=e+a,u=t+c;s>0?await o.longPress(d,u,s):await o.tap(d,u),i<r-1&&n>0&&await tB(n)}return{x:e,y:t,count:r,intervalMs:n,holdMs:s,jitterPx:l}}case"swipe":{let t=Number(i[0]),r=Number(i[1]),n=Number(i[2]),s=Number(i[3]);if([t,r,n,s].some(Number.isNaN))throw new f("INVALID_ARGS","swipe requires x1 y1 x2 y2 [durationMs]");let l=tG(i[4]?Number(i[4]):250,"durationMs",16,1e4),c="ios"===e.platform?60:l,d=tG(a?.count??1,"count",1,200),u=tG(a?.pauseMs??0,"pause-ms",0,1e4),p=a?.pattern??"one-way";if("one-way"!==p&&"ping-pong"!==p)throw new f("INVALID_ARGS",`Invalid pattern: ${p}`);for(let e=0;e<d;e+=1)"ping-pong"===p&&e%2==1?await o.swipe(n,s,t,r,c):await o.swipe(t,r,n,s,c),e<d-1&&u>0&&await tB(u);return{x1:t,y1:r,x2:n,y2:s,durationMs:l,effectiveDurationMs:c,timingMode:"ios"===e.platform?"safe-normalized":"direct",count:d,pauseMs:u,pattern:p}}case"long-press":{let e=Number(i[0]),t=Number(i[1]),r=i[2]?Number(i[2]):void 0;if(Number.isNaN(e)||Number.isNaN(t))throw new f("INVALID_ARGS","long-press requires x y [durationMs]");return await o.longPress(e,t,r),{x:e,y:t,durationMs:r}}case"focus":{let[e,t]=i.map(Number);if(Number.isNaN(e)||Number.isNaN(t))throw new f("INVALID_ARGS","focus requires x y");return await o.focus(e,t),{x:e,y:t}}case"type":{let e=i.join(" ");if(!e)throw new f("INVALID_ARGS","type requires text");return await o.type(e),{text:e}}case"fill":{let e=Number(i[0]),t=Number(i[1]),r=i.slice(2).join(" ");if(Number.isNaN(e)||Number.isNaN(t)||!r)throw new f("INVALID_ARGS","fill requires x y text");return await o.fill(e,t,r),{x:e,y:t,text:r}}case"scroll":{let e=i[0],t=i[1]?Number(i[1]):void 0;if(!e)throw new f("INVALID_ARGS","scroll requires direction");return await o.scroll(e,t),{direction:e,amount:t}}case"scrollintoview":{let e=i.join(" ").trim();if(!e)throw new f("INVALID_ARGS","scrollintoview requires text");let t=await o.scrollIntoView(e);if(t?.attempts)return{text:e,attempts:t.attempts};return{text:e}}case"pinch":{if("android"===e.platform)throw new f("UNSUPPORTED_OPERATION","Android pinch is not supported in current adb backend; requires instrumentation-based backend.");let t=Number(i[0]),r=i[1]?Number(i[1]):void 0,n=i[2]?Number(i[2]):void 0;if(Number.isNaN(t)||t<=0)throw new f("INVALID_ARGS","pinch requires scale > 0");return await tc(e,{command:"pinch",scale:t,x:r,y:n,appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{scale:t,x:r,y:n}}case"screenshot":{let e=i[0]??n??`./screenshot-${Date.now()}.png`;return await d.mkdir(r.dirname(e),{recursive:!0}),await o.screenshot(e),{path:e}}case"back":if("ios"===e.platform)return await tc(e,{command:"back",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{action:"back"};return await el(e),{action:"back"};case"home":if("ios"===e.platform)return await tc(e,{command:"home",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{action:"home"};return await ec(e),{action:"home"};case"app-switcher":if("ios"===e.platform)return await tc(e,{command:"appSwitcher",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{action:"app-switcher"};return await ed(e),{action:"app-switcher"};case"settings":{let[t,r,n]=i;if("ios"===e.platform)return await e1(e,t,r,n??a?.appBundleId),{setting:t,state:r};return await ev(e,t,r),{setting:t,state:r}}case"snapshot":{let t=a?.snapshotBackend??"xctest";if("ios"===e.platform){if("ax"===t&&"simulator"!==e.kind)throw new f("UNSUPPORTED_OPERATION","AX snapshot backend is not supported on iOS physical devices; use --backend xctest");if("ax"===t)return{nodes:(await tP(e,{traceLogPath:a?.traceLogPath})).nodes??[],truncated:!1,backend:"ax"};let i=await tc(e,{command:"snapshot",appBundleId:a?.appBundleId,interactiveOnly:a?.snapshotInteractiveOnly,compact:a?.snapshotCompact,depth:a?.snapshotDepth,scope:a?.snapshotScope,raw:a?.snapshotRaw},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),r=i.nodes??[];if(0===r.length&&"simulator"===e.kind)throw new f("COMMAND_FAILED","XCTest snapshot returned 0 nodes on iOS simulator. You can try --backend ax for diagnostics, but AX snapshots are not recommended.");return{nodes:r,truncated:i.truncated??!1,backend:"xctest"}}let i=await eI(e,{interactiveOnly:a?.snapshotInteractiveOnly,compact:a?.snapshotCompact,depth:a?.snapshotDepth,scope:a?.snapshotScope,raw:a?.snapshotRaw});return{nodes:i.nodes??[],truncated:i.truncated??!1,backend:"android"}}default:throw new f("INVALID_ARGS",`Unknown command: ${t}`)}}let tU=[[0,0],[1,0],[0,1],[-1,0],[0,-1],[1,1],[-1,1],[1,-1],[-1,-1]];function tG(e,t,i,r){if(!Number.isFinite(e)||!Number.isInteger(e)||e<i||e>r)throw new f("INVALID_ARGS",`${t} must be an integer between ${i} and ${r}`);return e}async function tB(e){await new Promise(t=>setTimeout(t,e))}let tj={alert:{ios:{simulator:!0},android:{}},pinch:{ios:{simulator:!0},android:{}},"app-switcher":{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},apps:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},back:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},boot:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},click:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},close:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},fill:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},find:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},focus:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},get:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},is:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},home:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},"long-press":{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},open:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},reinstall:{ios:{simulator:!0},android:{emulator:!0,device:!0,unknown:!0}},press:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},record:{ios:{simulator:!0},android:{emulator:!0,device:!0,unknown:!0}},screenshot:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},scroll:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},scrollintoview:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},swipe:{ios:{simulator:!0},android:{emulator:!0,device:!0,unknown:!0}},settings:{ios:{simulator:!0},android:{emulator:!0,device:!0,unknown:!0}},snapshot:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},type:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},wait:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}}};function tq(e,t){let i=tj[e];if(!i)return!0;let r=i[t.platform];return!!r&&!0===r[t.kind??"unknown"]}function tW(e){let t=e.result?.text;if("string"==typeof t&&t.trim().length>0)return t;let i=e.positionals??[];return 0===i.length?"":i[0].startsWith("@")?i.length>=3?i.slice(2).join(" ").trim():i.slice(1).join(" ").trim():!(i.length>=3)||Number.isNaN(Number(i[0]))||Number.isNaN(Number(i[1]))?i.slice(1).join(" ").trim():i.slice(2).join(" ").trim()}function tJ(e){let t=new Set,i=[];for(let r of e)t.has(r)||(t.add(r),i.push(r));return i}function tX(e,t,i){return t in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}class tH{get(e){return this.sessions.get(e)}has(e){return this.sessions.has(e)}set(e,t){this.sessions.set(e,t)}delete(e){return this.sessions.delete(e)}values(){return this.sessions.values()}toArray(){return Array.from(this.sessions.values())}recordAction(e,t){t.flags?.noRecord||(t.flags?.saveScript&&(e.recordSession=!0,"string"==typeof t.flags.saveScript&&(e.saveScriptPath=tH.expandHome(t.flags.saveScript))),e.actions.push({ts:Date.now(),command:t.command,positionals:t.positionals,flags:function(e){if(!e)return{};let{platform:t,device:i,udid:r,serial:n,out:a,verbose:o,snapshotInteractiveOnly:s,snapshotCompact:l,snapshotDepth:c,snapshotScope:d,snapshotRaw:u,snapshotBackend:p,relaunch:f,saveScript:m,noRecord:h}=e;return{platform:t,device:i,udid:r,serial:n,out:a,verbose:o,snapshotInteractiveOnly:s,snapshotCompact:l,snapshotDepth:c,snapshotScope:d,snapshotRaw:u,snapshotBackend:p,relaunch:f,saveScript:m,noRecord:h}}(t.flags),result:t.result}))}writeSessionLog(e){try{if(!e.recordSession)return;let t=this.resolveScriptPath(e),i=r.dirname(t);m.existsSync(i)||m.mkdirSync(i,{recursive:!0});let n=function(e,t){let i=[],r=e.device.name.replace(/"/g,'\\"'),n=e.device.kind?` kind=${e.device.kind}`:"";for(let a of(i.push(`context platform=${e.device.platform} device="${r}"${n} theme=unknown`),t))a.flags?.noRecord||i.push(function(e){let t=[e.command];if("click"===e.command){let i=e.positionals?.[0];if(i){if(t.push(tz(i)),i.startsWith("@")){let i=e.result?.refLabel;"string"==typeof i&&i.trim().length>0&&t.push(tz(i))}return t.join(" ")}}if("fill"===e.command){let i=e.positionals?.[0];if(i&&i.startsWith("@")){t.push(tz(i));let r=e.result?.refLabel,n=e.positionals.slice(1).join(" ");return"string"==typeof r&&r.trim().length>0&&t.push(tz(r)),n&&t.push(tz(n)),t.join(" ")}}if("get"===e.command){let i=e.positionals?.[0],r=e.positionals?.[1];if(i&&r){if(t.push(tz(i)),t.push(tz(r)),r.startsWith("@")){let i=e.result?.refLabel;"string"==typeof i&&i.trim().length>0&&t.push(tz(i))}return 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",tz(e.flags.snapshotScope)),e.flags?.snapshotRaw&&t.push("--raw"),e.flags?.snapshotBackend&&t.push("--backend",e.flags.snapshotBackend),t.join(" ");if("open"===e.command){for(let i of e.positionals??[])t.push(tz(i));return e.flags?.relaunch&&t.push("--relaunch"),t.join(" ")}for(let i of e.positionals??[])t.push(tz(i));return t.join(" ")}(a));return`${i.join("\n")}
|
|
17
|
+
`}(e,this.buildOptimizedActions(e));m.writeFileSync(t,n)}catch{}}defaultTracePath(e){let t=e.name.replace(/[^a-zA-Z0-9._-]/g,"_"),i=new Date().toISOString().replace(/[:.]/g,"-");return r.join(this.sessionsDir,`${t}-${i}.trace.log`)}static expandHome(e){return e.startsWith("~/")?r.join(h.homedir(),e.slice(2)):r.resolve(e)}resolveScriptPath(e){if(e.saveScriptPath)return tH.expandHome(e.saveScriptPath);m.existsSync(this.sessionsDir)||m.mkdirSync(this.sessionsDir,{recursive:!0});let t=e.name.replace(/[^a-zA-Z0-9._-]/g,"_"),i=new Date(e.createdAt).toISOString().replace(/[:.]/g,"-");return r.join(this.sessionsDir,`${t}-${i}.ad`)}buildOptimizedActions(e){let t=[];for(let i of e.actions){if("snapshot"===i.command)continue;let r=Array.isArray(i.result?.selectorChain)&&i.result?.selectorChain.every(e=>"string"==typeof e)?i.result.selectorChain:[];if(r.length>0&&("click"===i.command||"fill"===i.command||"get"===i.command)){let e=r.join(" || ");if("click"===i.command){t.push({...i,positionals:[e]});continue}if("fill"===i.command){let r=tW(i);if(r.length>0){t.push({...i,positionals:[e,r]});continue}}if("get"===i.command){let r=i.positionals?.[0];if("text"===r||"attrs"===r){t.push({...i,positionals:[r,e]});continue}}}if("click"===i.command||"fill"===i.command||"get"===i.command){let r=i.result?.refLabel;"string"==typeof r&&r.trim().length>0&&t.push({ts:i.ts,command:"snapshot",positionals:[],flags:{platform:e.device.platform,snapshotInteractiveOnly:!0,snapshotCompact:!0,snapshotScope:r.trim()},result:{scope:r.trim()}})}t.push(i)}return t}constructor(e){tX(this,"sessions",new Map),tX(this,"sessionsDir",void 0),this.sessionsDir=e}}function tz(e){let t=e.trim();return t.startsWith("@")||/^-?\d+(\.\d+)?$/.test(t)?t:JSON.stringify(t)}function tY(e,t,i,r){return{appBundleId:i,activity:t?.activity,verbose:t?.verbose,logPath:e,traceLogPath:r,snapshotInteractiveOnly:t?.snapshotInteractiveOnly,snapshotCompact:t?.snapshotCompact,snapshotDepth:t?.snapshotDepth,snapshotScope:t?.snapshotScope,snapshotRaw:t?.snapshotRaw,snapshotBackend:t?.snapshotBackend,count:t?.count,intervalMs:t?.intervalMs,holdMs:t?.holdMs,jitterPx:t?.jitterPx,pauseMs:t?.pauseMs,pattern:t?.pattern}}let tK=eM(process.env.AGENT_DEVICE_IOS_DEVICE_READY_TIMEOUT_MS,15e3,1e3);async function tZ(e){if("ios"===e.platform){if("simulator"===e.kind){let{ensureBootedSimulator:t}=await Promise.resolve().then(()=>({ensureBootedSimulator:eq}));await t(e);return}if("device"===e.kind)return void await tQ(e.id)}if("android"===e.platform){let{waitForAndroidBoot:t}=await Promise.resolve().then(()=>({waitForAndroidBoot:P}));await t(e.id)}}async function tQ(e){let t=r.join(h.tmpdir(),`agent-device-ready-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`),i=Math.max(1,Math.ceil(tK/1e3));try{let r=await c("xcrun",["devicectl","device","info","details","--device",e,"--json-output",t,"--timeout",String(i)],{allowFailure:!0,timeoutMs:tK+3e3}),n=String(r.stdout??""),a=String(r.stderr??""),o=await t0(t);if(0===r.exitCode){if(!o.parsed)throw new f("COMMAND_FAILED","iOS device readiness probe failed",{kind:"probe_inconclusive",deviceId:e,stdout:n,stderr:a,hint:"CoreDevice returned success but readiness JSON output was missing or invalid. Retry; if it persists restart Xcode and the iOS device."});let t=o?.tunnelState?.toLowerCase();if("connecting"===t)throw new f("COMMAND_FAILED","iOS device is not ready for automation",{kind:"not_ready",deviceId:e,tunnelState:t,hint:"Device tunnel is still connecting. Keep the device unlocked and connected by cable until it is fully available in Xcode Devices, then retry."});return}throw new f("COMMAND_FAILED","iOS device is not ready for automation",{kind:"not_ready",deviceId:e,stdout:n,stderr:a,exitCode:r.exitCode,tunnelState:o?.tunnelState,hint:t1(n,a)})}catch(t){if(t instanceof f&&"COMMAND_FAILED"===t.code){if("not_ready"===("string"==typeof t.details?.kind?t.details.kind:""))throw t;let i=t.details??{},r=String(i.stdout??""),n=String(i.stderr??""),a=Number(i.timeoutMs??tK),o=`CoreDevice did not respond within ${a}ms. Keep the device unlocked and trusted, then retry; if it persists restart Xcode and the iOS device.`;throw new f("COMMAND_FAILED","iOS device readiness probe failed",{deviceId:e,cause:t.message,timeoutMs:a,stdout:r,stderr:n,hint:r||n?t1(r,n):o},t)}throw new f("COMMAND_FAILED","iOS device readiness probe failed",{deviceId:e,hint:"Reconnect the device, keep it unlocked, and retry."},t instanceof Error?t:void 0)}finally{await d.rm(t,{force:!0}).catch(()=>{})}}async function t0(e){try{let t=await d.readFile(e,"utf8"),i=JSON.parse(t),r=function(e){let t=e?.result;if(!t||"object"!=typeof t)return{};let i=t.connectionProperties?.tunnelState,r=t.device?.connectionProperties?.tunnelState,n="string"==typeof i?i:"string"==typeof r?r:void 0;return n?{tunnelState:n}:{}}(i);return{parsed:!0,tunnelState:r.tunnelState}}catch{return{parsed:!1}}}function t1(e,t){let i=eB(e,t);return i||(`${e}
|
|
18
|
+
${t}`.toLowerCase().includes("timed out waiting for all destinations")?"Xcode destination did not become available in time. Keep device unlocked and retry.":eG)}function t2(e){return e.map((e,t)=>({...e,ref:`e${t+1}`}))}function t3(e){let t=e.trim();return t.startsWith("@")?t.slice(1)||null:t.startsWith("e")?t:null}function t4(e,t){return e.find(e=>e.ref===t)??null}function t5(e){return{x:Math.round(e.x+e.width/2),y:Math.round(e.y+e.height/2)}}function t8(e,t){let i=t.toLowerCase();return e.find(e=>{let t=(e.label??"").toLowerCase(),r=(e.value??"").toLowerCase(),n=(e.identifier??"").toLowerCase();return t.includes(i)||r.includes(i)||n.includes(i)})??null}function t6(e,t){let i=[e.label,e.value,e.identifier].map(e=>"string"==typeof e?e.trim():"").find(e=>e&&e.length>0);return i&&t7(i)?i:function(e,t){if(!e.rect)return;let i=e.rect.y+e.rect.height/2,r=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||!t7(t))continue;let n=Math.abs(e.rect.y+e.rect.height/2-i);(!r||n<r.distance)&&(r={label:t,distance:n})}return r?.label}(e,t)??(i&&t7(i)?i:void 0)}function t7(e){let t=e.trim();return!(!t||/^(true|false)$/i.test(t)||/^\d+$/.test(t))}function t9(e){let t=[],i=[];for(let r of e){let e=r.depth??0;for(;t.length>0&&e<=t[t.length-1];)t.pop();let n=ie(r.type??""),a=[r.label,r.value,r.identifier].map(e=>"string"==typeof e?e.trim():"").find(e=>e&&e.length>0),o=!!a&&t7(a);if(("group"===n||"ioscontentgroup"===n)&&!o){t.push(e);continue}let s=Math.max(0,e-t.length);i.push({...r,depth:s})}return i}function ie(e){let t=e.trim().replace(/XCUIElementType/gi,"").toLowerCase();t.startsWith("ax")&&(t=t.replace(/^ax/,""));let i=Math.max(t.lastIndexOf("."),t.lastIndexOf("/"));return -1!==i&&(t=t.slice(i+1)),t}function it(e,t){let i=ie(e);return!i||("android"===t?i.includes("edittext")||i.includes("autocompletetextview"):i.includes("textfield")||i.includes("securetextfield")||i.includes("searchfield")||i.includes("textview")||i.includes("textarea")||"search"===i)}function ii(e){return[e.label,e.value,e.identifier].map(e=>"string"==typeof e?e.trim():"").filter(e=>e.length>0)[0]??""}let ir=new Set(["id","role","text","label","value"]),ia=new Set(["visible","hidden","editable","selected","enabled","hittable"]),io=new Set([...ir,...ia]);function is(e){let t=e.trim();if(!t)throw new f("INVALID_ARGS","Selector expression cannot be empty");let i=function(e){let t=[],i="",r=null;for(let n=0;n<e.length;n+=1){let a=e[n];if(('"'===a||"'"===a)&&!ib(e,n)){r?r===a&&(r=null):r=a,i+=a;continue}if(!r&&"|"===a&&"|"===e[n+1]){let r=i.trim();if(!r)throw new f("INVALID_ARGS",`Invalid selector fallback expression: ${e}`);t.push(r),i="",n+=1;continue}i+=a}let n=i.trim();if(!n)throw new f("INVALID_ARGS",`Invalid selector fallback expression: ${e}`);return t.push(n),t}(t);if(0===i.length)throw new f("INVALID_ARGS","Selector expression cannot be empty");return{raw:t,selectors:i.map(e=>(function(e){let t=e.trim();if(!t)throw new f("INVALID_ARGS","Selector segment cannot be empty");let i=function(e){let t=[],i="",r=null;for(let n=0;n<e.length;n+=1){let a=e[n];if(('"'===a||"'"===a)&&!ib(e,n)){r?r===a&&(r=null):r=a,i+=a;continue}if(!r&&/\s/.test(a)){i.trim().length>0&&t.push(i.trim()),i="";continue}i+=a}if(r)throw new f("INVALID_ARGS",`Unclosed quote in selector: ${e}`);return i.trim().length>0&&t.push(i.trim()),t}(t);if(0===i.length)throw new f("INVALID_ARGS",`Invalid selector segment: ${e}`);return{raw:t,terms:i.map(iv)}})(e))}}function il(e){try{return is(e)}catch{return null}}function ic(e,t,i){let r=i.requireRect??!1,n=i.requireUnique??!0,a=i.disambiguateAmbiguous??!1,o=[];for(let s=0;s<t.selectors.length;s+=1){let l=t.selectors[s],c=function(e,t,i){let r=0,n=null,a=null,o=!1;for(let s of e){if(i.requireRect&&!s.rect||!iI(s,t,i.platform))continue;if(r+=1,n||(n=s),!a){a=s;continue}let e=function(e,t){let i=e.depth??0,r=t.depth??0;if(i!==r)return i>r?1:-1;let n=i_(e),a=i_(t);return n!==a?n<a?1:-1:0}(s,a);if(e>0){a=s,o=!1;continue}0===e&&(o=!0)}return{count:r,firstNode:n,disambiguated:o?null:a}}(e,l,{platform:i.platform,requireRect:r});if(o.push({selector:l.raw,matches:c.count}),0!==c.count&&c.firstNode){if(n&&1!==c.count){if(!a)continue;let e=c.disambiguated;if(!e)continue;return{node:e,selector:l,selectorIndex:s,matches:c.count,diagnostics:o}}return{node:c.firstNode,selector:l,selectorIndex:s,matches:c.count,diagnostics:o}}}return null}function id(e,t,i){let r=i.requireRect??!1,n=[];for(let a=0;a<t.selectors.length;a+=1){let o=t.selectors[a],s=function(e,t,i){let r=0;for(let n of e)(!i.requireRect||n.rect)&&iI(n,t,i.platform)&&(r+=1);return r}(e,o,{platform:i.platform,requireRect:r});if(n.push({selector:o.raw,matches:s}),s>0)return{selectorIndex:a,selector:o,matches:s,diagnostics:n}}return null}function iu(e,t,i){let r=i.unique??!0;if(0===t.length)return`Selector did not match: ${e.raw}`;let n=t.map(e=>`${e.selector} -> ${e.matches}`).join(", ");return r?`Selector did not resolve uniquely (${n})`:`Selector did not match (${n})`}function ip(e,t={}){if(0===e.length)return null;let i=t.preferTrailingValue??!1,r=0,n=[];for(;r<e.length&&function(e){let t=e.trim();if(!t)return!1;if("||"===t)return!0;let i=t.indexOf("=");if(-1!==i){let e=t.slice(0,i).trim().toLowerCase();return io.has(e)}return io.has(t.toLowerCase())}(e[r]);){r+=1;let t=e.slice(0,r).join(" ").trim();t&&il(t)&&n.push(r)}if(0===n.length)return null;let a=n[n.length-1];if(i){for(let t=n.length-1;t>=0;t-=1)if(n[t]<e.length){a=n[t];break}}let o=e.slice(0,a).join(" ").trim();return o?{selectorExpression:o,rest:e.slice(a)}:null}function im(e){let t=e[0]??"",i=ip(e.slice(1),{preferTrailingValue:"text"===t});return{predicate:t,split:i}}function ih(e){return!0===e.hittable||!!e.rect&&e.rect.width>0&&e.rect.height>0}function iw(e,t){return it(e.type??"",t)&&!1!==e.enabled}function ig(e,t,i={}){let r=[],n=ie(e.type??""),a=iD(e.identifier),o=iD(e.label),s=iD(e.value),l=iD(ii(e)),c="fill"===i.action;a&&r.push(`id=${iS(a)}`),n&&o&&r.push(c?`role=${iS(n)} label=${iS(o)} editable=true`:`role=${iS(n)} label=${iS(o)}`),o&&r.push(c?`label=${iS(o)} editable=true`:`label=${iS(o)}`),s&&r.push(c?`value=${iS(s)} editable=true`:`value=${iS(s)}`),l&&l!==o&&l!==s&&r.push(c?`text=${iS(l)} editable=true`:`text=${iS(l)}`),n&&c&&!r.some(e=>e.includes("editable=true"))&&r.push(`role=${iS(n)} editable=true`);let d=tJ(r);return 0===d.length&&n&&d.push(c?`role=${iS(n)} editable=true`:`role=${iS(n)}`),0===d.length&&ih(e)&&d.push("visible=true"),d}function iv(e){let t=e.trim();if(!t)throw new f("INVALID_ARGS","Empty selector term");let i=t.indexOf("=");if(-1===i){let i=t.toLowerCase();if(!ia.has(i))throw new f("INVALID_ARGS",`Invalid selector term "${e}", expected key=value`);return{key:i,value:!0}}let r=t.slice(0,i).trim().toLowerCase(),n=t.slice(i+1).trim();if(!io.has(r))throw new f("INVALID_ARGS",`Unknown selector key: ${r}`);if(!n)throw new f("INVALID_ARGS",`Missing selector value for key: ${r}`);if(ia.has(r)){let e,t="true"===(e=iA(n).toLowerCase())||"false"!==e&&null;if(null===t)throw new f("INVALID_ARGS",`Invalid boolean value for ${r}: ${n}`);return{key:r,value:t}}return{key:r,value:iA(n)}}function iI(e,t,i){return t.terms.every(t=>(function(e,t,i){switch(t.key){case"id":return iy(e.identifier,String(t.value));case"role":var r,n;return r=e.type,n=String(t.value),function(e){return ie(e)}(r??"")===function(e){return ie(e)}(n);case"label":return iy(e.label,String(t.value));case"value":return iy(e.value,String(t.value));case"text":{let i=iN(String(t.value));return iN(ii(e))===i}case"visible":return ih(e)===!!t.value;case"hidden":return!ih(e)==!!t.value;case"editable":return iw(e,i)===!!t.value;case"selected":return!0===e.selected==!!t.value;case"enabled":return!1!==e.enabled==!!t.value;case"hittable":return!0===e.hittable==!!t.value;default:return!1}})(e,t,i))}function iA(e){let t=e.trim();return t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1).replace(/\\(["'])/g,"$1"):t}function iy(e,t){return iN(e??"")===iN(t)}function iN(e){return e.trim().toLowerCase().replace(/\s+/g," ")}function iS(e){return JSON.stringify(e)}function iD(e){if(!e)return null;let t=e.trim();return t||null}function i_(e){return e.rect?e.rect.width*e.rect.height:1/0}function ib(e,t){let i=0;for(let r=t-1;r>=0&&"\\"===e[r];r-=1)i+=1;return i%2==1}let iO='iOS appstate requires an active session on the target device. Run open first (for example: open --session sim --platform ios --device "<name>" <app>).';function iE(e,t,i){return t||ix(i)?null:{ok:!1,error:{code:"INVALID_ARGS",message:`${e} requires an active session or an explicit device selector (e.g. --platform ios).`}}}function ix(e){return!!(e?.platform||e?.device||e?.udid||e?.serial)}async function ik(e){let t=ix(e.flags)||!e.session?await e.resolveTargetDeviceFn(e.flags??{}):e.session.device;return!1!==e.ensureReady&&await e.ensureReadyFn(t),t}let iM={ios:async(e,t,i)=>{let{reinstallIosApp:r}=await Promise.resolve().then(()=>({reinstallIosApp:eQ}));return await r(e,t,i)},android:async(e,t,i)=>{let{reinstallAndroidApp:r}=await Promise.resolve().then(()=>({reinstallAndroidApp:ea}));return await r(e,t,i)}};async function iL(e,t,i){if("ios"===e.platform&&t)return $(t)?"device"===e.kind?F(i,t):void 0:await iR(e,t)}async function iR(e,t){try{let{resolveIosApp:i}=await Promise.resolve().then(()=>({resolveIosApp:eX}));return await i(e,t)}catch{return}}async function iC(e){let{req:t,sessionName:i,sessionStore:r,ensureReady:n,resolveDevice:a}=e,o=r.get(i),s=t.flags??{};if(!o&&"string"==typeof s?.session&&s.session.trim().length>0)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"ios"===s.platform?`No active session "${i}". Run open with --session ${i} first.`:`No active session "${i}". Run open with --session ${i} first, or omit --session to query by device selector.`}};let l=iE("appstate",o,s);if(l)return l;let c=o?.device.platform==="ios"&&!!o&&(!ix(s)||!(s?.platform&&s.platform!==o.device.platform||s?.udid&&s.udid!==o.device.id||s?.serial&&s.serial!==o.device.id)&&(!s?.device||s.device.trim().toLowerCase()===o.device.name.trim().toLowerCase()));if("ios"===s.platform&&!c)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:iO}};if(c){let e=o.appName??o.appBundleId;return o.appName||o.appBundleId?{ok:!0,data:{platform:"ios",appName:e??"unknown",appBundleId:o.appBundleId,source:"session"}}:{ok:!1,error:{code:"COMMAND_FAILED",message:"No foreground app is tracked for this iOS session. Open an app in the session, then retry appstate."}}}let d=await ik({session:o,flags:s,ensureReadyFn:n,resolveTargetDeviceFn:a,ensureReady:!0});if("ios"===d.platform)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:iO}};let{getAndroidAppState:u}=await Promise.resolve().then(()=>({getAndroidAppState:K})),p=await u(d);return{ok:!0,data:{platform:"android",package:p.package,activity:p.activity}}}async function iT(e){let{req:t,sessionName:i,logPath:r,sessionStore:n,invoke:a,dispatch:o,ensureReady:s,resolveTargetDevice:l,reinstallOps:c=iM}=e,d=o??tV,p=s??tZ,h=l??tF,w=t.command;if("session_list"===w)return{ok:!0,data:{sessions:n.toArray().map(e=>({name:e.name,platform:e.device.platform,device:e.device.name,id:e.device.id,createdAt:e.createdAt}))}};if("devices"===w)try{let e=[];if(t.flags?.platform==="android"){let{listAndroidDevices:t}=await Promise.resolve().then(()=>({listAndroidDevices:C}));e.push(...await t())}else if(t.flags?.platform==="ios"){let{listIosDevices:t}=await Promise.resolve().then(()=>({listIosDevices:eR}));e.push(...await t())}else{let{listAndroidDevices:t}=await Promise.resolve().then(()=>({listAndroidDevices:C})),{listIosDevices:i}=await Promise.resolve().then(()=>({listIosDevices:eR}));try{e.push(...await t())}catch{}try{e.push(...await i())}catch{}}return{ok:!0,data:{devices:e}}}catch(t){let e=u(t);return{ok:!1,error:{code:e.code,message:e.message,details:e.details}}}if("apps"===w){let e=n.get(i),r=t.flags??{},a=iE(w,e,r);if(a)return a;let o=await ik({session:e,flags:r,ensureReadyFn:p,resolveTargetDeviceFn:h,ensureReady:!0});if(!tq("apps",o))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"apps is not supported on this device"}};let s=t.flags?.appsFilter??"all";if("ios"===o.platform){let{listIosApps:e}=await Promise.resolve().then(()=>({listIosApps:e2}));return{ok:!0,data:{apps:(await e(o,s)).map(e=>e.name&&e.name!==e.bundleId?`${e.name} (${e.bundleId})`:e.bundleId)}}}let{listAndroidApps:l}=await Promise.resolve().then(()=>({listAndroidApps:H}));return{ok:!0,data:{apps:(await l(o,s)).map(e=>e.name&&e.name!==e.package?`${e.name} (${e.package})`:e.package)}}}if("boot"===w){let e=n.get(i),r=t.flags??{},a=iE(w,e,r);if(a)return a;let o=await ik({session:e,flags:r,ensureReadyFn:p,resolveTargetDeviceFn:h,ensureReady:!0});return tq("boot",o)?{ok:!0,data:{platform:o.platform,device:o.name,id:o.id,kind:o.kind,booted:!0}}:{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"boot is not supported on this device"}}}if("appstate"===w)return await iC({req:t,sessionName:i,sessionStore:n,ensureReady:p,resolveDevice:h});if("reinstall"===w){let e,r=n.get(i),a=t.flags??{},o=iE(w,r,a);if(o)return o;let s=t.positionals?.[0]?.trim(),l=t.positionals?.[1]?.trim();if(!s||!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"reinstall requires: reinstall <app> <path-to-app-binary>"}};let d=tH.expandHome(l);if(!m.existsSync(d))return{ok:!1,error:{code:"INVALID_ARGS",message:`App binary not found: ${d}`}};let u=await ik({session:r,flags:a,ensureReadyFn:p,resolveTargetDeviceFn:h,ensureReady:!1});if(!tq("reinstall",u))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"reinstall is not supported on this device"}};if("ios"===u.platform){let t=await c.ios(u,s,d);e={platform:"ios",appId:t.bundleId,bundleId:t.bundleId}}else{let t=await c.android(u,s,d);e={platform:"android",appId:t.package,package:t.package}}let f={app:s,appPath:d,...e};return r&&n.recordAction(r,{command:w,positionals:t.positionals??[],flags:t.flags??{},result:f}),{ok:!0,data:f}}if("open"===w){let e=t.flags?.relaunch===!0;if(n.has(i)){let a=n.get(i),o=t.positionals?.[0],s=o??(e?a?.appName:void 0);if(!a||!s)return e?{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch requires an app name or an active session app."}}:{ok:!1,error:{code:"INVALID_ARGS",message:"Session already active. Close it first or pass a new --session name."}};if(e&&$(s))return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch does not support URL targets."}};await p(a.device);let l=await iL(a.device,s,a.appBundleId),c=o?t.positionals??[]:[s];if(e){let e=l??s;await d(a.device,"close",[e],t.flags?.out,{...tY(r,t.flags,l??a.appBundleId,a.trace?.outPath)})}await d(a.device,"open",c,t.flags?.out,{...tY(r,t.flags,l)});let u={...a,appBundleId:l,appName:s,recordSession:a.recordSession||!!t.flags?.saveScript,snapshot:void 0};return n.recordAction(u,{command:w,positionals:c,flags:t.flags??{},result:{session:i,appName:s,appBundleId:l}}),n.set(i,u),{ok:!0,data:{session:i,appName:s,appBundleId:l}}}let a=t.positionals?.[0];if(e&&!a)return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch requires an app argument."}};if(e&&a&&$(a))return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch does not support URL targets."}};let o=await h(t.flags??{}),s=n.toArray().find(e=>e.device.id===o.id);if(s)return{ok:!1,error:{code:"DEVICE_IN_USE",message:`Device is already in use by session "${s.name}".`,details:{session:s.name,deviceId:o.id,deviceName:o.name}}};await p(o);let l=await iL(o,a);if(e&&a){let e=l??a;await d(o,"close",[e],t.flags?.out,{...tY(r,t.flags,l)})}await d(o,"open",t.positionals??[],t.flags?.out,{...tY(r,t.flags,l)});let c={name:i,device:o,createdAt:Date.now(),appBundleId:l,appName:a,recordSession:!!t.flags?.saveScript,actions:[]};return n.recordAction(c,{command:w,positionals:t.positionals??[],flags:t.flags??{},result:{session:i}}),n.set(i,c),{ok:!0,data:{session:i}}}if("replay"===w){let e=t.positionals?.[0];if(!e)return{ok:!1,error:{code:"INVALID_ARGS",message:"replay requires a path"}};try{let o=tH.expandHome(e),s=m.readFileSync(o,"utf8"),l=s.trimStart()[0];if("{"===l||"["===l)return{ok:!1,error:{code:"INVALID_ARGS",message:"replay accepts .ad script files. JSON replay payloads are no longer supported."}};let c=function(e){let t=[];for(let i of e.split(/\r?\n/)){let e=function(e){let t=e.trim();if(0===t.length||t.startsWith("#"))return null;let i=function(e){let t=[],i=0;for(;i<e.length;){for(;i<e.length&&/\s/.test(e[i]);)i+=1;if(i>=e.length)break;if('"'===e[i]){let r=i+1,n=!1;for(;r<e.length;){let t=e[r];if('"'===t&&!n)break;n="\\"===t&&!n,"\\"!==t&&(n=!1),r+=1}if(r>=e.length)throw new f("INVALID_ARGS",`Invalid replay script line: ${e}`);let a=e.slice(i,r+1);t.push(JSON.parse(a)),i=r+1;continue}let r=i;for(;r<e.length&&!/\s/.test(e[r]);)r+=1;t.push(e.slice(i,r)),i=r}return t}(t);if(0===i.length)return null;let[r,...n]=i;if("context"===r)return null;let a={ts:Date.now(),command:r,positionals:[],flags:{}};if("snapshot"===r){a.positionals=[];for(let e=0;e<n.length;e+=1){let t=n[e];if("-i"===t){a.flags.snapshotInteractiveOnly=!0;continue}if("-c"===t){a.flags.snapshotCompact=!0;continue}if("--raw"===t){a.flags.snapshotRaw=!0;continue}if(("-d"===t||"--depth"===t)&&e+1<n.length){let t=Number(n[e+1]);Number.isFinite(t)&&t>=0&&(a.flags.snapshotDepth=Math.floor(t)),e+=1;continue}if(("-s"===t||"--scope"===t)&&e+1<n.length){a.flags.snapshotScope=n[e+1],e+=1;continue}if("--backend"===t&&e+1<n.length){let t=n[e+1];("ax"===t||"xctest"===t)&&(a.flags.snapshotBackend=t),e+=1}}return a}if("open"===r){a.positionals=[];for(let e=0;e<n.length;e+=1){let t=n[e];if("--relaunch"===t){a.flags.relaunch=!0;continue}a.positionals.push(t)}return a}if("click"===r){if(0===n.length)return a;let e=n[0];return e.startsWith("@")?(a.positionals=[e],n[1]&&(a.result={refLabel:n[1]})):a.positionals=[n.join(" ")],a}if("fill"===r){if(n.length<2)return a.positionals=n,a;let e=n[0];return e.startsWith("@")?(n.length>=3?(a.positionals=[e,n.slice(2).join(" ")],a.result={refLabel:n[1]}):a.positionals=[e,n[1]],a):(a.positionals=[e,n.slice(1).join(" ")],a)}if("get"===r){if(n.length<2)return a.positionals=n,a;let e=n[0],t=n[1];return t.startsWith("@")?(a.positionals=[e,t],n[2]&&(a.result={refLabel:n[2]})):a.positionals=[e,n.slice(1).join(" ")],a}return a.positionals=n,a}(i);e&&t.push(e)}return t}(s),u=t.flags?.replayUpdate===!0,p=0;for(let e=0;e<c.length;e+=1){let s=c[e];if(!s||"replay"===s.command)continue;let l=await a({token:t.token,session:i,command:s.command,positionals:s.positionals??[],flags:s.flags??{}});if(l.ok)continue;if(!u)return iP(l,s,e,o);let f=await i$({action:s,sessionName:i,logPath:r,sessionStore:n,dispatch:d});if(!f)return iP(l,s,e,o);if(c[e]=f,!(l=await a({token:t.token,session:i,command:f.command,positionals:f.positionals??[],flags:f.flags??{}})).ok)return iP(l,f,e,o);p+=1}if(u&&p>0){let e=n.get(i);!function(e,t,i){let r=[];if(i){let e=i.device.name.replace(/"/g,'\\"'),t=i.device.kind?` kind=${i.device.kind}`:"";r.push(`context platform=${i.device.platform} device="${e}"${t} theme=unknown`)}for(let e of t)r.push(function(e){let t=[e.command];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",iU(e.flags.snapshotScope)),e.flags?.snapshotRaw&&t.push("--raw"),e.flags?.snapshotBackend&&t.push("--backend",e.flags.snapshotBackend),t.join(" ");if("open"===e.command){for(let i of e.positionals??[])t.push(iU(i));return e.flags?.relaunch&&t.push("--relaunch"),t.join(" ")}for(let i of e.positionals??[])t.push(iU(i));return t.join(" ")}(e));let n=`${r.join("\n")}
|
|
19
|
+
`,a=`${e}.tmp-${process.pid}-${Date.now()}`;m.writeFileSync(a,n),m.renameSync(a,e)}(o,c,e)}return{ok:!0,data:{replayed:c.length,healed:p,session:i}}}catch(t){let e=u(t);return{ok:!1,error:{code:e.code,message:e.message}}}}if("close"===w){let e=n.get(i);return e?(t.positionals&&t.positionals.length>0&&await d(e.device,"close",t.positionals??[],t.flags?.out,{...tY(r,t.flags,e.appBundleId,e.trace?.outPath)}),"ios"===e.device.platform&&await tf(e.device.id),n.recordAction(e,{command:w,positionals:t.positionals??[],flags:t.flags??{},result:{session:i}}),t.flags?.saveScript&&(e.recordSession=!0),n.writeSessionLog(e),n.delete(i),{ok:!0,data:{session:i}}):{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session"}}}return null}function iP(e,t,i,r){var n;let a;if(e.ok)return e;let o=i+1,s=(a=((n=t).positionals??[]).map(e=>{let t=e.trim();return/^-?\d+(\.\d+)?$/.test(t)||t.startsWith("@")?t:JSON.stringify(t)}),[n.command,...a].join(" ")),l={...e.error.details??{},replayPath:r,step:o,action:t.command,positionals:t.positionals??[]};return{ok:!1,error:{code:e.error.code,message:`Replay failed at step ${o} (${s}): ${e.error.message}`,details:l}}}async function i$(e){let{action:t,sessionName:i,logPath:r,sessionStore:n,dispatch:a}=e;if(!["click","fill","get","is","wait"].includes(t.command))return null;let o=n.get(i);if(!o)return null;let s="click"===t.command||"fill"===t.command,l="click"===t.command||"fill"===t.command||"get"===t.command&&t.positionals?.[0]==="text",c=await iF(o,t,r,s,a,n);for(let e of function(e){let t=[],i=Array.isArray(e.result?.selectorChain)&&e.result?.selectorChain.every(e=>"string"==typeof e)?e.result.selectorChain:[];if(t.push(...i),"click"===e.command){let i=e.positionals?.[0]??"";i&&!i.startsWith("@")&&t.push(e.positionals.join(" "))}if("fill"===e.command){let i=e.positionals?.[0]??"";i&&!i.startsWith("@")&&Number.isNaN(Number(i))&&t.push(i)}if("get"===e.command){let i=e.positionals?.[1]??"";i&&!i.startsWith("@")&&t.push(e.positionals.slice(1).join(" "))}if("is"===e.command){let{split:i}=im(e.positionals);i&&t.push(i.selectorExpression)}if("wait"===e.command){let{selectorExpression:i}=iV(e.positionals??[]);i&&t.push(i)}let r="string"==typeof e.result?.refLabel?e.result.refLabel.trim():"";if(r.length>0){let i=JSON.stringify(r);"fill"===e.command?(t.push(`id=${i} editable=true`),t.push(`label=${i} editable=true`),t.push(`text=${i} editable=true`),t.push(`value=${i} editable=true`)):(t.push(`id=${i}`),t.push(`label=${i}`),t.push(`text=${i}`),t.push(`value=${i}`))}return tJ(t).filter(e=>e.trim().length>0)}(t)){let i=il(e);if(!i)continue;let r=ic(c.nodes,i,{platform:o.device.platform,requireRect:s,requireUnique:!0,disambiguateAmbiguous:l});if(!r)continue;let n=ig(r.node,o.device.platform,{action:"click"===t.command?"click":"fill"===t.command?"fill":"get"}).join(" || ");if("click"===t.command)return{...t,positionals:[n]};if("fill"===t.command){let e=tW(t);if(!e)continue;return{...t,positionals:[n,e]}}if("get"===t.command){let e=t.positionals?.[0];if("text"!==e&&"attrs"!==e)continue;return{...t,positionals:[e,n]}}if("is"===t.command){let{predicate:e,split:i}=im(t.positionals);if(!e)continue;let r=i?.rest.join(" ").trim()??"",a=[e,n];return"text"===e&&r.length>0&&a.push(r),{...t,positionals:a}}if("wait"===t.command){let{selectorTimeout:e}=iV(t.positionals??[]),i=[n];return e&&i.push(e),{...t,positionals:i}}}let d=function(e,t,i){if("get"!==e.command||e.positionals?.[0]!=="text")return null;let r=e.positionals?.[1];if(!r)return null;let n=il(r);if(!n)return null;let a=new Set,o=!1;for(let e of n.selectors)for(let t of e.terms)"role"===t.key&&"string"==typeof t.value&&a.add(ie(t.value)),("text"===t.key||"label"===t.key||"value"===t.key)&&"string"==typeof t.value&&/^\d+$/.test(t.value.trim())&&(o=!0);if(!o)return null;let s=t.nodes.filter(e=>{let t=ii(e).trim();return!!/^\d+$/.test(t)&&(0===a.size||a.has(ie(e.type??"")))});if(0===s.length||1!==tJ(s.map(e=>ii(e).trim())).length)return null;let l=s[0];if(!l)return null;let c=ig(l,i.device.platform,{action:"get"});return 0===c.length?null:{...e,positionals:["text",c.join(" || ")]}}(t,c,o);return d||null}async function iF(e,t,i,r,n,a){let o=await n(e.device,"snapshot",[],t.flags?.out,{...tY(i,{...t.flags??{},snapshotInteractiveOnly:r,snapshotCompact:r},e.appBundleId,e.trace?.outPath)}),s=o?.nodes??[],l={nodes:t2(t.flags?.snapshotRaw?s:t9(s)),truncated:o?.truncated,createdAt:Date.now(),backend:o?.backend};return e.snapshot=l,a.set(e.name,e),l}function iV(e){if(0===e.length)return{selectorExpression:null,selectorTimeout:null};let t=e[e.length-1],i=/^\d+$/.test(t??""),r=ip(i?e.slice(0,-1):e.slice());return!r||r.rest.length>0?{selectorExpression:null,selectorTimeout:null}:{selectorExpression:r.selectorExpression,selectorTimeout:i?t:null}}function iU(e){let t=e.trim();return t.startsWith("@")||/^-?\d+(\.\d+)?$/.test(t)?t:JSON.stringify(t)}function iG(e){if(!e)return null;let t=Number(e);return Number.isFinite(t)?t:null}async function iB(e){let{req:t,sessionName:i,logPath:r,sessionStore:n}=e,a=t.command;if("snapshot"===a){let{session:e,device:a}=await ij(n,i,t.flags);if(!tq("snapshot",a))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"snapshot is not supported on this device"}};if("ios"===a.platform&&"device"===a.kind&&t.flags?.snapshotBackend==="ax")return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"AX snapshot backend is not supported on iOS physical devices; use --backend xctest"}};let o=e?.appBundleId,s=t.flags?.snapshotScope;if(s&&s.trim().startsWith("@")){if(!e?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"Ref scope requires an existing snapshot in session."}};let t=t3(s.trim());if(!t)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref scope: ${s}`}};let i=t4(e.snapshot.nodes,t),r=i?t6(i,e.snapshot.nodes):void 0;if(!r)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${s} not found or has no label`}};s=r}return await iq(e,a,async()=>{let l=await tV(a,"snapshot",[],t.flags?.out,{...tY(r,{...t.flags,snapshotScope:s},o,e?.trace?.outPath)}),c=l?.nodes??[],d=t2(t.flags?.snapshotRaw?c:t9(c)),u={nodes:d,truncated:l?.truncated,createdAt:Date.now(),backend:l?.backend},p=e?{...e,snapshot:u}:{name:i,device:a,createdAt:Date.now(),appBundleId:o,snapshot:u,actions:[]};return iW(n,p,t,{nodes:d.length,truncated:l?.truncated??!1}),n.set(i,p),{ok:!0,data:{nodes:d,truncated:l?.truncated??!1,appName:p.appBundleId?p.appName??p.appBundleId:void 0,appBundleId:p.appBundleId}}})}if("wait"===a){let{session:e,device:a}=await ij(n,i,t.flags),o=function(e){if(0===e.length)return null;let t=iG(e[0]);if(null!==t)return{kind:"sleep",durationMs:t};if("text"===e[0]){let t=iG(e[e.length-1]);return{kind:"text",text:(null!==t?e.slice(1,-1).join(" "):e.slice(1).join(" ")).trim(),timeoutMs:t}}if(e[0].startsWith("@")){let t=iG(e[e.length-1]);return{kind:"ref",rawRef:e[0],timeoutMs:t}}let i=iG(e[e.length-1]),r=ip(null!==i?e.slice(0,-1):e.slice());if(r&&0===r.rest.length){let e=il(r.selectorExpression);if(e)return{kind:"selector",selector:e,selectorExpression:r.selectorExpression,timeoutMs:i}}return{kind:"text",text:(null!==i?e.slice(0,-1).join(" "):e.join(" ")).trim(),timeoutMs:i}}(t.positionals??[]);return o?"sleep"===o.kind?(await new Promise(e=>setTimeout(e,o.durationMs)),iW(n,e,t,{waitedMs:o.durationMs}),{ok:!0,data:{waitedMs:o.durationMs}}):tq("wait",a)?await iq(e,a,async()=>{let s,l;if("selector"===o.kind){let s=o.timeoutMs??1e4,l=Date.now();for(;Date.now()-l<s;){let s=await tV(a,"snapshot",[],t.flags?.out,{...tY(r,{...t.flags,snapshotInteractiveOnly:!1,snapshotCompact:!1},e?.appBundleId,e?.trace?.outPath)}),c=s?.nodes??[],d=t2(t.flags?.snapshotRaw?c:t9(c));e&&(e.snapshot={nodes:d,truncated:s?.truncated,createdAt:Date.now(),backend:s?.backend},n.set(i,e));let u=id(d,o.selector,{platform:a.platform});if(u)return iW(n,e,t,{selector:u.selector.raw,waitedMs:Date.now()-l}),{ok:!0,data:{selector:u.selector.raw,waitedMs:Date.now()-l}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:`wait timed out for selector: ${o.selectorExpression}`}}}if("ref"===o.kind){if(!e?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"Ref wait requires an existing snapshot in session."}};let t=t3(o.rawRef);if(!t)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref: ${o.rawRef}`}};let i=t4(e.snapshot.nodes,t),r=i?t6(i,e.snapshot.nodes):void 0;if(!r)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${o.rawRef} not found or has no label`}};s=r,l=o.timeoutMs}else s=o.text,l=o.timeoutMs;if(!s)return{ok:!1,error:{code:"INVALID_ARGS",message:"wait requires text"}};let c=l??1e4,d=Date.now();for(;Date.now()-d<c;){if("ios"===a.platform){let i=await tc(a,{command:"findText",text:s,appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:r,traceLogPath:e?.trace?.outPath});if(i?.found)return iW(n,e,t,{text:s,waitedMs:Date.now()-d}),{ok:!0,data:{text:s,waitedMs:Date.now()-d}}}else if("android"===a.platform&&t8(t2((await eI(a,{scope:s})).nodes??[]),s))return iW(n,e,t,{text:s,waitedMs:Date.now()-d}),{ok:!0,data:{text:s,waitedMs:Date.now()-d}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:`wait timed out for text: ${s}`}}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"wait is not supported on this device"}}:{ok:!1,error:{code:"INVALID_ARGS",message:"wait requires a duration or text"}}}if("alert"===a){let{session:e,device:a}=await ij(n,i,t.flags),o=(t.positionals?.[0]??"get").toLowerCase();return tq("alert",a)?await iq(e,a,async()=>{if("wait"===o){let i=iG(t.positionals?.[1])??1e4,o=Date.now();for(;Date.now()-o<i;){try{let i=await tc(a,{command:"alert",action:"get",appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:r,traceLogPath:e?.trace?.outPath});return iW(n,e,t,i),{ok:!0,data:i}}catch{}await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:"alert wait timed out"}}}let i=await tc(a,{command:"alert",action:"accept"===o||"dismiss"===o?o:"get",appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:r,traceLogPath:e?.trace?.outPath});return iW(n,e,t,i),{ok:!0,data:i}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"alert is only supported on iOS simulators"}}}if("settings"===a){let e=t.positionals?.[0],a=t.positionals?.[1];if(!e||!a)return{ok:!1,error:{code:"INVALID_ARGS",message:"settings requires <wifi|airplane|location> <on|off>"}};let{session:o,device:s}=await ij(n,i,t.flags);return tq("settings",s)?await iq(o,s,async()=>{let i=o?.appBundleId,l=await tV(s,"settings",[e,a,i??""],t.flags?.out,{...tY(r,t.flags,i,o?.trace?.outPath)});return iW(n,o,t,l??{setting:e,state:a}),{ok:!0,data:l??{setting:e,state:a}}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"settings is not supported on this device"}}}return null}async function ij(e,t,i){let r=e.get(t),n=r?.device??await tF(i??{});return r||await tZ(n),{session:r,device:n}}async function iq(e,t,i){let r=!e&&"ios"===t.platform;try{return await i()}finally{r&&await tf(t.id)}}function iW(e,t,i,r){t&&e.recordAction(t,{command:i.command,positionals:i.positionals??[],flags:i.flags??{},result:r})}function iJ(e,t,i,r={}){let n=iH(i);if(!n)return{matches:[],score:0};let a=0,o=[];for(let i of e){if(r.requireRect&&!i.rect)continue;let e=function(e,t,i){switch(t){case"role":return function(e,t){let i=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 i?i===t?2:+!!i.includes(t):0}(e.type,i);case"label":return iX(e.label,i);case"value":return iX(e.value,i);case"id":return iX(e.identifier,i);default:return Math.max(iX(e.label,i),iX(e.value,i),iX(e.identifier,i))}}(i,t,n);if(!(e<=0)){if(e>a){a=e,o.length=0,o.push(i);continue}e===a&&o.push(i)}}return{matches:o,score:a}}function iX(e,t){let i=iH(e??"");return i?i===t?2:+!!i.includes(t):0}function iH(e){return e.trim().toLowerCase().replace(/\s+/g," ")}async function iz(e){let{req:t,sessionName:i,logPath:r,sessionStore:n,invoke:a}=e,o=t.command;if("find"!==o)return null;let s=t.positionals??[];if(0===s.length)return{ok:!1,error:{code:"INVALID_ARGS",message:"find requires a locator or text"}};let{locator:l,query:c,action:d,value:u,timeoutMs:p}=function(e){let t="any",i=0;["text","label","value","role","id"].includes(e[0])&&(t=e[0],i=1);let r=e[i]??"",n=e.slice(i+1);if(0===n.length)return{locator:t,query:r,action:"click"};let a=n[0].toLowerCase();if("get"===a){let e=n[1]?.toLowerCase();if("text"===e)return{locator:t,query:r,action:"get_text"};if("attrs"===e)return{locator:t,query:r,action:"get_attrs"};throw new f("INVALID_ARGS","find get only supports text or attrs")}if("wait"===a)return{locator:t,query:r,action:"wait",timeoutMs:iG(n[1])??void 0};if("exists"===a)return{locator:t,query:r,action:"exists"};if("click"===a)return{locator:t,query:r,action:"click"};if("focus"===a)return{locator:t,query:r,action:"focus"};if("fill"===a)return{locator:t,query:r,action:"fill",value:n.slice(1).join(" ")};if("type"===a)return{locator:t,query:r,action:"type",value:n.slice(1).join(" ")};throw new f("INVALID_ARGS",`Unsupported find action: ${n[0]}`)}(s);if(!c)return{ok:!1,error:{code:"INVALID_ARGS",message:"find requires a value"}};let m=n.get(i);if(!m&&"exists"!==d&&"wait"!==d&&"get_text"!==d&&"get_attrs"!==d)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let h=m?.device??await tF(t.flags??{});m||await tZ(h);let w=m?.appBundleId,g="role"!==l?c:void 0,v="click"===d||"focus"===d||"fill"===d||"type"===d,I=0,A=null,y=async()=>{let e=Date.now();if(A&&e-I<750)return{nodes:A};let a=await tV(h,"snapshot",[],t.flags?.out,{...tY(r,{...t.flags,snapshotScope:g,snapshotInteractiveOnly:v,snapshotCompact:v},w,m?.trace?.outPath)}),o=a?.nodes??[],s=t2(t.flags?.snapshotRaw?o:t9(o));return I=e,A=s,m&&(m.snapshot={nodes:s,truncated:a?.truncated,createdAt:Date.now(),backend:a?.backend},n.set(i,m)),{nodes:s,truncated:a?.truncated,backend:a?.backend}};if("wait"===d){let e=p??1e4,i=Date.now();for(;Date.now()-i<e;){let{nodes:e}=await y();if(iJ(e,l,c,{requireRect:!1}).matches[0])return m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.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:N}=await y(),S=iJ(N,l,c,{requireRect:v});if(v&&S.matches.length>1){let e=S.matches.slice(0,8).map(e=>{let t=ii(e)||e.label||e.identifier||e.type||"";return`@${e.ref}${t?`(${t})`:""}`});return{ok:!1,error:{code:"AMBIGUOUS_MATCH",message:`find matched ${S.matches.length} elements for ${l} "${c}". Use a more specific locator or selector.`,details:{locator:l,query:c,matches:S.matches.length,candidates:e}}}}let D=S.matches[0]??null;if(!D)return{ok:!1,error:{code:"COMMAND_FAILED",message:"find did not match any element"}};let _="click"===d||"focus"===d||"fill"===d||"type"===d?function(e,t){if(t.hittable)return t;let i=t,r=new Set;for(;void 0!==i.parentIndex&&!r.has(i.ref);){r.add(i.ref);let t=e[i.parentIndex];if(!t)break;if(t.hittable)return t;i=t}return null}(N,D)??D:D,b=`@${_.ref}`,O={...t.flags??{},noRecord:!0};if("exists"===d)return m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{found:!0}}),{ok:!0,data:{found:!0}};if("get_text"===d){let e=ii(D);return m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:b,action:"get text",text:e}}),{ok:!0,data:{ref:b,text:e,node:D}}}if("get_attrs"===d)return m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:b,action:"get attrs"}}),{ok:!0,data:{ref:b,node:D}};if("click"===d){let e=await a({token:t.token,session:i,command:"click",positionals:[b],flags:O});return e.ok&&m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:b,action:"click"}}),e}if("fill"===d){if(!u)return{ok:!1,error:{code:"INVALID_ARGS",message:"find fill requires text"}};let e=await a({token:t.token,session:i,command:"fill",positionals:[b,u],flags:O});return e.ok&&m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:b,action:"fill"}}),e}if("focus"===d){let e=D.rect?t5(D.rect):null;if(!e)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};let i=await tV(h,"focus",[String(e.x),String(e.y)],t.flags?.out,{...tY(r,t.flags,m?.appBundleId,m?.trace?.outPath)});return m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:b,action:"focus"}}),{ok:!0,data:i??{ref:b}}}if("type"===d){if(!u)return{ok:!1,error:{code:"INVALID_ARGS",message:"find type requires text"}};let e=D.rect?t5(D.rect):null;if(!e)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};await tV(h,"focus",[String(e.x),String(e.y)],t.flags?.out,{...tY(r,t.flags,m?.appBundleId,m?.trace?.outPath)});let i=await tV(h,"type",[u],t.flags?.out,{...tY(r,t.flags,m?.appBundleId,m?.trace?.outPath)});return m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:b,action:"type"}}),{ok:!0,data:i??{ref:b}}}return null}async function iY(e){let{req:t,sessionName:i,sessionStore:n}=e,a=t.command;if("record"===a){let e=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"record requires start|stop"}};let o=n.get(i),l=o?.device??await tF(t.flags??{});o||await tZ(l);let d=o??{name:i,device:l,createdAt:Date.now(),actions:[]};if("start"===e){if(d.recording)return{ok:!1,error:{code:"INVALID_ARGS",message:"recording already in progress"}};let e=t.positionals?.[1]??`./recording-${Date.now()}.mp4`,o=r.resolve(e),c=r.dirname(o);if(m.existsSync(c)||m.mkdirSync(c,{recursive:!0}),!tq("record",l))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"record is only supported on iOS simulators"}};if("ios"===l.platform){let{child:e,wait:t}=s("xcrun",["simctl","io",l.id,"recordVideo",o],{allowFailure:!0});d.recording={platform:"ios",outPath:o,child:e,wait:t}}else{let e=`/sdcard/agent-device-recording-${Date.now()}.mp4`,{child:t,wait:i}=s("adb",["-s",l.id,"shell","screenrecord",e],{allowFailure:!0});d.recording={platform:"android",outPath:o,remotePath:e,child:t,wait:i}}return n.set(i,d),n.recordAction(d,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{action:"start"}}),{ok:!0,data:{recording:"started",outPath:e}}}if(!d.recording)return{ok:!1,error:{code:"INVALID_ARGS",message:"no active recording"}};let u=d.recording;u.child.kill("SIGINT");try{await u.wait}catch{}if("android"===u.platform&&u.remotePath)try{await c("adb",["-s",l.id,"pull",u.remotePath,u.outPath],{allowFailure:!0}),await c("adb",["-s",l.id,"shell","rm","-f",u.remotePath],{allowFailure:!0})}catch{}return d.recording=void 0,n.recordAction(d,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:u.outPath}}),{ok:!0,data:{recording:"stopped",outPath:u.outPath}}}if("trace"===a){let e=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"trace requires start|stop"}};let o=n.get(i);if(!o)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session"}};if("start"===e){if(o.trace)return{ok:!1,error:{code:"INVALID_ARGS",message:"trace already in progress"}};let e=t.positionals?.[1]??n.defaultTracePath(o),i=tH.expandHome(e);return m.mkdirSync(r.dirname(i),{recursive:!0}),m.appendFileSync(i,""),o.trace={outPath:i,startedAt:Date.now()},n.recordAction(o,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{action:"start",outPath:i}}),{ok:!0,data:{trace:"started",outPath:i}}}if(!o.trace)return{ok:!1,error:{code:"INVALID_ARGS",message:"no active trace"}};let s=o.trace.outPath;if(t.positionals?.[1]){let e=tH.expandHome(t.positionals[1]);m.mkdirSync(r.dirname(e),{recursive:!0}),m.existsSync(s)?m.renameSync(s,e):m.appendFileSync(e,""),s=e}return o.trace=void 0,n.recordAction(o,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:s}}),{ok:!0,data:{trace:"stopped",outPath:s}}}return null}async function iK(e){let{req:t,sessionName:i,sessionStore:r,contextFromFlags:n}=e,a=t.command;if("click"===a){let e=r.get(i);if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let o=t.positionals?.[0]??"";if(o.startsWith("@")){let i=i0("click",t.flags);if(i)return i;if(!e.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}};let s=t3(o);if(!s)return{ok:!1,error:{code:"INVALID_ARGS",message:"click requires a ref like @e2"}};let l=t4(e.snapshot.nodes,s);if(!l?.rect&&t.positionals.length>1){let i=t.positionals.slice(1).join(" ").trim();i.length>0&&(l=t8(e.snapshot.nodes,i))}if(!l?.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${o} not found or has no bounds`}};let c=t6(l,e.snapshot.nodes),d=ig(l,e.device.platform,{action:"click"}),{x:u,y:p}=t5(l.rect);return await tV(e.device,"press",[String(u),String(p)],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)}),r.recordAction(e,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{ref:s,x:u,y:p,refLabel:c,selectorChain:d}}),{ok:!0,data:{ref:s,x:u,y:p}}}let s=(t.positionals??[]).join(" ").trim();if(!s)return{ok:!1,error:{code:"INVALID_ARGS",message:"click requires @ref or selector expression"}};let l=is(s),c=await iZ(e,t.flags,r,n,{interactiveOnly:!0}),d=ic(c.nodes,l,{platform:e.device.platform,requireRect:!0,requireUnique:!0,disambiguateAmbiguous:!0});if(!d||!d.node.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:iu(l,d?.diagnostics??[],{unique:!0})}};let{x:u,y:p}=t5(d.node.rect);await tV(e.device,"press",[String(u),String(p)],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)});let f=ig(d.node,e.device.platform,{action:"click"}),m=t6(d.node,c.nodes);return r.recordAction(e,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{x:u,y:p,selector:d.selector.raw,selectorChain:f,refLabel:m}}),{ok:!0,data:{selector:d.selector.raw,x:u,y:p}}}if("fill"===a){let e=r.get(i);if(t.positionals?.[0]?.startsWith("@")){let i=i0("fill",t.flags);if(i)return i;if(!e?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}};let o=t3(t.positionals[0]);if(!o)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires a ref like @e2"}};let s=t.positionals.length>=3?t.positionals[1]:"",l=t.positionals.length>=3?t.positionals.slice(2).join(" "):t.positionals.slice(1).join(" ");if(!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after ref"}};let c=t4(e.snapshot.nodes,o);if(!c?.rect&&s&&(c=t8(e.snapshot.nodes,s)),!c?.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${t.positionals[0]} not found or has no bounds`}};let d=c.type??"",u=d&&!it(d,e.device.platform)?`fill target ${t.positionals[0]} resolved to "${d}", attempting fill anyway.`:void 0,p=t6(c,e.snapshot.nodes),f=ig(c,e.device.platform,{action:"fill"}),{x:m,y:h}=t5(c.rect),w={...await tV(e.device,"fill",[String(m),String(h),l],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)})??{ref:o,x:m,y:h}};return u&&(w.warning=u),r.recordAction(e,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{...w,refLabel:p,selectorChain:f}}),{ok:!0,data:w}}if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let o=ip(t.positionals??[],{preferTrailingValue:!0});if(o){if(0===o.rest.length)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after selector"}};let i=o.rest.join(" ").trim();if(!i)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after selector"}};let s=is(o.selectorExpression),l=await iZ(e,t.flags,r,n,{interactiveOnly:!0}),c=ic(l.nodes,s,{platform:e.device.platform,requireRect:!0,requireUnique:!0,disambiguateAmbiguous:!0});if(!c||!c.node.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:iu(s,c?.diagnostics??[],{unique:!0})}};let d=c.node,u=d.type??"",p=u&&!it(u,e.device.platform)?`fill target ${c.selector.raw} resolved to "${u}", attempting fill anyway.`:void 0,{x:f,y:m}=t5(c.node.rect),h=await tV(e.device,"fill",[String(f),String(m),i],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)}),w=ig(d,e.device.platform,{action:"fill"}),g={...h??{x:f,y:m,text:i},selector:c.selector.raw,selectorChain:w,refLabel:t6(d,l.nodes)};return p&&(g.warning=p),r.recordAction(e,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:g}),{ok:!0,data:g}}return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires x y text, @ref text, or selector text"}}}if("get"===a){let e=t.positionals?.[0];if("text"!==e&&"attrs"!==e)return{ok:!1,error:{code:"INVALID_ARGS",message:"get only supports text or attrs"}};let o=r.get(i);if(!o)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let s=t.positionals?.[1]??"";if(s.startsWith("@")){let i=i0("get",t.flags);if(i)return i;if(!o.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}};let n=t3(s??"");if(!n)return{ok:!1,error:{code:"INVALID_ARGS",message:"get text requires a ref like @e2"}};let l=t4(o.snapshot.nodes,n);if(!l&&t.positionals.length>2){let e=t.positionals.slice(2).join(" ").trim();e.length>0&&(l=t8(o.snapshot.nodes,e))}if(!l)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${s} not found`}};let c=ig(l,o.device.platform,{action:"get"});if("attrs"===e)return r.recordAction(o,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{ref:n,selectorChain:c}}),{ok:!0,data:{ref:n,node:l}};let d=ii(l);return r.recordAction(o,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{ref:n,text:d,refLabel:d||void 0,selectorChain:c}}),{ok:!0,data:{ref:n,text:d,node:l}}}let l=t.positionals.slice(1).join(" ").trim();if(!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"get requires @ref or selector expression"}};let c=is(l),d=ic((await iZ(o,t.flags,r,n,{interactiveOnly:!1})).nodes,c,{platform:o.device.platform,requireRect:!1,requireUnique:!0,disambiguateAmbiguous:"text"===e});if(!d)return{ok:!1,error:{code:"COMMAND_FAILED",message:iu(c,[],{unique:!0})}};let u=d.node,p=ig(u,o.device.platform,{action:"get"});if("attrs"===e)return r.recordAction(o,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{selector:d.selector.raw,selectorChain:p}}),{ok:!0,data:{selector:d.selector.raw,node:u}};let f=ii(u);return r.recordAction(o,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{text:f,refLabel:f||void 0,selector:d.selector.raw,selectorChain:p}}),{ok:!0,data:{selector:d.selector.raw,text:f,node:u}}}if("is"===a){let e=(t.positionals?.[0]??"").toLowerCase();if(!["visible","hidden","exists","editable","selected","text"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"is requires predicate: visible|hidden|exists|editable|selected|text"}};let o=r.get(i);if(!o)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};if(!tq("is",o.device))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"is is not supported on this device"}};let{split:s}=im(t.positionals);if(!s)return{ok:!1,error:{code:"INVALID_ARGS",message:"is requires a selector expression"}};let l=s.rest.join(" ").trim();if("text"===e&&!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"is text requires expected text value"}};if("text"!==e&&s.rest.length>0)return{ok:!1,error:{code:"INVALID_ARGS",message:`is ${e} does not accept trailing values`}};let c=is(s.selectorExpression),d=await iZ(o,t.flags,r,n,{interactiveOnly:!1});if("exists"===e){let i=id(d.nodes,c,{platform:o.device.platform});return i?(r.recordAction(o,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{predicate:e,selector:i.selector.raw,selectorChain:c.selectors.map(e=>e.raw),pass:!0,matches:i.matches}}),{ok:!0,data:{predicate:e,pass:!0,selector:i.selector.raw,matches:i.matches}}):{ok:!1,error:{code:"COMMAND_FAILED",message:iu(c,[],{unique:!1})}}}let u=ic(d.nodes,c,{platform:o.device.platform,requireUnique:!0});if(!u)return{ok:!1,error:{code:"COMMAND_FAILED",message:iu(c,[],{unique:!0})}};let p=function(e){let{predicate:t,node:i,expectedText:r,platform:n}=e,a=ii(i),o=!1;switch(t){case"visible":o=ih(i);break;case"hidden":o=!ih(i);break;case"editable":o=iw(i,n);break;case"selected":o=!0===i.selected;break;case"text":o=a===(r??"")}let s="text"===t?`expected="${r??""}" actual="${a}"`:`actual=${JSON.stringify({visible:ih(i),editable:iw(i,n),selected:!0===i.selected})}`;return{pass:o,actualText:a,details:s}}({predicate:e,node:u.node,expectedText:l,platform:o.device.platform});return p.pass?(r.recordAction(o,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{predicate:e,selector:u.selector.raw,selectorChain:c.selectors.map(e=>e.raw),pass:!0,text:"text"===e?p.actualText:void 0}}),{ok:!0,data:{predicate:e,pass:!0,selector:u.selector.raw}}):{ok:!1,error:{code:"COMMAND_FAILED",message:`is ${e} failed for selector ${u.selector.raw}: ${p.details}`}}}return null}async function iZ(e,t,i,r,n){let a=await tV(e.device,"snapshot",[],t?.out,{...r({...t??{},snapshotInteractiveOnly:n.interactiveOnly,snapshotCompact:n.interactiveOnly},e.appBundleId,e.trace?.outPath)}),o=a?.nodes??[];return e.snapshot={nodes:t2(t?.snapshotRaw?o:t9(o)),truncated:a?.truncated,createdAt:Date.now(),backend:a?.backend},i.set(e.name,e),e.snapshot}let iQ=[["snapshotDepth","--depth"],["snapshotScope","--scope"],["snapshotRaw","--raw"],["snapshotBackend","--backend"]];function i0(e,t){let i=function(e){if(!e)return[];let t=[];for(let[i,r]of iQ)void 0!==e[i]&&t.push(r);return t}(t);return 0===i.length?null:{ok:!1,error:{code:"INVALID_ARGS",message:`${e} @ref does not support ${i.join(", ")}.`}}}let i1=r.join(h.homedir(),".agent-device"),i2=r.join(i1,"daemon.json"),i3=r.join(i1,"daemon.lock"),i4=r.join(i1,"daemon.log"),i5=new tH(r.join(i1,"sessions")),i8=l(),i6=e.randomBytes(24).toString("hex"),i7=new Set(["session_list","devices"]),i9=w(process.pid)??void 0;function re(e,t,i){return tY(i4,e,t,i)}async function rt(e){if(e.token!==i6)return{ok:!1,error:{code:"UNAUTHORIZED",message:"Invalid token"}};let t=e.command,i=function(e,t){var i;let r,n=e.session||"default";if(i=e,"string"==typeof(r=i.flags?.session)&&r.trim().length>0||"default"!==n||t.has(n))return n;let a=t.toArray();return 1===a.length?a[0].name:n}(e,i5),r=i5.get(i);r&&!i7.has(t)&&function(e,t){if(!t)return;let i=[],r=e.device;if(t.platform&&t.platform!==r.platform&&i.push(`--platform=${t.platform}`),t.udid&&("ios"!==r.platform||t.udid!==r.id)&&i.push(`--udid=${t.udid}`),t.serial&&("android"!==r.platform||t.serial!==r.id)&&i.push(`--serial=${t.serial}`),0!==i.length){var n;let t,r,a;throw new f("INVALID_ARGS",`Session "${e.name}" is bound to ${(t=(n=e).device.platform,r=n.device.name.trim(),a=n.device.id,`${t} device "${r}" (${a})`)} and cannot be used with ${i.join(", ")}. Use a different --session name or close this session first.`)}}(r,e.flags);let n=await iT({req:e,sessionName:i,logPath:i4,sessionStore:i5,invoke:rt});if(n)return n;let a=await iB({req:e,sessionName:i,logPath:i4,sessionStore:i5});if(a)return a;let o=await iY({req:e,sessionName:i,sessionStore:i5});if(o)return o;let s=await iz({req:e,sessionName:i,logPath:i4,sessionStore:i5,invoke:rt});if(s)return s;let l=await iK({req:e,sessionName:i,sessionStore:i5,contextFromFlags:re});if(l)return l;let c=i5.get(i);if(!c)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};if(!tq(t,c.device))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:`${t} is not supported on this device`}};let d=await tV(c.device,t,e.positionals??[],e.flags?.out,{...re(e.flags,c.appBundleId,c.trace?.outPath)});return i5.recordAction(c,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:d??{}}),{ok:!0,data:d??{}}}function ri(){if(!m.existsSync(i3))return null;try{let e=JSON.parse(m.readFileSync(i3,"utf8"));if(!Number.isInteger(e.pid)||e.pid<=0)return null;return e}catch{return null}}!function(){if(!function(){m.existsSync(i1)||m.mkdirSync(i1,{recursive:!0});let e=JSON.stringify({pid:process.pid,version:i8,startedAt:Date.now(),processStartTime:i9},null,2),t=()=>{try{return m.writeFileSync(i3,e,{flag:"wx",mode:384}),!0}catch(e){if("EEXIST"===e.code)return!1;throw e}};if(t())return!0;let i=ri();if(i?.pid&&i.pid!==process.pid&&o(i.pid,i.processStartTime))return!1;try{m.unlinkSync(i3)}catch{}return t()}()){process.stderr.write("Daemon lock is held by another process; exiting.\n"),process.exit(0);return}let e=g.createServer(e=>{let t="";e.setEncoding("utf8"),e.on("data",async i=>{let r=(t+=i).indexOf("\n");for(;-1!==r;){let i,n=t.slice(0,r).trim();if(t=t.slice(r+1),0===n.length){r=t.indexOf("\n");continue}try{let e=JSON.parse(n);i=await rt(e)}catch(t){let e=u(t);i={ok:!1,error:{code:e.code,message:e.message,details:e.details}}}e.write(`${JSON.stringify(i)}
|
|
20
|
+
`),r=t.indexOf("\n")}})});e.listen(0,"127.0.0.1",()=>{let t=e.address();if("object"==typeof t&&t?.port){var i;i=t.port,m.existsSync(i1)||m.mkdirSync(i1,{recursive:!0}),m.writeFileSync(i4,""),m.writeFileSync(i2,JSON.stringify({port:i,token:i6,pid:process.pid,version:i8,processStartTime:i9},null,2),{mode:384}),process.stdout.write(`AGENT_DEVICE_DAEMON_PORT=${t.port}
|
|
21
|
+
`)}});let t=!1,i=async()=>{await new Promise(t=>{try{e.close(()=>t())}catch{t()}})},r=async()=>{if(t)return;for(let e of(t=!0,await i(),i5.toArray()))i5.writeSessionLog(e);await tm(),m.existsSync(i2)&&m.unlinkSync(i2);let e=ri();if(!e||e.pid===process.pid)try{m.existsSync(i3)&&m.unlinkSync(i3)}catch{}process.exit(0)};process.on("SIGINT",()=>{r()}),process.on("SIGTERM",()=>{r()}),process.on("SIGHUP",()=>{r()}),process.on("uncaughtException",e=>{let t=e instanceof f?e:u(e);process.stderr.write(`Daemon error: ${t.message}
|
|
18
22
|
`),r()})}();
|