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