agent-device 0.5.0 → 0.5.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 +62 -6
- package/dist/src/50.js +1 -0
- package/dist/src/bin.js +33 -32
- package/dist/src/daemon.js +18 -17
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.pbxproj +2 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/AgentDeviceRunnerUITests-Bridging-Header.h +1 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerObjCExceptionCatcher.h +11 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerObjCExceptionCatcher.m +16 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +255 -15
- package/package.json +1 -1
- package/skills/agent-device/SKILL.md +54 -4
- package/skills/agent-device/references/batching.md +79 -0
- package/skills/agent-device/references/permissions.md +4 -0
- package/skills/agent-device/references/snapshot-refs.md +3 -2
- package/dist/src/797.js +0 -1
package/dist/src/daemon.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import e from"node:crypto";import{isCancel as t,select as i}from"@clack/prompts";import{node_path as
|
|
2
|
-
`)}})}catch(
|
|
3
|
-
${
|
|
4
|
-
${
|
|
5
|
-
${t}`,
|
|
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}
|
|
7
|
-
${t.stderr}`.toLowerCase(),s=o.includes("already booted")||o.includes("current state: booted");if(0!==t.exitCode&&!s)throw new
|
|
8
|
-
`)}})}catch(a){let
|
|
9
|
-
${
|
|
10
|
-
${
|
|
1
|
+
import e from"node:crypto";import{isCancel as t,select as i}from"@clack/prompts";import{node_path as n,runCmdStreaming as r,fileURLToPath as a,validateAndNormalizeBatchSteps as o,isAgentDeviceDaemonProcess as s,runCmdBackground as l,readVersion as c,runCmd as d,promises as u,asAppError as p,DEFAULT_BATCH_MAX_STEPS as f,isProcessAlive as m,AppError as h,node_fs as w,node_os as g,readProcessStartTime as v,node_net as I,whichCmd as A}from"./50.js";async function y(e,n){let r=e,a=e=>e.toLowerCase().replace(/_/g," ").replace(/\s+/g," ").trim();if(n.platform&&(r=r.filter(e=>e.platform===n.platform)),n.udid){let e=r.find(e=>e.id===n.udid&&"ios"===e.platform);if(!e)throw new h("DEVICE_NOT_FOUND",`No iOS device with UDID ${n.udid}`);return e}if(n.serial){let e=r.find(e=>e.id===n.serial&&"android"===e.platform);if(!e)throw new h("DEVICE_NOT_FOUND",`No Android device with serial ${n.serial}`);return e}if(n.deviceName){let e=a(n.deviceName),t=r.find(t=>a(t.name)===e);if(!t)throw new h("DEVICE_NOT_FOUND",`No device named ${n.deviceName}`);return t}if(1===r.length)return r[0];if(0===r.length)throw new h("DEVICE_NOT_FOUND","No devices found",{selector:n});let o=r.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:r).map(e=>({label:`${e.name} (${e.platform}${e.kind?`, ${e.kind}`:""}${e.booted?", booted":""})`,value:e.id}))});if(t(e))throw new h("INVALID_ARGS","Device selection cancelled");if(e){let t=r.find(t=>t.id===e);if(t)return t}}return o[0]??r[0]}function N(e){return["1","true","yes","on"].includes((e??"").toLowerCase())}let S=2e4,b=12e4,D=1e4;class _{startedAtMs;expiresAtMs;constructor(e,t){this.startedAtMs=e,this.expiresAtMs=e+Math.max(0,t)}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)}}async function O(e,t={},i={}){let n,r={maxAttempts:t.maxAttempts??3,baseDelayMs:t.baseDelayMs??200,maxDelayMs:t.maxDelayMs??2e3,jitter:t.jitter??.2,shouldRetry:t.shouldRetry};for(let t=1;t<=r.maxAttempts&&(!i.deadline?.isExpired()||!(t>1));t+=1)try{let n=await e({attempt:t,maxAttempts:r.maxAttempts,deadline:i.deadline});return i.onEvent?.({phase:i.phase,event:"succeeded",attempt:t,maxAttempts:r.maxAttempts,elapsedMs:i.deadline?.elapsedMs(),remainingMs:i.deadline?.remainingMs()}),n}catch(s){n=s;let e=i.classifyReason?.(s);if(i.onEvent?.({phase:i.phase,event:"attempt_failed",attempt:t,maxAttempts:r.maxAttempts,elapsedMs:i.deadline?.elapsedMs(),remainingMs:i.deadline?.remainingMs(),reason:e}),t>=r.maxAttempts||r.shouldRetry&&!r.shouldRetry(s,t))break;let a=function(e,t,i,n){let r=Math.min(t,e*2**(n-1));return Math.max(0,r+r*i*(2*Math.random()-1))}(r.baseDelayMs,r.maxDelayMs,r.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:r.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:r.maxAttempts,maxAttempts:r.maxAttempts,elapsedMs:i.deadline?.elapsedMs(),remainingMs:i.deadline?.remainingMs(),reason:i.classifyReason?.(n)}),n)throw n;throw new h("COMMAND_FAILED","retry failed")}async function M(e,t={}){return O(()=>e(),{maxAttempts:t.attempts,baseDelayMs:t.baseDelayMs,maxDelayMs:t.maxDelayMs,jitter:t.jitter,shouldRetry:t.shouldRetry})}function E(e){let t=e.error?p(e.error):null,i=e.context?.platform,n=e.context?.phase;if(t?.code==="TOOL_MISSING")return"android"===i?"ADB_TRANSPORT_UNAVAILABLE":"IOS_TOOL_MISSING";let r=t?.details??{},a="string"==typeof r.message?r.message:void 0,o="string"==typeof r.stdout?r.stdout:void 0,s="string"==typeof r.stderr?r.stderr:void 0,l=r.boot&&"object"==typeof r.boot?r.boot:null,c=r.bootstatus&&"object"==typeof r.bootstatus?r.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"===n&&(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"===n&&(d.includes("timed out")||d.includes("timeout"))?"IOS_BOOT_TIMEOUT":"android"===i&&"boot"===n&&(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=N(process.env.AGENT_DEVICE_RETRY_LOGS);function L(e){return e.startsWith("emulator-")}async function R(e,t=D){return d("adb",["-s",e,"shell","getprop","sys.boot_completed"],{allowFailure:!0,timeoutMs:t})}async function C(e,t){let i=t.replace(/_/g," ").trim();if(!L(e))return i||e;let n=await d("adb",["-s",e,"emu","avd","name"],{allowFailure:!0,timeoutMs:D}),r=n.stdout.trim();return 0===n.exitCode&&r?r.replace(/_/g," "):i||e}async function T(){if(!await A("adb"))throw new h("TOOL_MISSING","adb not found in PATH");let e=(await d("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,n]=await Promise.all([C(e,t),P(e)]);return{platform:"android",id:e,name:i,kind:L(e)?"emulator":"device",booted:n}}))}async function P(e){try{let t=await R(e);return"1"===t.stdout.trim()}catch{return!1}}async function $(e,t=6e4){let i,n=_.fromTimeoutMs(t),r=Math.max(1,Math.ceil(t/1e3)),a=!1;try{await O(async({deadline:r})=>{if(r?.isExpired())throw a=!0,new h("COMMAND_FAILED","Android boot deadline exceeded",{serial:e,timeoutMs:t,elapsedMs:n.elapsedMs(),message:"timeout"});let o=Math.max(1e3,r?.remainingMs()??t),s=await R(e,Math.min(o,D));if(i=s,"1"!==s.stdout.trim())throw new h("COMMAND_FAILED","Android device is still booting",{serial:e,stdout:s.stdout,stderr:s.stderr,exitCode:s.exitCode})},{maxAttempts:r,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:n,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(u){let r=p(u),o=i?.stdout,s=i?.stderr,l=i?.exitCode,c=E({error:u,stdout:o,stderr:s,context:{platform:"android",phase:"boot"}});"BOOT_COMMAND_FAILED"===c&&"Android device is still booting"===r.message&&(c="ANDROID_BOOT_TIMEOUT");let d={serial:e,timeoutMs:t,elapsedMs:n.elapsedMs(),reason:c,hint:x(c),stdout:o,stderr:s,exitCode:l};if(a||"ANDROID_BOOT_TIMEOUT"===c)throw new h("COMMAND_FAILED","Android device did not finish booting in time",d);if("TOOL_MISSING"===r.code)throw new h("TOOL_MISSING",r.message,{...d,...r.details??{}});if("ADB_TRANSPORT_UNAVAILABLE"===c)throw new h("COMMAND_FAILED",r.message,{...d,...r.details??{}});throw new h(r.code,r.message,{...d,...r.details??{}},r.cause)}}function F(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 n=i[1]?.toLowerCase(),r=i[2]??"";return"http"!==n&&"https"!==n&&"ws"!==n&&"wss"!==n&&"ftp"!==n&&"ftps"!==n||r.startsWith("//")}function V(e,t){let i,n=e?.trim();return n?n:"http"===(i=t.trim().split(":")[0]?.toLowerCase())||"https"===i?"com.apple.mobilesafari":void 0}function U(e){let t=G(e),i=e=>{let i=j(t,e);if(null!==i)return"true"===i};return{text:j(t,"text"),desc:j(t,"content-desc"),resourceId:j(t,"resource-id"),className:j(t,"class"),bounds:j(t,"bounds"),clickable:i("clickable"),enabled:i("enabled"),focusable:i("focusable"),focused:i("focused")}}function G(e){let t=new Map,i=e.indexOf(" "),n=e.lastIndexOf(">");if(i<0||n<=i)return t;let r=/([^\s=/>]+)\s*=\s*(["'])([\s\S]*?)\2/y,a=i;for(;a<n;){for(;a<n;){let t=e[a];if(" "!==t&&"\n"!==t&&"\r"!==t&&" "!==t)break;a+=1}if(a>=n)break;let i=e[a];if("/"===i||">"===i)break;r.lastIndex=a;let o=r.exec(e);if(!o)break;t.set(o[1],o[3]),a=r.lastIndex}return t}function j(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]),n=Number(t[2]);return{x:i,y:n,width:Math.max(0,Number(t[3])-i),height:Math.max(0,Number(t[4])-n)}}function q(e){return e?e.toLowerCase():""}function W(e){let t=e.trim();return!!t&&/^[\w.]+:id\/[\w.-]+$/i.test(t)}let J={settings:{type:"intent",value:"android.settings.SETTINGS"}};function H(e,t){return["-s",e.id,...t]}async function z(e,t){let i=t.trim();if(i.includes("."))return{type:"package",value:i};let n=J[i.toLowerCase()];if(n)return n;let r=(await d("adb",H(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===r.length)return{type:"package",value:r[0]};if(r.length>1)throw new h("INVALID_ARGS",`Multiple packages matched "${t}"`,{matches:r});throw new h("APP_NOT_INSTALLED",`No package found matching "${t}"`)}async function X(e,t="all"){let i=await Y(e);return("user-installed"===t?(await K(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),n=i[i.length-1]??e;for(let e=i.length-1;e>=0;e-=1){let r=i[e];if(!t.has(r)){n=r;break}}return n.split(/[^a-z0-9]+/i).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}(e)}))}async function Y(e){let t=await d("adb",H(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 n=t.split(/\s+/)[0],r=n.includes("/")?n.split("/")[0]:n;r&&i.add(r)}return i}async function K(e){return(await d("adb",H(e,["shell","pm","list","packages","-3"]))).stdout.split("\n").map(e=>e.replace("package:","").trim()).filter(Boolean)}async function Z(e){let t=await Q(e,[["shell","dumpsys","window","windows"],["shell","dumpsys","window"]]);if(t)return t;let i=await Q(e,[["shell","dumpsys","activity","activities"],["shell","dumpsys","activity"]]);return i||{}}async function Q(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 d("adb",H(e,i),{allowFailure:!0})).stdout??"");if(t)return t}return null}async function ee(e,t,i){e.booted||await $(e.id);let n=t.trim();if(F(n)){if(i)throw new h("INVALID_ARGS","Activity override is not supported when opening a deep link URL");await d("adb",H(e,["shell","am","start","-W","-a","android.intent.action.VIEW","-d",n]));return}let r=await z(e,t);if("intent"===r.type){if(i)throw new h("INVALID_ARGS","Activity override requires a package name, not an intent");await d("adb",H(e,["shell","am","start","-W","-a",r.value]));return}if(i){let t=i.includes("/")?i:`${r.value}/${i.startsWith(".")?i:`.${i}`}`;await d("adb",H(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 d("adb",H(e,["shell","am","start","-W","-a","android.intent.action.MAIN","-c","android.intent.category.DEFAULT","-c","android.intent.category.LAUNCHER","-p",r.value]));return}catch(i){let t=await et(e,r.value);if(!t)throw i;await d("adb",H(e,["shell","am","start","-W","-a","android.intent.action.MAIN","-c","android.intent.category.DEFAULT","-c","android.intent.category.LAUNCHER","-n",t]))}}async function et(e,t){let i=await d("adb",H(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 ei(e){e.booted||await $(e.id)}async function en(e,t){if("settings"===t.trim().toLowerCase())return void await d("adb",H(e,["shell","am","force-stop","com.android.settings"]));let i=await z(e,t);if("intent"===i.type)throw new h("INVALID_ARGS","Close requires a package name, not an intent");await d("adb",H(e,["shell","am","force-stop",i.value]))}async function er(e,t){let i=await z(e,t);if("intent"===i.type)throw new h("INVALID_ARGS","reinstall requires a package name, not an intent");let n=await d("adb",H(e,["uninstall",i.value]),{allowFailure:!0});if(0!==n.exitCode){let e=`${n.stdout}
|
|
3
|
+
${n.stderr}`.toLowerCase();if(!e.includes("unknown package")&&!e.includes("not installed"))throw new h("COMMAND_FAILED",`adb uninstall failed for ${i.value}`,{stdout:n.stdout,stderr:n.stderr,exitCode:n.exitCode})}return{package:i.value}}async function ea(e,t){await d("adb",H(e,["install",t]))}async function eo(e,t,i){e.booted||await $(e.id);let{package:n}=await er(e,t);return await ea(e,i),{package:n}}async function es(e,t,i){await d("adb",H(e,["shell","input","tap",String(t),String(i)]))}async function el(e,t,i,n,r,a=250){await d("adb",H(e,["shell","input","swipe",String(t),String(i),String(n),String(r),String(a)]))}async function ec(e){await d("adb",H(e,["shell","input","keyevent","4"]))}async function ed(e){await d("adb",H(e,["shell","input","keyevent","3"]))}async function eu(e){await d("adb",H(e,["shell","input","keyevent","187"]))}async function ep(e,t,i,n=800){await d("adb",H(e,["shell","input","swipe",String(t),String(i),String(t),String(i),String(n)]))}async function ef(e,t){let i=t.replace(/ /g,"%s");await d("adb",H(e,["shell","input","text",i]))}async function em(e,t,i){await es(e,t,i)}async function eh(e,t,i,n){await em(e,t,i);let r=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=n.length+s.clearPadding,o=s.minClear,Math.max(o,Math.min(s.maxClear,a)));if(await eM(e,l),await eO(e,n,s.chunkSize,s.delayMs),(r=await eE(e,t,i))===n)return}throw new h("COMMAND_FAILED","Android fill verification failed",{expected:n,actual:r??null})}async function ew(e,t,i=.6){let{width:n,height:r}=await eN(e),a=Math.floor(n*i),o=Math.floor(r*i),s=Math.floor(n/2),l=Math.floor(r/2),c=s,u=l,p=s,f=l;switch(t){case"up":u=l-Math.floor(o/2),f=l+Math.floor(o/2);break;case"down":u=l+Math.floor(o/2),f=l-Math.floor(o/2);break;case"left":c=s-Math.floor(a/2),p=s+Math.floor(a/2);break;case"right":c=s+Math.floor(a/2),p=s-Math.floor(a/2);break;default:throw new h("INVALID_ARGS",`Unknown direction: ${t}`)}await d("adb",H(e,["shell","input","swipe",String(c),String(u),String(p),String(f),"300"]))}async function eg(e,t){for(let i=0;i<8;i+=1){let i="";try{i=await eS(e)}catch(t){let e=t instanceof Error?t.message:String(t);throw new h("UNSUPPORTED_OPERATION",`uiautomator dump failed: ${e}`)}if(function(e,t){let i=t.toLowerCase(),n=/<node[^>]+>/g,r=n.exec(e);for(;r;){let t=G(r[0]),a=(j(t,"text")??"").toLowerCase(),o=(j(t,"content-desc")??"").toLowerCase();if(a.includes(i)||o.includes(i)){let e=B(j(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}}r=n.exec(e)}return null}(i,t))return;await ew(e,"down",.5)}throw new h("COMMAND_FAILED",`Could not find element containing "${t}" after scrolling`)}async function ev(e,t){let i=await d("adb",H(e,["exec-out","screencap","-p"]),{binaryStdout:!0});if(!i.stdoutBuffer)throw new h("COMMAND_FAILED","Failed to capture screenshot");await u.writeFile(t,i.stdoutBuffer)}async function eI(e,t,i){let n=t.toLowerCase(),r=function(e){let t=e.toLowerCase();if("on"===t||"true"===t||"1"===t)return!0;if("off"===t||"false"===t||"0"===t)return!1;throw new h("INVALID_ARGS",`Invalid setting state: ${e}`)}(i);switch(n){case"wifi":return void await d("adb",H(e,["shell","svc","wifi",r?"enable":"disable"]));case"airplane":await d("adb",H(e,["shell","settings","put","global","airplane_mode_on",r?"1":"0"])),await d("adb",H(e,["shell","am","broadcast","-a","android.intent.action.AIRPLANE_MODE","--ez","state",r?"true":"false"]));return;case"location":return void await d("adb",H(e,["shell","settings","put","secure","location_mode",r?"3":"0"]));default:throw new h("INVALID_ARGS",`Unsupported setting: ${t}`)}}async function eA(e,t={}){return function(e,t,i){let n=function(e){let t={type:null,label:null,value:null,identifier:null,depth:-1,children:[]},i=[t],n=/<node\b[^>]*>|<\/node>/g,r=n.exec(e);for(;r;){let t=r[0];if(t.startsWith("</node")){i.length>1&&i.pop(),r=n.exec(e);continue}let a=U(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),r=n.exec(e)}return t}(e),r=[],a=!1,o=i.depth??1/0,s=i.scope?function(e,t){let i=t.toLowerCase(),n=[...e.children];for(;n.length>0;){let e=n.shift(),t=e.label?.toLowerCase()??"",r=e.value?.toLowerCase()??"",a=e.identifier?.toLowerCase()??"";if(t.includes(i)||r.includes(i)||a.includes(i))return e;n.push(...e.children)}return null}(n,i.scope):null,l=s?[s]:n.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,n,s=!1,l=!1)=>{var c,p,f,m,h,w;let g,v,I,A,y,N,S,b;if(r.length>=800){a=!0;return}if(t>o)return;let D=!!i.raw||(c=e,p=i,f=s,m=d(e),h=l,v=q(c.type),I=!!(c.label&&c.label.trim().length>0),A=!!(c.identifier&&c.identifier.trim().length>0),y=I&&!W(c.label??""),N=A&&!W(c.identifier??""),S=(g=(w=v).split(".").pop()??w).includes("layout")||"viewgroup"===g||"view"===g,b="imageview"===v||"imagebutton"===v,p.interactiveOnly?!!c.hittable||!!(y||N)&&!b&&(!S||!!h)&&(f||m||h):p.compact?y||N||!!c.hittable:!S&&!b||!!c.hittable||!!y||!!N&&!!m||m),_=n;D&&(_=r.length,r.push({index:_,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:n}));let O=s||!!e.hittable,M=l||function(e){if(!e)return!1;let t=q(e);return t.includes("recyclerview")||t.includes("listview")||t.includes("gridview")}(e.type);for(let i of e.children)if(u(i,t+1,_,O,M),a)return};for(let e of l)if(u(e,0,void 0,!1,!1),a)break;return a?{nodes:r,truncated:a}:{nodes:r}}(await eS(e),0,t)}async function ey(){if(!await A("adb"))throw new h("TOOL_MISSING","adb not found in PATH")}async function eN(e){let t=(await d("adb",H(e,["shell","wm","size"]))).stdout.match(/Physical size:\s*(\d+)x(\d+)/);if(!t)throw new h("COMMAND_FAILED","Unable to read screen size");return{width:Number(t[1]),height:Number(t[2])}}async function eS(e){return M(()=>eb(e),{shouldRetry:e_})}async function eb(e){var t,i,n;let r,a,o=await d("adb",H(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 d("adb",H(e,["shell","uiautomator","dump",s])),c=(t=s,i=l.stdout,n=l.stderr,r=`${i}
|
|
4
|
+
${n}`,a=/dumped to:\s*(\S+)/i.exec(r),a?.[1]??t),u=await d("adb",H(e,["shell","cat",c])),p=eD(u.stdout,u.stderr);if(!p)throw new h("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}`,n=i.indexOf("<?xml"),r=n>=0?n:i.indexOf("<hierarchy");if(r<0)return null;let a=i.lastIndexOf("</hierarchy>");if(a<0||a<r)return null;let o=i.slice(r,a+12).trim();return o.length>0?o:null}function e_(e){if(!(e instanceof h)||"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 eO(e,t,i,n){let r=Math.max(1,Math.floor(i));for(let i=0;i<t.length;i+=r){let a=t.slice(i,i+r);await ef(e,a),n>0&&i+r<t.length&&await ek(n)}}async function eM(e,t){let i=Math.max(0,t);await d("adb",H(e,["shell","input","keyevent","KEYCODE_MOVE_END"]),{allowFailure:!0});for(let t=0;t<i;t+=24){let n=Math.min(24,i-t);await d("adb",H(e,["shell","input","keyevent",...Array(n).fill("KEYCODE_DEL")]),{allowFailure:!0})}}async function eE(e,t,i){let n,r=await eS(e),a=/<node\b[^>]*>/g,o=null,s=null,l=null;for(;null!==(n=a.exec(r));){let e=U(n[0]),r=B(e.bounds);if(!r)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,r.width*r.height),p=t>=r.x&&t<=r.x+r.width&&i>=r.y&&i<=r.y+r.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 eL(e,t,i){if(!e)return t;let n=Number(e);return Number.isFinite(n)?Math.max(i,Math.floor(n)):t}let eR=eL(process.env.AGENT_DEVICE_IOS_DEVICECTL_LIST_TIMEOUT_MS,8e3,500);async function eC(){if("darwin"!==process.platform)throw new h("UNSUPPORTED_PLATFORM","iOS tools are only available on macOS");if(!await A("xcrun"))throw new h("TOOL_MISSING","xcrun not found in PATH");let e=[],t=await d("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 h("COMMAND_FAILED","Failed to parse simctl devices JSON",void 0,e)}let i=null;try{i=n.join(g.tmpdir(),`agent-device-devicectl-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);let t=await d("xcrun",["devicectl","list","devices","--json-output",i],{allowFailure:!0,timeoutMs:eR});if(0!==t.exitCode)return e;let r=await u.readFile(i,"utf8"),a=JSON.parse(r);for(let t of a.result?.devices??[])if((t.hardwareProperties?.platform??"").toLowerCase().includes("ios")){let i=t.hardwareProperties?.udid??t.identifier??"",n=t.name??t.deviceProperties?.name??i;if(!i)continue;e.push({platform:"ios",id:i,name:n,kind:"device",booted:!0})}}catch{}finally{i&&await u.rm(i,{force:!0}).catch(()=>{})}return e}let eT=eL(process.env.AGENT_DEVICE_IOS_BOOT_TIMEOUT_MS,b,5e3),eP=eL(process.env.AGENT_DEVICE_IOS_SIMCTL_LIST_TIMEOUT_MS,S,1e3),e$=eL(process.env.AGENT_DEVICE_IOS_APP_LAUNCH_TIMEOUT_MS,3e4,5e3),eF=eL(process.env.AGENT_DEVICE_IOS_DEVICECTL_TIMEOUT_MS,2e4,1e3),eV=N(process.env.AGENT_DEVICE_RETRY_LOGS);async function eU(e,t){let i=["devicectl",...e],n=await d("xcrun",i,{allowFailure:!0,timeoutMs:eF});if(0===n.exitCode)return;let r=String(n.stdout??""),a=String(n.stderr??"");throw new h("COMMAND_FAILED",`Failed to ${t.action}`,{cmd:"xcrun",args:i,exitCode:n.exitCode,stdout:r,stderr:a,deviceId:t.deviceId,hint:eB(r,a)??ej})}async function eG(e,t){let i=n.join(g.tmpdir(),`agent-device-ios-apps-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`),r=["devicectl","device","info","apps","--device",e.id,"--include-all-apps","--json-output",i],a=await d("xcrun",r,{allowFailure:!0,timeoutMs:eF});try{var o,s;if(0!==a.exitCode){let t=String(a.stdout??""),i=String(a.stderr??"");throw new h("COMMAND_FAILED","Failed to list iOS apps",{cmd:"xcrun",args:r,exitCode:a.exitCode,stdout:t,stderr:i,deviceId:e.id,hint:eB(t,i)??ej})}let n=await u.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 n="string"==typeof e.name&&e.name.trim().length>0?e.name.trim():t;i.push({bundleId:t,name:n})}return i}(JSON.parse(n)),s=t,"user-installed"===s?o.filter(e=>!e.bundleId.startsWith("com.apple.")):o}catch(t){if(t instanceof h)throw t;throw new h("COMMAND_FAILED","Failed to parse iOS apps list",{deviceId:e.id,cause:String(t)})}finally{await u.unlink(i).catch(()=>{})}}let ej="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}async function eq(e){let t,i;if("simulator"!==e.kind||"Booted"===await eW(e.id))return;let n=_.fromTimeoutMs(eT);try{await O(async({deadline:n})=>{if(n?.isExpired())throw new h("COMMAND_FAILED","iOS simulator boot deadline exceeded",{timeoutMs:eT});let r=Math.max(1e3,n?.remainingMs()??eT),a=await d("xcrun",["simctl","boot",e.id],{allowFailure:!0,timeoutMs:r});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 h("COMMAND_FAILED","simctl boot failed",{stdout:t.stdout,stderr:t.stderr,exitCode:t.exitCode});let l=await d("xcrun",["simctl","bootstatus",e.id,"-b"],{allowFailure:!0,timeoutMs:r});if(i={stdout:String(l.stdout??""),stderr:String(l.stderr??""),exitCode:l.exitCode},0!==i.exitCode)throw new h("COMMAND_FAILED","simctl bootstatus failed",{stdout:i.stdout,stderr:i.stderr,exitCode:i.exitCode});let c=await eW(e.id);if("Booted"!==c)throw new h("COMMAND_FAILED","Simulator is still booting",{state:c})},{maxAttempts:3,baseDelayMs:500,maxDelayMs:2e3,jitter:.2,shouldRetry:e=>{let n=E({error:e,stdout:i?.stdout??t?.stdout,stderr:i?.stderr??t?.stderr,context:{platform:"ios",phase:"boot"}});return"IOS_BOOT_TIMEOUT"!==n&&"CI_RESOURCE_STARVATION_SUSPECTED"!==n}},{deadline:n,phase:"boot",classifyReason:e=>E({error:e,stdout:i?.stdout??t?.stdout,stderr:i?.stderr??t?.stderr,context:{platform:"ios",phase:"boot"}}),onEvent:e=>{eV&&process.stderr.write(`[agent-device][retry] ${JSON.stringify(e)}
|
|
8
|
+
`)}})}catch(a){let r=E({error:a,stdout:i?.stdout??t?.stdout,stderr:i?.stderr??t?.stderr,context:{platform:"ios",phase:"boot"}});throw new h("COMMAND_FAILED","iOS simulator failed to boot",{platform:"ios",deviceId:e.id,timeoutMs:eT,elapsedMs:n.elapsedMs(),reason:r,hint:x(r),boot:t,bootstatus:i})}}async function eW(e){let t=await d("xcrun",["simctl","list","devices","-j"],{allowFailure:!0,timeoutMs:eP});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"};function eH(e){return e.includes("not installed")||e.includes("not found")||e.includes("no such file")}async function ez(e,t){let i=t.trim();if(i.includes("."))return i;let n=eJ[i.toLowerCase()];if(n)return n;let r=("simulator"===e.kind?await e4(e):await eG(e,"all")).filter(e=>e.name.toLowerCase()===i.toLowerCase());if(1===r.length)return r[0].bundleId;if(r.length>1)throw new h("INVALID_ARGS",`Multiple apps matched "${t}"`,{matches:r});throw new h("APP_NOT_INSTALLED",`No app found matching "${t}"`)}async function eX(e,t,i){let n=i?.url?.trim();if(n){if(!F(n))throw new h("INVALID_ARGS","open <app> <url> requires a valid URL target");if("simulator"===e.kind){await eq(e),await d("open",["-a","Simulator"],{allowFailure:!0}),await d("xcrun",["simctl","openurl",e.id,n]);return}let r=V(i?.appBundleId??await ez(e,t),n);if(!r)throw new h("INVALID_ARGS","Deep link open on iOS devices requires an active app bundle ID. Open the app first, then open the URL.");await e6(e,r,{payloadUrl:n});return}let r=t.trim();if(F(r)){if("simulator"===e.kind){await eq(e),await d("open",["-a","Simulator"],{allowFailure:!0}),await d("xcrun",["simctl","openurl",e.id,r]);return}let t=V(i?.appBundleId,r);if(!t)throw new h("INVALID_ARGS","Deep link open on iOS devices requires an active app bundle ID. Open the app first, then open the URL.");await e6(e,t,{payloadUrl:r});return}let a=i?.appBundleId??await ez(e,t);"simulator"===e.kind?await e8(e,a):await e6(e,a)}async function eY(e){"simulator"!==e.kind||"Booted"!==await eW(e.id)&&(await eq(e),await d("open",["-a","Simulator"],{allowFailure:!0}))}async function eK(e,t){let i=await ez(e,t);if("simulator"===e.kind){await eq(e);let t=await d("xcrun",["simctl","terminate",e.id,i],{allowFailure:!0});if(0!==t.exitCode){if(t.stderr.toLowerCase().includes("found nothing to terminate"))return;throw new h("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 eU(["device","process","terminate","--device",e.id,i],{action:"terminate iOS app",deviceId:e.id})}async function eZ(e,t){let i=await ez(e,t);if("simulator"!==e.kind){let t=["devicectl","device","uninstall","app","--device",e.id,i],n=await d("xcrun",t,{allowFailure:!0,timeoutMs:eF});if(0!==n.exitCode){let r=String(n.stdout??""),a=String(n.stderr??"");if(!eH(`${r}
|
|
9
|
+
${a}`.toLowerCase()))throw new h("COMMAND_FAILED",`Failed to uninstall iOS app ${i}`,{cmd:"xcrun",args:t,exitCode:n.exitCode,stdout:r,stderr:a,deviceId:e.id,hint:eB(r,a)??ej})}return{bundleId:i}}await eq(e);let n=await d("xcrun",["simctl","uninstall",e.id,i],{allowFailure:!0});if(0!==n.exitCode&&!eH(`${n.stdout}
|
|
10
|
+
${n.stderr}`.toLowerCase()))throw new h("COMMAND_FAILED",`simctl uninstall failed for ${i}`,{stdout:n.stdout,stderr:n.stderr,exitCode:n.exitCode});return{bundleId:i}}async function eQ(e,t){"simulator"!==e.kind?await eU(["device","install","app","--device",e.id,t],{action:"install iOS app",deviceId:e.id}):(await eq(e),await d("xcrun",["simctl","install",e.id,t]))}async function e0(e,t,i){let{bundleId:n}=await eZ(e,t);return await eQ(e,i),{bundleId:n}}async function e1(e,t){if("simulator"===e.kind){await eq(e),await d("xcrun",["simctl","io",e.id,"screenshot",t]);return}await eU(["device","screenshot","--device",e.id,t],{action:"capture iOS screenshot",deviceId:e.id})}async function e2(e,t,i,n){if("simulator"!==e.kind)throw new h("UNSUPPORTED_OPERATION","settings is only supported on iOS simulators");await eq(e);let r=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 h("INVALID_ARGS",`Invalid setting state: ${e}`)}(i);switch(r){case"wifi":return void await d("xcrun",["simctl","status_bar",e.id,"override","--wifiMode",a?"active":"failed"]);case"airplane":a?await d("xcrun",["simctl","status_bar",e.id,"override","--dataNetwork","hide","--wifiMode","failed","--wifiBars","0","--cellularMode","failed","--cellularBars","0","--operatorName",""]):await d("xcrun",["simctl","status_bar",e.id,"clear"]);return;case"location":if(!n)throw new h("INVALID_ARGS","location setting requires an active app in session");await d("xcrun",["simctl","privacy",e.id,a?"grant":"revoke","location",n]);return;default:throw new h("INVALID_ARGS",`Unsupported setting: ${t}`)}}async function e3(e,t="all"){var i;return"simulator"===e.kind?(i=await e4(e),"user-installed"===t?i.filter(e=>!e.bundleId.startsWith("com.apple.")):i):await eG(e,t)}async function e4(e){let t=(await d("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 d("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 e5(e){if(!(e instanceof h)||"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 e8(e,t){await eq(e),await d("open",["-a","Simulator"],{allowFailure:!0});let i=_.fromTimeoutMs(e$);await O(async({deadline:i})=>{if(i?.isExpired())throw new h("COMMAND_FAILED","App launch deadline exceeded",{timeoutMs:e$});let n=await d("xcrun",["simctl","launch",e.id,t],{allowFailure:!0});if(0!==n.exitCode)throw new h("COMMAND_FAILED",`xcrun exited with code ${n.exitCode}`,{cmd:"xcrun",args:["simctl","launch",e.id,t],stdout:n.stdout,stderr:n.stderr,exitCode:n.exitCode})},{maxAttempts:30,baseDelayMs:1e3,maxDelayMs:5e3,jitter:.2,shouldRetry:e5},{deadline:i})}async function e6(e,t,i){let n=["device","process","launch","--device",e.id,t];i?.payloadUrl&&n.push("--payload-url",i.payloadUrl),await eU(n,{action:"launch iOS app",deviceId:e.id})}async function e9(e,t,i){let n=(e.get(t)??Promise.resolve()).catch(()=>{}).then(i);return e.set(t,n),n.finally(()=>{e.get(t)===n&&e.delete(t)})}let e7=new Map,te=new Map,tt=eL(process.env.AGENT_DEVICE_RUNNER_STARTUP_TIMEOUT_MS,45e3,5e3),ti=eL(process.env.AGENT_DEVICE_RUNNER_COMMAND_TIMEOUT_MS,15e3,1e3),tn=eL(process.env.AGENT_DEVICE_RUNNER_CONNECT_ATTEMPT_INTERVAL_MS,250,50),tr=eL(process.env.AGENT_DEVICE_RUNNER_CONNECT_RETRY_BASE_DELAY_MS,300,10),ta=eL(process.env.AGENT_DEVICE_RUNNER_CONNECT_RETRY_MAX_DELAY_MS,2e3,10),to=eL(process.env.AGENT_DEVICE_RUNNER_CONNECT_REQUEST_TIMEOUT_MS,5e3,250),ts=eL(process.env.AGENT_DEVICE_IOS_DEVICE_INFO_TIMEOUT_MS,1e4,500),tl=eL(process.env.AGENT_DEVICE_RUNNER_DESTINATION_TIMEOUT_SECONDS,20,5),tc=n.join(g.homedir(),".agent-device","ios-runner");async function td(e,t,i={}){var n;return(function(e){if("ios"!==e.platform)throw new h("UNSUPPORTED_PLATFORM",`Unsupported platform for iOS runner: ${e.platform}`);if("simulator"!==e.kind&&"device"!==e.kind)throw new h("UNSUPPORTED_OPERATION",`Unsupported iOS device kind for runner: ${e.kind}`)}(e),"snapshot"===(n=t.command)||"findText"===n||"listTappables"===n||"alert"===n)?M(()=>tu(e,t,i),{shouldRetry:tD}):tu(e,t,i)}async function tu(e,t,i={}){let n;try{let r=(n=await tI(e,i)).ready?ti:tt;return await tp(e,n,t,i.logPath,r)}catch(a){let r=a instanceof h?a:new h("COMMAND_FAILED",String(a));if("COMMAND_FAILED"===r.code&&"string"==typeof r.message&&r.message.includes("Runner did not accept connection")&&tO(r)&&n?.ready){n?await tw(n):await tm(e.id),n=await tI(e,i);let r=await tE(n.device,n.port,t,i.logPath,tt);return await tf(r,n,i.logPath)}throw a}}async function tp(e,t,i,n,r){let a=await tE(e,t.port,i,n,r,t);return await tf(a,t,n)}async function tf(e,t,i){let n=await e.text(),r={};try{r=JSON.parse(n)}catch{throw new h("COMMAND_FAILED","Invalid runner response",{text:n})}if(!r.ok)throw new h("COMMAND_FAILED",r.error?.message??"Runner error",{runner:r,xcodebuild:{exitCode:1,stdout:"",stderr:""},logPath:i});return t.ready=!0,r.data??{}}async function tm(e){await e9(te,e,async()=>{await tg(e)})}async function th(){let e=Array.from(e7.keys());await Promise.allSettled(e.map(async e=>{await tm(e)}))}async function tw(e){await e9(te,e.deviceId,async()=>{await tg(e.deviceId,e)})}async function tg(e,t){let i=t??e7.get(e);if(i){try{await tE(i.device,i.port,{command:"shutdown"},void 0,15e3)}catch{await tA(i.child.pid,"SIGTERM")}try{await Promise.race([i.testPromise,new Promise(e=>setTimeout(e,1e4))])}catch{}await tA(i.child.pid,"SIGKILL"),tP(i.xctestrunPath),tP(i.jsonPath),e7.get(e)===i&&e7.delete(e)}}async function tv(e){await d("xcrun",["simctl","bootstatus",e,"-b"],{allowFailure:!0,timeoutMs:tt})}async function tI(e,t){return await e9(te,e.id,async()=>{var i,n;let r=e7.get(e.id);if(r){if((i=r.child.pid)&&m(i))return r;await tg(e.id,r)}await ("simulator"!==(n=e).kind?Promise.resolve():tv(n.id));let a=await ty(e,t),o=await tC(),{xctestrunPath:s,jsonPath:c}=await tT(a,{AGENT_DEVICE_RUNNER_PORT:String(o)},`session-${e.id}-${o}`),{child:d,wait:u}=l("xcodebuild",["test-without-building","-only-testing","AgentDeviceRunnerUITests/RunnerTests/testCommand","-parallel-testing-enabled","NO","-test-timeouts-enabled","NO",tN(e),"1","-destination-timeout",String(tl),"-xctestrun",s,"-destination",function(e){if("ios"!==e.platform)throw new h("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=>{tb(e,t.logPath,t.traceLogPath,t.verbose)}),d.stderr?.on("data",e=>{tb(e,t.logPath,t.traceLogPath,t.verbose)});let p={device:e,deviceId:e.id,port:o,xctestrunPath:s,jsonPath:c,testPromise:u,child:d,ready:!1};return e7.set(e.id,p),p})}async function tA(e,t){if(!e||e<=0)return;try{process.kill(e,t)}catch{}let i="SIGTERM"===t?"TERM":"KILL";try{await d("pkill",[`-${i}`,"-P",String(e)],{allowFailure:!0})}catch{}}async function ty(e,t){var i,o;let s,l=(i=e.kind,(s=process.env.AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH?.trim())?n.resolve(s):"simulator"===i?n.join(tc,"derived"):n.join(tc,"derived",i));if(N(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 N(e.AGENT_DEVICE_IOS_ALLOW_OVERRIDE_DERIVED_CLEAN)}(t))throw new h("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{w.rmSync(l,{recursive:!0,force:!0})}catch{}}let c=tS(l);if(c)return c;let d=function(){let e=n.dirname(a(import.meta.url)),t=e;for(let e=0;e<6;e+=1){let e=n.join(t,"package.json");if(w.existsSync(e))return t;t=n.dirname(t)}return e}(),u=n.join(d,"ios-runner","AgentDeviceRunner","AgentDeviceRunner.xcodeproj");if(!w.existsSync(u))throw new h("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()||"",n=e.AGENT_DEVICE_IOS_SIGNING_IDENTITY?.trim()||"",r=e.AGENT_DEVICE_IOS_PROVISIONING_PROFILE?.trim()||"",a=["CODE_SIGN_STYLE=Automatic"];return i&&a.push(`DEVELOPMENT_TEAM=${i}`),n&&a.push(`CODE_SIGN_IDENTITY=${n}`),r&&a.push(`PROVISIONING_PROFILE_SPECIFIER=${r}`),a}(process.env,"device"===e.kind),f="device"===e.kind?["-allowProvisioningUpdates"]:[];try{await r("xcodebuild",["build-for-testing","-project",u,"-scheme","AgentDeviceRunner","-parallel-testing-enabled","NO",tN(e),"1","-destination",function(e){if("ios"!==e.platform)throw new h("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,...f,...p],{onStdoutChunk:e=>{tb(e,t.logPath,t.traceLogPath,t.verbose)},onStderrChunk:e=>{tb(e,t.logPath,t.traceLogPath,t.verbose)}})}catch(a){let e,i,n=a instanceof h?a:new h("COMMAND_FAILED",String(a)),r=(e=(o=n).details?JSON.stringify(o.details):"",(i=`${o.message}
|
|
11
|
+
${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 h("COMMAND_FAILED","xcodebuild build-for-testing failed",{error:n.message,details:n.details,logPath:t.logPath,hint:r})}let m=tS(l);if(!m)throw new h("COMMAND_FAILED","Failed to locate .xctestrun after build");return m}function tN(e){return"device"===e.kind?"-maximum-concurrent-test-device-destinations":"-maximum-concurrent-test-simulator-destinations"}function tS(e){if(!w.existsSync(e))return null;let t=[],i=[e];for(;i.length>0;){let e=i.pop();for(let r of w.readdirSync(e,{withFileTypes:!0})){let a=n.join(e,r.name);if(r.isDirectory()){i.push(a);continue}if(r.isFile()&&r.name.endsWith(".xctestrun"))try{let e=w.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 tb(e,t,i,n){t&&w.appendFileSync(t,e),i&&w.appendFileSync(i,e),n&&process.stderr.write(e)}function tD(e){if(!(e instanceof h)||"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:n,lastError:r}=e,a="Runner did not accept connection";return new h("COMMAND_FAILED",a,{port:t,endpoints:i,logPath:n,lastError:r?String(r):void 0,reason:E({error:r,message:a,context:{platform:"ios",phase:"connect"}}),hint:x("IOS_RUNNER_CONNECT_TIMEOUT")})}function tO(e){return!(e instanceof h)||"COMMAND_FAILED"!==e.code||!String(e.message??"").toLowerCase().includes("xcodebuild exited early")}async function tM(e){var t,i;let n,{session:r,port:a,logPath:o}=e,s=await r.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 h("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,(n=`${l}
|
|
11
12
|
${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.":E("IOS_RUNNER_CONNECT_TIMEOUT"))})}async function tO(e,t,i,r,n=e9,a){let o=D.fromTimeoutMs(n),s=await tE(e,t,o.remainingMs()),l=null,c=Math.max(1,Math.ceil(n/tt));try{return await _(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 tb({session:a,port:t,logPath:r});for(let r of("device"===e.kind&&(s=await tE(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 tM(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)},Math.min(tn,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:ti,maxDelayMs:tr,jitter:.2,shouldRetry:t_},{deadline:o,phase:"ios_runner_connect"})}catch(e){l||(l=e)}if("simulator"===e.kind){let n=o.remainingMs();if(n<=0)throw tD({port:t,endpoints:s,logPath:r,lastError:l});let a=await tx(e.id,t,i,n);return new Response(a.body,{status:a.status})}throw tD({port:t,endpoints:s,logPath:r,lastError:l})}async function tE(e,t,i){let r=[`http://127.0.0.1:${t}/command`];if("device"!==e.kind)return r;let n=await tk(e.id,i);return n&&r.unshift(`http://[${n}]:${t}/command`),r}async function tM(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 tk(e,t){if("number"==typeof t&&t<=0)return null;let i="number"==typeof t?Math.max(1,Math.min(ta,t)):ta,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{tC(n)}}async function tx(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=O({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:E(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 tL(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 tC(e){try{m.existsSync(e)&&m.unlinkSync(e)}catch{}}async function tT(e){let t={platform:e.platform,deviceName:e.device,udid:e.udid,serial:e.serial};if("android"===t.platform){await eI();let e=await L();return await I(e,t)}if("ios"===t.platform){let e=await eR();return await I(e,t)}let i=[];try{i.push(...await L())}catch{}try{i.push(...await eR())}catch{}return await I(i,t)}async function tP(e,t,i,n,a){let o=function(e,t){switch(e.platform){case"android":return{open:(t,i)=>Z(e,t,i?.activity),openDevice:()=>ee(e),close:t=>et(e,t),tap:(t,i)=>ea(e,t,i),swipe:(t,i,r,n,a)=>eo(e,t,i,r,n,a),longPress:(t,i,r)=>ed(e,t,i,r),focus:(t,i)=>ep(e,t,i),type:t=>eu(e,t),fill:(t,i,r)=>ef(e,t,i,r),scroll:(t,i)=>em(e,t,i),scrollIntoView:t=>eh(e,t),screenshot:t=>ew(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=>eX(e,t),screenshot:t=>eQ(e,t),...(i=e,n={verbose:(r=t).verbose,logPath:r.logPath,traceLogPath:r.traceLogPath},{tap:async(e,t)=>{await tl(i,{command:"tap",x:e,y:t,appBundleId:r.appBundleId},n)},swipe:async(e,t,a,o,s)=>{await tl(i,{command:"drag",x:e,y:t,x2:a,y2:o,durationMs:s,appBundleId:r.appBundleId},n)},longPress:async(e,t,a)=>{await tl(i,{command:"longPress",x:e,y:t,durationMs:a,appBundleId:r.appBundleId},n)},focus:async(e,t)=>{await tl(i,{command:"tap",x:e,y:t,appBundleId:r.appBundleId},n)},type:async e=>{await tl(i,{command:"type",text:e,appBundleId:r.appBundleId},n)},fill:async(e,t,a)=>{await tl(i,{command:"tap",x:e,y:t,appBundleId:r.appBundleId},n),await tl(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 tl(i,{command:"swipe",direction:a,appBundleId:r.appBundleId},n)},scrollIntoView:async e=>{for(let t=0;t<8;t+=1){let a=await tl(i,{command:"findText",text:e,appBundleId:r.appBundleId},n);if(a?.found)return{attempts:t+1};await tl(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(P(t))throw new f("INVALID_ARGS","open <app> <url> requires an app target as the first argument");if(!P(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=tF(a?.count??1,"count",1,200),n=tF(a?.intervalMs??0,"interval-ms",0,1e4),s=tF(a?.holdMs??0,"hold-ms",0,1e4),l=tF(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]=t$[e%t$.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 tV(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=tF(i[4]?Number(i[4]):250,"durationMs",16,1e4),c="ios"===e.platform?60:l,d=tF(a?.count??1,"count",1,200),u=tF(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 tV(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 tl(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 tl(e,{command:"back",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{action:"back"};return await es(e),{action:"back"};case"home":if("ios"===e.platform)return await tl(e,{command:"home",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{action:"home"};return await el(e),{action:"home"};case"app-switcher":if("ios"===e.platform)return await tl(e,{command:"appSwitcher",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{action:"app-switcher"};return await ec(e),{action:"app-switcher"};case"settings":{let[t,r,n]=i;if("ios"===e.platform)return await e0(e,t,r,n??a?.appBundleId),{setting:t,state:r};return await eg(e,t,r),{setting:t,state:r}}case"snapshot":{if("ios"===e.platform){let t=await tl(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}),i=t.nodes??[];if(0===i.length&&"simulator"===e.kind)throw new f("COMMAND_FAILED","XCTest snapshot returned 0 nodes on iOS simulator.");return{nodes:i,truncated:t.truncated??!1,backend:"xctest"}}let t=await ev(e,{interactiveOnly:a?.snapshotInteractiveOnly,compact:a?.snapshotCompact,depth:a?.snapshotDepth,scope:a?.snapshotScope,raw:a?.snapshotRaw});return{nodes:t.nodes??[],truncated:t.truncated??!1,backend:"android"}}default:throw new f("INVALID_ARGS",`Unknown command: ${t}`)}}let t$=[[0,0],[1,0],[0,1],[-1,0],[0,-1],[1,1],[-1,1],[1,-1],[-1,-1]];function tF(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 tV(e){await new Promise(t=>setTimeout(t,e))}let tU={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 tG(e,t){let i=tU[e];if(!i)return!0;let r=i[t.platform];return!!r&&!0===r[t.kind??"unknown"]}function tj(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 tB(e){let t=new Set,i=[];for(let r of e)t.has(r)||(t.add(r),i.push(r));return i}class tq{sessions=new Map;sessionsDir;constructor(e){this.sessionsDir=e}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=tq.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,relaunch:p,saveScript:f,noRecord:m}=e;return{platform:t,device:i,udid:r,serial:n,out:a,verbose:o,snapshotInteractiveOnly:s,snapshotCompact:l,snapshotDepth:c,snapshotScope:d,snapshotRaw:u,relaunch:p,saveScript:f,noRecord:m}}(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(tW(i)),i.startsWith("@")){let i=e.result?.refLabel;"string"==typeof i&&i.trim().length>0&&t.push(tW(i))}return t.join(" ")}}if("fill"===e.command){let i=e.positionals?.[0];if(i&&i.startsWith("@")){t.push(tW(i));let r=e.result?.refLabel,n=e.positionals.slice(1).join(" ");return"string"==typeof r&&r.trim().length>0&&t.push(tW(r)),n&&t.push(tW(n)),t.join(" ")}}if("get"===e.command){let i=e.positionals?.[0],r=e.positionals?.[1];if(i&&r){if(t.push(tW(i)),t.push(tW(r)),r.startsWith("@")){let i=e.result?.refLabel;"string"==typeof i&&i.trim().length>0&&t.push(tW(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",tW(e.flags.snapshotScope)),e.flags?.snapshotRaw&&t.push("--raw"),t.join(" ");if("open"===e.command){for(let i of e.positionals??[])t.push(tW(i));return e.flags?.relaunch&&t.push("--relaunch"),t.join(" ")}for(let i of e.positionals??[])t.push(tW(i));return t.join(" ")}(a));return`${i.join("\n")}
|
|
13
|
-
`}(e,this.buildOptimizedActions(e));
|
|
14
|
-
${t}`.toLowerCase().includes("timed out waiting for all destinations")?"Xcode destination did not become available in time. Keep device unlocked and retry.":eU)}function tZ(e){return e.map((e,t)=>({...e,ref:`e${t+1}`}))}function tQ(e){let t=e.trim();return t.startsWith("@")?t.slice(1)||null:t.startsWith("e")?t:null}function t0(e,t){return e.find(e=>e.ref===t)??null}function t1(e){return{x:Math.round(e.x+e.width/2),y:Math.round(e.y+e.height/2)}}function t2(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 t3(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&&t4(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||!t4(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&&t4(i)?i:void 0)}function t4(e){let t=e.trim();return!(!t||/^(true|false)$/i.test(t)||/^\d+$/.test(t))}function t5(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=t8(r.type??""),a=[r.label,r.value,r.identifier].map(e=>"string"==typeof e?e.trim():"").find(e=>e&&e.length>0),o=!!a&&t4(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 t8(e){let t=e.trim().replace(/XCUIElementType/gi,"").toLowerCase(),i=Math.max(t.lastIndexOf("."),t.lastIndexOf("/"));return -1!==i&&(t=t.slice(i+1)),t}function t6(e,t){let i=t8(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 t7(e){return[e.label,e.value,e.identifier].map(e=>"string"==typeof e?e.trim():"").filter(e=>e.length>0)[0]??""}let t9=new Set(["id","role","text","label","value"]),ie=new Set(["visible","hidden","editable","selected","enabled","hittable"]),it=new Set([...t9,...ie]);function ii(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)&&!iN(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)&&!iN(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(im)}})(e))}}function ir(e){try{return ii(e)}catch{return null}}function ia(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||!ih(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=iy(e),a=iy(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 io(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)&&ih(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 is(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 il(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 it.has(e)}return it.has(t.toLowerCase())}(e[r]);){r+=1;let t=e.slice(0,r).join(" ").trim();t&&ir(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 ic(e){let t=e[0]??"",i=il(e.slice(1),{preferTrailingValue:"text"===t});return{predicate:t,split:i}}function id(e){return!0===e.hittable||!!e.rect&&e.rect.width>0&&e.rect.height>0}function iu(e,t){return t6(e.type??"",t)&&!1!==e.enabled}function ip(e,t,i={}){let r=[],n=t8(e.type??""),a=iA(e.identifier),o=iA(e.label),s=iA(e.value),l=iA(t7(e)),c="fill"===i.action;a&&r.push(`id=${iI(a)}`),n&&o&&r.push(c?`role=${iI(n)} label=${iI(o)} editable=true`:`role=${iI(n)} label=${iI(o)}`),o&&r.push(c?`label=${iI(o)} editable=true`:`label=${iI(o)}`),s&&r.push(c?`value=${iI(s)} editable=true`:`value=${iI(s)}`),l&&l!==o&&l!==s&&r.push(c?`text=${iI(l)} editable=true`:`text=${iI(l)}`),n&&c&&!r.some(e=>e.includes("editable=true"))&&r.push(`role=${iI(n)} editable=true`);let d=tB(r);return 0===d.length&&n&&d.push(c?`role=${iI(n)} editable=true`:`role=${iI(n)}`),0===d.length&&id(e)&&d.push("visible=true"),d}function im(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(!ie.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(!it.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(ie.has(r)){let e,t="true"===(e=iw(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:iw(n)}}function ih(e,t,i){return t.terms.every(t=>(function(e,t,i){switch(t.key){case"id":return ig(e.identifier,String(t.value));case"role":var r,n;return r=e.type,n=String(t.value),function(e){return t8(e)}(r??"")===function(e){return t8(e)}(n);case"label":return ig(e.label,String(t.value));case"value":return ig(e.value,String(t.value));case"text":{let i=iv(String(t.value));return iv(t7(e))===i}case"visible":return id(e)===!!t.value;case"hidden":return!id(e)==!!t.value;case"editable":return iu(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 iw(e){let t=e.trim();return t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1).replace(/\\(["'])/g,"$1"):t}function ig(e,t){return iv(e??"")===iv(t)}function iv(e){return e.trim().toLowerCase().replace(/\s+/g," ")}function iI(e){return JSON.stringify(e)}function iA(e){if(!e)return null;let t=e.trim();return t||null}function iy(e){return e.rect?e.rect.width*e.rect.height:1/0}function iN(e,t){let i=0;for(let r=t-1;r>=0&&"\\"===e[r];r-=1)i+=1;return i%2==1}let iS='iOS appstate requires an active session on the target device. Run open first (for example: open --session sim --platform ios --device "<name>" <app>).';function iD(e,t,i){return t||i_(i)?null:{ok:!1,error:{code:"INVALID_ARGS",message:`${e} requires an active session or an explicit device selector (e.g. --platform ios).`}}}function i_(e){return!!(e?.platform||e?.device||e?.udid||e?.serial)}async function ib(e){let t=i_(e.flags)||!e.session?await e.resolveTargetDeviceFn(e.flags??{}):e.session.device;return!1!==e.ensureReady&&await e.ensureReadyFn(t),t}let iO={ios:async(e,t,i)=>{let{reinstallIosApp:r}=await Promise.resolve().then(()=>({reinstallIosApp:eZ}));return await r(e,t,i)},android:async(e,t,i)=>{let{reinstallAndroidApp:r}=await Promise.resolve().then(()=>({reinstallAndroidApp:en}));return await r(e,t,i)}};async function iE(e,t,i){if("ios"===e.platform&&t)return P(t)?"device"===e.kind?$(i,t):void 0:await iM(e,t)}async function iM(e,t){try{let{resolveIosApp:i}=await Promise.resolve().then(()=>({resolveIosApp:eJ}));return await i(e,t)}catch{return}}async function ik(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=iD("appstate",o,s);if(l)return l;let c=o?.device.platform==="ios"&&!!o&&(!i_(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:iS}};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 ib({session:o,flags:s,ensureReadyFn:n,resolveTargetDeviceFn:a,ensureReady:!0});if("ios"===d.platform)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:iS}};let{getAndroidAppState:u}=await Promise.resolve().then(()=>({getAndroidAppState:Y})),p=await u(d);return{ok:!0,data:{platform:"android",package:p.package,activity:p.activity}}}async function ix(e){let{req:t,sessionName:i,logPath:r,sessionStore:n,invoke:a,dispatch:o,ensureReady:s,resolveTargetDevice:l,reinstallOps:c=iO}=e,d=o??tP,p=s??tz,h=l??tT,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:L}));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:L})),{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=iD(w,e,r);if(a)return a;let o=await ib({session:e,flags:r,ensureReadyFn:p,resolveTargetDeviceFn:h,ensureReady:!0});if(!tG("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:e1}));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=iD(w,e,r);if(a)return a;let o=await ib({session:e,flags:r,ensureReadyFn:p,resolveTargetDeviceFn:h,ensureReady:!0});return tG("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 ik({req:t,sessionName:i,sessionStore:n,ensureReady:p,resolveDevice:h});if("reinstall"===w){let e,r=n.get(i),a=t.flags??{},o=iD(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=tq.expandHome(l);if(!m.existsSync(d))return{ok:!1,error:{code:"INVALID_ARGS",message:`App binary not found: ${d}`}};let u=await ib({session:r,flags:a,ensureReadyFn:p,resolveTargetDeviceFn:h,ensureReady:!1});if(!tG("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&&P(s))return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch does not support URL targets."}};await p(a.device);let l=await iE(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,{...tJ(r,t.flags,l??a.appBundleId,a.trace?.outPath)})}await d(a.device,"open",c,t.flags?.out,{...tJ(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&&P(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 iE(o,a);if(e&&a){let e=l??a;await d(o,"close",[e],t.flags?.out,{...tJ(r,t.flags,l)})}await d(o,"open",t.positionals??[],t.flags?.out,{...tJ(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=tq.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){e+=1;continue}}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 iR(l,s,e,o);let f=await iL({action:s,sessionName:i,logPath:r,sessionStore:n,dispatch:d});if(!f)return iR(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 iR(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",iP(e.flags.snapshotScope)),e.flags?.snapshotRaw&&t.push("--raw"),t.join(" ");if("open"===e.command){for(let i of e.positionals??[])t.push(iP(i));return e.flags?.relaunch&&t.push("--relaunch"),t.join(" ")}for(let i of e.positionals??[])t.push(iP(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,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,{...tJ(r,t.flags,e.appBundleId,e.trace?.outPath)}),"ios"===e.device.platform&&await tp(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 iR(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 iL(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 iC(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}=ic(e.positionals);i&&t.push(i.selectorExpression)}if("wait"===e.command){let{selectorExpression:i}=iT(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 tB(t).filter(e=>e.trim().length>0)}(t)){let i=ir(e);if(!i)continue;let r=ia(c.nodes,i,{platform:o.device.platform,requireRect:s,requireUnique:!0,disambiguateAmbiguous:l});if(!r)continue;let n=ip(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=tj(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}=ic(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}=iT(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=ir(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(t8(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=t7(e).trim();return!!/^\d+$/.test(t)&&(0===a.size||a.has(t8(e.type??"")))});if(0===s.length||1!==tB(s.map(e=>t7(e).trim())).length)return null;let l=s[0];if(!l)return null;let c=ip(l,i.device.platform,{action:"get"});return 0===c.length?null:{...e,positionals:["text",c.join(" || ")]}}(t,c,o);return d||null}async function iC(e,t,i,r,n,a){let o=await n(e.device,"snapshot",[],t.flags?.out,{...tJ(i,{...t.flags??{},snapshotInteractiveOnly:r,snapshotCompact:r},e.appBundleId,e.trace?.outPath)}),s=o?.nodes??[],l={nodes:tZ(t.flags?.snapshotRaw?s:t5(s)),truncated:o?.truncated,createdAt:Date.now(),backend:o?.backend};return e.snapshot=l,a.set(e.name,e),l}function iT(e){if(0===e.length)return{selectorExpression:null,selectorTimeout:null};let t=e[e.length-1],i=/^\d+$/.test(t??""),r=il(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 iP(e){let t=e.trim();return t.startsWith("@")||/^-?\d+(\.\d+)?$/.test(t)?t:JSON.stringify(t)}function i$(e){if(!e)return null;let t=Number(e);return Number.isFinite(t)?t:null}async function iF(e){let{req:t,sessionName:i,logPath:r,sessionStore:n}=e,a=t.command;if("snapshot"===a){let{session:e,device:a}=await iV(n,i,t.flags);if(!tG("snapshot",a))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"snapshot is not supported on this device"}};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=tQ(s.trim());if(!t)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref scope: ${s}`}};let i=t0(e.snapshot.nodes,t),r=i?t3(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 iU(e,a,async()=>{let l=await tP(a,"snapshot",[],t.flags?.out,{...tJ(r,{...t.flags,snapshotScope:s},o,e?.trace?.outPath)}),c=l?.nodes??[],d=tZ(t.flags?.snapshotRaw?c:t5(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 iG(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 iV(n,i,t.flags),o=function(e){if(0===e.length)return null;let t=i$(e[0]);if(null!==t)return{kind:"sleep",durationMs:t};if("text"===e[0]){let t=i$(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=i$(e[e.length-1]);return{kind:"ref",rawRef:e[0],timeoutMs:t}}let i=i$(e[e.length-1]),r=il(null!==i?e.slice(0,-1):e.slice());if(r&&0===r.rest.length){let e=ir(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)),iG(n,e,t,{waitedMs:o.durationMs}),{ok:!0,data:{waitedMs:o.durationMs}}):tG("wait",a)?await iU(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 tP(a,"snapshot",[],t.flags?.out,{...tJ(r,{...t.flags,snapshotInteractiveOnly:!1,snapshotCompact:!1},e?.appBundleId,e?.trace?.outPath)}),c=s?.nodes??[],d=tZ(t.flags?.snapshotRaw?c:t5(c));e&&(e.snapshot={nodes:d,truncated:s?.truncated,createdAt:Date.now(),backend:s?.backend},n.set(i,e));let u=io(d,o.selector,{platform:a.platform});if(u)return iG(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=tQ(o.rawRef);if(!t)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref: ${o.rawRef}`}};let i=t0(e.snapshot.nodes,t),r=i?t3(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 tl(a,{command:"findText",text:s,appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:r,traceLogPath:e?.trace?.outPath});if(i?.found)return iG(n,e,t,{text:s,waitedMs:Date.now()-d}),{ok:!0,data:{text:s,waitedMs:Date.now()-d}}}else if("android"===a.platform&&t2(tZ((await ev(a,{scope:s})).nodes??[]),s))return iG(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 iV(n,i,t.flags),o=(t.positionals?.[0]??"get").toLowerCase();return tG("alert",a)?await iU(e,a,async()=>{if("wait"===o){let i=i$(t.positionals?.[1])??1e4,o=Date.now();for(;Date.now()-o<i;){try{let i=await tl(a,{command:"alert",action:"get",appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:r,traceLogPath:e?.trace?.outPath});return iG(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 tl(a,{command:"alert",action:"accept"===o||"dismiss"===o?o:"get",appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:r,traceLogPath:e?.trace?.outPath});return iG(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 iV(n,i,t.flags);return tG("settings",s)?await iU(o,s,async()=>{let i=o?.appBundleId,l=await tP(s,"settings",[e,a,i??""],t.flags?.out,{...tJ(r,t.flags,i,o?.trace?.outPath)});return iG(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 iV(e,t,i){let r=e.get(t),n=r?.device??await tT(i??{});return r||await tz(n),{session:r,device:n}}async function iU(e,t,i){let r=!e&&"ios"===t.platform;try{return await i()}finally{r&&await tp(t.id)}}function iG(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=iq(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():""}(e??"");return i?i===t?2:+!!i.includes(t):0}(e.type,i);case"label":return iB(e.label,i);case"value":return iB(e.value,i);case"id":return iB(e.identifier,i);default:return Math.max(iB(e.label,i),iB(e.value,i),iB(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 iB(e,t){let i=iq(e??"");return i?i===t?2:+!!i.includes(t):0}function iq(e){return e.trim().toLowerCase().replace(/\s+/g," ")}async function iW(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:i$(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 tT(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 tP(h,"snapshot",[],t.flags?.out,{...tJ(r,{...t.flags,snapshotScope:g,snapshotInteractiveOnly:v,snapshotCompact:v},w,m?.trace?.outPath)}),o=a?.nodes??[],s=tZ(t.flags?.snapshotRaw?o:t5(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=t7(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=t7(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?t1(D.rect):null;if(!e)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};let i=await tP(h,"focus",[String(e.x),String(e.y)],t.flags?.out,{...tJ(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?t1(D.rect):null;if(!e)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};await tP(h,"focus",[String(e.x),String(e.y)],t.flags?.out,{...tJ(r,t.flags,m?.appBundleId,m?.trace?.outPath)});let i=await tP(h,"type",[u],t.flags?.out,{...tJ(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 iJ(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 tT(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}),!tG("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=tq.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=tq.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 iH(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=iY("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=tQ(o);if(!s)return{ok:!1,error:{code:"INVALID_ARGS",message:"click requires a ref like @e2"}};let l=t0(e.snapshot.nodes,s);if(!l?.rect&&t.positionals.length>1){let i=t.positionals.slice(1).join(" ").trim();i.length>0&&(l=t2(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=t3(l,e.snapshot.nodes),d=ip(l,e.device.platform,{action:"click"}),{x:u,y:p}=t1(l.rect);return await tP(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=ii(s),c=await iz(e,t.flags,r,n,{interactiveOnly:!0}),d=ia(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:is(l,d?.diagnostics??[],{unique:!0})}};let{x:u,y:p}=t1(d.node.rect);await tP(e.device,"press",[String(u),String(p)],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)});let f=ip(d.node,e.device.platform,{action:"click"}),m=t3(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=iY("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=tQ(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=t0(e.snapshot.nodes,o);if(!c?.rect&&s&&(c=t2(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&&!t6(d,e.device.platform)?`fill target ${t.positionals[0]} resolved to "${d}", attempting fill anyway.`:void 0,p=t3(c,e.snapshot.nodes),f=ip(c,e.device.platform,{action:"fill"}),{x:m,y:h}=t1(c.rect),w={...await tP(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=il(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=ii(o.selectorExpression),l=await iz(e,t.flags,r,n,{interactiveOnly:!0}),c=ia(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:is(s,c?.diagnostics??[],{unique:!0})}};let d=c.node,u=d.type??"",p=u&&!t6(u,e.device.platform)?`fill target ${c.selector.raw} resolved to "${u}", attempting fill anyway.`:void 0,{x:f,y:m}=t1(c.node.rect),h=await tP(e.device,"fill",[String(f),String(m),i],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)}),w=ip(d,e.device.platform,{action:"fill"}),g={...h??{x:f,y:m,text:i},selector:c.selector.raw,selectorChain:w,refLabel:t3(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=iY("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=tQ(s??"");if(!n)return{ok:!1,error:{code:"INVALID_ARGS",message:"get text requires a ref like @e2"}};let l=t0(o.snapshot.nodes,n);if(!l&&t.positionals.length>2){let e=t.positionals.slice(2).join(" ").trim();e.length>0&&(l=t2(o.snapshot.nodes,e))}if(!l)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${s} not found`}};let c=ip(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=t7(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=ii(l),d=ia((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:is(c,[],{unique:!0})}};let u=d.node,p=ip(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=t7(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(!tG("is",o.device))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"is is not supported on this device"}};let{split:s}=ic(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=ii(s.selectorExpression),d=await iz(o,t.flags,r,n,{interactiveOnly:!1});if("exists"===e){let i=io(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:is(c,[],{unique:!1})}}}let u=ia(d.nodes,c,{platform:o.device.platform,requireUnique:!0});if(!u)return{ok:!1,error:{code:"COMMAND_FAILED",message:is(c,[],{unique:!0})}};let p=function(e){let{predicate:t,node:i,expectedText:r,platform:n}=e,a=t7(i),o=!1;switch(t){case"visible":o=id(i);break;case"hidden":o=!id(i);break;case"editable":o=iu(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:id(i),editable:iu(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 tP(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:tZ(t?.snapshotRaw?o:t5(o)),truncated:a?.truncated,createdAt:Date.now(),backend:a?.backend},i.set(e.name,e),e.snapshot}let iX=[["snapshotDepth","--depth"],["snapshotScope","--scope"],["snapshotRaw","--raw"]];function iY(e,t){let i=function(e){if(!e)return[];let t=[];for(let[i,r]of iX)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 iK=r.join(h.homedir(),".agent-device"),iZ=r.join(iK,"daemon.json"),iQ=r.join(iK,"daemon.lock"),i0=r.join(iK,"daemon.log"),i1=new tq(r.join(iK,"sessions")),i2=l(),i3=e.randomBytes(24).toString("hex"),i4=new Set(["session_list","devices"]),i5=w(process.pid)??void 0;function i8(e,t,i){return tJ(i0,e,t,i)}async function i6(e){if(e.token!==i3)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,i1),r=i1.get(i);r&&!i4.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 ix({req:e,sessionName:i,logPath:i0,sessionStore:i1,invoke:i6});if(n)return n;let a=await iF({req:e,sessionName:i,logPath:i0,sessionStore:i1});if(a)return a;let o=await iJ({req:e,sessionName:i,sessionStore:i1});if(o)return o;let s=await iW({req:e,sessionName:i,logPath:i0,sessionStore:i1,invoke:i6});if(s)return s;let l=await iH({req:e,sessionName:i,sessionStore:i1,contextFromFlags:i8});if(l)return l;let c=i1.get(i);if(!c)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};if(!tG(t,c.device))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:`${t} is not supported on this device`}};let d=await tP(c.device,t,e.positionals??[],e.flags?.out,{...i8(e.flags,c.appBundleId,c.trace?.outPath)});return i1.recordAction(c,{command:t,positionals:e.positionals??[],flags:e.flags??{},result:d??{}}),{ok:!0,data:d??{}}}function i7(){if(!m.existsSync(iQ))return null;try{let e=JSON.parse(m.readFileSync(iQ,"utf8"));if(!Number.isInteger(e.pid)||e.pid<=0)return null;return e}catch{return null}}!function(){if(!function(){m.existsSync(iK)||m.mkdirSync(iK,{recursive:!0});let e=JSON.stringify({pid:process.pid,version:i2,startedAt:Date.now(),processStartTime:i5},null,2),t=()=>{try{return m.writeFileSync(iQ,e,{flag:"wx",mode:384}),!0}catch(e){if("EEXIST"===e.code)return!1;throw e}};if(t())return!0;let i=i7();if(i?.pid&&i.pid!==process.pid&&o(i.pid,i.processStartTime))return!1;try{m.unlinkSync(iQ)}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 i6(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)}
|
|
16
|
-
`),
|
|
17
|
-
`)}});let t=!1,i=async()=>{await new Promise(t=>{try{e.close(()=>t())}catch{t()}})},
|
|
18
|
-
`),
|
|
13
|
+
${i}`.toLowerCase()).includes("device is busy")&&n.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,n,r=tt,a){let o=_.fromTimeoutMs(r),s=await tx(e,t,o.remainingMs()),l=null,c=Math.max(1,Math.ceil(r/tn));try{return await O(async({deadline:o})=>{if(o?.isExpired())throw new h("COMMAND_FAILED","Runner connection deadline exceeded",{port:t,timeoutMs:r});if(a&&null!==a.child.exitCode&&void 0!==a.child.exitCode)throw await tM({session:a,port:t,logPath:n});for(let n of("device"===e.kind&&(s=await tx(e,t,o?.remainingMs())),s))try{let e=o?.remainingMs()??r;if(e<=0)throw new h("COMMAND_FAILED","Runner connection deadline exceeded",{port:t,timeoutMs:r});return await tk(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)},Math.min(to,e))}catch(e){l=e}throw new h("COMMAND_FAILED","Runner endpoint probe failed",{port:t,endpoints:s,lastError:l?String(l):void 0})},{maxAttempts:c,baseDelayMs:tr,maxDelayMs:ta,jitter:.2,shouldRetry:tO},{deadline:o,phase:"ios_runner_connect"})}catch(e){l||(l=e)}if("simulator"===e.kind){let r=o.remainingMs();if(r<=0)throw t_({port:t,endpoints:s,logPath:n,lastError:l});let a=await tR(e.id,t,i,r);return new Response(a.body,{status:a.status})}throw t_({port:t,endpoints:s,logPath:n,lastError:l})}async function tx(e,t,i){let n=[`http://127.0.0.1:${t}/command`];if("device"!==e.kind)return n;let r=await tL(e.id,i);return r&&n.unshift(`http://[${r}]:${t}/command`),n}async function tk(e,t,i){let n=new AbortController,r=setTimeout(()=>n.abort(),i);try{return await fetch(e,{...t,signal:n.signal})}finally{clearTimeout(r)}}async function tL(e,t){if("number"==typeof t&&t<=0)return null;let i="number"==typeof t?Math.max(1,Math.min(ts,t)):ts,r=n.join(g.tmpdir(),`agent-device-devicectl-info-${process.pid}-${Date.now()}.json`);try{let t=Math.max(1,Math.ceil(i/1e3)),n=await d("xcrun",["devicectl","device","info","details","--device",e,"--json-output",r,"--timeout",String(t)],{allowFailure:!0,timeoutMs:i});if(0!==n.exitCode||!w.existsSync(r))return null;let a=JSON.parse(w.readFileSync(r,"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{tP(r)}}async function tR(e,t,i,n){let r=JSON.stringify(i),a=await d("xcrun",["simctl","spawn",e,"/usr/bin/curl","-s","-X","POST","-H","Content-Type: application/json","--data",r,`http://127.0.0.1:${t}/command`],{allowFailure:!0,timeoutMs:n}),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 h("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 tC(){return await new Promise((e,t)=>{let i=I.createServer();i.listen(0,"127.0.0.1",()=>{let n=i.address();i.close(),"object"==typeof n&&n?.port?e(n.port):t(new h("COMMAND_FAILED","Failed to allocate port"))}),i.on("error",t)})}async function tT(e,t,i){let r,a=n.dirname(e),o=i.replace(/[^a-zA-Z0-9._-]/g,"_"),s=n.join(a,`AgentDeviceRunner.env.${o}.json`),l=n.join(a,`AgentDeviceRunner.env.${o}.xctestrun`),c=await d("plutil",["-convert","json","-o","-",e],{allowFailure:!0});if(0!==c.exitCode||!c.stdout.trim())throw new h("COMMAND_FAILED","Failed to read xctestrun plist",{xctestrunPath:e,stderr:c.stderr});try{r=JSON.parse(c.stdout)}catch(t){throw new h("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=r.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(r))t&&"object"==typeof t&&t.TestBundlePath&&(u(t),r[e]=t);w.writeFileSync(s,JSON.stringify(r,null,2));let f=await d("plutil",["-convert","xml1","-o",l,s],{allowFailure:!0});if(0!==f.exitCode)throw new h("COMMAND_FAILED","Failed to write xctestrun plist",{tmpXctestrunPath:l,stderr:f.stderr});return{xctestrunPath:l,jsonPath:s}}function tP(e){try{w.existsSync(e)&&w.unlinkSync(e)}catch{}}async function t$(e){let t={platform:e.platform,deviceName:e.device,udid:e.udid,serial:e.serial};if("android"===t.platform){await ey();let e=await T();return await y(e,t)}if("ios"===t.platform){let e=await eC();return await y(e,t)}let i=[];try{i.push(...await T())}catch{}try{i.push(...await eC())}catch{}return await y(i,t)}async function tF(e,t,i,r,a){var o,s,l,c,d,p;let f=function(e,t){switch(e.platform){case"android":return{open:(t,i)=>ee(e,t,i?.activity),openDevice:()=>ei(e),close:t=>en(e,t),tap:(t,i)=>es(e,t,i),doubleTap:async(t,i)=>{await es(e,t,i),await es(e,t,i)},swipe:(t,i,n,r,a)=>el(e,t,i,n,r,a),longPress:(t,i,n)=>ep(e,t,i,n),focus:(t,i)=>em(e,t,i),type:t=>ef(e,t),fill:(t,i,n)=>eh(e,t,i,n),scroll:(t,i)=>ew(e,t,i),scrollIntoView:t=>eg(e,t),screenshot:t=>ev(e,t)};case"ios":var i,n;let r;return{open:(t,i)=>eX(e,t,{appBundleId:i?.appBundleId,url:i?.url}),openDevice:()=>eY(e),close:t=>eK(e,t),screenshot:t=>e1(e,t),...(i=e,r={verbose:(n=t).verbose,logPath:n.logPath,traceLogPath:n.traceLogPath},{tap:async(e,t)=>{await td(i,{command:"tap",x:e,y:t,appBundleId:n.appBundleId},r)},doubleTap:async(e,t)=>{await td(i,{command:"tapSeries",x:e,y:t,count:1,intervalMs:0,doubleTap:!0,appBundleId:n.appBundleId},r)},swipe:async(e,t,a,o,s)=>{await td(i,{command:"drag",x:e,y:t,x2:a,y2:o,durationMs:s,appBundleId:n.appBundleId},r)},longPress:async(e,t,a)=>{await td(i,{command:"longPress",x:e,y:t,durationMs:a,appBundleId:n.appBundleId},r)},focus:async(e,t)=>{await td(i,{command:"tap",x:e,y:t,appBundleId:n.appBundleId},r)},type:async e=>{await td(i,{command:"type",text:e,appBundleId:n.appBundleId},r)},fill:async(e,t,a)=>{await td(i,{command:"tap",x:e,y:t,appBundleId:n.appBundleId},r),await td(i,{command:"type",text:a,clearFirst:!0,appBundleId:n.appBundleId},r)},scroll:async(e,t)=>{if(!["up","down","left","right"].includes(e))throw new h("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 td(i,{command:"swipe",direction:a,appBundleId:n.appBundleId},r)},scrollIntoView:async e=>{for(let t=0;t<8;t+=1){let a=await td(i,{command:"findText",text:e,appBundleId:n.appBundleId},r);if(a?.found)return{attempts:t+1};await td(i,{command:"swipe",direction:"up",appBundleId:n.appBundleId},r),await new Promise(e=>setTimeout(e,300))}throw new h("COMMAND_FAILED",`scrollintoview could not find text: ${e}`)}})};default:throw new h("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],n=i[1];if(i.length>2)throw new h("INVALID_ARGS","open accepts at most two arguments: <app|url> [url]");if(!t)return await f.openDevice(),{app:null};if(void 0!==n){if("ios"!==e.platform)throw new h("INVALID_ARGS","open <app> <url> is supported only on iOS");if(F(t))throw new h("INVALID_ARGS","open <app> <url> requires an app target as the first argument");if(!F(n))throw new h("INVALID_ARGS","open <app> <url> requires a valid URL target");return await f.open(t,{activity:a?.activity,appBundleId:a?.appBundleId,url:n}),{app:t,url:n}}return await f.open(t,{activity:a?.activity,appBundleId:a?.appBundleId}),{app:t}}case"close":{let e=i[0];if(!e)return{closed:"session"};return await f.close(e),{app:e}}case"press":{let[t,n]=i.map(Number);if(Number.isNaN(t)||Number.isNaN(n))throw new h("INVALID_ARGS","press requires x y");let r=tU(a?.count??1,"count",1,200),d=tU(a?.intervalMs??0,"interval-ms",0,1e4),u=tU(a?.holdMs??0,"hold-ms",0,1e4),p=tU(a?.jitterPx??0,"jitter-px",0,100),m=a?.doubleTap===!0;if(m&&u>0)throw new h("INVALID_ARGS","double-tap cannot be combined with hold-ms");if(m&&p>0)throw new h("INVALID_ARGS","double-tap cannot be combined with jitter-px");if(o=e,s=r,l=u,c=p,"ios"===o.platform&&s>1&&0===l&&0===c)return await td(e,{command:"tapSeries",x:t,y:n,count:r,intervalMs:d,doubleTap:m,appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{x:t,y:n,count:r,intervalMs:d,holdMs:u,jitterPx:p,doubleTap:m,timingMode:"runner-series"};return await tG(r,d,async e=>{let[i,r]=function(e,t){if(t<=0)return[0,0];let[i,n]=tV[e%tV.length];return[i*t,n*t]}(e,p),a=t+i,o=n+r;m?await f.doubleTap(a,o):u>0?await f.longPress(a,o,u):await f.tap(a,o)}),{x:t,y:n,count:r,intervalMs:d,holdMs:u,jitterPx:p,doubleTap:m}}case"swipe":{let t=Number(i[0]),n=Number(i[1]),r=Number(i[2]),o=Number(i[3]);if([t,n,r,o].some(Number.isNaN))throw new h("INVALID_ARGS","swipe requires x1 y1 x2 y2 [durationMs]");let s=tU(i[4]?Number(i[4]):250,"durationMs",16,1e4),l="ios"===e.platform?60:s,c=tU(a?.count??1,"count",1,200),u=tU(a?.pauseMs??0,"pause-ms",0,1e4),m=a?.pattern??"one-way";if("one-way"!==m&&"ping-pong"!==m)throw new h("INVALID_ARGS",`Invalid pattern: ${m}`);if(d=e,p=c,"ios"===d.platform&&p>1)return await td(e,{command:"dragSeries",x:t,y:n,x2:r,y2:o,durationMs:l,count:c,pauseMs:u,pattern:m,appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{x1:t,y1:n,x2:r,y2:o,durationMs:s,effectiveDurationMs:l,timingMode:"runner-series",count:c,pauseMs:u,pattern:m};return await tG(c,u,async e=>{"ping-pong"===m&&e%2==1?await f.swipe(r,o,t,n,l):await f.swipe(t,n,r,o,l)}),{x1:t,y1:n,x2:r,y2:o,durationMs:s,effectiveDurationMs:l,timingMode:"ios"===e.platform?"safe-normalized":"direct",count:c,pauseMs:u,pattern:m}}case"long-press":{let e=Number(i[0]),t=Number(i[1]),n=i[2]?Number(i[2]):void 0;if(Number.isNaN(e)||Number.isNaN(t))throw new h("INVALID_ARGS","long-press requires x y [durationMs]");return await f.longPress(e,t,n),{x:e,y:t,durationMs:n}}case"focus":{let[e,t]=i.map(Number);if(Number.isNaN(e)||Number.isNaN(t))throw new h("INVALID_ARGS","focus requires x y");return await f.focus(e,t),{x:e,y:t}}case"type":{let e=i.join(" ");if(!e)throw new h("INVALID_ARGS","type requires text");return await f.type(e),{text:e}}case"fill":{let e=Number(i[0]),t=Number(i[1]),n=i.slice(2).join(" ");if(Number.isNaN(e)||Number.isNaN(t)||!n)throw new h("INVALID_ARGS","fill requires x y text");return await f.fill(e,t,n),{x:e,y:t,text:n}}case"scroll":{let e=i[0],t=i[1]?Number(i[1]):void 0;if(!e)throw new h("INVALID_ARGS","scroll requires direction");return await f.scroll(e,t),{direction:e,amount:t}}case"scrollintoview":{let e=i.join(" ").trim();if(!e)throw new h("INVALID_ARGS","scrollintoview requires text");let t=await f.scrollIntoView(e);if(t?.attempts)return{text:e,attempts:t.attempts};return{text:e}}case"pinch":{if("android"===e.platform)throw new h("UNSUPPORTED_OPERATION","Android pinch is not supported in current adb backend; requires instrumentation-based backend.");let t=Number(i[0]),n=i[1]?Number(i[1]):void 0,r=i[2]?Number(i[2]):void 0;if(Number.isNaN(t)||t<=0)throw new h("INVALID_ARGS","pinch requires scale > 0");return await td(e,{command:"pinch",scale:t,x:n,y:r,appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{scale:t,x:n,y:r}}case"screenshot":{let e=i[0]??r??`./screenshot-${Date.now()}.png`;return await u.mkdir(n.dirname(e),{recursive:!0}),await f.screenshot(e),{path:e}}case"back":if("ios"===e.platform)return await td(e,{command:"back",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{action:"back"};return await ec(e),{action:"back"};case"home":if("ios"===e.platform)return await td(e,{command:"home",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{action:"home"};return await ed(e),{action:"home"};case"app-switcher":if("ios"===e.platform)return await td(e,{command:"appSwitcher",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{action:"app-switcher"};return await eu(e),{action:"app-switcher"};case"settings":{let[t,n,r]=i;if("ios"===e.platform)return await e2(e,t,n,r??a?.appBundleId),{setting:t,state:n};return await eI(e,t,n),{setting:t,state:n}}case"snapshot":{if("ios"===e.platform){let t=await td(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}),i=t.nodes??[];if(0===i.length&&"simulator"===e.kind)throw new h("COMMAND_FAILED","XCTest snapshot returned 0 nodes on iOS simulator.");return{nodes:i,truncated:t.truncated??!1,backend:"xctest"}}let t=await eA(e,{interactiveOnly:a?.snapshotInteractiveOnly,compact:a?.snapshotCompact,depth:a?.snapshotDepth,scope:a?.snapshotScope,raw:a?.snapshotRaw});return{nodes:t.nodes??[],truncated:t.truncated??!1,backend:"android"}}default:throw new h("INVALID_ARGS",`Unknown command: ${t}`)}}let tV=[[0,0],[1,0],[0,1],[-1,0],[0,-1],[1,1],[-1,1],[1,-1],[-1,-1]];function tU(e,t,i,n){if(!Number.isFinite(e)||!Number.isInteger(e)||e<i||e>n)throw new h("INVALID_ARGS",`${t} must be an integer between ${i} and ${n}`);return e}async function tG(e,t,i){for(let n=0;n<e;n+=1)await i(n),n<e-1&&t>0&&await tj(t)}async function tj(e){await new Promise(t=>setTimeout(t,e))}let tB={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,device:!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=tB[e];if(!i)return!0;let n=i[t.platform];return!!n&&!0===n[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 n of e)t.has(n)||(t.add(n),i.push(n));return i}let tH=/^-?\d+(\.\d+)?$/,tz=new Map([["--count","count"],["--interval-ms","intervalMs"],["--hold-ms","holdMs"],["--jitter-px","jitterPx"]]),tX=new Map([["--count","count"],["--pause-ms","pauseMs"]]);function tY(e){return"click"===e||"press"===e}function tK(e){let t=e.trim();return t.startsWith("@")||tH.test(t)?t:JSON.stringify(t)}function tZ(e,t){let i=t.flags??{};if(tY(t.command)){"number"==typeof i.count&&e.push("--count",String(i.count)),"number"==typeof i.intervalMs&&e.push("--interval-ms",String(i.intervalMs)),"number"==typeof i.holdMs&&e.push("--hold-ms",String(i.holdMs)),"number"==typeof i.jitterPx&&e.push("--jitter-px",String(i.jitterPx)),!0===i.doubleTap&&e.push("--double-tap");return}"swipe"===t.command&&("number"==typeof i.count&&e.push("--count",String(i.count)),"number"==typeof i.pauseMs&&e.push("--pause-ms",String(i.pauseMs)),("one-way"===i.pattern||"ping-pong"===i.pattern)&&e.push("--pattern",i.pattern))}function tQ(e,t){let i=[],n={},r=tY(e)?tz:"swipe"===e?tX:void 0;for(let a=0;a<t.length;a+=1){let o=t[a];if(tY(e)&&"--double-tap"===o){n.doubleTap=!0;continue}let s=r?.get(o);if(s&&a+1<t.length){let e=function(e){if(!e)return null;let t=Number(e);return!Number.isFinite(t)||t<0?null:Math.floor(t)}(t[a+1]);null!==e&&(n[s]=e),a+=1;continue}if("swipe"===e&&"--pattern"===o&&a+1<t.length){let e=t[a+1];("one-way"===e||"ping-pong"===e)&&(n.pattern=e),a+=1;continue}i.push(o)}return{positionals:i,flags:n}}class t0{sessions=new Map;sessionsDir;constructor(e){this.sessionsDir=e}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=t0.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:n,serial:r,out:a,verbose:o,snapshotInteractiveOnly:s,snapshotCompact:l,snapshotDepth:c,snapshotScope:d,snapshotRaw:u,relaunch:p,saveScript:f,noRecord:m,count:h,intervalMs:w,holdMs:g,jitterPx:v,doubleTap:I,pauseMs:A,pattern:y}=e;return{platform:t,device:i,udid:n,serial:r,out:a,verbose:o,snapshotInteractiveOnly:s,snapshotCompact:l,snapshotDepth:c,snapshotScope:d,snapshotRaw:u,relaunch:p,saveScript:f,noRecord:m,count:h,intervalMs:w,holdMs:g,jitterPx:v,doubleTap:I,pauseMs:A,pattern:y}}(t.flags),result:t.result}))}writeSessionLog(e){try{if(!e.recordSession)return;let t=this.resolveScriptPath(e),i=n.dirname(t);w.existsSync(i)||w.mkdirSync(i,{recursive:!0});let r=function(e,t){let i=[],n=e.device.name.replace(/"/g,'\\"'),r=e.device.kind?` kind=${e.device.kind}`:"";for(let a of(i.push(`context platform=${e.device.platform} device="${n}"${r} theme=unknown`),t))a.flags?.noRecord||i.push(function(e){let t=[e.command];if(tY(e.command)){let i=e.positionals?.[0];if(i){if(i.startsWith("@")){t.push(tK(i));let n=e.result?.refLabel;return"string"==typeof n&&n.trim().length>0&&t.push(tK(n)),tZ(t,e),t.join(" ")}if(1===e.positionals.length)return t.push(tK(i)),tZ(t,e),t.join(" ")}}if("fill"===e.command){let i=e.positionals?.[0];if(i&&i.startsWith("@")){t.push(tK(i));let n=e.result?.refLabel,r=e.positionals.slice(1).join(" ");return"string"==typeof n&&n.trim().length>0&&t.push(tK(n)),r&&t.push(tK(r)),t.join(" ")}}if("get"===e.command){let i=e.positionals?.[0],n=e.positionals?.[1];if(i&&n){if(t.push(tK(i)),t.push(tK(n)),n.startsWith("@")){let i=e.result?.refLabel;"string"==typeof i&&i.trim().length>0&&t.push(tK(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",tK(e.flags.snapshotScope)),e.flags?.snapshotRaw&&t.push("--raw"),t.join(" ");if("open"===e.command){for(let i of e.positionals??[])t.push(tK(i));return e.flags?.relaunch&&t.push("--relaunch"),t.join(" ")}for(let i of e.positionals??[])t.push(tK(i));return tZ(t,e),t.join(" ")}(a));return`${i.join("\n")}
|
|
14
|
+
`}(e,this.buildOptimizedActions(e));w.writeFileSync(t,r)}catch{}}defaultTracePath(e){let t=e.name.replace(/[^a-zA-Z0-9._-]/g,"_"),i=new Date().toISOString().replace(/[:.]/g,"-");return n.join(this.sessionsDir,`${t}-${i}.trace.log`)}static expandHome(e){return e.startsWith("~/")?n.join(g.homedir(),e.slice(2)):n.resolve(e)}resolveScriptPath(e){if(e.saveScriptPath)return t0.expandHome(e.saveScriptPath);w.existsSync(this.sessionsDir)||w.mkdirSync(this.sessionsDir,{recursive:!0});let t=e.name.replace(/[^a-zA-Z0-9._-]/g,"_"),i=new Date(e.createdAt).toISOString().replace(/[:.]/g,"-");return n.join(this.sessionsDir,`${t}-${i}.ad`)}buildOptimizedActions(e){let t=[];for(let i of e.actions){if("snapshot"===i.command)continue;let n=Array.isArray(i.result?.selectorChain)&&i.result?.selectorChain.every(e=>"string"==typeof e)?i.result.selectorChain:[];if(n.length>0&&(tY(i.command)||"fill"===i.command||"get"===i.command)){let e=n.join(" || ");if(tY(i.command)){t.push({...i,positionals:[e]});continue}if("fill"===i.command){let n=tW(i);if(n.length>0){t.push({...i,positionals:[e,n]});continue}}if("get"===i.command){let n=i.positionals?.[0];if("text"===n||"attrs"===n){t.push({...i,positionals:[n,e]});continue}}}if(tY(i.command)||"fill"===i.command||"get"===i.command){let n=i.result?.refLabel;"string"==typeof n&&n.trim().length>0&&t.push({ts:i.ts,command:"snapshot",positionals:[],flags:{platform:e.device.platform,snapshotInteractiveOnly:!0,snapshotCompact:!0,snapshotScope:n.trim()},result:{scope:n.trim()}})}t.push(i)}return t}}function t1(e,t,i,n){return{appBundleId:i,activity:t?.activity,verbose:t?.verbose,logPath:e,traceLogPath:n,snapshotInteractiveOnly:t?.snapshotInteractiveOnly,snapshotCompact:t?.snapshotCompact,snapshotDepth:t?.snapshotDepth,snapshotScope:t?.snapshotScope,snapshotRaw:t?.snapshotRaw,count:t?.count,intervalMs:t?.intervalMs,holdMs:t?.holdMs,jitterPx:t?.jitterPx,doubleTap:t?.doubleTap,pauseMs:t?.pauseMs,pattern:t?.pattern}}let t2=eL(process.env.AGENT_DEVICE_IOS_DEVICE_READY_TIMEOUT_MS,15e3,1e3);async function t3(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 t4(e.id)}if("android"===e.platform){let{waitForAndroidBoot:t}=await Promise.resolve().then(()=>({waitForAndroidBoot:$}));await t(e.id)}}async function t4(e){let t=n.join(g.tmpdir(),`agent-device-ready-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`),i=Math.max(1,Math.ceil(t2/1e3));try{let n=await d("xcrun",["devicectl","device","info","details","--device",e,"--json-output",t,"--timeout",String(i)],{allowFailure:!0,timeoutMs:t2+3e3}),r=String(n.stdout??""),a=String(n.stderr??""),o=await t5(t);if(0===n.exitCode){if(!o.parsed)throw new h("COMMAND_FAILED","iOS device readiness probe failed",{kind:"probe_inconclusive",deviceId:e,stdout:r,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 h("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 h("COMMAND_FAILED","iOS device is not ready for automation",{kind:"not_ready",deviceId:e,stdout:r,stderr:a,exitCode:n.exitCode,tunnelState:o?.tunnelState,hint:t8(r,a)})}catch(t){if(t instanceof h&&"COMMAND_FAILED"===t.code){if("not_ready"===("string"==typeof t.details?.kind?t.details.kind:""))throw t;let i=t.details??{},n=String(i.stdout??""),r=String(i.stderr??""),a=Number(i.timeoutMs??t2),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 h("COMMAND_FAILED","iOS device readiness probe failed",{deviceId:e,cause:t.message,timeoutMs:a,stdout:n,stderr:r,hint:n||r?t8(n,r):o},t)}throw new h("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 u.rm(t,{force:!0}).catch(()=>{})}}async function t5(e){try{let t=await u.readFile(e,"utf8"),i=JSON.parse(t),n=function(e){let t=e?.result;if(!t||"object"!=typeof t)return{};let i=t.connectionProperties?.tunnelState,n=t.device?.connectionProperties?.tunnelState,r="string"==typeof i?i:"string"==typeof n?n:void 0;return r?{tunnelState:r}:{}}(i);return{parsed:!0,tunnelState:n.tunnelState}}catch{return{parsed:!1}}}function t8(e,t){let i=eB(e,t);return i||(`${e}
|
|
15
|
+
${t}`.toLowerCase().includes("timed out waiting for all destinations")?"Xcode destination did not become available in time. Keep device unlocked and retry.":ej)}function t6(e){return e.map((e,t)=>({...e,ref:`e${t+1}`}))}function t9(e){let t=e.trim();return t.startsWith("@")?t.slice(1)||null:t.startsWith("e")?t:null}function t7(e,t){return e.find(e=>e.ref===t)??null}function ie(e){return{x:Math.round(e.x+e.width/2),y:Math.round(e.y+e.height/2)}}function it(e,t){let i=t.toLowerCase();return e.find(e=>{let t=(e.label??"").toLowerCase(),n=(e.value??"").toLowerCase(),r=(e.identifier??"").toLowerCase();return t.includes(i)||n.includes(i)||r.includes(i)})??null}function ii(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&&ir(i)?i:function(e,t){if(!e.rect)return;let i=e.rect.y+e.rect.height/2,n=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||!ir(t))continue;let r=Math.abs(e.rect.y+e.rect.height/2-i);(!n||r<n.distance)&&(n={label:t,distance:r})}return n?.label}(e,t)??(i&&ir(i)?i:void 0)}function ir(e){let t=e.trim();return!(!t||/^(true|false)$/i.test(t)||/^\d+$/.test(t))}function ia(e){let t=[],i=[];for(let n of e){let e=n.depth??0;for(;t.length>0&&e<=t[t.length-1];)t.pop();let r=io(n.type??""),a=[n.label,n.value,n.identifier].map(e=>"string"==typeof e?e.trim():"").find(e=>e&&e.length>0),o=!!a&&ir(a);if(("group"===r||"ioscontentgroup"===r)&&!o){t.push(e);continue}let s=Math.max(0,e-t.length);i.push({...n,depth:s})}return i}function io(e){let t=e.trim().replace(/XCUIElementType/gi,"").toLowerCase(),i=Math.max(t.lastIndexOf("."),t.lastIndexOf("/"));return -1!==i&&(t=t.slice(i+1)),t}function is(e,t){let i=io(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 il(e){return[e.label,e.value,e.identifier].map(e=>"string"==typeof e?e.trim():"").filter(e=>e.length>0)[0]??""}let ic=new Set(["id","role","text","label","value"]),id=new Set(["visible","hidden","editable","selected","enabled","hittable"]),iu=new Set([...ic,...id]);function ip(e){let t=e.trim();if(!t)throw new h("INVALID_ARGS","Selector expression cannot be empty");let i=function(e){let t=[],i="",n=null;for(let r=0;r<e.length;r+=1){let a=e[r];if(('"'===a||"'"===a)&&!ik(e,r)){n?n===a&&(n=null):n=a,i+=a;continue}if(!n&&"|"===a&&"|"===e[r+1]){let n=i.trim();if(!n)throw new h("INVALID_ARGS",`Invalid selector fallback expression: ${e}`);t.push(n),i="",r+=1;continue}i+=a}let r=i.trim();if(!r)throw new h("INVALID_ARGS",`Invalid selector fallback expression: ${e}`);return t.push(r),t}(t);if(0===i.length)throw new h("INVALID_ARGS","Selector expression cannot be empty");return{raw:t,selectors:i.map(e=>(function(e){let t=e.trim();if(!t)throw new h("INVALID_ARGS","Selector segment cannot be empty");let i=function(e){let t=[],i="",n=null;for(let r=0;r<e.length;r+=1){let a=e[r];if(('"'===a||"'"===a)&&!ik(e,r)){n?n===a&&(n=null):n=a,i+=a;continue}if(!n&&/\s/.test(a)){i.trim().length>0&&t.push(i.trim()),i="";continue}i+=a}if(n)throw new h("INVALID_ARGS",`Unclosed quote in selector: ${e}`);return i.trim().length>0&&t.push(i.trim()),t}(t);if(0===i.length)throw new h("INVALID_ARGS",`Invalid selector segment: ${e}`);return{raw:t,terms:i.map(iS)}})(e))}}function im(e){try{return ip(e)}catch{return null}}function ih(e,t,i){let n=i.requireRect??!1,r=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 n=0,r=null,a=null,o=!1;for(let s of e){if(i.requireRect&&!s.rect||!ib(s,t,i.platform))continue;if(n+=1,r||(r=s),!a){a=s;continue}let e=function(e,t){let i=e.depth??0,n=t.depth??0;if(i!==n)return i>n?1:-1;let r=ix(e),a=ix(t);return r!==a?r<a?1:-1:0}(s,a);if(e>0){a=s,o=!1;continue}0===e&&(o=!0)}return{count:n,firstNode:r,disambiguated:o?null:a}}(e,l,{platform:i.platform,requireRect:n});if(o.push({selector:l.raw,matches:c.count}),0!==c.count&&c.firstNode){if(r&&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 iw(e,t,i){let n=i.requireRect??!1,r=[];for(let a=0;a<t.selectors.length;a+=1){let o=t.selectors[a],s=function(e,t,i){let n=0;for(let r of e)(!i.requireRect||r.rect)&&ib(r,t,i.platform)&&(n+=1);return n}(e,o,{platform:i.platform,requireRect:n});if(r.push({selector:o.raw,matches:s}),s>0)return{selectorIndex:a,selector:o,matches:s,diagnostics:r}}return null}function ig(e,t,i){let n=i.unique??!0;if(0===t.length)return`Selector did not match: ${e.raw}`;let r=t.map(e=>`${e.selector} -> ${e.matches}`).join(", ");return n?`Selector did not resolve uniquely (${r})`:`Selector did not match (${r})`}function iv(e,t={}){if(0===e.length)return null;let i=t.preferTrailingValue??!1,n=0,r=[];for(;n<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 iu.has(e)}return iu.has(t.toLowerCase())}(e[n]);){n+=1;let t=e.slice(0,n).join(" ").trim();t&&im(t)&&r.push(n)}if(0===r.length)return null;let a=r[r.length-1];if(i){for(let t=r.length-1;t>=0;t-=1)if(r[t]<e.length){a=r[t];break}}let o=e.slice(0,a).join(" ").trim();return o?{selectorExpression:o,rest:e.slice(a)}:null}function iI(e){let t=e[0]??"",i=iv(e.slice(1),{preferTrailingValue:"text"===t});return{predicate:t,split:i}}function iA(e){return!0===e.hittable||!!e.rect&&e.rect.width>0&&e.rect.height>0}function iy(e,t){return is(e.type??"",t)&&!1!==e.enabled}function iN(e,t,i={}){let n=[],r=io(e.type??""),a=iE(e.identifier),o=iE(e.label),s=iE(e.value),l=iE(il(e)),c="fill"===i.action;a&&n.push(`id=${iM(a)}`),r&&o&&n.push(c?`role=${iM(r)} label=${iM(o)} editable=true`:`role=${iM(r)} label=${iM(o)}`),o&&n.push(c?`label=${iM(o)} editable=true`:`label=${iM(o)}`),s&&n.push(c?`value=${iM(s)} editable=true`:`value=${iM(s)}`),l&&l!==o&&l!==s&&n.push(c?`text=${iM(l)} editable=true`:`text=${iM(l)}`),r&&c&&!n.some(e=>e.includes("editable=true"))&&n.push(`role=${iM(r)} editable=true`);let d=tJ(n);return 0===d.length&&r&&d.push(c?`role=${iM(r)} editable=true`:`role=${iM(r)}`),0===d.length&&iA(e)&&d.push("visible=true"),d}function iS(e){let t=e.trim();if(!t)throw new h("INVALID_ARGS","Empty selector term");let i=t.indexOf("=");if(-1===i){let i=t.toLowerCase();if(!id.has(i))throw new h("INVALID_ARGS",`Invalid selector term "${e}", expected key=value`);return{key:i,value:!0}}let n=t.slice(0,i).trim().toLowerCase(),r=t.slice(i+1).trim();if(!iu.has(n))throw new h("INVALID_ARGS",`Unknown selector key: ${n}`);if(!r)throw new h("INVALID_ARGS",`Missing selector value for key: ${n}`);if(id.has(n)){let e,t="true"===(e=iD(r).toLowerCase())||"false"!==e&&null;if(null===t)throw new h("INVALID_ARGS",`Invalid boolean value for ${n}: ${r}`);return{key:n,value:t}}return{key:n,value:iD(r)}}function ib(e,t,i){return t.terms.every(t=>(function(e,t,i){switch(t.key){case"id":return i_(e.identifier,String(t.value));case"role":var n,r;return n=e.type,r=String(t.value),function(e){return io(e)}(n??"")===function(e){return io(e)}(r);case"label":return i_(e.label,String(t.value));case"value":return i_(e.value,String(t.value));case"text":{let i=iO(String(t.value));return iO(il(e))===i}case"visible":return iA(e)===!!t.value;case"hidden":return!iA(e)==!!t.value;case"editable":return iy(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 iD(e){let t=e.trim();return t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1).replace(/\\(["'])/g,"$1"):t}function i_(e,t){return iO(e??"")===iO(t)}function iO(e){return e.trim().toLowerCase().replace(/\s+/g," ")}function iM(e){return JSON.stringify(e)}function iE(e){if(!e)return null;let t=e.trim();return t||null}function ix(e){return e.rect?e.rect.width*e.rect.height:1/0}function ik(e,t){let i=0;for(let n=t-1;n>=0&&"\\"===e[n];n-=1)i+=1;return i%2==1}let iL='iOS appstate requires an active session on the target device. Run open first (for example: open --session sim --platform ios --device "<name>" <app>).',iR=["platform","device","udid","serial","verbose","out"];function iC(e,t,i){return t||iT(i)?null:{ok:!1,error:{code:"INVALID_ARGS",message:`${e} requires an active session or an explicit device selector (e.g. --platform ios).`}}}function iT(e){return!!(e?.platform||e?.device||e?.udid||e?.serial)}async function iP(e){let t=iT(e.flags)||!e.session?await e.resolveTargetDeviceFn(e.flags??{}):e.session.device;return!1!==e.ensureReady&&await e.ensureReadyFn(t),t}let i$={ios:async(e,t,i)=>{let{reinstallIosApp:n}=await Promise.resolve().then(()=>({reinstallIosApp:e0}));return await n(e,t,i)},android:async(e,t,i)=>{let{reinstallAndroidApp:n}=await Promise.resolve().then(()=>({reinstallAndroidApp:eo}));return await n(e,t,i)}};async function iF(e,t,i){if("ios"===e.platform&&t)return F(t)?"device"===e.kind?V(i,t):void 0:await iV(e,t)}async function iV(e,t){try{let{resolveIosApp:i}=await Promise.resolve().then(()=>({resolveIosApp:ez}));return await i(e,t)}catch{return}}async function iU(e){let{req:t,sessionName:i,sessionStore:n,ensureReady:r,resolveDevice:a}=e,o=n.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=iC("appstate",o,s);if(l)return l;let c=o?.device.platform==="ios"&&!!o&&(!iT(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:iL}};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 iP({session:o,flags:s,ensureReadyFn:r,resolveTargetDeviceFn:a,ensureReady:!0});if("ios"===d.platform)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:iL}};let{getAndroidAppState:u}=await Promise.resolve().then(()=>({getAndroidAppState:Z})),p=await u(d);return{ok:!0,data:{platform:"android",package:p.package,activity:p.activity}}}async function iG(e){let{req:t,sessionName:i,logPath:n,sessionStore:r,invoke:a,dispatch:o,ensureReady:s,resolveTargetDevice:l,reinstallOps:c=i$}=e,d=o??tF,u=s??t3,f=l??t$,m=t.command;if("session_list"===m)return{ok:!0,data:{sessions:r.toArray().map(e=>({name:e.name,platform:e.device.platform,device:e.device.name,id:e.device.id,createdAt:e.createdAt}))}};if("devices"===m)try{let e=[];if(t.flags?.platform==="android"){let{listAndroidDevices:t}=await Promise.resolve().then(()=>({listAndroidDevices:T}));e.push(...await t())}else if(t.flags?.platform==="ios"){let{listIosDevices:t}=await Promise.resolve().then(()=>({listIosDevices:eC}));e.push(...await t())}else{let{listAndroidDevices:t}=await Promise.resolve().then(()=>({listAndroidDevices:T})),{listIosDevices:i}=await Promise.resolve().then(()=>({listIosDevices:eC}));try{e.push(...await t())}catch{}try{e.push(...await i())}catch{}}return{ok:!0,data:{devices:e}}}catch(t){let e=p(t);return{ok:!1,error:{code:e.code,message:e.message,details:e.details}}}if("apps"===m){let e=r.get(i),n=t.flags??{},a=iC(m,e,n);if(a)return a;let o=await iP({session:e,flags:n,ensureReadyFn:u,resolveTargetDeviceFn:f,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:e3}));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:X}));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"===m){let e=r.get(i),n=t.flags??{},a=iC(m,e,n);if(a)return a;let o=await iP({session:e,flags:n,ensureReadyFn:u,resolveTargetDeviceFn:f,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"===m)return await iU({req:t,sessionName:i,sessionStore:r,ensureReady:u,resolveDevice:f});if("reinstall"===m){let e,n=r.get(i),a=t.flags??{},o=iC(m,n,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=t0.expandHome(l);if(!w.existsSync(d))return{ok:!1,error:{code:"INVALID_ARGS",message:`App binary not found: ${d}`}};let p=await iP({session:n,flags:a,ensureReadyFn:u,resolveTargetDeviceFn:f,ensureReady:!1});if(!tq("reinstall",p))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"reinstall is not supported on this device"}};if("ios"===p.platform){let t=await c.ios(p,s,d);e={platform:"ios",appId:t.bundleId,bundleId:t.bundleId}}else{let t=await c.android(p,s,d);e={platform:"android",appId:t.package,package:t.package}}let h={app:s,appPath:d,...e};return n&&r.recordAction(n,{command:m,positionals:t.positionals??[],flags:t.flags??{},result:h}),{ok:!0,data:h}}if("open"===m){let e=t.flags?.relaunch===!0;if(r.has(i)){let a=r.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&&F(s))return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch does not support URL targets."}};await u(a.device);let l=await iF(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,{...t1(n,t.flags,l??a.appBundleId,a.trace?.outPath)})}await d(a.device,"open",c,t.flags?.out,{...t1(n,t.flags,l)});let p={...a,appBundleId:l,appName:s,recordSession:a.recordSession||!!t.flags?.saveScript,snapshot:void 0};return r.recordAction(p,{command:m,positionals:c,flags:t.flags??{},result:{session:i,appName:s,appBundleId:l}}),r.set(i,p),{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&&F(a))return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch does not support URL targets."}};let o=await f(t.flags??{}),s=r.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 u(o);let l=await iF(o,a);if(e&&a){let e=l??a;await d(o,"close",[e],t.flags?.out,{...t1(n,t.flags,l)})}await d(o,"open",t.positionals??[],t.flags?.out,{...t1(n,t.flags,l)});let c={name:i,device:o,createdAt:Date.now(),appBundleId:l,appName:a,recordSession:!!t.flags?.saveScript,actions:[]};return r.recordAction(c,{command:m,positionals:t.positionals??[],flags:t.flags??{},result:{session:i}}),r.set(i,c),{ok:!0,data:{session:i}}}if("replay"===m){let e=t.positionals?.[0];if(!e)return{ok:!1,error:{code:"INVALID_ARGS",message:"replay requires a path"}};try{let o=t0.expandHome(e),s=w.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 n=i+1,r=!1;for(;n<e.length;){let t=e[n];if('"'===t&&!r)break;r="\\"===t&&!r,"\\"!==t&&(r=!1),n+=1}if(n>=e.length)throw new h("INVALID_ARGS",`Invalid replay script line: ${e}`);let a=e.slice(i,n+1);t.push(JSON.parse(a)),i=n+1;continue}let n=i;for(;n<e.length&&!/\s/.test(e[n]);)n+=1;t.push(e.slice(i,n)),i=n}return t}(t);if(0===i.length)return null;let[n,...r]=i;if("context"===n)return null;let a={ts:Date.now(),command:n,positionals:[],flags:{}};if("snapshot"===n){a.positionals=[];for(let e=0;e<r.length;e+=1){let t=r[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<r.length){let t=Number(r[e+1]);Number.isFinite(t)&&t>=0&&(a.flags.snapshotDepth=Math.floor(t)),e+=1;continue}if(("-s"===t||"--scope"===t)&&e+1<r.length){a.flags.snapshotScope=r[e+1],e+=1;continue}if("--backend"===t&&e+1<r.length){e+=1;continue}}return a}if("open"===n){a.positionals=[];for(let e=0;e<r.length;e+=1){let t=r[e];if("--relaunch"===t){a.flags.relaunch=!0;continue}a.positionals.push(t)}return a}if(tY(n)){let e=tQ(n,r);if(Object.assign(a.flags,e.flags),0===e.positionals.length)return a;let t=e.positionals[0];if(t.startsWith("@"))return a.positionals=[t],e.positionals[1]&&(a.result={refLabel:e.positionals[1]}),a;let i=e.positionals[0],o=e.positionals[1];return iz(i)&&iz(o)&&e.positionals.length>=2?a.positionals=[i,o]:a.positionals=[e.positionals.join(" ")],a}if("fill"===n){if(r.length<2)return a.positionals=r,a;let e=r[0];return e.startsWith("@")?(r.length>=3?(a.positionals=[e,r.slice(2).join(" ")],a.result={refLabel:r[1]}):a.positionals=[e,r[1]],a):(a.positionals=[e,r.slice(1).join(" ")],a)}if("get"===n){if(r.length<2)return a.positionals=r,a;let e=r[0],t=r[1];return t.startsWith("@")?(a.positionals=[e,t],r[2]&&(a.result={refLabel:r[2]})):a.positionals=[e,r.slice(1).join(" ")],a}if("swipe"===n){let e=tQ(n,r);return Object.assign(a.flags,e.flags),a.positionals=e.positionals,a}return a.positionals=r,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 iq(l,s,e,o);let f=await iW({action:s,sessionName:i,logPath:n,sessionStore:r,dispatch:d});if(!f)return iq(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 iq(l,f,e,o);p+=1}if(u&&p>0){let e=r.get(i);!function(e,t,i){let n=[];if(i){let e=i.device.name.replace(/"/g,'\\"'),t=i.device.kind?` kind=${i.device.kind}`:"";n.push(`context platform=${i.device.platform} device="${e}"${t} theme=unknown`)}for(let e of t)n.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",tK(e.flags.snapshotScope)),e.flags?.snapshotRaw&&t.push("--raw"),t.join(" ");if("open"===e.command){for(let i of e.positionals??[])t.push(tK(i));return e.flags?.relaunch&&t.push("--relaunch"),t.join(" ")}for(let i of e.positionals??[])t.push(tK(i));return tZ(t,e),t.join(" ")}(e));let r=`${n.join("\n")}
|
|
16
|
+
`,a=`${e}.tmp-${process.pid}-${Date.now()}`;w.writeFileSync(a,r),w.renameSync(a,e)}(o,c,e)}return{ok:!0,data:{replayed:c.length,healed:p,session:i}}}catch(t){let e=p(t);return{ok:!1,error:{code:e.code,message:e.message}}}}if("batch"===m)return await ij(t,i,a);if("close"===m){let e=r.get(i);return e?(t.positionals&&t.positionals.length>0&&await d(e.device,"close",t.positionals??[],t.flags?.out,{...t1(n,t.flags,e.appBundleId,e.trace?.outPath)}),"ios"===e.device.platform&&await tm(e.device.id),r.recordAction(e,{command:m,positionals:t.positionals??[],flags:t.flags??{},result:{session:i}}),t.flags?.saveScript&&(e.recordSession=!0),r.writeSessionLog(e),r.delete(i),{ok:!0,data:{session:i}}):{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session"}}}return null}async function ij(e,t,i){let n=e.flags?.batchOnError??"stop";if("stop"!==n)return{ok:!1,error:{code:"INVALID_ARGS",message:`Unsupported batch on-error mode: ${n}.`}};let r=e.flags?.batchMaxSteps??f;if(!Number.isInteger(r)||r<1||r>1e3)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid batch max-steps: ${String(e.flags?.batchMaxSteps)}`}};try{let n=o(e.flags?.batchSteps,r),a=Date.now(),s=[];for(let r=0;r<n.length;r+=1){let a=n[r],o=await iB(e,t,a,i,r+1);if(!o.ok)return{ok:!1,error:{code:o.error.code,message:`Batch failed at step ${o.step} (${a.command}): ${o.error.message}`,details:{...o.error.details??{},step:o.step,command:a.command,positionals:a.positionals,executed:r,total:n.length,partialResults:s}}};s.push(o.result)}return{ok:!0,data:{total:n.length,executed:n.length,totalDurationMs:Date.now()-a,results:s}}}catch(t){let e=p(t);return{ok:!1,error:{code:e.code,message:e.message,details:e.details}}}}async function iB(e,t,i,n,r){let a=Date.now(),o=await n({token:e.token,session:t,command:i.command,positionals:i.positionals,flags:function(e,t){let i={...t??{}};delete i.batchSteps,delete i.batchOnError,delete i.batchMaxSteps;let n=e??{};for(let e of iR)void 0===i[e]&&void 0!==n[e]&&(i[e]=n[e]);return i}(e.flags,i.flags)}),s=Date.now()-a;return o.ok?{ok:!0,step:r,result:{step:r,command:i.command,ok:!0,data:o.data??{},durationMs:s}}:{ok:!1,step:r,error:o.error}}function iq(e,t,i,n){if(e.ok)return e;let r=i+1,a=function(e){let t;return t=(e.positionals??[]).map(e=>tK(e)),[e.command,...t].join(" ")}(t),o={...e.error.details??{},replayPath:n,step:r,action:t.command,positionals:t.positionals??[]};return{ok:!1,error:{code:e.error.code,message:`Replay failed at step ${r} (${a}): ${e.error.message}`,details:o}}}async function iW(e){let{action:t,sessionName:i,logPath:n,sessionStore:r,dispatch:a}=e;if(!(tY(t.command)||["fill","get","is","wait"].includes(t.command)))return null;let o=r.get(i);if(!o)return null;let s=tY(t.command)||"fill"===t.command,l=tY(t.command)||"fill"===t.command||"get"===t.command&&t.positionals?.[0]==="text",c=await iJ(o,t,n,s,a,r);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),tY(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}=iI(e.positionals);i&&t.push(i.selectorExpression)}if("wait"===e.command){let{selectorExpression:i}=iH(e.positionals??[]);i&&t.push(i)}let n="string"==typeof e.result?.refLabel?e.result.refLabel.trim():"";if(n.length>0){let i=JSON.stringify(n);"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=im(e);if(!i)continue;let n=ih(c.nodes,i,{platform:o.device.platform,requireRect:s,requireUnique:!0,disambiguateAmbiguous:l});if(!n)continue;let r=iN(n.node,o.device.platform,{action:tY(t.command)?"click":"fill"===t.command?"fill":"get"}).join(" || ");if(tY(t.command))return{...t,positionals:[r]};if("fill"===t.command){let e=tW(t);if(!e)continue;return{...t,positionals:[r,e]}}if("get"===t.command){let e=t.positionals?.[0];if("text"!==e&&"attrs"!==e)continue;return{...t,positionals:[e,r]}}if("is"===t.command){let{predicate:e,split:i}=iI(t.positionals);if(!e)continue;let n=i?.rest.join(" ").trim()??"",a=[e,r];return"text"===e&&n.length>0&&a.push(n),{...t,positionals:a}}if("wait"===t.command){let{selectorTimeout:e}=iH(t.positionals??[]),i=[r];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 n=e.positionals?.[1];if(!n)return null;let r=im(n);if(!r)return null;let a=new Set,o=!1;for(let e of r.selectors)for(let t of e.terms)"role"===t.key&&"string"==typeof t.value&&a.add(io(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=il(e).trim();return!!/^\d+$/.test(t)&&(0===a.size||a.has(io(e.type??"")))});if(0===s.length||1!==tJ(s.map(e=>il(e).trim())).length)return null;let l=s[0];if(!l)return null;let c=iN(l,i.device.platform,{action:"get"});return 0===c.length?null:{...e,positionals:["text",c.join(" || ")]}}(t,c,o);return d||null}async function iJ(e,t,i,n,r,a){let o=await r(e.device,"snapshot",[],t.flags?.out,{...t1(i,{...t.flags??{},snapshotInteractiveOnly:n,snapshotCompact:n},e.appBundleId,e.trace?.outPath)}),s=o?.nodes??[],l={nodes:t6(t.flags?.snapshotRaw?s:ia(s)),truncated:o?.truncated,createdAt:Date.now(),backend:o?.backend};return e.snapshot=l,a.set(e.name,e),l}function iH(e){if(0===e.length)return{selectorExpression:null,selectorTimeout:null};let t=e[e.length-1],i=/^\d+$/.test(t??""),n=iv(i?e.slice(0,-1):e.slice());return!n||n.rest.length>0?{selectorExpression:null,selectorTimeout:null}:{selectorExpression:n.selectorExpression,selectorTimeout:i?t:null}}function iz(e){return!!e&&!Number.isNaN(Number(e))}function iX(e){if(!e)return null;let t=Number(e);return Number.isFinite(t)?t:null}async function iY(e){let{req:t,sessionName:i,logPath:n,sessionStore:r}=e,a=t.command;if("snapshot"===a){let{session:e,device:a}=await iK(r,i,t.flags);if(!tq("snapshot",a))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"snapshot is not supported on this device"}};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=t9(s.trim());if(!t)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref scope: ${s}`}};let i=t7(e.snapshot.nodes,t),n=i?ii(i,e.snapshot.nodes):void 0;if(!n)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${s} not found or has no label`}};s=n}return await iZ(e,a,async()=>{let l=await tF(a,"snapshot",[],t.flags?.out,{...t1(n,{...t.flags,snapshotScope:s},o,e?.trace?.outPath)}),c=l?.nodes??[],d=t6(t.flags?.snapshotRaw?c:ia(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 iQ(r,p,t,{nodes:d.length,truncated:l?.truncated??!1}),r.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 iK(r,i,t.flags),o=function(e){if(0===e.length)return null;let t=iX(e[0]);if(null!==t)return{kind:"sleep",durationMs:t};if("text"===e[0]){let t=iX(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=iX(e[e.length-1]);return{kind:"ref",rawRef:e[0],timeoutMs:t}}let i=iX(e[e.length-1]),n=iv(null!==i?e.slice(0,-1):e.slice());if(n&&0===n.rest.length){let e=im(n.selectorExpression);if(e)return{kind:"selector",selector:e,selectorExpression:n.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)),iQ(r,e,t,{waitedMs:o.durationMs}),{ok:!0,data:{waitedMs:o.durationMs}}):tq("wait",a)?await iZ(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 tF(a,"snapshot",[],t.flags?.out,{...t1(n,{...t.flags,snapshotInteractiveOnly:!1,snapshotCompact:!1},e?.appBundleId,e?.trace?.outPath)}),c=s?.nodes??[],d=t6(t.flags?.snapshotRaw?c:ia(c));e&&(e.snapshot={nodes:d,truncated:s?.truncated,createdAt:Date.now(),backend:s?.backend},r.set(i,e));let u=iw(d,o.selector,{platform:a.platform});if(u)return iQ(r,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=t9(o.rawRef);if(!t)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref: ${o.rawRef}`}};let i=t7(e.snapshot.nodes,t),n=i?ii(i,e.snapshot.nodes):void 0;if(!n)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${o.rawRef} not found or has no label`}};s=n,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 td(a,{command:"findText",text:s,appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:n,traceLogPath:e?.trace?.outPath});if(i?.found)return iQ(r,e,t,{text:s,waitedMs:Date.now()-d}),{ok:!0,data:{text:s,waitedMs:Date.now()-d}}}else if("android"===a.platform&&it(t6((await eA(a,{scope:s})).nodes??[]),s))return iQ(r,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 iK(r,i,t.flags),o=(t.positionals?.[0]??"get").toLowerCase();return tq("alert",a)?await iZ(e,a,async()=>{if("wait"===o){let i=iX(t.positionals?.[1])??1e4,o=Date.now();for(;Date.now()-o<i;){try{let i=await td(a,{command:"alert",action:"get",appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:n,traceLogPath:e?.trace?.outPath});return iQ(r,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 td(a,{command:"alert",action:"accept"===o||"dismiss"===o?o:"get",appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:n,traceLogPath:e?.trace?.outPath});return iQ(r,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 iK(r,i,t.flags);return tq("settings",s)?await iZ(o,s,async()=>{let i=o?.appBundleId,l=await tF(s,"settings",[e,a,i??""],t.flags?.out,{...t1(n,t.flags,i,o?.trace?.outPath)});return iQ(r,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 iK(e,t,i){let n=e.get(t),r=n?.device??await t$(i??{});return n||await t3(r),{session:n,device:r}}async function iZ(e,t,i){let n=!e&&"ios"===t.platform;try{return await i()}finally{n&&await tm(t.id)}}function iQ(e,t,i,n){t&&e.recordAction(t,{command:i.command,positionals:i.positionals??[],flags:i.flags??{},result:n})}function i0(e,t,i,n={}){let r=i2(i);if(!r)return{matches:[],score:0};let a=0,o=[];for(let i of e){if(n.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():""}(e??"");return i?i===t?2:+!!i.includes(t):0}(e.type,i);case"label":return i1(e.label,i);case"value":return i1(e.value,i);case"id":return i1(e.identifier,i);default:return Math.max(i1(e.label,i),i1(e.value,i),i1(e.identifier,i))}}(i,t,r);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 i1(e,t){let i=i2(e??"");return i?i===t?2:+!!i.includes(t):0}function i2(e){return e.trim().toLowerCase().replace(/\s+/g," ")}async function i3(e){let{req:t,sessionName:i,logPath:n,sessionStore:r,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 n=e[i]??"",r=e.slice(i+1);if(0===r.length)return{locator:t,query:n,action:"click"};let a=r[0].toLowerCase();if("get"===a){let e=r[1]?.toLowerCase();if("text"===e)return{locator:t,query:n,action:"get_text"};if("attrs"===e)return{locator:t,query:n,action:"get_attrs"};throw new h("INVALID_ARGS","find get only supports text or attrs")}if("wait"===a)return{locator:t,query:n,action:"wait",timeoutMs:iX(r[1])??void 0};if("exists"===a)return{locator:t,query:n,action:"exists"};if("click"===a)return{locator:t,query:n,action:"click"};if("focus"===a)return{locator:t,query:n,action:"focus"};if("fill"===a)return{locator:t,query:n,action:"fill",value:r.slice(1).join(" ")};if("type"===a)return{locator:t,query:n,action:"type",value:r.slice(1).join(" ")};throw new h("INVALID_ARGS",`Unsupported find action: ${r[0]}`)}(s);if(!c)return{ok:!1,error:{code:"INVALID_ARGS",message:"find requires a value"}};let f=r.get(i);if(!f&&"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 m=f?.device??await t$(t.flags??{});f||await t3(m);let w=f?.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 tF(m,"snapshot",[],t.flags?.out,{...t1(n,{...t.flags,snapshotScope:g,snapshotInteractiveOnly:v,snapshotCompact:v},w,f?.trace?.outPath)}),o=a?.nodes??[],s=t6(t.flags?.snapshotRaw?o:ia(o));return I=e,A=s,f&&(f.snapshot={nodes:s,truncated:a?.truncated,createdAt:Date.now(),backend:a?.backend},r.set(i,f)),{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(i0(e,l,c,{requireRect:!1}).matches[0])return f&&r.recordAction(f,{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=i0(N,l,c,{requireRect:v});if(v&&S.matches.length>1){let e=S.matches.slice(0,8).map(e=>{let t=il(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 D="click"===d||"focus"===d||"fill"===d||"type"===d?function(e,t){if(t.hittable)return t;let i=t,n=new Set;for(;void 0!==i.parentIndex&&!n.has(i.ref);){n.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}`,O={...t.flags??{},noRecord:!0};if("exists"===d)return f&&r.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{found:!0}}),{ok:!0,data:{found:!0}};if("get_text"===d){let e=il(b);return f&&r.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:_,action:"get text",text:e}}),{ok:!0,data:{ref:_,text:e,node:b}}}if("get_attrs"===d)return f&&r.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:_,action:"get attrs"}}),{ok:!0,data:{ref:_,node:b}};if("click"===d){let e=await a({token:t.token,session:i,command:"click",positionals:[_],flags:O});return e.ok&&f&&r.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:_,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:[_,u],flags:O});return e.ok&&f&&r.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:_,action:"fill"}}),e}if("focus"===d){let e=b.rect?ie(b.rect):null;if(!e)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};let i=await tF(m,"focus",[String(e.x),String(e.y)],t.flags?.out,{...t1(n,t.flags,f?.appBundleId,f?.trace?.outPath)});return f&&r.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:_,action:"focus"}}),{ok:!0,data:i??{ref:_}}}if("type"===d){if(!u)return{ok:!1,error:{code:"INVALID_ARGS",message:"find type requires text"}};let e=b.rect?ie(b.rect):null;if(!e)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};await tF(m,"focus",[String(e.x),String(e.y)],t.flags?.out,{...t1(n,t.flags,f?.appBundleId,f?.trace?.outPath)});let i=await tF(m,"type",[u],t.flags?.out,{...t1(n,t.flags,f?.appBundleId,f?.trace?.outPath)});return f&&r.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:_,action:"type"}}),{ok:!0,data:i??{ref:_}}}return null}async function i4(e){let{req:t,sessionName:i,sessionStore:r}=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=r.get(i),s=o?.device??await t$(t.flags??{});o||await t3(s);let c=o??{name:i,device:s,createdAt:Date.now(),actions:[]};if("start"===e){if(c.recording)return{ok:!1,error:{code:"INVALID_ARGS",message:"recording already in progress"}};let e=t.positionals?.[1]??`./recording-${Date.now()}.mp4`,o=n.resolve(e),d=n.dirname(o);if(w.existsSync(d)||w.mkdirSync(d,{recursive:!0}),!tq("record",s))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"record is only supported on iOS simulators"}};if("ios"===s.platform){let{child:e,wait:t}=l("xcrun",["simctl","io",s.id,"recordVideo",o],{allowFailure:!0});c.recording={platform:"ios",outPath:o,child:e,wait:t}}else{let e=`/sdcard/agent-device-recording-${Date.now()}.mp4`,{child:t,wait:i}=l("adb",["-s",s.id,"shell","screenrecord",e],{allowFailure:!0});c.recording={platform:"android",outPath:o,remotePath:e,child:t,wait:i}}return r.set(i,c),r.recordAction(c,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{action:"start"}}),{ok:!0,data:{recording:"started",outPath:e}}}if(!c.recording)return{ok:!1,error:{code:"INVALID_ARGS",message:"no active recording"}};let u=c.recording;u.child.kill("SIGINT");try{await u.wait}catch{}if("android"===u.platform&&u.remotePath)try{await d("adb",["-s",s.id,"pull",u.remotePath,u.outPath],{allowFailure:!0}),await d("adb",["-s",s.id,"shell","rm","-f",u.remotePath],{allowFailure:!0})}catch{}return c.recording=void 0,r.recordAction(c,{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=r.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]??r.defaultTracePath(o),i=t0.expandHome(e);return w.mkdirSync(n.dirname(i),{recursive:!0}),w.appendFileSync(i,""),o.trace={outPath:i,startedAt:Date.now()},r.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=t0.expandHome(t.positionals[1]);w.mkdirSync(n.dirname(e),{recursive:!0}),w.existsSync(s)?w.renameSync(s,e):w.appendFileSync(e,""),s=e}return o.trace=void 0,r.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 i5(e){let{req:t,sessionName:i,sessionStore:n,contextFromFlags:r}=e,a=e.dispatch??tF,o=t.command;if("press"===o){let e=n.get(i);if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let s=function(e){if(e.length<2)return null;let t=Number(e[0]),i=Number(e[1]);return Number.isFinite(t)&&Number.isFinite(i)?{x:t,y:i}:null}(t.positionals??[]);if(s){let i=await a(e.device,"press",[String(s.x),String(s.y)],t.flags?.out,{...r(t.flags,e.appBundleId,e.trace?.outPath)});return n.recordAction(e,{command:o,positionals:t.positionals??[String(s.x),String(s.y)],flags:t.flags??{},result:i??{x:s.x,y:s.y}}),{ok:!0,data:i??{x:s.x,y:s.y}}}let l="click",c=t.positionals?.[0]??"";if(c.startsWith("@")){let i=i9("press",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=t9(c);if(!s)return{ok:!1,error:{code:"INVALID_ARGS",message:`${o} requires a ref like @e2`}};let d=t7(e.snapshot.nodes,s);if(!d?.rect&&t.positionals.length>1){let i=t.positionals.slice(1).join(" ").trim();i.length>0&&(d=it(e.snapshot.nodes,i))}if(!d?.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${c} not found or has no bounds`}};let u=ii(d,e.snapshot.nodes),p=iN(d,e.device.platform,{action:l}),{x:f,y:m}=ie(d.rect),h=await a(e.device,"press",[String(f),String(m)],t.flags?.out,{...r(t.flags,e.appBundleId,e.trace?.outPath)});return n.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:s,x:f,y:m,refLabel:u,selectorChain:p}}),{ok:!0,data:{...h??{},ref:s,x:f,y:m}}}let d=(t.positionals??[]).join(" ").trim();if(!d)return{ok:!1,error:{code:"INVALID_ARGS",message:`${o} requires @ref, selector expression, or x y coordinates`}};let u=ip(d),p=await i8(e,t.flags,n,r,{interactiveOnly:!0},a),f=ih(p.nodes,u,{platform:e.device.platform,requireRect:!0,requireUnique:!0,disambiguateAmbiguous:!0});if(!f||!f.node.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:ig(u,f?.diagnostics??[],{unique:!0})}};let{x:m,y:h}=ie(f.node.rect),w=await a(e.device,"press",[String(m),String(h)],t.flags?.out,{...r(t.flags,e.appBundleId,e.trace?.outPath)}),g=iN(f.node,e.device.platform,{action:l}),v=ii(f.node,p.nodes);return n.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{x:m,y:h,selector:f.selector.raw,selectorChain:g,refLabel:v}}),{ok:!0,data:{...w??{},selector:f.selector.raw,x:m,y:h}}}if("fill"===o){let e=n.get(i);if(t.positionals?.[0]?.startsWith("@")){let i=i9("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 s=t9(t.positionals[0]);if(!s)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires a ref like @e2"}};let l=t.positionals.length>=3?t.positionals[1]:"",c=t.positionals.length>=3?t.positionals.slice(2).join(" "):t.positionals.slice(1).join(" ");if(!c)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after ref"}};let d=t7(e.snapshot.nodes,s);if(!d?.rect&&l&&(d=it(e.snapshot.nodes,l)),!d?.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${t.positionals[0]} not found or has no bounds`}};let u=d.type??"",p=u&&!is(u,e.device.platform)?`fill target ${t.positionals[0]} resolved to "${u}", attempting fill anyway.`:void 0,f=ii(d,e.snapshot.nodes),m=iN(d,e.device.platform,{action:"fill"}),{x:h,y:w}=ie(d.rect),g={...await a(e.device,"fill",[String(h),String(w),c],t.flags?.out,{...r(t.flags,e.appBundleId,e.trace?.outPath)})??{ref:s,x:h,y:w}};return p&&(g.warning=p),n.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{...g,refLabel:f,selectorChain:m}}),{ok:!0,data:g}}if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let s=iv(t.positionals??[],{preferTrailingValue:!0});if(s){if(0===s.rest.length)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after selector"}};let i=s.rest.join(" ").trim();if(!i)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after selector"}};let l=ip(s.selectorExpression),c=await i8(e,t.flags,n,r,{interactiveOnly:!0},a),d=ih(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:ig(l,d?.diagnostics??[],{unique:!0})}};let u=d.node,p=u.type??"",f=p&&!is(p,e.device.platform)?`fill target ${d.selector.raw} resolved to "${p}", attempting fill anyway.`:void 0,{x:m,y:h}=ie(d.node.rect),w=await a(e.device,"fill",[String(m),String(h),i],t.flags?.out,{...r(t.flags,e.appBundleId,e.trace?.outPath)}),g=iN(u,e.device.platform,{action:"fill"}),v={...w??{x:m,y:h,text:i},selector:d.selector.raw,selectorChain:g,refLabel:ii(u,c.nodes)};return f&&(v.warning=f),n.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:v}),{ok:!0,data:v}}return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires x y text, @ref text, or selector text"}}}if("get"===o){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 s=n.get(i);if(!s)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let l=t.positionals?.[1]??"";if(l.startsWith("@")){let i=i9("get",t.flags);if(i)return i;if(!s.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}};let r=t9(l??"");if(!r)return{ok:!1,error:{code:"INVALID_ARGS",message:"get text requires a ref like @e2"}};let a=t7(s.snapshot.nodes,r);if(!a&&t.positionals.length>2){let e=t.positionals.slice(2).join(" ").trim();e.length>0&&(a=it(s.snapshot.nodes,e))}if(!a)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${l} not found`}};let c=iN(a,s.device.platform,{action:"get"});if("attrs"===e)return n.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:r,selectorChain:c}}),{ok:!0,data:{ref:r,node:a}};let d=il(a);return n.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:r,text:d,refLabel:d||void 0,selectorChain:c}}),{ok:!0,data:{ref:r,text:d,node:a}}}let c=t.positionals.slice(1).join(" ").trim();if(!c)return{ok:!1,error:{code:"INVALID_ARGS",message:"get requires @ref or selector expression"}};let d=ip(c),u=ih((await i8(s,t.flags,n,r,{interactiveOnly:!1},a)).nodes,d,{platform:s.device.platform,requireRect:!1,requireUnique:!0,disambiguateAmbiguous:"text"===e});if(!u)return{ok:!1,error:{code:"COMMAND_FAILED",message:ig(d,[],{unique:!0})}};let p=u.node,f=iN(p,s.device.platform,{action:"get"});if("attrs"===e)return n.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{selector:u.selector.raw,selectorChain:f}}),{ok:!0,data:{selector:u.selector.raw,node:p}};let m=il(p);return n.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{text:m,refLabel:m||void 0,selector:u.selector.raw,selectorChain:f}}),{ok:!0,data:{selector:u.selector.raw,text:m,node:p}}}if("is"===o){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 s=n.get(i);if(!s)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};if(!tq("is",s.device))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"is is not supported on this device"}};let{split:l}=iI(t.positionals);if(!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"is requires a selector expression"}};let c=l.rest.join(" ").trim();if("text"===e&&!c)return{ok:!1,error:{code:"INVALID_ARGS",message:"is text requires expected text value"}};if("text"!==e&&l.rest.length>0)return{ok:!1,error:{code:"INVALID_ARGS",message:`is ${e} does not accept trailing values`}};let d=ip(l.selectorExpression),u=await i8(s,t.flags,n,r,{interactiveOnly:!1},a);if("exists"===e){let i=iw(u.nodes,d,{platform:s.device.platform});return i?(n.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{predicate:e,selector:i.selector.raw,selectorChain:d.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:ig(d,[],{unique:!1})}}}let p=ih(u.nodes,d,{platform:s.device.platform,requireUnique:!0});if(!p)return{ok:!1,error:{code:"COMMAND_FAILED",message:ig(d,[],{unique:!0})}};let f=function(e){let{predicate:t,node:i,expectedText:n,platform:r}=e,a=il(i),o=!1;switch(t){case"visible":o=iA(i);break;case"hidden":o=!iA(i);break;case"editable":o=iy(i,r);break;case"selected":o=!0===i.selected;break;case"text":o=a===(n??"")}let s="text"===t?`expected="${n??""}" actual="${a}"`:`actual=${JSON.stringify({visible:iA(i),editable:iy(i,r),selected:!0===i.selected})}`;return{pass:o,actualText:a,details:s}}({predicate:e,node:p.node,expectedText:c,platform:s.device.platform});return f.pass?(n.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{predicate:e,selector:p.selector.raw,selectorChain:d.selectors.map(e=>e.raw),pass:!0,text:"text"===e?f.actualText:void 0}}),{ok:!0,data:{predicate:e,pass:!0,selector:p.selector.raw}}):{ok:!1,error:{code:"COMMAND_FAILED",message:`is ${e} failed for selector ${p.selector.raw}: ${f.details}`}}}return null}async function i8(e,t,i,n,r,a=tF){let o=await a(e.device,"snapshot",[],t?.out,{...n({...t??{},snapshotInteractiveOnly:r.interactiveOnly,snapshotCompact:r.interactiveOnly},e.appBundleId,e.trace?.outPath)}),s=o?.nodes??[];return e.snapshot={nodes:t6(t?.snapshotRaw?s:ia(s)),truncated:o?.truncated,createdAt:Date.now(),backend:o?.backend},i.set(e.name,e),e.snapshot}let i6=[["snapshotDepth","--depth"],["snapshotScope","--scope"],["snapshotRaw","--raw"]];function i9(e,t){let i=function(e){if(!e)return[];let t=[];for(let[i,n]of i6)void 0!==e[i]&&t.push(n);return t}(t);return 0===i.length?null:{ok:!1,error:{code:"INVALID_ARGS",message:`${e} @ref does not support ${i.join(", ")}.`}}}let i7=n.join(g.homedir(),".agent-device"),ne=n.join(i7,"daemon.json"),nt=n.join(i7,"daemon.lock"),ni=n.join(i7,"daemon.log"),nn=new t0(n.join(i7,"sessions")),nr=c(),na=e.randomBytes(24).toString("hex"),no=new Set(["session_list","devices"]),ns=v(process.pid)??void 0;function nl(e,t,i){return t1(ni,e,t,i)}async function nc(e){var t;if(e.token!==na)return{ok:!1,error:{code:"UNAUTHORIZED",message:"Invalid token"}};let i="click"!==(t=e).command?t:{...t,command:"press"},n=i.command,r=function(e,t){var i;let n,r=e.session||"default";if(i=e,"string"==typeof(n=i.flags?.session)&&n.trim().length>0||"default"!==r||t.has(r))return r;let a=t.toArray();return 1===a.length?a[0].name:r}(i,nn),a=nn.get(r);a&&!no.has(n)&&function(e,t){if(!t)return;let i=[],n=e.device;if(t.platform&&t.platform!==n.platform&&i.push(`--platform=${t.platform}`),t.udid&&("ios"!==n.platform||t.udid!==n.id)&&i.push(`--udid=${t.udid}`),t.serial&&("android"!==n.platform||t.serial!==n.id)&&i.push(`--serial=${t.serial}`),0!==i.length){var r;let t,n,a;throw new h("INVALID_ARGS",`Session "${e.name}" is bound to ${(t=(r=e).device.platform,n=r.device.name.trim(),a=r.device.id,`${t} device "${n}" (${a})`)} and cannot be used with ${i.join(", ")}. Use a different --session name or close this session first.`)}}(a,i.flags);let o=await iG({req:i,sessionName:r,logPath:ni,sessionStore:nn,invoke:nc});if(o)return o;let s=await iY({req:i,sessionName:r,logPath:ni,sessionStore:nn});if(s)return s;let l=await i4({req:i,sessionName:r,sessionStore:nn});if(l)return l;let c=await i3({req:i,sessionName:r,logPath:ni,sessionStore:nn,invoke:nc});if(c)return c;let d=await i5({req:i,sessionName:r,sessionStore:nn,contextFromFlags:nl});if(d)return d;let u=nn.get(r);if(!u)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};if(!tq(n,u.device))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:`${n} is not supported on this device`}};let p=await tF(u.device,n,i.positionals??[],i.flags?.out,{...nl(i.flags,u.appBundleId,u.trace?.outPath)});return nn.recordAction(u,{command:n,positionals:i.positionals??[],flags:i.flags??{},result:p??{}}),{ok:!0,data:p??{}}}function nd(){if(!w.existsSync(nt))return null;try{let e=JSON.parse(w.readFileSync(nt,"utf8"));if(!Number.isInteger(e.pid)||e.pid<=0)return null;return e}catch{return null}}!function(){if(!function(){w.existsSync(i7)||w.mkdirSync(i7,{recursive:!0});let e=JSON.stringify({pid:process.pid,version:nr,startedAt:Date.now(),processStartTime:ns},null,2),t=()=>{try{return w.writeFileSync(nt,e,{flag:"wx",mode:384}),!0}catch(e){if("EEXIST"===e.code)return!1;throw e}};if(t())return!0;let i=nd();if(i?.pid&&i.pid!==process.pid&&s(i.pid,i.processStartTime))return!1;try{w.unlinkSync(nt)}catch{}return t()}()){process.stderr.write("Daemon lock is held by another process; exiting.\n"),process.exit(0);return}let e=I.createServer(e=>{let t="";e.setEncoding("utf8"),e.on("data",async i=>{let n=(t+=i).indexOf("\n");for(;-1!==n;){let i,r=t.slice(0,n).trim();if(t=t.slice(n+1),0===r.length){n=t.indexOf("\n");continue}try{let e=JSON.parse(r);i=await nc(e)}catch(t){let e=p(t);i={ok:!1,error:{code:e.code,message:e.message,details:e.details}}}e.write(`${JSON.stringify(i)}
|
|
17
|
+
`),n=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,w.existsSync(i7)||w.mkdirSync(i7,{recursive:!0}),w.writeFileSync(ni,""),w.writeFileSync(ne,JSON.stringify({port:i,token:na,pid:process.pid,version:nr,processStartTime:ns},null,2),{mode:384}),process.stdout.write(`AGENT_DEVICE_DAEMON_PORT=${t.port}
|
|
18
|
+
`)}});let t=!1,i=async()=>{await new Promise(t=>{try{e.close(()=>t())}catch{t()}})},n=async()=>{if(t)return;for(let e of(t=!0,await i(),nn.toArray()))nn.writeSessionLog(e);await th(),w.existsSync(ne)&&w.unlinkSync(ne);let e=nd();if(!e||e.pid===process.pid)try{w.existsSync(nt)&&w.unlinkSync(nt)}catch{}process.exit(0)};process.on("SIGINT",()=>{n()}),process.on("SIGTERM",()=>{n()}),process.on("SIGHUP",()=>{n()}),process.on("uncaughtException",e=>{let t=e instanceof h?e:p(e);process.stderr.write(`Daemon error: ${t.message}
|
|
19
|
+
`),n()})}();
|