agent-device 0.5.2 → 0.5.3
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 +9 -2
- package/dist/src/407.js +3 -0
- package/dist/src/bin.js +38 -35
- package/dist/src/daemon.js +17 -18
- package/package.json +3 -4
- package/skills/agent-device/SKILL.md +6 -0
- package/dist/src/50.js +0 -1
package/README.md
CHANGED
|
@@ -140,6 +140,7 @@ agent-device swipe 540 1500 540 500 120 --count 8 --pause-ms 30 --pattern ping-p
|
|
|
140
140
|
- `alert`, `wait`, `screenshot`
|
|
141
141
|
- `trace start`, `trace stop`
|
|
142
142
|
- `settings wifi|airplane|location on|off`
|
|
143
|
+
- `settings faceid match|nonmatch|enroll|unenroll` (iOS simulator only)
|
|
143
144
|
- `appstate`, `apps`, `devices`, `session list`
|
|
144
145
|
|
|
145
146
|
## iOS Snapshots
|
|
@@ -164,7 +165,7 @@ Flags:
|
|
|
164
165
|
- `--double-tap` use a double-tap gesture per `press`/`click` iteration (cannot be combined with `--hold-ms` or `--jitter-px`)
|
|
165
166
|
- `--pause-ms <ms>` delay between `swipe` iterations
|
|
166
167
|
- `--pattern one-way|ping-pong` repeat pattern for `swipe`
|
|
167
|
-
- `--verbose` for
|
|
168
|
+
- `--debug` (alias: `--verbose`) for debug diagnostics + daemon/runner logs
|
|
168
169
|
- `--json` for structured output
|
|
169
170
|
- `--steps <json>` batch: JSON array of steps
|
|
170
171
|
- `--steps-file <path>` batch: read step JSON from file
|
|
@@ -289,7 +290,13 @@ Boot diagnostics:
|
|
|
289
290
|
- Reason codes: `IOS_BOOT_TIMEOUT`, `IOS_RUNNER_CONNECT_TIMEOUT`, `ANDROID_BOOT_TIMEOUT`, `ADB_TRANSPORT_UNAVAILABLE`, `CI_RESOURCE_STARVATION_SUSPECTED`, `BOOT_COMMAND_FAILED`, `UNKNOWN`.
|
|
290
291
|
- Android boot waits fail fast for permission/tooling issues and do not always collapse into timeout errors.
|
|
291
292
|
- Use `agent-device boot --platform ios|android` when starting a new session only if `open` cannot find/connect to an available target.
|
|
292
|
-
-
|
|
293
|
+
- `--debug` captures retry telemetry in diagnostics logs.
|
|
294
|
+
- Set `AGENT_DEVICE_RETRY_LOGS=1` to also print retry telemetry directly to stderr (ad-hoc troubleshooting).
|
|
295
|
+
|
|
296
|
+
Diagnostics files:
|
|
297
|
+
- Failed commands persist diagnostics in `~/.agent-device/logs/<session>/<date>/<timestamp>-<diagnosticId>.ndjson`.
|
|
298
|
+
- `--debug` persists diagnostics for successful commands too and streams live diagnostic events.
|
|
299
|
+
- JSON failures include `error.hint`, `error.diagnosticId`, and `error.logPath`.
|
|
293
300
|
|
|
294
301
|
## App resolution
|
|
295
302
|
- Bundle/package identifiers are accepted directly (e.g., `com.apple.Preferences`).
|
package/dist/src/407.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{AsyncLocalStorage as e}from"node:async_hooks";import t from"node:crypto";import r,{promises as n}from"node:fs";import o from"node:os";import i from"node:path";import{fileURLToPath as s,pathToFileURL as a}from"node:url";import{spawn as d,spawnSync as u}from"node:child_process";let c=new e,l=/(token|secret|password|authorization|cookie|api[_-]?key|access[_-]?key|private[_-]?key)/i,f=/(bearer\s+[a-z0-9._-]+|(?:api[_-]?key|token|secret|password)\s*[=:]\s*\S+)/i;function m(){return t.randomBytes(8).toString("hex")}async function p(e,r){let n={...e,diagnosticId:`${Date.now().toString(36)}-${t.randomBytes(4).toString("hex")}`,events:[]};return await c.run(n,r)}function h(){let e=c.getStore();return e?{diagnosticId:e.diagnosticId,requestId:e.requestId,session:e.session,command:e.command,debug:e.debug}:{}}function g(e){let t=c.getStore();if(!t)return;let n={ts:new Date().toISOString(),level:e.level??"info",phase:e.phase,session:t.session,requestId:t.requestId,command:t.command,durationMs:e.durationMs,data:e.data?A(e.data):void 0};if(t.events.push(n),!t.debug)return;let o=`[agent-device][diag] ${JSON.stringify(n)}
|
|
2
|
+
`;try{t.logPath&&r.appendFile(t.logPath,o,()=>{}),t.traceLogPath&&r.appendFile(t.traceLogPath,o,()=>{}),t.logPath||t.traceLogPath||process.stderr.write(o)}catch{}}async function w(e,t,r){let n=Date.now();try{let o=await t();return g({level:"info",phase:e,durationMs:Date.now()-n,data:r}),o}catch(t){throw g({level:"error",phase:e,durationMs:Date.now()-n,data:{...r??{},error:t instanceof Error?t.message:String(t)}}),t}}function S(e={}){let t=c.getStore();if(!t||!e.force&&!t.debug||0===t.events.length)return null;try{let e=(t.session??"default").replace(/[^a-zA-Z0-9._-]/g,"_"),n=new Date().toISOString().slice(0,10),s=i.join(o.homedir(),".agent-device","logs",e,n);r.mkdirSync(s,{recursive:!0});let a=new Date().toISOString().replace(/[:.]/g,"-"),d=i.join(s,`${a}-${t.diagnosticId}.ndjson`),u=t.events.map(e=>JSON.stringify(A(e)));return r.writeFileSync(d,`${u.join("\n")}
|
|
3
|
+
`),t.events=[],d}catch{return null}}function A(e){return function e(t,r,n){if(null==t)return t;if("string"==typeof t){var o=t,i=n;let e=o.trim();if(!e)return o;if(i&&l.test(i)||f.test(e))return"[REDACTED]";let r=function(e){try{let t=new URL(e);return t.search&&(t.search="?REDACTED"),(t.username||t.password)&&(t.username="REDACTED",t.password="REDACTED"),t.toString()}catch{return null}}(e);return r||(e.length>400?`${e.slice(0,200)}...<truncated>`:e)}if("object"!=typeof t)return t;if(r.has(t))return"[Circular]";if(r.add(t),Array.isArray(t))return t.map(t=>e(t,r));let s={};for(let[n,o]of Object.entries(t)){if(l.test(n)){s[n]="[REDACTED]";continue}s[n]=e(o,r,n)}return s}(e,new WeakSet)}class y extends Error{code;details;cause;constructor(e,t,r,n){super(t),this.code=e,this.details=r,this.cause=n}}function I(e){return e instanceof y?e:e instanceof Error?new y("UNKNOWN",e.message,void 0,e):new y("UNKNOWN","Unknown error",{err:e})}function D(e,t={}){let r=I(e),n=r.details?A(r.details):void 0,o=n&&"string"==typeof n.hint?n.hint:void 0,i=(n&&"string"==typeof n.diagnosticId?n.diagnosticId:void 0)??t.diagnosticId,s=(n&&"string"==typeof n.logPath?n.logPath:void 0)??t.logPath,a=o??function(e){switch(e){case"INVALID_ARGS":return"Check command arguments and run --help for usage examples.";case"SESSION_NOT_FOUND":return"Run open first or pass an explicit device selector.";case"TOOL_MISSING":return"Install required platform tooling and ensure it is available in PATH.";case"DEVICE_NOT_FOUND":return"Verify the target device is booted/connected and selectors match.";case"UNSUPPORTED_OPERATION":return"This command is not available for the selected platform/device.";case"COMMAND_FAILED":default:return"Retry with --debug and inspect diagnostics log for details.";case"UNAUTHORIZED":return"Refresh daemon metadata and retry the command."}}(r.code),d=function(e){if(!e)return;let t={...e};return delete t.hint,delete t.diagnosticId,delete t.logPath,Object.keys(t).length>0?t:void 0}(n);return{code:r.code,message:r.message,hint:a,diagnosticId:i,logPath:s,details:d}}function N(){try{let e=E();return JSON.parse(r.readFileSync(i.join(e,"package.json"),"utf8")).version??"0.0.0"}catch{return"0.0.0"}}function E(){let e=i.dirname(s(import.meta.url)),t=e;for(let e=0;e<6;e+=1){let e=i.join(t,"package.json");if(r.existsSync(e))return t;t=i.dirname(t)}return e}async function v(e,t,r={}){return new Promise((n,o)=>{let i=d(e,t,{cwd:r.cwd,env:r.env,stdio:["pipe","pipe","pipe"]}),s="",a=r.binaryStdout?Buffer.alloc(0):void 0,u="",c=!1,l=$(r.timeoutMs),f=l?setTimeout(()=>{c=!0,i.kill("SIGKILL")},l):null;r.binaryStdout||i.stdout.setEncoding("utf8"),i.stderr.setEncoding("utf8"),void 0!==r.stdin&&i.stdin.write(r.stdin),i.stdin.end(),i.stdout.on("data",e=>{r.binaryStdout?a=Buffer.concat([a??Buffer.alloc(0),Buffer.isBuffer(e)?e:Buffer.from(e)]):s+=e}),i.stderr.on("data",e=>{u+=e}),i.on("error",r=>{(f&&clearTimeout(f),"ENOENT"===r.code)?o(new y("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},r)):o(new y("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},r))}),i.on("close",i=>{f&&clearTimeout(f);let d=i??1;c&&l?o(new y("COMMAND_FAILED",`${e} timed out after ${l}ms`,{cmd:e,args:t,stdout:s,stderr:u,exitCode:d,timeoutMs:l})):0===d||r.allowFailure?n({stdout:s,stderr:u,exitCode:d,stdoutBuffer:a}):o(new y("COMMAND_FAILED",`${e} exited with code ${d}`,{cmd:e,args:t,stdout:s,stderr:u,exitCode:d}))})})}async function _(e){try{var t;let{shell:r,args:n}=(t=e,"win32"===process.platform?{shell:"cmd.exe",args:["/c","where",t]}:{shell:"bash",args:["-lc",`command -v ${t}`]}),o=await v(r,n,{allowFailure:!0});return 0===o.exitCode&&o.stdout.trim().length>0}catch{return!1}}function O(e,t,r={}){let n=u(e,t,{cwd:r.cwd,env:r.env,stdio:["pipe","pipe","pipe"],encoding:r.binaryStdout?void 0:"utf8",input:r.stdin,timeout:$(r.timeoutMs)});if(n.error){let o=n.error.code;if("ETIMEDOUT"===o)throw new y("COMMAND_FAILED",`${e} timed out after ${$(r.timeoutMs)}ms`,{cmd:e,args:t,timeoutMs:$(r.timeoutMs)},n.error);if("ENOENT"===o)throw new y("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},n.error);throw new y("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},n.error)}let o=r.binaryStdout?Buffer.isBuffer(n.stdout)?n.stdout:Buffer.from(n.stdout??""):void 0,i=r.binaryStdout?"":"string"==typeof n.stdout?n.stdout:(n.stdout??"").toString(),s="string"==typeof n.stderr?n.stderr:(n.stderr??"").toString(),a=n.status??1;if(0!==a&&!r.allowFailure)throw new y("COMMAND_FAILED",`${e} exited with code ${a}`,{cmd:e,args:t,stdout:i,stderr:s,exitCode:a});return{stdout:i,stderr:s,exitCode:a,stdoutBuffer:o}}function M(e,t,r={}){d(e,t,{cwd:r.cwd,env:r.env,stdio:"ignore",detached:!0}).unref()}async function T(e,t,r={}){return new Promise((n,o)=>{let i=d(e,t,{cwd:r.cwd,env:r.env,stdio:["pipe","pipe","pipe"]}),s="",a="",u=r.binaryStdout?Buffer.alloc(0):void 0;r.binaryStdout||i.stdout.setEncoding("utf8"),i.stderr.setEncoding("utf8"),void 0!==r.stdin&&i.stdin.write(r.stdin),i.stdin.end(),i.stdout.on("data",e=>{if(r.binaryStdout){u=Buffer.concat([u??Buffer.alloc(0),Buffer.isBuffer(e)?e:Buffer.from(e)]);return}let t=String(e);s+=t,r.onStdoutChunk?.(t)}),i.stderr.on("data",e=>{let t=String(e);a+=t,r.onStderrChunk?.(t)}),i.on("error",r=>{"ENOENT"===r.code?o(new y("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},r)):o(new y("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},r))}),i.on("close",i=>{let d=i??1;0===d||r.allowFailure?n({stdout:s,stderr:a,exitCode:d,stdoutBuffer:u}):o(new y("COMMAND_FAILED",`${e} exited with code ${d}`,{cmd:e,args:t,stdout:s,stderr:a,exitCode:d}))})})}function L(e,t,r={}){let n=d(e,t,{cwd:r.cwd,env:r.env,stdio:["ignore","pipe","pipe"]}),o="",i="";n.stdout.setEncoding("utf8"),n.stderr.setEncoding("utf8"),n.stdout.on("data",e=>{o+=e}),n.stderr.on("data",e=>{i+=e});let s=new Promise((s,a)=>{n.on("error",r=>{"ENOENT"===r.code?a(new y("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},r)):a(new y("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},r))}),n.on("close",n=>{let d=n??1;0===d||r.allowFailure?s({stdout:o,stderr:i,exitCode:d}):a(new y("COMMAND_FAILED",`${e} exited with code ${d}`,{cmd:e,args:t,stdout:o,stderr:i,exitCode:d}))})});return{child:n,wait:s}}function $(e){if(!Number.isFinite(e))return;let t=Math.floor(e);if(!(t<=0))return t}let b=[/(^|[\/\s"'=])dist\/src\/daemon\.js($|[\s"'])/,/(^|[\/\s"'=])src\/daemon\.ts($|[\s"'])/];function C(e){if(!Number.isInteger(e)||e<=0)return!1;try{return process.kill(e,0),!0}catch(e){return"EPERM"===e.code}}function F(e){if(!Number.isInteger(e)||e<=0)return null;try{let t=O("ps",["-p",String(e),"-o","lstart="],{allowFailure:!0,timeoutMs:1e3});if(0!==t.exitCode)return null;let r=t.stdout.trim();return r.length>0?r:null}catch{return null}}function P(e,t){let r;if(!C(e))return!1;if(t){let r=F(e);if(!r||r!==t)return!1}let n=function(e){if(!Number.isInteger(e)||e<=0)return null;try{let t=O("ps",["-p",String(e),"-o","command="],{allowFailure:!0,timeoutMs:1e3});if(0!==t.exitCode)return null;let r=t.stdout.trim();return r.length>0?r:null}catch{return null}}(e);return!!n&&!!(r=n.toLowerCase().replaceAll("\\","/")).includes("agent-device")&&b.some(e=>e.test(r))}function R(e,t){try{return process.kill(e,t),!0}catch(t){let e=t.code;if("ESRCH"===e||"EPERM"===e)return!1;throw t}}async function B(e,t){if(!C(e))return!0;let r=Date.now();for(;Date.now()-r<t;)if(await new Promise(e=>setTimeout(e,50)),!C(e))return!0;return!C(e)}async function k(e,t){!P(e,t.expectedStartTime)||!R(e,"SIGTERM")||await B(e,t.termTimeoutMs)||R(e,"SIGKILL")&&await B(e,t.killTimeoutMs)}let x=100,G=new Set(["batch","replay"]);function j(e){let t;try{t=JSON.parse(e)}catch{throw new y("INVALID_ARGS","Batch steps must be valid JSON.")}if(!Array.isArray(t)||0===t.length)throw new y("INVALID_ARGS","Batch steps must be a non-empty JSON array.");return t}function U(e,t){if(!Array.isArray(e)||0===e.length)throw new y("INVALID_ARGS","batch requires a non-empty batchSteps array.");if(e.length>t)throw new y("INVALID_ARGS",`batch has ${e.length} steps; max allowed is ${t}.`);let r=[];for(let t=0;t<e.length;t+=1){let n=e[t];if(!n||"object"!=typeof n)throw new y("INVALID_ARGS",`Invalid batch step at index ${t}.`);let o="string"==typeof n.command?n.command.trim().toLowerCase():"";if(!o)throw new y("INVALID_ARGS",`Batch step ${t+1} requires command.`);if(G.has(o))throw new y("INVALID_ARGS",`Batch step ${t+1} cannot run ${o}.`);if(void 0!==n.positionals&&!Array.isArray(n.positionals))throw new y("INVALID_ARGS",`Batch step ${t+1} positionals must be an array.`);let i=n.positionals??[];if(i.some(e=>"string"!=typeof e))throw new y("INVALID_ARGS",`Batch step ${t+1} positionals must contain only strings.`);if(void 0!==n.flags&&("object"!=typeof n.flags||Array.isArray(n.flags)||!n.flags))throw new y("INVALID_ARGS",`Batch step ${t+1} flags must be an object.`);r.push({command:o,positionals:i,flags:n.flags??{}})}return r}export{default as node_net}from"node:net";export{y as AppError,x as DEFAULT_BATCH_MAX_STEPS,I as asAppError,m as createRequestId,g as emitDiagnostic,s as fileURLToPath,E as findProjectRoot,S as flushDiagnosticsToSessionFile,h as getDiagnosticsMeta,P as isAgentDeviceDaemonProcess,C as isProcessAlive,t as node_crypto,r as node_fs,o as node_os,i as node_path,D as normalizeError,j as parseBatchStepsJson,a as pathToFileURL,n as promises,F as readProcessStartTime,N as readVersion,v as runCmd,L as runCmdBackground,M as runCmdDetached,T as runCmdStreaming,k as stopProcessForTakeover,U as validateAndNormalizeBatchSteps,_ as whichCmd,w as withDiagnosticTimer,p as withDiagnosticsScope};
|
package/dist/src/bin.js
CHANGED
|
@@ -1,54 +1,57 @@
|
|
|
1
|
-
import{node_path as e,parseBatchStepsJson as t,asAppError as s,isAgentDeviceDaemonProcess as a,runCmdDetached as i,node_fs as r,node_os as o,node_net as n,AppError as l,readVersion as p,findProjectRoot as c,stopProcessForTakeover as u,pathToFileURL as d}from"./50.js";let g=["snapshotDepth","snapshotScope","snapshotRaw"],m=[{key:"platform",names:["--platform"],type:"enum",enumValues:["ios","android"],usageLabel:"--platform ios|android",usageDescription:"Platform to target"},{key:"device",names:["--device"],type:"string",usageLabel:"--device <name>",usageDescription:"Device name to target"},{key:"udid",names:["--udid"],type:"string",usageLabel:"--udid <udid>",usageDescription:"iOS device UDID"},{key:"serial",names:["--serial"],type:"string",usageLabel:"--serial <serial>",usageDescription:"Android device serial"},{key:"activity",names:["--activity"],type:"string",usageLabel:"--activity <component>",usageDescription:"Android app launch activity (package/Activity); not for URL opens"},{key:"session",names:["--session"],type:"string",usageLabel:"--session <name>",usageDescription:"Named session"},{key:"count",names:["--count"],type:"int",min:1,max:200,usageLabel:"--count <n>",usageDescription:"Repeat count for press/swipe series"},{key:"intervalMs",names:["--interval-ms"],type:"int",min:0,max:1e4,usageLabel:"--interval-ms <ms>",usageDescription:"Delay between press iterations"},{key:"holdMs",names:["--hold-ms"],type:"int",min:0,max:1e4,usageLabel:"--hold-ms <ms>",usageDescription:"Press hold duration for each iteration"},{key:"jitterPx",names:["--jitter-px"],type:"int",min:0,max:100,usageLabel:"--jitter-px <n>",usageDescription:"Deterministic coordinate jitter radius for press"},{key:"doubleTap",names:["--double-tap"],type:"boolean",usageLabel:"--double-tap",usageDescription:"Use double-tap gesture per press iteration"},{key:"pauseMs",names:["--pause-ms"],type:"int",min:0,max:1e4,usageLabel:"--pause-ms <ms>",usageDescription:"Delay between swipe iterations"},{key:"pattern",names:["--pattern"],type:"enum",enumValues:["one-way","ping-pong"],usageLabel:"--pattern one-way|ping-pong",usageDescription:"Swipe repeat pattern"},{key:"verbose",names:["--verbose","-v"],type:"boolean",usageLabel:"--verbose",usageDescription:"Stream daemon/runner logs"},{key:"json",names:["--json"],type:"boolean",usageLabel:"--json",usageDescription:"JSON output"},{key:"help",names:["--help","-h"],type:"boolean",usageLabel:"--help, -h",usageDescription:"Print help and exit"},{key:"version",names:["--version","-V"],type:"boolean",usageLabel:"--version, -V",usageDescription:"Print version and exit"},{key:"saveScript",names:["--save-script"],type:"booleanOrString",usageLabel:"--save-script [path]",usageDescription:"Save session script (.ad) on close; optional custom output path"},{key:"relaunch",names:["--relaunch"],type:"boolean",usageLabel:"--relaunch",usageDescription:"open: terminate app process before launching it"},{key:"noRecord",names:["--no-record"],type:"boolean",usageLabel:"--no-record",usageDescription:"Do not record this action"},{key:"replayUpdate",names:["--update","-u"],type:"boolean",usageLabel:"--update, -u",usageDescription:"Replay: update selectors and rewrite replay file in place"},{key:"steps",names:["--steps"],type:"string",usageLabel:"--steps <json>",usageDescription:"Batch: JSON array of steps"},{key:"stepsFile",names:["--steps-file"],type:"string",usageLabel:"--steps-file <path>",usageDescription:"Batch: read steps JSON from file"},{key:"batchOnError",names:["--on-error"],type:"enum",enumValues:["stop"],usageLabel:"--on-error stop",usageDescription:"Batch: stop when a step fails"},{key:"batchMaxSteps",names:["--max-steps"],type:"int",min:1,max:1e3,usageLabel:"--max-steps <n>",usageDescription:"Batch: maximum number of allowed steps"},{key:"appsFilter",names:["--user-installed"],type:"enum",setValue:"user-installed",usageLabel:"--user-installed",usageDescription:"Apps: list user-installed apps"},{key:"appsFilter",names:["--all"],type:"enum",setValue:"all",usageLabel:"--all",usageDescription:"Apps: list all apps (include system/default apps)"},{key:"snapshotInteractiveOnly",names:["-i"],type:"boolean",usageLabel:"-i",usageDescription:"Snapshot: interactive elements only"},{key:"snapshotCompact",names:["-c"],type:"boolean",usageLabel:"-c",usageDescription:"Snapshot: compact output (drop empty structure)"},{key:"snapshotDepth",names:["--depth","-d"],type:"int",min:0,usageLabel:"--depth, -d <depth>",usageDescription:"Snapshot: limit snapshot depth"},{key:"snapshotScope",names:["--scope","-s"],type:"string",usageLabel:"--scope, -s <scope>",usageDescription:"Snapshot: scope snapshot to label/identifier"},{key:"snapshotRaw",names:["--raw"],type:"boolean",usageLabel:"--raw",usageDescription:"Snapshot: raw node output"},{key:"out",names:["--out"],type:"string",usageLabel:"--out <path>",usageDescription:"Output path"}],f=new Set(["json","help","version","verbose","platform","device","udid","serial","session","noRecord"]),h={boot:{description:"Ensure target device/simulator is booted and ready",positionalArgs:[],allowedFlags:[]},open:{description:"Boot device/simulator; optionally launch app or deep link URL",positionalArgs:["appOrUrl?","url?"],allowedFlags:["activity","saveScript","relaunch"]},close:{description:"Close app or just end session",positionalArgs:["app?"],allowedFlags:["saveScript"]},reinstall:{description:"Uninstall + install app from binary path",positionalArgs:["app","path"],allowedFlags:[]},snapshot:{description:"Capture accessibility tree",positionalArgs:[],allowedFlags:["snapshotInteractiveOnly","snapshotCompact","snapshotDepth","snapshotScope","snapshotRaw"]},devices:{description:"List available devices",positionalArgs:[],allowedFlags:[],skipCapabilityCheck:!0},apps:{description:"List installed apps (includes default/system apps by default)",positionalArgs:[],allowedFlags:["appsFilter"],defaults:{appsFilter:"all"}},appstate:{description:"Show foreground app/activity",positionalArgs:[],allowedFlags:[],skipCapabilityCheck:!0},back:{description:"Navigate back (where supported)",positionalArgs:[],allowedFlags:[]},home:{description:"Go to home screen (where supported)",positionalArgs:[],allowedFlags:[]},"app-switcher":{description:"Open app switcher (where supported)",positionalArgs:[],allowedFlags:[]},wait:{usageOverride:"wait <ms>|text <text>|@ref|<selector> [timeoutMs]",description:"Wait for duration, text, ref, or selector to appear",positionalArgs:["durationOrSelector","timeoutMs?"],allowsExtraPositionals:!0,allowedFlags:[...g]},alert:{usageOverride:"alert [get|accept|dismiss|wait] [timeout]",description:"Inspect or handle alert (iOS simulator)",positionalArgs:["action?","timeout?"],allowedFlags:[]},click:{usageOverride:"click <x y|@ref|selector>",description:"Tap/click by coordinates, snapshot ref, or selector",positionalArgs:["target"],allowsExtraPositionals:!0,allowedFlags:["count","intervalMs","holdMs","jitterPx","doubleTap",...g]},get:{usageOverride:"get text|attrs <@ref|selector>",description:"Return element text/attributes by ref or selector",positionalArgs:["subcommand","target"],allowedFlags:[...g]},replay:{description:"Replay a recorded session",positionalArgs:["path"],allowedFlags:["replayUpdate"],skipCapabilityCheck:!0},batch:{usageOverride:"batch [--steps <json> | --steps-file <path>]",description:"Execute multiple commands in one daemon request",positionalArgs:[],allowedFlags:["steps","stepsFile","batchOnError","batchMaxSteps","out"],skipCapabilityCheck:!0},press:{usageOverride:"press <x y|@ref|selector>",description:"Tap/press by coordinates, snapshot ref, or selector (supports repeated series)",positionalArgs:["targetOrX","y?"],allowsExtraPositionals:!0,allowedFlags:["count","intervalMs","holdMs","jitterPx","doubleTap",...g]},"long-press":{description:"Long press (where supported)",positionalArgs:["x","y","durationMs?"],allowedFlags:[]},swipe:{description:"Swipe coordinates with optional repeat pattern",positionalArgs:["x1","y1","x2","y2","durationMs?"],allowedFlags:["count","pauseMs","pattern"]},focus:{description:"Focus input at coordinates",positionalArgs:["x","y"],allowedFlags:[]},type:{description:"Type text in focused field",positionalArgs:["text"],allowsExtraPositionals:!0,allowedFlags:[]},fill:{usageOverride:"fill <x> <y> <text> | fill <@ref|selector> <text>",description:"Tap then type",positionalArgs:["targetOrX","yOrText","text?"],allowsExtraPositionals:!0,allowedFlags:[...g]},scroll:{description:"Scroll in direction (0-1 amount)",positionalArgs:["direction","amount?"],allowedFlags:[]},scrollintoview:{description:"Scroll until text appears",positionalArgs:["text"],allowedFlags:[]},pinch:{description:"Pinch/zoom gesture (iOS simulator)",positionalArgs:["scale","x?","y?"],allowedFlags:[]},screenshot:{description:"Capture screenshot",positionalArgs:["path?"],allowedFlags:["out"]},record:{usageOverride:"record start [path] | record stop",description:"Start/stop screen recording",positionalArgs:["start|stop","path?"],allowedFlags:[]},trace:{usageOverride:"trace start [path] | trace stop [path]",description:"Start/stop trace log capture",positionalArgs:["start|stop","path?"],allowedFlags:[],skipCapabilityCheck:!0},find:{usageOverride:"find <locator|text> <action> [value]",description:"Find by text/label/value/role/id and run action",positionalArgs:["query","action","value?"],allowsExtraPositionals:!0,allowedFlags:["snapshotDepth","snapshotRaw"]},is:{description:"Assert UI state (visible|hidden|exists|editable|selected|text)",positionalArgs:["predicate","selector","value?"],allowsExtraPositionals:!0,allowedFlags:[...g]},settings:{description:"Toggle OS settings (simulators)",positionalArgs:["setting","state"],allowedFlags:[]},session:{usageOverride:"session list",description:"List active sessions",positionalArgs:["list?"],allowedFlags:[],skipCapabilityCheck:!0}},y=new Map,w=new Map;for(let e of m){for(let t of e.names)y.set(t,e);let t=w.get(e.key);t?t.push(e):w.set(e.key,[e])}function v(e){if(e)return h[e]}function b(e){let t=e.endsWith("?"),s=t?e.slice(0,-1):e;return t?`[${s}]`:`<${s}>`}function x(e,t){return t.usageOverride?t.usageOverride:[e,...t.positionalArgs.map(b),...t.allowedFlags.flatMap(e=>(w.get(e)??[]).map(e=>e.usageLabel??e.names[0])).map(e=>`[${e}]`)].join(" ")}let $=function(){let e=`agent-device <command> [args] [--json]
|
|
1
|
+
import{createRequestId as e,node_path as t,parseBatchStepsJson as s,normalizeError as a,isAgentDeviceDaemonProcess as r,runCmdDetached as i,readVersion as o,findProjectRoot as n,getDiagnosticsMeta as l,withDiagnosticTimer as p,emitDiagnostic as c,asAppError as u,pathToFileURL as d,AppError as g,node_fs as m,node_os as f,flushDiagnosticsToSessionFile as h,node_net as w,withDiagnosticsScope as y,stopProcessForTakeover as v}from"./407.js";let b=["snapshotDepth","snapshotScope","snapshotRaw"],x=[{key:"platform",names:["--platform"],type:"enum",enumValues:["ios","android"],usageLabel:"--platform ios|android",usageDescription:"Platform to target"},{key:"device",names:["--device"],type:"string",usageLabel:"--device <name>",usageDescription:"Device name to target"},{key:"udid",names:["--udid"],type:"string",usageLabel:"--udid <udid>",usageDescription:"iOS device UDID"},{key:"serial",names:["--serial"],type:"string",usageLabel:"--serial <serial>",usageDescription:"Android device serial"},{key:"activity",names:["--activity"],type:"string",usageLabel:"--activity <component>",usageDescription:"Android app launch activity (package/Activity); not for URL opens"},{key:"session",names:["--session"],type:"string",usageLabel:"--session <name>",usageDescription:"Named session"},{key:"count",names:["--count"],type:"int",min:1,max:200,usageLabel:"--count <n>",usageDescription:"Repeat count for press/swipe series"},{key:"intervalMs",names:["--interval-ms"],type:"int",min:0,max:1e4,usageLabel:"--interval-ms <ms>",usageDescription:"Delay between press iterations"},{key:"holdMs",names:["--hold-ms"],type:"int",min:0,max:1e4,usageLabel:"--hold-ms <ms>",usageDescription:"Press hold duration for each iteration"},{key:"jitterPx",names:["--jitter-px"],type:"int",min:0,max:100,usageLabel:"--jitter-px <n>",usageDescription:"Deterministic coordinate jitter radius for press"},{key:"doubleTap",names:["--double-tap"],type:"boolean",usageLabel:"--double-tap",usageDescription:"Use double-tap gesture per press iteration"},{key:"pauseMs",names:["--pause-ms"],type:"int",min:0,max:1e4,usageLabel:"--pause-ms <ms>",usageDescription:"Delay between swipe iterations"},{key:"pattern",names:["--pattern"],type:"enum",enumValues:["one-way","ping-pong"],usageLabel:"--pattern one-way|ping-pong",usageDescription:"Swipe repeat pattern"},{key:"verbose",names:["--debug","--verbose","-v"],type:"boolean",usageLabel:"--debug, --verbose, -v",usageDescription:"Enable debug diagnostics and stream daemon/runner logs"},{key:"json",names:["--json"],type:"boolean",usageLabel:"--json",usageDescription:"JSON output"},{key:"help",names:["--help","-h"],type:"boolean",usageLabel:"--help, -h",usageDescription:"Print help and exit"},{key:"version",names:["--version","-V"],type:"boolean",usageLabel:"--version, -V",usageDescription:"Print version and exit"},{key:"saveScript",names:["--save-script"],type:"booleanOrString",usageLabel:"--save-script [path]",usageDescription:"Save session script (.ad) on close; optional custom output path"},{key:"relaunch",names:["--relaunch"],type:"boolean",usageLabel:"--relaunch",usageDescription:"open: terminate app process before launching it"},{key:"noRecord",names:["--no-record"],type:"boolean",usageLabel:"--no-record",usageDescription:"Do not record this action"},{key:"replayUpdate",names:["--update","-u"],type:"boolean",usageLabel:"--update, -u",usageDescription:"Replay: update selectors and rewrite replay file in place"},{key:"steps",names:["--steps"],type:"string",usageLabel:"--steps <json>",usageDescription:"Batch: JSON array of steps"},{key:"stepsFile",names:["--steps-file"],type:"string",usageLabel:"--steps-file <path>",usageDescription:"Batch: read steps JSON from file"},{key:"batchOnError",names:["--on-error"],type:"enum",enumValues:["stop"],usageLabel:"--on-error stop",usageDescription:"Batch: stop when a step fails"},{key:"batchMaxSteps",names:["--max-steps"],type:"int",min:1,max:1e3,usageLabel:"--max-steps <n>",usageDescription:"Batch: maximum number of allowed steps"},{key:"appsFilter",names:["--user-installed"],type:"enum",setValue:"user-installed",usageLabel:"--user-installed",usageDescription:"Apps: list user-installed apps"},{key:"appsFilter",names:["--all"],type:"enum",setValue:"all",usageLabel:"--all",usageDescription:"Apps: list all apps (include system/default apps)"},{key:"snapshotInteractiveOnly",names:["-i"],type:"boolean",usageLabel:"-i",usageDescription:"Snapshot: interactive elements only"},{key:"snapshotCompact",names:["-c"],type:"boolean",usageLabel:"-c",usageDescription:"Snapshot: compact output (drop empty structure)"},{key:"snapshotDepth",names:["--depth","-d"],type:"int",min:0,usageLabel:"--depth, -d <depth>",usageDescription:"Snapshot: limit snapshot depth"},{key:"snapshotScope",names:["--scope","-s"],type:"string",usageLabel:"--scope, -s <scope>",usageDescription:"Snapshot: scope snapshot to label/identifier"},{key:"snapshotRaw",names:["--raw"],type:"boolean",usageLabel:"--raw",usageDescription:"Snapshot: raw node output"},{key:"out",names:["--out"],type:"string",usageLabel:"--out <path>",usageDescription:"Output path"}],$=new Set(["json","help","version","verbose","platform","device","udid","serial","session","noRecord"]),k={boot:{description:"Ensure target device/simulator is booted and ready",positionalArgs:[],allowedFlags:[]},open:{description:"Boot device/simulator; optionally launch app or deep link URL",positionalArgs:["appOrUrl?","url?"],allowedFlags:["activity","saveScript","relaunch"]},close:{description:"Close app or just end session",positionalArgs:["app?"],allowedFlags:["saveScript"]},reinstall:{description:"Uninstall + install app from binary path",positionalArgs:["app","path"],allowedFlags:[]},snapshot:{description:"Capture accessibility tree",positionalArgs:[],allowedFlags:["snapshotInteractiveOnly","snapshotCompact","snapshotDepth","snapshotScope","snapshotRaw"]},devices:{description:"List available devices",positionalArgs:[],allowedFlags:[],skipCapabilityCheck:!0},apps:{description:"List installed apps (includes default/system apps by default)",positionalArgs:[],allowedFlags:["appsFilter"],defaults:{appsFilter:"all"}},appstate:{description:"Show foreground app/activity",positionalArgs:[],allowedFlags:[],skipCapabilityCheck:!0},back:{description:"Navigate back (where supported)",positionalArgs:[],allowedFlags:[]},home:{description:"Go to home screen (where supported)",positionalArgs:[],allowedFlags:[]},"app-switcher":{description:"Open app switcher (where supported)",positionalArgs:[],allowedFlags:[]},wait:{usageOverride:"wait <ms>|text <text>|@ref|<selector> [timeoutMs]",description:"Wait for duration, text, ref, or selector to appear",positionalArgs:["durationOrSelector","timeoutMs?"],allowsExtraPositionals:!0,allowedFlags:[...b]},alert:{usageOverride:"alert [get|accept|dismiss|wait] [timeout]",description:"Inspect or handle alert (iOS simulator)",positionalArgs:["action?","timeout?"],allowedFlags:[]},click:{usageOverride:"click <x y|@ref|selector>",description:"Tap/click by coordinates, snapshot ref, or selector",positionalArgs:["target"],allowsExtraPositionals:!0,allowedFlags:["count","intervalMs","holdMs","jitterPx","doubleTap",...b]},get:{usageOverride:"get text|attrs <@ref|selector>",description:"Return element text/attributes by ref or selector",positionalArgs:["subcommand","target"],allowedFlags:[...b]},replay:{description:"Replay a recorded session",positionalArgs:["path"],allowedFlags:["replayUpdate"],skipCapabilityCheck:!0},batch:{usageOverride:"batch [--steps <json> | --steps-file <path>]",description:"Execute multiple commands in one daemon request",positionalArgs:[],allowedFlags:["steps","stepsFile","batchOnError","batchMaxSteps","out"],skipCapabilityCheck:!0},press:{usageOverride:"press <x y|@ref|selector>",description:"Tap/press by coordinates, snapshot ref, or selector (supports repeated series)",positionalArgs:["targetOrX","y?"],allowsExtraPositionals:!0,allowedFlags:["count","intervalMs","holdMs","jitterPx","doubleTap",...b]},"long-press":{description:"Long press (where supported)",positionalArgs:["x","y","durationMs?"],allowedFlags:[]},swipe:{description:"Swipe coordinates with optional repeat pattern",positionalArgs:["x1","y1","x2","y2","durationMs?"],allowedFlags:["count","pauseMs","pattern"]},focus:{description:"Focus input at coordinates",positionalArgs:["x","y"],allowedFlags:[]},type:{description:"Type text in focused field",positionalArgs:["text"],allowsExtraPositionals:!0,allowedFlags:[]},fill:{usageOverride:"fill <x> <y> <text> | fill <@ref|selector> <text>",description:"Tap then type",positionalArgs:["targetOrX","yOrText","text?"],allowsExtraPositionals:!0,allowedFlags:[...b]},scroll:{description:"Scroll in direction (0-1 amount)",positionalArgs:["direction","amount?"],allowedFlags:[]},scrollintoview:{description:"Scroll until text appears",positionalArgs:["text"],allowedFlags:[]},pinch:{description:"Pinch/zoom gesture (iOS simulator)",positionalArgs:["scale","x?","y?"],allowedFlags:[]},screenshot:{description:"Capture screenshot",positionalArgs:["path?"],allowedFlags:["out"]},record:{usageOverride:"record start [path] | record stop",description:"Start/stop screen recording",positionalArgs:["start|stop","path?"],allowedFlags:[]},trace:{usageOverride:"trace start [path] | trace stop [path]",description:"Start/stop trace log capture",positionalArgs:["start|stop","path?"],allowedFlags:[],skipCapabilityCheck:!0},find:{usageOverride:"find <locator|text> <action> [value]",description:"Find by text/label/value/role/id and run action",positionalArgs:["query","action","value?"],allowsExtraPositionals:!0,allowedFlags:["snapshotDepth","snapshotRaw"]},is:{description:"Assert UI state (visible|hidden|exists|editable|selected|text)",positionalArgs:["predicate","selector","value?"],allowsExtraPositionals:!0,allowedFlags:[...b]},settings:{usageOverride:"settings <wifi|airplane|location|faceid> <on|off|match|nonmatch|enroll|unenroll>",description:"Toggle OS settings (simulators), including Face ID on iOS simulators",positionalArgs:["setting","state"],allowedFlags:[]},session:{usageOverride:"session list",description:"List active sessions",positionalArgs:["list?"],allowedFlags:[],skipCapabilityCheck:!0}},A=new Map,S=new Map;for(let e of x){for(let t of e.names)A.set(t,e);let t=S.get(e.key);t?t.push(e):S.set(e.key,[e])}function D(e){if(e)return k[e]}function I(e){let t=e.endsWith("?"),s=t?e.slice(0,-1):e;return t?`[${s}]`:`<${s}>`}function F(e,t){return t.usageOverride?t.usageOverride:[e,...t.positionalArgs.map(I),...t.allowedFlags.flatMap(e=>(S.get(e)??[]).map(e=>e.usageLabel??e.names[0])).map(e=>`[${e}]`)].join(" ")}let L=function(){let e=`agent-device <command> [args] [--json]
|
|
2
2
|
|
|
3
3
|
CLI to control iOS and Android devices for AI agents.
|
|
4
|
-
`,t=Object.keys(
|
|
4
|
+
`,t=Object.keys(k).map(e=>{let t=k[e];if(!t)throw Error(`Missing command schema for ${e}`);return{name:e,schema:t,usage:F(e,t)}}),s=Math.max(...t.map(e=>e.usage.length))+2,a=["Commands:"];for(let e of t)a.push(` ${e.usage.padEnd(s)}${e.schema.description}`);let r=O("Flags:",x.filter(e=>e.usageLabel&&e.usageDescription));return`${e}
|
|
5
5
|
${a.join("\n")}
|
|
6
6
|
|
|
7
|
-
${
|
|
8
|
-
`}();function
|
|
9
|
-
(none)`;let s=Math.max(...t.map(e=>(e.usageLabel??"").length))+2,a=[e];for(let e of t)a.push(` ${(e.usageLabel??"").padEnd(s)}${e.usageDescription??""}`);return a.join("\n")}function
|
|
10
|
-
`)}function
|
|
11
|
-
|
|
12
|
-
`)
|
|
13
|
-
`)
|
|
14
|
-
`)
|
|
15
|
-
`),
|
|
16
|
-
`),
|
|
7
|
+
${r}
|
|
8
|
+
`}();function N(e){return x.filter(t=>e.has(t.key)&&void 0!==t.usageLabel&&void 0!==t.usageDescription)}function O(e,t){if(0===t.length)return`${e}
|
|
9
|
+
(none)`;let s=Math.max(...t.map(e=>(e.usageLabel??"").length))+2,a=[e];for(let e of t)a.push(` ${(e.usageLabel??"").padEnd(s)}${e.usageDescription??""}`);return a.join("\n")}function _(e){let t=e.indexOf("=");return -1===t?[e,void 0]:[e.slice(0,t),e.slice(t+1)]}function E(e){return e.replace(/^-+/,"")}function j(e){if(!e.startsWith("-")||"-"===e)return!1;let[t]=e.startsWith("--")?_(e):[e,void 0];return void 0!==A.get(t)}function M(e){process.stdout.write(`${JSON.stringify(e,null,2)}
|
|
10
|
+
`)}function C(e,t={}){let s=e instanceof g?a(e):e;process.stderr.write(`Error (${s.code}): ${s.message}
|
|
11
|
+
`),s.hint&&process.stderr.write(`Hint: ${s.hint}
|
|
12
|
+
`),s.diagnosticId&&process.stderr.write(`Diagnostic ID: ${s.diagnosticId}
|
|
13
|
+
`),s.logPath&&process.stderr.write(`Diagnostics Log: ${s.logPath}
|
|
14
|
+
`),t.showDetails&&s.details&&process.stderr.write(`${JSON.stringify(s.details,null,2)}
|
|
15
|
+
`)}function T(e,t,s){let a=P(e.type??"Element"),r=function(e,t){var s,a;let r=e.label?.trim(),i=e.value?.trim();if("text-field"===(s=t)||"text-view"===s||"search"===s){if(i)return i;if(r)return r}else if(r)return r;if(i)return i;let o=e.identifier?.trim();return!o||(a=o,/^[\w.]+:id\/[\w.-]+$/i.test(a)&&("group"===t||"image"===t||"list"===t||"collection"===t))?"":o}(e,a),i=" ".repeat(t),o=e.ref?`@${e.ref}`:"",n=[!1===e.enabled?"disabled":null].filter(Boolean).join(", "),l=n?` [${n}]`:"",p=r?` "${r}"`:"";return s?`${i}${o} [${a}]${l}`.trimEnd():`${i}${o} [${a}]${p}${l}`.trimEnd()}function P(e){let t=e.replace(/XCUIElementType/gi,"").toLowerCase(),s=e.includes(".")&&(e.startsWith("android.")||e.startsWith("androidx.")||e.startsWith("com."));switch(t.includes(".")&&(t=t.replace(/^android\.widget\./,"").replace(/^android\.view\./,"").replace(/^android\.webkit\./,"").replace(/^androidx\./,"").replace(/^com\.google\.android\./,"").replace(/^com\.android\./,"")),t){case"application":return"application";case"navigationbar":return"navigation-bar";case"tabbar":return"tab-bar";case"button":case"imagebutton":return"button";case"link":return"link";case"cell":return"cell";case"statictext":case"checkedtextview":return"text";case"textfield":case"edittext":return"text-field";case"textview":return s?"text":"text-view";case"textarea":return"text-view";case"switch":return"switch";case"slider":return"slider";case"image":case"imageview":return"image";case"webview":return"webview";case"framelayout":case"linearlayout":case"relativelayout":case"constraintlayout":case"viewgroup":case"view":case"group":return"group";case"listview":case"recyclerview":return"list";case"collectionview":return"collection";case"searchfield":return"search";case"segmentedcontrol":return"segmented-control";case"window":return"window";case"checkbox":return"checkbox";case"radio":return"radio";case"menuitem":return"menu-item";case"toolbar":return"toolbar";case"scrollarea":case"scrollview":case"nestedscrollview":return"scroll-area";case"table":return"table";default:return t||"element"}}let R=t.join(f.homedir(),".agent-device"),V=t.join(R,"daemon.json"),q=t.join(R,"daemon.lock"),G=function(e=process.env.AGENT_DEVICE_DAEMON_TIMEOUT_MS){if(!e)return 9e4;let t=Number(e);return Number.isFinite(t)?Math.max(1e3,Math.floor(t)):9e4}();async function W(t){let s=t.meta?.requestId??e(),a=!!(t.meta?.debug||t.flags?.verbose),r=await p("daemon_startup",async()=>await U(),{requestId:s,session:t.session}),i={...t,token:r.token,meta:{requestId:s,debug:a}};return c({level:"info",phase:"daemon_request_prepare",data:{requestId:s,command:t.command,session:t.session}}),await p("daemon_request",async()=>await et(r,i),{requestId:s,command:t.command})}async function U(){var e;let t=X(),s=o(),a=!!t&&await Y(t);if(t&&t.version===s&&a)return t;t&&(t.version!==s||!a)&&(await z(t),Q(V)),function(){let e=K();if(!e.hasLock||e.hasInfo)return;let t=H();t&&r(t.pid,t.processStartTime)||Q(q)}(),await ee();let i=await B(5e3);if(i)return i;if(await J()){await ee();let e=await B(5e3);if(e)return e}throw new g("COMMAND_FAILED","Failed to start daemon",{kind:"daemon_startup_failed",infoPath:V,lockPath:q,hint:(e=K()).hasLock&&!e.hasInfo?"Detected ~/.agent-device/daemon.lock without daemon.json. If no agent-device daemon process is running, delete ~/.agent-device/daemon.lock and retry.":e.hasLock&&e.hasInfo?"Daemon metadata may be stale. If no agent-device daemon process is running, delete ~/.agent-device/daemon.json and ~/.agent-device/daemon.lock, then retry.":"Daemon metadata is missing or stale. Delete ~/.agent-device/daemon.json if present and retry."})}async function B(e){let t=Date.now();for(;Date.now()-t<e;){let e=X();if(e&&await Y(e))return e;await new Promise(e=>setTimeout(e,100))}return null}async function J(){let e=K();if(!e.hasLock||e.hasInfo)return!1;let t=H();return t&&r(t.pid,t.processStartTime)&&await v(t.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:t.processStartTime}),Q(q),!0}async function z(e){await v(e.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:e.processStartTime})}function X(){let e=Z(V);return e&&e.port&&e.token?{...e,pid:Number.isInteger(e.pid)&&e.pid>0?e.pid:0}:null}function H(){let e=Z(q);return e&&Number.isInteger(e.pid)&&!(e.pid<=0)?e:null}function K(){return{hasInfo:m.existsSync(V),hasLock:m.existsSync(q)}}function Z(e){if(!m.existsSync(e))return null;try{return JSON.parse(m.readFileSync(e,"utf8"))}catch{return null}}function Q(e){try{m.existsSync(e)&&m.unlinkSync(e)}catch{}}async function Y(e){return new Promise(t=>{let s=w.createConnection({host:"127.0.0.1",port:e.port},()=>{s.destroy(),t(!0)});s.on("error",()=>{t(!1)})})}async function ee(){let e=n(),s=t.join(e,"dist","src","daemon.js"),a=t.join(e,"src","daemon.ts"),r=m.existsSync(s),o=m.existsSync(a);if(!r&&!o)throw new g("COMMAND_FAILED","Daemon entry not found",{distPath:s,srcPath:a});let l=(process.execArgv.includes("--experimental-strip-types")?o:!r&&o)?["--experimental-strip-types",a]:[s];i(process.execPath,l)}async function et(e,t){return new Promise((s,a)=>{let r=w.createConnection({host:"127.0.0.1",port:e.port},()=>{r.write(`${JSON.stringify(t)}
|
|
16
|
+
`)}),i=setTimeout(()=>{r.destroy(),c({level:"error",phase:"daemon_request_timeout",data:{timeoutMs:G,requestId:t.meta?.requestId,command:t.command}}),a(new g("COMMAND_FAILED","Daemon request timed out",{timeoutMs:G,requestId:t.meta?.requestId,hint:"Retry with --debug and check daemon diagnostics logs."}))},G),o="";r.setEncoding("utf8"),r.on("data",e=>{let n=(o+=e).indexOf("\n");if(-1===n)return;let l=o.slice(0,n).trim();if(l)try{let e=JSON.parse(l);r.end(),clearTimeout(i),s(e)}catch(e){clearTimeout(i),a(new g("COMMAND_FAILED","Invalid daemon response",{requestId:t.meta?.requestId,line:l},e instanceof Error?e:void 0))}}),r.on("error",e=>{clearTimeout(i),c({level:"error",phase:"daemon_request_socket_error",data:{requestId:t.meta?.requestId,message:e instanceof Error?e.message:String(e)}}),a(new g("COMMAND_FAILED","Failed to communicate with daemon",{requestId:t.meta?.requestId,hint:"Retry command. If this persists, clean stale daemon metadata and start a fresh session."},e))})})}let es={sendToDaemon:W};async function ea(r,i=es){let n=e(),p=r.includes("--debug")||r.includes("--verbose")||r.includes("-v"),d=r.includes("--json"),w=function(e){for(let t=0;t<e.length;t+=1){let s=e[t];if(s.startsWith("--session=")){let e=s.slice(10).trim();return e.length>0?e:null}if("--session"===s){let s=e[t+1]?.trim();if(s&&!s.startsWith("-"))return s;break}}return null}(r)??process.env.AGENT_DEVICE_SESSION??"default";await y({session:w,requestId:n,command:r[0],debug:p},async()=>{var e,w;let y;try{y=function(e,t){let s=(void 0)??function(e){if(!e)return!1;let t=e.trim().toLowerCase();return"1"===t||"true"===t||"yes"===t||"on"===t}(process.env.AGENT_DEVICE_STRICT_FLAGS),a={json:!1,help:!1,version:!1},r=null,i=[],o=[],n=[],l=!0;for(let t=0;t<e.length;t+=1){let s=e[t];if(l&&"--"===s){l=!1;continue}if(!l){r?i.push(s):r=s;continue}let o=s.startsWith("--"),p=s.startsWith("-")&&s.length>1;if(!o&&!p){r?i.push(s):r=s;continue}let[c,u]=o?_(s):[s,void 0],d=A.get(c);if(!d){if(function(e,t,s){var a;if(a=s,!/^-\d+(\.\d+)?$/.test(a)||!e)return!1;let r=D(e);return!r||!!r.allowsExtraPositionals||0!==r.positionalArgs.length&&(t.length<r.positionalArgs.length||r.positionalArgs.some(e=>e.includes("?")))}(r,i,s)){r?i.push(s):r=s;continue}throw new g("INVALID_ARGS",`Unknown flag: ${c}`)}let m=function(e,t,s,a){if(void 0!==e.setValue){if(void 0!==s)throw new g("INVALID_ARGS",`Flag ${t} does not take a value.`);return{value:e.setValue,consumeNext:!1}}if("boolean"===e.type){if(void 0!==s)throw new g("INVALID_ARGS",`Flag ${t} does not take a value.`);return{value:!0,consumeNext:!1}}if("booleanOrString"===e.type){if(void 0!==s){if(0===s.trim().length)throw new g("INVALID_ARGS",`Flag ${t} requires a non-empty value when provided.`);return{value:s,consumeNext:!1}}return void 0===a||j(a)||!function(e){let t=e.trim();return!(!t||/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(t))&&!!(t.startsWith("./")||t.startsWith("../")||t.startsWith("~/")||t.startsWith("/")||t.includes("/")||t.includes("\\"))}(a)?{value:!0,consumeNext:!1}:{value:a,consumeNext:!0}}let r=s??a;if(void 0===r||void 0===s&&j(r))throw new g("INVALID_ARGS",`Flag ${t} requires a value.`);if("string"===e.type)return{value:r,consumeNext:void 0===s};if("enum"===e.type){if(!e.enumValues?.includes(r))throw new g("INVALID_ARGS",`Invalid ${E(t)}: ${r}`);return{value:r,consumeNext:void 0===s}}let i=Number(r);if(!Number.isFinite(i)||"number"==typeof e.min&&i<e.min||"number"==typeof e.max&&i>e.max)throw new g("INVALID_ARGS",`Invalid ${E(t)}: ${r}`);return{value:Math.floor(i),consumeNext:void 0===s}}(d,c,u,e[t+1]);m.consumeNext&&(t+=1),a[d.key]=m.value,n.push({key:d.key,token:c})}let p=D(r),c=new Set([...$,...p?.allowedFlags??[]]),u=n.filter(e=>!c.has(e.key));if(u.length>0){var d,m;let e=(d=r,m=u.map(e=>e.token),d?1===m.length?`Flag ${m[0]} is not supported for command ${d}.`:`Flags ${m.join(", ")} are not supported for command ${d}.`:1===m.length?`Flag ${m[0]} requires a command that supports it.`:`Flags ${m.join(", ")} require a command that supports them.`);if(s)throw new g("INVALID_ARGS",e);for(let t of(o.push(`${e} Enable AGENT_DEVICE_STRICT_FLAGS=1 to fail fast.`),u))delete a[t.key]}if(p?.defaults)for(let[e,t]of Object.entries(p.defaults))void 0===a[e]&&(a[e]=t);if("batch"===r&&1!=+!!a.steps+ +!!a.stepsFile)throw new g("INVALID_ARGS","batch requires exactly one step source: --steps or --steps-file.");return{command:r,positionals:i,flags:a,warnings:o}}(r)}catch(t){c({level:"error",phase:"cli_parse_failed",data:{error:t instanceof Error?t.message:String(t)}});let e=a(t,{diagnosticId:l().diagnosticId,logPath:h({force:!0})??void 0});d?M({success:!1,error:e}):C(e,{showDetails:p}),process.exit(1);return}for(let e of y.warnings)process.stderr.write(`Warning: ${e}
|
|
17
|
+
`);y.flags.version&&(process.stdout.write(`${o()}
|
|
18
|
+
`),process.exit(0));let v="help"===y.command,b=y.flags.help;if(v||b){v&&y.positionals.length>1&&(C(new g("INVALID_ARGS","help accepts at most one command.")),process.exit(1));let e=v?y.positionals[0]:y.command;e||(process.stdout.write(`${L}
|
|
19
|
+
`),process.exit(0));let t=function(e){let t=D(e);if(!t)return null;let s=F(e,t),a=N(new Set(t.allowedFlags)),r=N($),i=[];return a.length>0&&i.push(O("Command flags:",a)),i.push(O("Global flags:",r)),`agent-device ${s}
|
|
17
20
|
|
|
18
21
|
${t.description}
|
|
19
22
|
|
|
20
23
|
Usage:
|
|
21
24
|
agent-device ${s}
|
|
22
25
|
|
|
23
|
-
${
|
|
24
|
-
`}(e);t&&(process.stdout.write(t),process.exit(0)),
|
|
25
|
-
`),process.exit(1)}
|
|
26
|
-
`),process.exit(1));let{command:
|
|
27
|
-
`)),
|
|
28
|
-
`),
|
|
26
|
+
${i.join("\n\n")}
|
|
27
|
+
`}(e);t&&(process.stdout.write(t),process.exit(0)),C(new g("INVALID_ARGS",`Unknown command: ${e}`)),process.stdout.write(`${L}
|
|
28
|
+
`),process.exit(1)}y.command||(process.stdout.write(`${L}
|
|
29
|
+
`),process.exit(1));let{command:x,positionals:k,flags:S}=y,I=function(e){let{json:t,help:s,version:a,...r}=e;return r}(S),R=S.session??process.env.AGENT_DEVICE_SESSION??"default",V=S.verbose&&!S.json?function(){try{let e=t.join(f.homedir(),".agent-device","daemon.log"),s=0,a=!1,r=setInterval(()=>{if(!a&&m.existsSync(e))try{let t=m.statSync(e);if(t.size<s&&(s=0),t.size<=s)return;let a=m.openSync(e,"r");try{let e=Buffer.alloc(t.size-s);m.readSync(a,e,0,e.length,s),s=t.size,e.length>0&&process.stdout.write(e.toString("utf8"))}finally{m.closeSync(a)}}catch{}},200);return()=>{a=!0,clearInterval(r)}}catch{return null}}():null,q=async e=>await i.sendToDaemon({session:R,command:e.command,positionals:e.positionals,flags:e.flags,meta:{requestId:n,debug:!!S.verbose}});try{if("batch"===x){let t,a,r;if(k.length>0)throw new g("INVALID_ARGS","batch does not accept positional arguments.");let i=function(e){let t="";if(e.steps)t=e.steps;else if(e.stepsFile)try{t=m.readFileSync(e.stepsFile,"utf8")}catch(s){let t=s instanceof Error?s.message:String(s);throw new g("INVALID_ARGS",`Failed to read --steps-file ${e.stepsFile}: ${t}`)}return s(t)}(S),o={...I,batchSteps:i};delete o.steps,delete o.stepsFile;let n=await q({command:"batch",positionals:k,flags:o});if(!n.ok)throw new g(n.error.code,n.error.message,{...n.error.details??{},hint:n.error.hint,diagnosticId:n.error.diagnosticId,logPath:n.error.logPath});S.json?M({success:!0,data:n.data??{}}):(e=n.data??{},t="number"==typeof e.total?e.total:0,a="number"==typeof e.executed?e.executed:0,r="number"==typeof e.totalDurationMs?e.totalDurationMs:void 0,process.stdout.write(`Batch completed: ${a}/${t} steps${void 0!==r?` in ${r}ms`:""}
|
|
30
|
+
`)),V&&V();return}if("session"===x){let e=k[0]??"list";if("list"!==e)throw new g("INVALID_ARGS","session only supports list");let t=await q({command:"session_list",positionals:[],flags:I});if(!t.ok)throw new g(t.error.code,t.error.message,{...t.error.details??{},hint:t.error.hint,diagnosticId:t.error.diagnosticId,logPath:t.error.logPath});S.json?M({success:!0,data:t.data??{}}):process.stdout.write(`${JSON.stringify(t.data??{},null,2)}
|
|
31
|
+
`),V&&V();return}let t=await q({command:x,positionals:k,flags:I});if(t.ok){if(S.json){M({success:!0,data:t.data??{}}),V&&V();return}if("snapshot"===x){process.stdout.write(function(e,t={}){let s=e.nodes,a=Array.isArray(s)?s:[],r=!!e.truncated,i="string"==typeof e.appName?e.appName:void 0,o="string"==typeof e.appBundleId?e.appBundleId:void 0,n=[];i&&n.push(`Page: ${i}`),o&&n.push(`App: ${o}`);let l=`Snapshot: ${a.length} nodes${r?" (truncated)":""}`,p=n.length>0?`${n.join("\n")}
|
|
29
32
|
`:"";if(0===a.length)return`${p}${l}
|
|
30
33
|
`;if(t.raw){let e=a.map(e=>JSON.stringify(e));return`${p}${l}
|
|
31
34
|
${e.join("\n")}
|
|
32
|
-
`}if(t.flatten){let e=a.map(e=>
|
|
35
|
+
`}if(t.flatten){let e=a.map(e=>T(e,0,!1));return`${p}${l}
|
|
33
36
|
${e.join("\n")}
|
|
34
|
-
`}let c=[],u=[];for(let e of a){let t=e.depth??0;for(;c.length>0&&t<=c[c.length-1];)c.pop();let s=e.label?.trim()||e.value?.trim()||e.identifier?.trim()||"",a="group"===
|
|
37
|
+
`}let c=[],u=[];for(let e of a){let t=e.depth??0;for(;c.length>0&&t<=c[c.length-1];)c.pop();let s=e.label?.trim()||e.value?.trim()||e.identifier?.trim()||"",a="group"===P(e.type??"Element")&&!s;a&&c.push(t);let r=a?t:Math.max(0,t-c.length);u.push(T(e,r,a))}return`${p}${l}
|
|
35
38
|
${u.join("\n")}
|
|
36
|
-
`}(
|
|
37
|
-
`),
|
|
38
|
-
`),
|
|
39
|
-
`),
|
|
40
|
-
`),
|
|
41
|
-
`),
|
|
42
|
-
`),
|
|
43
|
-
`),
|
|
44
|
-
`),
|
|
45
|
-
`),
|
|
46
|
-
`),
|
|
39
|
+
`}(t.data??{},{raw:S.snapshotRaw,flatten:S.snapshotInteractiveOnly})),V&&V();return}if("get"===x){let e=k[0];if("text"===e){let e=t.data?.text??"";process.stdout.write(`${e}
|
|
40
|
+
`),V&&V();return}if("attrs"===e){let e=t.data?.node??{};process.stdout.write(`${JSON.stringify(e,null,2)}
|
|
41
|
+
`),V&&V();return}}if("find"===x){let e=t.data;if("string"==typeof e?.text){process.stdout.write(`${e.text}
|
|
42
|
+
`),V&&V();return}if("boolean"==typeof e?.found){process.stdout.write(`Found: ${e.found}
|
|
43
|
+
`),V&&V();return}if(e?.node){process.stdout.write(`${JSON.stringify(e.node,null,2)}
|
|
44
|
+
`),V&&V();return}}if("is"===x){let e=t.data?.predicate??"assertion";process.stdout.write(`Passed: is ${e}
|
|
45
|
+
`),V&&V();return}if("boot"===x){let e=t.data?.platform??"unknown",s=t.data?.device??t.data?.id??"unknown";process.stdout.write(`Boot ready: ${s} (${e})
|
|
46
|
+
`),V&&V();return}if("click"===x||"press"===x){let e=t.data?.ref??"",s=t.data?.x,a=t.data?.y;e&&"number"==typeof s&&"number"==typeof a&&process.stdout.write(`Tapped @${e} (${s}, ${a})
|
|
47
|
+
`),V&&V();return}if(t.data&&"object"==typeof t.data){let e=t.data;if("devices"===x){let t=(Array.isArray(e.devices)?e.devices:[]).map(e=>{let t=e?.name??e?.id??"unknown",s=e?.platform??"unknown",a=e?.kind?` ${e.kind}`:"",r="boolean"==typeof e?.booted?` booted=${e.booted}`:"";return`${t} (${s}${a})${r}`});process.stdout.write(`${t.join("\n")}
|
|
48
|
+
`),V&&V();return}if("apps"===x){let t=(Array.isArray(e.apps)?e.apps:[]).map(e=>{if("string"==typeof e)return e;if(e&&"object"==typeof e){let t=e.bundleId??e.package,s=e.name??e.label;return s&&t?`${s} (${t})`:t?String(t):JSON.stringify(e)}return String(e)});process.stdout.write(`${t.join("\n")}
|
|
49
|
+
`),V&&V();return}if("appstate"===x){let t=e?.platform,s=e?.appBundleId,a=e?.appName,r=e?.source,i=e?.package,o=e?.activity;if("ios"===t){process.stdout.write(`Foreground app: ${a??s??"unknown"}
|
|
47
50
|
`),s&&process.stdout.write(`Bundle: ${s}
|
|
48
|
-
`),
|
|
49
|
-
`),
|
|
51
|
+
`),r&&process.stdout.write(`Source: ${r}
|
|
52
|
+
`),V&&V();return}if("android"===t){process.stdout.write(`Foreground app: ${i??"unknown"}
|
|
50
53
|
`),o&&process.stdout.write(`Activity: ${o}
|
|
51
|
-
`),
|
|
54
|
+
`),V&&V();return}}}V&&V();return}throw new g(t.error.code,t.error.message,{...t.error.details??{},hint:t.error.hint,diagnosticId:t.error.diagnosticId,logPath:t.error.logPath})}catch(r){let e=u(r),s=a(e,{diagnosticId:l().diagnosticId,logPath:h({force:!0})??void 0});if("close"===x&&"COMMAND_FAILED"===(w=e).code&&(w.details?.kind==="daemon_startup_failed"||w.message.toLowerCase().includes("failed to start daemon")&&("string"==typeof w.details?.infoPath||"string"==typeof w.details?.lockPath))){S.json&&M({success:!0,data:{closed:"session",source:"no-daemon"}}),V&&V();return}if(S.json)M({success:!1,error:s});else if(C(s,{showDetails:S.verbose}),S.verbose)try{let e=t.join(f.homedir(),".agent-device","daemon.log");if(m.existsSync(e)){let t=m.readFileSync(e,"utf8").split("\n"),s=t.slice(Math.max(0,t.length-200)).join("\n");s.trim().length>0&&process.stderr.write(`
|
|
52
55
|
[daemon log]
|
|
53
56
|
${s}
|
|
54
|
-
`)}}catch{}
|
|
57
|
+
`)}}catch{}V&&V(),process.exit(1)}})}d(process.argv[1]??"").href===import.meta.url&&ea(process.argv.slice(2)).catch(e=>{C(a(u(e)),{showDetails:!0}),process.exit(1)}),ea(process.argv.slice(2));
|
package/dist/src/daemon.js
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
|
-
import
|
|
2
|
-
`)}})}catch(u){let r=p(u),o=i?.stdout,s=i?.stderr,l=i?.exitCode,c=E({error:u,stdout:o,stderr:s,context:{platform:"android",phase:"boot"}});"BOOT_COMMAND_FAILED"===c&&"Android device is still booting"===r.message&&(c="ANDROID_BOOT_TIMEOUT");let d={serial:e,timeoutMs:t,elapsedMs:n.elapsedMs(),reason:c,hint:x(c),stdout:o,stderr:s,exitCode:l};if(a||"ANDROID_BOOT_TIMEOUT"===c)throw new h("COMMAND_FAILED","Android device did not finish booting in time",d);if("TOOL_MISSING"===r.code)throw new h("TOOL_MISSING",r.message,{...d,...r.details??{}});if("ADB_TRANSPORT_UNAVAILABLE"===c)throw new h("COMMAND_FAILED",r.message,{...d,...r.details??{}});throw new h(r.code,r.message,{...d,...r.details??{}},r.cause)}}function F(e){let t=e.trim();if(!t||/\s/.test(t))return!1;let i=/^([A-Za-z][A-Za-z0-9+.-]*):(.+)$/.exec(t);if(!i)return!1;let n=i[1]?.toLowerCase(),r=i[2]??"";return"http"!==n&&"https"!==n&&"ws"!==n&&"wss"!==n&&"ftp"!==n&&"ftps"!==n||r.startsWith("//")}function V(e,t){let i,n=e?.trim();return n?n:"http"===(i=t.trim().split(":")[0]?.toLowerCase())||"https"===i?"com.apple.mobilesafari":void 0}function U(e){let t=G(e),i=e=>{let i=j(t,e);if(null!==i)return"true"===i};return{text:j(t,"text"),desc:j(t,"content-desc"),resourceId:j(t,"resource-id"),className:j(t,"class"),bounds:j(t,"bounds"),clickable:i("clickable"),enabled:i("enabled"),focusable:i("focusable"),focused:i("focused")}}function G(e){let t=new Map,i=e.indexOf(" "),n=e.lastIndexOf(">");if(i<0||n<=i)return t;let r=/([^\s=/>]+)\s*=\s*(["'])([\s\S]*?)\2/y,a=i;for(;a<n;){for(;a<n;){let t=e[a];if(" "!==t&&"\n"!==t&&"\r"!==t&&" "!==t)break;a+=1}if(a>=n)break;let i=e[a];if("/"===i||">"===i)break;r.lastIndex=a;let o=r.exec(e);if(!o)break;t.set(o[1],o[3]),a=r.lastIndex}return t}function j(e,t){return e.get(t)??null}function B(e){if(!e)return;let t=/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/.exec(e);if(!t)return;let i=Number(t[1]),n=Number(t[2]);return{x:i,y:n,width:Math.max(0,Number(t[3])-i),height:Math.max(0,Number(t[4])-n)}}function q(e){return e?e.toLowerCase():""}function W(e){let t=e.trim();return!!t&&/^[\w.]+:id\/[\w.-]+$/i.test(t)}let J={settings:{type:"intent",value:"android.settings.SETTINGS"}};function H(e,t){return["-s",e.id,...t]}async function z(e,t){let i=t.trim();if(i.includes("."))return{type:"package",value:i};let n=J[i.toLowerCase()];if(n)return n;let r=(await d("adb",H(e,["shell","pm","list","packages"]))).stdout.split("\n").map(e=>e.replace("package:","").trim()).filter(Boolean).filter(e=>e.toLowerCase().includes(i.toLowerCase()));if(1===r.length)return{type:"package",value:r[0]};if(r.length>1)throw new h("INVALID_ARGS",`Multiple packages matched "${t}"`,{matches:r});throw new h("APP_NOT_INSTALLED",`No package found matching "${t}"`)}async function X(e,t="all"){let i=await Y(e);return("user-installed"===t?(await K(e)).filter(e=>i.has(e)):Array.from(i)).sort((e,t)=>e.localeCompare(t)).map(e=>({package:e,name:function(e){let t=new Set(["com","android","google","app","apps","service","services","mobile","client"]),i=e.split(".").flatMap(e=>e.split(/[_-]+/)).map(e=>e.trim().toLowerCase()).filter(e=>e.length>0),n=i[i.length-1]??e;for(let e=i.length-1;e>=0;e-=1){let r=i[e];if(!t.has(r)){n=r;break}}return n.split(/[^a-z0-9]+/i).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}(e)}))}async function Y(e){let t=await d("adb",H(e,["shell","cmd","package","query-activities","--brief","-a","android.intent.action.MAIN","-c","android.intent.category.LAUNCHER"]),{allowFailure:!0});if(0!==t.exitCode||0===t.stdout.trim().length)return new Set;let i=new Set;for(let e of t.stdout.split("\n")){let t=e.trim();if(!t)continue;let n=t.split(/\s+/)[0],r=n.includes("/")?n.split("/")[0]:n;r&&i.add(r)}return i}async function K(e){return(await d("adb",H(e,["shell","pm","list","packages","-3"]))).stdout.split("\n").map(e=>e.replace("package:","").trim()).filter(Boolean)}async function Z(e){let t=await Q(e,[["shell","dumpsys","window","windows"],["shell","dumpsys","window"]]);if(t)return t;let i=await Q(e,[["shell","dumpsys","activity","activities"],["shell","dumpsys","activity"]]);return i||{}}async function Q(e,t){for(let i of t){let t=function(e){for(let t of[/mCurrentFocus=Window\{[^}]*\s([\w.]+)\/([\w.$]+)/,/mFocusedApp=AppWindowToken\{[^}]*\s([\w.]+)\/([\w.$]+)/,/mResumedActivity:.*?\s([\w.]+)\/([\w.$]+)/,/ResumedActivity:.*?\s([\w.]+)\/([\w.$]+)/]){let i=t.exec(e);if(i)return{package:i[1],activity:i[2]}}return null}((await d("adb",H(e,i),{allowFailure:!0})).stdout??"");if(t)return t}return null}async function ee(e,t,i){e.booted||await $(e.id);let n=t.trim();if(F(n)){if(i)throw new h("INVALID_ARGS","Activity override is not supported when opening a deep link URL");await d("adb",H(e,["shell","am","start","-W","-a","android.intent.action.VIEW","-d",n]));return}let r=await z(e,t);if("intent"===r.type){if(i)throw new h("INVALID_ARGS","Activity override requires a package name, not an intent");await d("adb",H(e,["shell","am","start","-W","-a",r.value]));return}if(i){let t=i.includes("/")?i:`${r.value}/${i.startsWith(".")?i:`.${i}`}`;await d("adb",H(e,["shell","am","start","-W","-a","android.intent.action.MAIN","-c","android.intent.category.DEFAULT","-c","android.intent.category.LAUNCHER","-n",t]));return}try{await d("adb",H(e,["shell","am","start","-W","-a","android.intent.action.MAIN","-c","android.intent.category.DEFAULT","-c","android.intent.category.LAUNCHER","-p",r.value]));return}catch(i){let t=await et(e,r.value);if(!t)throw i;await d("adb",H(e,["shell","am","start","-W","-a","android.intent.action.MAIN","-c","android.intent.category.DEFAULT","-c","android.intent.category.LAUNCHER","-n",t]))}}async function et(e,t){let i=await d("adb",H(e,["shell","cmd","package","resolve-activity","--brief",t]),{allowFailure:!0});return 0!==i.exitCode?null:function(e){let t=e.split("\n").map(e=>e.trim()).filter(Boolean);for(let e=t.length-1;e>=0;e-=1){let i=t[e];if(i.includes("/"))return i.split(/\s+/)[0]}return null}(i.stdout)}async function ei(e){e.booted||await $(e.id)}async function en(e,t){if("settings"===t.trim().toLowerCase())return void await d("adb",H(e,["shell","am","force-stop","com.android.settings"]));let i=await z(e,t);if("intent"===i.type)throw new h("INVALID_ARGS","Close requires a package name, not an intent");await d("adb",H(e,["shell","am","force-stop",i.value]))}async function er(e,t){let i=await z(e,t);if("intent"===i.type)throw new h("INVALID_ARGS","reinstall requires a package name, not an intent");let n=await d("adb",H(e,["uninstall",i.value]),{allowFailure:!0});if(0!==n.exitCode){let e=`${n.stdout}
|
|
3
|
-
${
|
|
4
|
-
${
|
|
5
|
-
${t}`,
|
|
6
|
-
${t}`.toLowerCase();return i.includes("device is busy")&&i.includes("connecting")?"iOS device is still connecting. Keep it unlocked and connected by cable until it is fully available in Xcode Devices, then retry.":i.includes("coredeviceservice")&&i.includes("timed out")?"CoreDevice service timed out. Reconnect the device and retry; if it persists restart Xcode and the iOS device.":null}async function
|
|
7
|
-
${t.stderr}`.toLowerCase(),s=o.includes("already booted")||o.includes("current state: booted");if(0!==t.exitCode&&!s)throw new
|
|
8
|
-
|
|
9
|
-
${a}`.toLowerCase()))throw new h("COMMAND_FAILED",`Failed to uninstall iOS app ${i}`,{cmd:"xcrun",args:t,exitCode:n.exitCode,stdout:r,stderr:a,deviceId:e.id,hint:eB(r,a)??ej})}return{bundleId:i}}await eq(e);let n=await d("xcrun",["simctl","uninstall",e.id,i],{allowFailure:!0});if(0!==n.exitCode&&!eH(`${n.stdout}
|
|
10
|
-
${
|
|
11
|
-
${e}`.toLowerCase()).includes("requires a development team")?"Configure signing in Xcode or set AGENT_DEVICE_IOS_TEAM_ID for physical-device runs.":i.includes("no profiles for")||i.includes("provisioning profile")?"Install/select a valid iOS provisioning profile, or set AGENT_DEVICE_IOS_PROVISIONING_PROFILE.":i.includes("code signing")?"Enable Automatic Signing in Xcode or provide AGENT_DEVICE_IOS_TEAM_ID and optional AGENT_DEVICE_IOS_SIGNING_IDENTITY.":void 0);throw new h("COMMAND_FAILED","xcodebuild build-for-testing failed",{error:n.message,details:n.details,logPath:t.logPath,hint:r})}let m=tS(l);if(!m)throw new h("COMMAND_FAILED","Failed to locate .xctestrun after build");return m}function tN(e){return"device"===e.kind?"-maximum-concurrent-test-device-destinations":"-maximum-concurrent-test-simulator-destinations"}function tS(e){if(!w.existsSync(e))return null;let t=[],i=[e];for(;i.length>0;){let e=i.pop();for(let r of w.readdirSync(e,{withFileTypes:!0})){let a=n.join(e,r.name);if(r.isDirectory()){i.push(a);continue}if(r.isFile()&&r.name.endsWith(".xctestrun"))try{let e=w.statSync(a);t.push({path:a,mtimeMs:e.mtimeMs})}catch{}}}return 0===t.length?null:(t.sort((e,t)=>t.mtimeMs-e.mtimeMs),t[0]?.path??null)}function tb(e,t,i,n){t&&w.appendFileSync(t,e),i&&w.appendFileSync(i,e),n&&process.stderr.write(e)}function tD(e){if(!(e instanceof h)||"COMMAND_FAILED"!==e.code)return!1;let t=`${e.message??""}`.toLowerCase();return!(t.includes("xcodebuild exited early")||t.includes("device is busy")&&t.includes("connecting"))&&!!(t.includes("runner did not accept connection")||t.includes("fetch failed")||t.includes("econnrefused")||t.includes("socket hang up"))}function t_(e){let{port:t,endpoints:i,logPath:n,lastError:r}=e,a="Runner did not accept connection";return new h("COMMAND_FAILED",a,{port:t,endpoints:i,logPath:n,lastError:r?String(r):void 0,reason:E({error:r,message:a,context:{platform:"ios",phase:"connect"}}),hint:x("IOS_RUNNER_CONNECT_TIMEOUT")})}function tO(e){return!(e instanceof h)||"COMMAND_FAILED"!==e.code||!String(e.message??"").toLowerCase().includes("xcodebuild exited early")}async function tM(e){var t,i;let n,{session:r,port:a,logPath:o}=e,s=await r.testPromise,l="Runner did not accept connection (xcodebuild exited early)",c=E({message:l,stdout:s.stdout,stderr:s.stderr,context:{platform:"ios",phase:"connect"}});return new h("COMMAND_FAILED",l,{port:a,logPath:o,xcodebuild:{exitCode:s.exitCode,stdout:s.stdout,stderr:s.stderr},reason:c,hint:(t=s.stdout,i=s.stderr,(n=`${l}
|
|
1
|
+
import{isCancel as e,select as t}from"@clack/prompts";import{node_path as i,runCmdStreaming as r,fileURLToPath as n,normalizeError as a,isAgentDeviceDaemonProcess as o,validateAndNormalizeBatchSteps as s,runCmdBackground as l,node_crypto as c,getDiagnosticsMeta as d,runCmd as u,withDiagnosticTimer as p,readVersion as f,emitDiagnostic as m,promises as h,asAppError as w,DEFAULT_BATCH_MAX_STEPS as g,isProcessAlive as v,AppError as I,node_fs as A,withDiagnosticsScope as y,flushDiagnosticsToSessionFile as N,node_os as S,readProcessStartTime as b,node_net as _,whichCmd as D}from"./407.js";async function O(i,r){let n=i,a=e=>e.toLowerCase().replace(/_/g," ").replace(/\s+/g," ").trim();if(r.platform&&(n=n.filter(e=>e.platform===r.platform)),r.udid){let e=n.find(e=>e.id===r.udid&&"ios"===e.platform);if(!e)throw new I("DEVICE_NOT_FOUND",`No iOS device with UDID ${r.udid}`);return e}if(r.serial){let e=n.find(e=>e.id===r.serial&&"android"===e.platform);if(!e)throw new I("DEVICE_NOT_FOUND",`No Android device with serial ${r.serial}`);return e}if(r.deviceName){let e=a(r.deviceName),t=n.find(t=>a(t.name)===e);if(!t)throw new I("DEVICE_NOT_FOUND",`No device named ${r.deviceName}`);return t}if(1===n.length)return n[0];if(0===n.length)throw new I("DEVICE_NOT_FOUND","No devices found",{selector:r});let o=n.filter(e=>e.booted);if(1===o.length)return o[0];if(!process.env.CI&&process.stdin.isTTY&&process.stdout.isTTY){let i=await t({message:"Multiple devices available. Choose a device to continue:",options:(o.length>0?o:n).map(e=>({label:`${e.name} (${e.platform}${e.kind?`, ${e.kind}`:""}${e.booted?", booted":""})`,value:e.id}))});if(e(i))throw new I("INVALID_ARGS","Device selection cancelled");if(i){let e=n.find(e=>e.id===i);if(e)return e}}return o[0]??n[0]}let M=E(process.env.AGENT_DEVICE_RETRY_LOGS);function E(e){return["1","true","yes","on"].includes((e??"").toLowerCase())}let x=2e4,k=12e4,L=1e4;class C{startedAtMs;expiresAtMs;constructor(e,t){this.startedAtMs=e,this.expiresAtMs=e+Math.max(0,t)}static fromTimeoutMs(e,t=Date.now()){return new C(t,e)}remainingMs(e=Date.now()){return Math.max(0,this.expiresAtMs-e)}elapsedMs(e=Date.now()){return Math.max(0,e-this.startedAtMs)}isExpired(e=Date.now()){return 0>=this.remainingMs(e)}}async function R(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()}),P({phase:i.phase,event:"succeeded",attempt:t,maxAttempts:n.maxAttempts,elapsedMs:i.deadline?.elapsedMs(),remainingMs:i.deadline?.remainingMs()}),r}catch(c){r=c;let e=i.classifyReason?.(c),a={phase:i.phase,event:"attempt_failed",attempt:t,maxAttempts:n.maxAttempts,elapsedMs:i.deadline?.elapsedMs(),remainingMs:i.deadline?.remainingMs(),reason:e};if(i.onEvent?.(a),P(a),t>=n.maxAttempts||n.shouldRetry&&!n.shouldRetry(c,t))break;let o=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),s=i.deadline?Math.min(o,i.deadline.remainingMs()):o;if(s<=0)break;let l={phase:i.phase,event:"retry_scheduled",attempt:t,maxAttempts:n.maxAttempts,delayMs:s,elapsedMs:i.deadline?.elapsedMs(),remainingMs:i.deadline?.remainingMs(),reason:e};i.onEvent?.(l),P(l),await function(e){return new Promise(t=>setTimeout(t,e))}(s)}let a={phase:i.phase,event:"exhausted",attempt:n.maxAttempts,maxAttempts:n.maxAttempts,elapsedMs:i.deadline?.elapsedMs(),remainingMs:i.deadline?.remainingMs(),reason:i.classifyReason?.(r)};if(i.onEvent?.(a),P(a),r)throw r;throw new I("COMMAND_FAILED","retry failed")}async function T(e,t={}){return R(()=>e(),{maxAttempts:t.attempts,baseDelayMs:t.baseDelayMs,maxDelayMs:t.maxDelayMs,jitter:t.jitter,shouldRetry:t.shouldRetry})}function P(e){m({level:"attempt_failed"===e.event||"exhausted"===e.event?"warn":"debug",phase:"retry",data:{...e}}),M&&process.stderr.write(`[agent-device][retry] ${JSON.stringify(e)}
|
|
2
|
+
`)}function $(e){let t=e.error?w(e.error):null,i=e.context?.platform,r=e.context?.phase;if(t?.code==="TOOL_MISSING")return"android"===i?"ADB_TRANSPORT_UNAVAILABLE":"IOS_TOOL_MISSING";let n=t?.details??{},a="string"==typeof n.message?n.message:void 0,o="string"==typeof n.stdout?n.stdout:void 0,s="string"==typeof n.stderr?n.stderr:void 0,l=n.boot&&"object"==typeof n.boot?n.boot:null,c=n.bootstatus&&"object"==typeof n.bootstatus?n.bootstatus:null,d=[e.message,t?.message,e.stdout,e.stderr,a,o,s,"string"==typeof l?.stdout?l.stdout:void 0,"string"==typeof l?.stderr?l.stderr:void 0,"string"==typeof c?.stdout?c.stdout:void 0,"string"==typeof c?.stderr?c.stderr:void 0].filter(Boolean).join("\n").toLowerCase();return"ios"===i&&(d.includes("runner did not accept connection")||"connect"===r&&(d.includes("timed out")||d.includes("timeout")||d.includes("econnrefused")||d.includes("connection refused")||d.includes("fetch failed")||d.includes("socket hang up")))?"IOS_RUNNER_CONNECT_TIMEOUT":"ios"===i&&"boot"===r&&(d.includes("timed out")||d.includes("timeout"))?"IOS_BOOT_TIMEOUT":"android"===i&&"boot"===r&&(d.includes("timed out")||d.includes("timeout"))?"ANDROID_BOOT_TIMEOUT":d.includes("resource temporarily unavailable")||d.includes("killed: 9")||d.includes("cannot allocate memory")||d.includes("system is low on memory")?"CI_RESOURCE_STARVATION_SUSPECTED":"android"===i&&(d.includes("device not found")||d.includes("no devices")||d.includes("device offline")||d.includes("offline")||d.includes("unauthorized")||d.includes("not authorized")||d.includes("unable to locate device")||d.includes("invalid device"))?"ADB_TRANSPORT_UNAVAILABLE":t?.code==="COMMAND_FAILED"||d.length>0?"BOOT_COMMAND_FAILED":"UNKNOWN"}function F(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."}}function V(e){return e.startsWith("emulator-")}async function U(e,t=L){return u("adb",["-s",e,"shell","getprop","sys.boot_completed"],{allowFailure:!0,timeoutMs:t})}async function G(e,t){let i=t.replace(/_/g," ").trim();if(!V(e))return i||e;let r=await u("adb",["-s",e,"emu","avd","name"],{allowFailure:!0,timeoutMs:L}),n=r.stdout.trim();return 0===r.exitCode&&n?n.replace(/_/g," "):i||e}async function j(){if(!await D("adb"))throw new I("TOOL_MISSING","adb not found in PATH");let e=(await u("adb",["devices","-l"],{timeoutMs:L})).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([G(e,t),B(e)]);return{platform:"android",id:e,name:i,kind:V(e)?"emulator":"device",booted:r}}))}async function B(e){try{let t=await U(e);return"1"===t.stdout.trim()}catch{return!1}}async function q(e,t=6e4){let i,r=C.fromTimeoutMs(t),n=Math.max(1,Math.ceil(t/1e3)),a=!1;try{await R(async({deadline:n})=>{if(n?.isExpired())throw a=!0,new I("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 U(e,Math.min(o,L));if(i=s,"1"!==s.stdout.trim())throw new I("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=$({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=>$({error:e,stdout:i?.stdout,stderr:i?.stderr,context:{platform:"android",phase:"boot"}})})}catch(u){let n=w(u),o=i?.stdout,s=i?.stderr,l=i?.exitCode,c=$({error:u,stdout:o,stderr:s,context:{platform:"android",phase:"boot"}});"BOOT_COMMAND_FAILED"===c&&"Android device is still booting"===n.message&&(c="ANDROID_BOOT_TIMEOUT");let d={serial:e,timeoutMs:t,elapsedMs:r.elapsedMs(),reason:c,hint:F(c),stdout:o,stderr:s,exitCode:l};if(a||"ANDROID_BOOT_TIMEOUT"===c)throw new I("COMMAND_FAILED","Android device did not finish booting in time",d);if("TOOL_MISSING"===n.code)throw new I("TOOL_MISSING",n.message,{...d,...n.details??{}});if("ADB_TRANSPORT_UNAVAILABLE"===c)throw new I("COMMAND_FAILED",n.message,{...d,...n.details??{}});throw new I(n.code,n.message,{...d,...n.details??{}},n.cause)}}function W(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 J(e,t){let i,r=e?.trim();return r?r:"http"===(i=t.trim().split(":")[0]?.toLowerCase())||"https"===i?"com.apple.mobilesafari":void 0}function H(e){let t=z(e),i=e=>{let i=X(t,e);if(null!==i)return"true"===i};return{text:X(t,"text"),desc:X(t,"content-desc"),resourceId:X(t,"resource-id"),className:X(t,"class"),bounds:X(t,"bounds"),clickable:i("clickable"),enabled:i("enabled"),focusable:i("focusable"),focused:i("focused")}}function z(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 X(e,t){return e.get(t)??null}function Y(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 K(e){return e?e.toLowerCase():""}function Z(e){let t=e.trim();return!!t&&/^[\w.]+:id\/[\w.-]+$/i.test(t)}let Q={settings:{type:"intent",value:"android.settings.SETTINGS"}};function ee(e,t){return["-s",e.id,...t]}async function et(e,t){let i=t.trim();if(i.includes("."))return{type:"package",value:i};let r=Q[i.toLowerCase()];if(r)return r;let n=(await u("adb",ee(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 I("INVALID_ARGS",`Multiple packages matched "${t}"`,{matches:n});throw new I("APP_NOT_INSTALLED",`No package found matching "${t}"`)}async function ei(e,t="all"){let i=await er(e);return("user-installed"===t?(await en(e)).filter(e=>i.has(e)):Array.from(i)).sort((e,t)=>e.localeCompare(t)).map(e=>({package:e,name:function(e){let t=new Set(["com","android","google","app","apps","service","services","mobile","client"]),i=e.split(".").flatMap(e=>e.split(/[_-]+/)).map(e=>e.trim().toLowerCase()).filter(e=>e.length>0),r=i[i.length-1]??e;for(let e=i.length-1;e>=0;e-=1){let n=i[e];if(!t.has(n)){r=n;break}}return r.split(/[^a-z0-9]+/i).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}(e)}))}async function er(e){let t=await u("adb",ee(e,["shell","cmd","package","query-activities","--brief","-a","android.intent.action.MAIN","-c","android.intent.category.LAUNCHER"]),{allowFailure:!0});if(0!==t.exitCode||0===t.stdout.trim().length)return new Set;let i=new Set;for(let e of t.stdout.split("\n")){let t=e.trim();if(!t)continue;let r=t.split(/\s+/)[0],n=r.includes("/")?r.split("/")[0]:r;n&&i.add(n)}return i}async function en(e){return(await u("adb",ee(e,["shell","pm","list","packages","-3"]))).stdout.split("\n").map(e=>e.replace("package:","").trim()).filter(Boolean)}async function ea(e){let t=await eo(e,[["shell","dumpsys","window","windows"],["shell","dumpsys","window"]]);if(t)return t;let i=await eo(e,[["shell","dumpsys","activity","activities"],["shell","dumpsys","activity"]]);return i||{}}async function eo(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 u("adb",ee(e,i),{allowFailure:!0})).stdout??"");if(t)return t}return null}async function es(e,t,i){e.booted||await q(e.id);let r=t.trim();if(W(r)){if(i)throw new I("INVALID_ARGS","Activity override is not supported when opening a deep link URL");await u("adb",ee(e,["shell","am","start","-W","-a","android.intent.action.VIEW","-d",r]));return}let n=await et(e,t);if("intent"===n.type){if(i)throw new I("INVALID_ARGS","Activity override requires a package name, not an intent");await u("adb",ee(e,["shell","am","start","-W","-a",n.value]));return}if(i){let t=i.includes("/")?i:`${n.value}/${i.startsWith(".")?i:`.${i}`}`;await u("adb",ee(e,["shell","am","start","-W","-a","android.intent.action.MAIN","-c","android.intent.category.DEFAULT","-c","android.intent.category.LAUNCHER","-n",t]));return}try{await u("adb",ee(e,["shell","am","start","-W","-a","android.intent.action.MAIN","-c","android.intent.category.DEFAULT","-c","android.intent.category.LAUNCHER","-p",n.value]));return}catch(i){let t=await el(e,n.value);if(!t)throw i;await u("adb",ee(e,["shell","am","start","-W","-a","android.intent.action.MAIN","-c","android.intent.category.DEFAULT","-c","android.intent.category.LAUNCHER","-n",t]))}}async function el(e,t){let i=await u("adb",ee(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 ec(e){e.booted||await q(e.id)}async function ed(e,t){if("settings"===t.trim().toLowerCase())return void await u("adb",ee(e,["shell","am","force-stop","com.android.settings"]));let i=await et(e,t);if("intent"===i.type)throw new I("INVALID_ARGS","Close requires a package name, not an intent");await u("adb",ee(e,["shell","am","force-stop",i.value]))}async function eu(e,t){let i=await et(e,t);if("intent"===i.type)throw new I("INVALID_ARGS","reinstall requires a package name, not an intent");let r=await u("adb",ee(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 I("COMMAND_FAILED",`adb uninstall failed for ${i.value}`,{stdout:r.stdout,stderr:r.stderr,exitCode:r.exitCode})}return{package:i.value}}async function ep(e,t){await u("adb",ee(e,["install",t]))}async function ef(e,t,i){e.booted||await q(e.id);let{package:r}=await eu(e,t);return await ep(e,i),{package:r}}async function em(e,t,i){await u("adb",ee(e,["shell","input","tap",String(t),String(i)]))}async function eh(e,t,i,r,n,a=250){await u("adb",ee(e,["shell","input","swipe",String(t),String(i),String(r),String(n),String(a)]))}async function ew(e){await u("adb",ee(e,["shell","input","keyevent","4"]))}async function eg(e){await u("adb",ee(e,["shell","input","keyevent","3"]))}async function ev(e){await u("adb",ee(e,["shell","input","keyevent","187"]))}async function eI(e,t,i,r=800){await u("adb",ee(e,["shell","input","swipe",String(t),String(i),String(t),String(i),String(r)]))}async function eA(e,t){let i=t.replace(/ /g,"%s");await u("adb",ee(e,["shell","input","text",i]))}async function ey(e,t,i){await em(e,t,i)}async function eN(e,t,i,r){await ey(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 eT(e,l),await eR(e,r,s.chunkSize,s.delayMs),(n=await eP(e,t,i))===r)return}throw new I("COMMAND_FAILED","Android fill verification failed",{expected:r,actual:n??null})}async function eS(e,t,i=.6){let{width:r,height:n}=await eE(e),a=Math.floor(r*i),o=Math.floor(n*i),s=Math.floor(r/2),l=Math.floor(n/2),c=s,d=l,p=s,f=l;switch(t){case"up":d=l-Math.floor(o/2),f=l+Math.floor(o/2);break;case"down":d=l+Math.floor(o/2),f=l-Math.floor(o/2);break;case"left":c=s-Math.floor(a/2),p=s+Math.floor(a/2);break;case"right":c=s+Math.floor(a/2),p=s-Math.floor(a/2);break;default:throw new I("INVALID_ARGS",`Unknown direction: ${t}`)}await u("adb",ee(e,["shell","input","swipe",String(c),String(d),String(p),String(f),"300"]))}async function eb(e,t){for(let i=0;i<8;i+=1){let i="";try{i=await ex(e)}catch(t){let e=t instanceof Error?t.message:String(t);throw new I("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=z(n[0]),a=(X(t,"text")??"").toLowerCase(),o=(X(t,"content-desc")??"").toLowerCase();if(a.includes(i)||o.includes(i)){let e=Y(X(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 eS(e,"down",.5)}throw new I("COMMAND_FAILED",`Could not find element containing "${t}" after scrolling`)}async function e_(e,t){let i=await u("adb",ee(e,["exec-out","screencap","-p"]),{binaryStdout:!0});if(!i.stdoutBuffer)throw new I("COMMAND_FAILED","Failed to capture screenshot");await h.writeFile(t,i.stdoutBuffer)}async function eD(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 I("INVALID_ARGS",`Invalid setting state: ${e}`)}(i);switch(r){case"wifi":return void await u("adb",ee(e,["shell","svc","wifi",n?"enable":"disable"]));case"airplane":await u("adb",ee(e,["shell","settings","put","global","airplane_mode_on",n?"1":"0"])),await u("adb",ee(e,["shell","am","broadcast","-a","android.intent.action.AIRPLANE_MODE","--ez","state",n?"true":"false"]));return;case"location":return void await u("adb",ee(e,["shell","settings","put","secure","location_mode",n?"3":"0"]));default:throw new I("INVALID_ARGS",`Unsupported setting: ${t}`)}}async function eO(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=H(t),o=Y(a.bounds),s=i[i.length-1],l={type:a.className,label:a.text||a.desc,value:a.text,identifier:a.resourceId,rect:o,enabled:a.enabled,hittable:a.clickable??a.focusable,depth:s.depth+1,parentIndex:void 0,children:[]};s.children.push(l),t.endsWith("/>")||i.push(l),n=r.exec(e)}return t}(e),n=[],a=!1,o=i.depth??1/0,s=i.scope?function(e,t){let i=t.toLowerCase(),r=[...e.children];for(;r.length>0;){let e=r.shift(),t=e.label?.toLowerCase()??"",n=e.value?.toLowerCase()??"",a=e.identifier?.toLowerCase()??"";if(t.includes(i)||n.includes(i)||a.includes(i))return e;r.push(...e.children)}return null}(r,i.scope):null,l=s?[s]:r.children,c=new Map,d=e=>{let t=c.get(e);if(void 0!==t)return t;for(let t of e.children)if(t.hittable||d(t))return c.set(e,!0),!0;return c.set(e,!1),!1},u=(e,t,r,s=!1,l=!1)=>{var c,p,f,m,h,w;let g,v,I,A,y,N,S,b;if(n.length>=800){a=!0;return}if(t>o)return;let _=!!i.raw||(c=e,p=i,f=s,m=d(e),h=l,v=K(c.type),I=!!(c.label&&c.label.trim().length>0),A=!!(c.identifier&&c.identifier.trim().length>0),y=I&&!Z(c.label??""),N=A&&!Z(c.identifier??""),S=(g=(w=v).split(".").pop()??w).includes("layout")||"viewgroup"===g||"view"===g,b="imageview"===v||"imagebutton"===v,p.interactiveOnly?!!c.hittable||!!(y||N)&&!b&&(!S||!!h)&&(f||m||h):p.compact?y||N||!!c.hittable:!S&&!b||!!c.hittable||!!y||!!N&&!!m||m),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,M=l||function(e){if(!e)return!1;let t=K(e);return t.includes("recyclerview")||t.includes("listview")||t.includes("gridview")}(e.type);for(let i of e.children)if(u(i,t+1,D,O,M),a)return};for(let e of l)if(u(e,0,void 0,!1,!1),a)break;return a?{nodes:n,truncated:a}:{nodes:n}}(await ex(e),0,t)}async function eM(){if(!await D("adb"))throw new I("TOOL_MISSING","adb not found in PATH")}async function eE(e){let t=(await u("adb",ee(e,["shell","wm","size"]))).stdout.match(/Physical size:\s*(\d+)x(\d+)/);if(!t)throw new I("COMMAND_FAILED","Unable to read screen size");return{width:Number(t[1]),height:Number(t[2])}}async function ex(e){return T(()=>ek(e),{shouldRetry:eC})}async function ek(e){var t,i,r;let n,a,o=await u("adb",ee(e,["exec-out","uiautomator","dump","/dev/tty"]),{allowFailure:!0});if(0===o.exitCode){let e=eL(o.stdout,o.stderr);if(e)return e}let s="/sdcard/window_dump.xml",l=await u("adb",ee(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),d=await u("adb",ee(e,["shell","cat",c])),p=eL(d.stdout,d.stderr);if(!p)throw new I("COMMAND_FAILED","uiautomator dump did not return XML",{stdout:d.stdout,stderr:d.stderr});return p}function eL(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 eC(e){if(!(e instanceof I)||"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 eR(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 eA(e,a),r>0&&i+n<t.length&&await eF(r)}}async function eT(e,t){let i=Math.max(0,t);await u("adb",ee(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 u("adb",ee(e,["shell","input","keyevent",...Array(r).fill("KEYCODE_DEL")]),{allowFailure:!0})}}async function eP(e,t,i){let r,n=await ex(e),a=/<node\b[^>]*>/g,o=null,s=null,l=null;for(;null!==(r=a.exec(n));){let e=H(r[0]),n=Y(e.bounds);if(!n)continue;let a=e.className??"",c=(e.text??"").replace(/"/g,'"').replace(/'/g,"'").replace(/</g,"<").replace(/>/g,">").replace(/&/g,"&"),d=e.focused??!1;if(!c)continue;let u=Math.max(1,n.width*n.height),p=t>=n.x&&t<=n.x+n.width&&i>=n.y&&i<=n.y+n.height;if(d&&e$(a)){(!o||u<=o.area)&&(o={text:c,area:u});continue}if(p&&e$(a)){(!s||u<=s.area)&&(s={text:c,area:u});continue}p&&(!l||u<=l.area)&&(l={text:c,area:u})}return o?.text??s?.text??l?.text??null}function e$(e){let t=e.toLowerCase();return t.includes("edittext")||t.includes("textfield")}async function eF(e){await new Promise(t=>setTimeout(t,e))}function eV(e,t,i){if(!e)return t;let r=Number(e);return Number.isFinite(r)?Math.max(i,Math.floor(r)):t}let eU=eV(process.env.AGENT_DEVICE_IOS_DEVICECTL_LIST_TIMEOUT_MS,8e3,500);async function eG(){if("darwin"!==process.platform)throw new I("UNSUPPORTED_PLATFORM","iOS tools are only available on macOS");if(!await D("xcrun"))throw new I("TOOL_MISSING","xcrun not found in PATH");let e=[],t=await u("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 I("COMMAND_FAILED","Failed to parse simctl devices JSON",void 0,e)}let r=null;try{r=i.join(S.tmpdir(),`agent-device-devicectl-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);let t=await u("xcrun",["devicectl","list","devices","--json-output",r],{allowFailure:!0,timeoutMs:eU});if(0!==t.exitCode)return e;let n=await h.readFile(r,"utf8"),a=JSON.parse(n);for(let t of a.result?.devices??[])if((t.hardwareProperties?.platform??"").toLowerCase().includes("ios")){let i=t.hardwareProperties?.udid??t.identifier??"",r=t.name??t.deviceProperties?.name??i;if(!i)continue;e.push({platform:"ios",id:i,name:r,kind:"device",booted:!0})}}catch{}finally{r&&await h.rm(r,{force:!0}).catch(()=>{})}return e}let ej=eV(process.env.AGENT_DEVICE_IOS_BOOT_TIMEOUT_MS,k,5e3),eB=eV(process.env.AGENT_DEVICE_IOS_SIMCTL_LIST_TIMEOUT_MS,x,1e3),eq=eV(process.env.AGENT_DEVICE_IOS_APP_LAUNCH_TIMEOUT_MS,3e4,5e3),eW=eV(process.env.AGENT_DEVICE_IOS_DEVICECTL_TIMEOUT_MS,2e4,1e3);async function eJ(e,t){let i=["devicectl",...e],r=await u("xcrun",i,{allowFailure:!0,timeoutMs:eW});if(0===r.exitCode)return;let n=String(r.stdout??""),a=String(r.stderr??"");throw new I("COMMAND_FAILED",`Failed to ${t.action}`,{cmd:"xcrun",args:i,exitCode:r.exitCode,stdout:n,stderr:a,deviceId:t.deviceId,hint:eX(n,a)??ez})}async function eH(e,t){let r=i.join(S.tmpdir(),`agent-device-ios-apps-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`),n=["devicectl","device","info","apps","--device",e.id,"--include-all-apps","--json-output",r],a=await u("xcrun",n,{allowFailure:!0,timeoutMs:eW});try{var o,s;if(0!==a.exitCode){let t=String(a.stdout??""),i=String(a.stderr??"");throw new I("COMMAND_FAILED","Failed to list iOS apps",{cmd:"xcrun",args:n,exitCode:a.exitCode,stdout:t,stderr:i,deviceId:e.id,hint:eX(t,i)??ez})}let i=await h.readFile(r,"utf8");return o=function(e){let t=e?.result?.apps;if(!Array.isArray(t))return[];let i=[];for(let e of t){if(!e||"object"!=typeof e)continue;let t="string"==typeof e.bundleIdentifier?e.bundleIdentifier.trim():"";if(!t)continue;let r="string"==typeof e.name&&e.name.trim().length>0?e.name.trim():t;i.push({bundleId:t,name:r})}return i}(JSON.parse(i)),s=t,"user-installed"===s?o.filter(e=>!e.bundleId.startsWith("com.apple.")):o}catch(t){if(t instanceof I)throw t;throw new I("COMMAND_FAILED","Failed to parse iOS apps list",{deviceId:e.id,cause:String(t)})}finally{await h.unlink(r).catch(()=>{})}}E(process.env.AGENT_DEVICE_RETRY_LOGS);let ez="Ensure the iOS device is unlocked, trusted, and available in Xcode > Devices, then retry.";function eX(e,t){let i=`${e}
|
|
6
|
+
${t}`.toLowerCase();return i.includes("device is busy")&&i.includes("connecting")?"iOS device is still connecting. Keep it unlocked and connected by cable until it is fully available in Xcode Devices, then retry.":i.includes("coredeviceservice")&&i.includes("timed out")?"CoreDevice service timed out. Reconnect the device and retry; if it persists restart Xcode and the iOS device.":null}async function eY(e){let t,i;if("simulator"!==e.kind||"Booted"===await eK(e.id))return;let r=C.fromTimeoutMs(ej);try{await R(async({deadline:r})=>{if(r?.isExpired())throw new I("COMMAND_FAILED","iOS simulator boot deadline exceeded",{timeoutMs:ej});let n=Math.max(1e3,r?.remainingMs()??ej),a=await u("xcrun",["simctl","boot",e.id],{allowFailure:!0,timeoutMs:n});t={stdout:String(a.stdout??""),stderr:String(a.stderr??""),exitCode:a.exitCode};let o=`${t.stdout}
|
|
7
|
+
${t.stderr}`.toLowerCase(),s=o.includes("already booted")||o.includes("current state: booted");if(0!==t.exitCode&&!s)throw new I("COMMAND_FAILED","simctl boot failed",{stdout:t.stdout,stderr:t.stderr,exitCode:t.exitCode});let l=await u("xcrun",["simctl","bootstatus",e.id,"-b"],{allowFailure:!0,timeoutMs:n});if(i={stdout:String(l.stdout??""),stderr:String(l.stderr??""),exitCode:l.exitCode},0!==i.exitCode)throw new I("COMMAND_FAILED","simctl bootstatus failed",{stdout:i.stdout,stderr:i.stderr,exitCode:i.exitCode});let c=await eK(e.id);if("Booted"!==c)throw new I("COMMAND_FAILED","Simulator is still booting",{state:c})},{maxAttempts:3,baseDelayMs:500,maxDelayMs:2e3,jitter:.2,shouldRetry:e=>{let r=$({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=>$({error:e,stdout:i?.stdout??t?.stdout,stderr:i?.stderr??t?.stderr,context:{platform:"ios",phase:"boot"}})})}catch(a){let n=$({error:a,stdout:i?.stdout??t?.stdout,stderr:i?.stderr??t?.stderr,context:{platform:"ios",phase:"boot"}});throw new I("COMMAND_FAILED","iOS simulator failed to boot",{platform:"ios",deviceId:e.id,timeoutMs:ej,elapsedMs:r.elapsedMs(),reason:n,hint:F(n),boot:t,bootstatus:i})}}async function eK(e){let t=await u("xcrun",["simctl","list","devices","-j"],{allowFailure:!0,timeoutMs:eB});if(0!==t.exitCode)return null;try{let i=JSON.parse(String(t.stdout??""));for(let t of Object.values(i.devices??{})){let i=t.find(t=>t.udid===e);if(i)return i.state}return null}catch{return null}}let eZ={settings:"com.apple.Preferences"};function eQ(e){return e.includes("not installed")||e.includes("not found")||e.includes("no such file")}async function e0(e,t){let i=t.trim();if(i.includes("."))return i;let r=eZ[i.toLowerCase()];if(r)return r;let n=("simulator"===e.kind?await te(e):await eH(e,"all")).filter(e=>e.name.toLowerCase()===i.toLowerCase());if(1===n.length)return n[0].bundleId;if(n.length>1)throw new I("INVALID_ARGS",`Multiple apps matched "${t}"`,{matches:n});throw new I("APP_NOT_INSTALLED",`No app found matching "${t}"`)}async function e1(e,t,i){let r=i?.url?.trim();if(r){if(!W(r))throw new I("INVALID_ARGS","open <app> <url> requires a valid URL target");if("simulator"===e.kind){await eY(e),await u("open",["-a","Simulator"],{allowFailure:!0}),await u("xcrun",["simctl","openurl",e.id,r]);return}let n=J(i?.appBundleId??await e0(e,t),r);if(!n)throw new I("INVALID_ARGS","Deep link open on iOS devices requires an active app bundle ID. Open the app first, then open the URL.");await ta(e,n,{payloadUrl:r});return}let n=t.trim();if(W(n)){if("simulator"===e.kind){await eY(e),await u("open",["-a","Simulator"],{allowFailure:!0}),await u("xcrun",["simctl","openurl",e.id,n]);return}let t=J(i?.appBundleId,n);if(!t)throw new I("INVALID_ARGS","Deep link open on iOS devices requires an active app bundle ID. Open the app first, then open the URL.");await ta(e,t,{payloadUrl:n});return}let a=i?.appBundleId??await e0(e,t);"simulator"===e.kind?await tn(e,a):await ta(e,a)}async function e2(e){"simulator"!==e.kind||"Booted"!==await eK(e.id)&&(await eY(e),await u("open",["-a","Simulator"],{allowFailure:!0}))}async function e3(e,t){let i=await e0(e,t);if("simulator"===e.kind){await eY(e);let t=await u("xcrun",["simctl","terminate",e.id,i],{allowFailure:!0});if(0!==t.exitCode){if(t.stderr.toLowerCase().includes("found nothing to terminate"))return;throw new I("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 eJ(["device","process","terminate","--device",e.id,i],{action:"terminate iOS app",deviceId:e.id})}async function e4(e,t){let i=await e0(e,t);if("simulator"!==e.kind){let t=["devicectl","device","uninstall","app","--device",e.id,i],r=await u("xcrun",t,{allowFailure:!0,timeoutMs:eW});if(0!==r.exitCode){let n=String(r.stdout??""),a=String(r.stderr??"");if(!eQ(`${n}
|
|
8
|
+
${a}`.toLowerCase()))throw new I("COMMAND_FAILED",`Failed to uninstall iOS app ${i}`,{cmd:"xcrun",args:t,exitCode:r.exitCode,stdout:n,stderr:a,deviceId:e.id,hint:eX(n,a)??ez})}return{bundleId:i}}await eY(e);let r=await u("xcrun",["simctl","uninstall",e.id,i],{allowFailure:!0});if(0!==r.exitCode&&!eQ(`${r.stdout}
|
|
9
|
+
${r.stderr}`.toLowerCase()))throw new I("COMMAND_FAILED",`simctl uninstall failed for ${i}`,{stdout:r.stdout,stderr:r.stderr,exitCode:r.exitCode});return{bundleId:i}}async function e5(e,t){"simulator"!==e.kind?await eJ(["device","install","app","--device",e.id,t],{action:"install iOS app",deviceId:e.id}):(await eY(e),await u("xcrun",["simctl","install",e.id,t]))}async function e8(e,t,i){let{bundleId:r}=await e4(e,t);return await e5(e,i),{bundleId:r}}async function e6(e,t){if("simulator"===e.kind){await eY(e),await u("xcrun",["simctl","io",e.id,"screenshot",t]);return}await eJ(["device","screenshot","--device",e.id,t],{action:"capture iOS screenshot",deviceId:e.id})}async function e7(e,t,i,r){if("simulator"!==e.kind)throw new I("UNSUPPORTED_OPERATION","settings is only supported on iOS simulators");switch(await eY(e),t.toLowerCase()){case"wifi":{let t=tt(i);await u("xcrun",["simctl","status_bar",e.id,"override","--wifiMode",t?"active":"failed"]);return}case"airplane":return void(tt(i)?await u("xcrun",["simctl","status_bar",e.id,"override","--dataNetwork","hide","--wifiMode","failed","--wifiBars","0","--cellularMode","failed","--cellularBars","0","--operatorName",""]):await u("xcrun",["simctl","status_bar",e.id,"clear"]));case"location":{let t=tt(i);if(!r)throw new I("INVALID_ARGS","location setting requires an active app in session");await u("xcrun",["simctl","privacy",e.id,t?"grant":"revoke","location",r]);return}case"faceid":{let t=function(e){let t=e.trim().toLowerCase();if("match"===t)return"match";if("nonmatch"===t)return"nonmatch";if("enroll"===t)return"enroll";if("unenroll"===t)return"unenroll";throw new I("INVALID_ARGS",`Invalid faceid state: ${e}. Use match|nonmatch|enroll|unenroll.`)}(i);await ti(e.id,t);return}default:throw new I("INVALID_ARGS",`Unsupported setting: ${t}`)}}async function e9(e,t="all"){var i;return"simulator"===e.kind?(i=await te(e),"user-installed"===t?i.filter(e=>!e.bundleId.startsWith("com.apple.")):i):await eH(e,t)}async function te(e){let t=(await u("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 u("plutil",["-convert","json","-o","-","-"],{allowFailure:!0,stdin:t});0===e.exitCode&&e.stdout.trim().startsWith("{")&&(i=JSON.parse(e.stdout))}catch{i=null}return i?Object.entries(i).map(([e,t])=>({bundleId:e,name:t.CFBundleDisplayName??t.CFBundleName??e})):[]}function tt(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 I("INVALID_ARGS",`Invalid setting state: ${e}`)}async function ti(e,t){let i=function(e,t){switch(t){case"match":return[["simctl","biometric",e,"match","face"],["simctl","biometric","match",e,"face"]];case"nonmatch":return[["simctl","biometric",e,"nonmatch","face"],["simctl","biometric",e,"nomatch","face"],["simctl","biometric","nonmatch",e,"face"],["simctl","biometric","nomatch",e,"face"]];case"enroll":return[["simctl","biometric",e,"enroll","yes"],["simctl","biometric",e,"enroll","1"],["simctl","biometric","enroll",e,"yes"],["simctl","biometric","enroll",e,"1"]];case"unenroll":return[["simctl","biometric",e,"enroll","no"],["simctl","biometric",e,"enroll","0"],["simctl","biometric","enroll",e,"no"],["simctl","biometric","enroll",e,"0"]]}}(e,t),r=[];for(let e of i){let t=await u("xcrun",e,{allowFailure:!0});if(0===t.exitCode)return;r.push({args:e,stderr:t.stderr,stdout:t.stdout,exitCode:t.exitCode})}throw new I("COMMAND_FAILED","simctl biometric command failed. Ensure your Xcode Simulator runtime supports Face ID control.",{deviceId:e,action:t,attempts:r.map(e=>({args:e.args.join(" "),exitCode:e.exitCode,stderr:e.stderr.slice(0,400)}))})}function tr(e){if(!(e instanceof I)||"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 tn(e,t){await eY(e),await u("open",["-a","Simulator"],{allowFailure:!0});let i=C.fromTimeoutMs(eq);await R(async({deadline:i})=>{if(i?.isExpired())throw new I("COMMAND_FAILED","App launch deadline exceeded",{timeoutMs:eq});let r=await u("xcrun",["simctl","launch",e.id,t],{allowFailure:!0});if(0!==r.exitCode)throw new I("COMMAND_FAILED",`xcrun exited with code ${r.exitCode}`,{cmd:"xcrun",args:["simctl","launch",e.id,t],stdout:r.stdout,stderr:r.stderr,exitCode:r.exitCode})},{maxAttempts:30,baseDelayMs:1e3,maxDelayMs:5e3,jitter:.2,shouldRetry:tr},{deadline:i})}async function ta(e,t,i){let r=["device","process","launch","--device",e.id,t];i?.payloadUrl&&r.push("--payload-url",i.payloadUrl),await eJ(r,{action:"launch iOS app",deviceId:e.id})}async function to(e,t,i){let r=(e.get(t)??Promise.resolve()).catch(()=>{}).then(i);return e.set(t,r),r.finally(()=>{e.get(t)===r&&e.delete(t)})}let ts=new Map,tl=new Map,tc=eV(process.env.AGENT_DEVICE_RUNNER_STARTUP_TIMEOUT_MS,45e3,5e3),td=eV(process.env.AGENT_DEVICE_RUNNER_COMMAND_TIMEOUT_MS,15e3,1e3),tu=eV(process.env.AGENT_DEVICE_RUNNER_CONNECT_ATTEMPT_INTERVAL_MS,250,50),tp=eV(process.env.AGENT_DEVICE_RUNNER_CONNECT_RETRY_BASE_DELAY_MS,300,10),tf=eV(process.env.AGENT_DEVICE_RUNNER_CONNECT_RETRY_MAX_DELAY_MS,2e3,10),tm=eV(process.env.AGENT_DEVICE_RUNNER_CONNECT_REQUEST_TIMEOUT_MS,5e3,250),th=eV(process.env.AGENT_DEVICE_IOS_DEVICE_INFO_TIMEOUT_MS,1e4,500),tw=eV(process.env.AGENT_DEVICE_RUNNER_DESTINATION_TIMEOUT_SECONDS,20,5),tg=i.join(S.homedir(),".agent-device","ios-runner");async function tv(e,t,i={}){var r;return(function(e){if("ios"!==e.platform)throw new I("UNSUPPORTED_PLATFORM",`Unsupported platform for iOS runner: ${e.platform}`);if("simulator"!==e.kind&&"device"!==e.kind)throw new I("UNSUPPORTED_OPERATION",`Unsupported iOS device kind for runner: ${e.kind}`)}(e),"snapshot"===(r=t.command)||"findText"===r||"listTappables"===r||"alert"===r)?T(()=>tI(e,t,i),{shouldRetry:tC}):tI(e,t,i)}async function tI(e,t,i={}){let r;try{let n=(r=await tO(e,i)).ready?td:tc;return await tA(e,r,t,i.logPath,n)}catch(a){let n=a instanceof I?a:new I("COMMAND_FAILED",String(a));if("COMMAND_FAILED"===n.code&&"string"==typeof n.message&&n.message.includes("Runner did not accept connection")&&tT(n)&&r?.ready){r?await tb(r):await tN(e.id),r=await tO(e,i);let n=await t$(r.device,r.port,t,i.logPath,tc);return await ty(n,r,i.logPath)}throw a}}async function tA(e,t,i,r,n){let a=await t$(e,t.port,i,r,n,t);return await ty(a,t,r)}async function ty(e,t,i){let r=await e.text(),n={};try{n=JSON.parse(r)}catch{throw new I("COMMAND_FAILED","Invalid runner response",{text:r})}if(!n.ok)throw new I("COMMAND_FAILED",n.error?.message??"Runner error",{runner:n,xcodebuild:{exitCode:1,stdout:"",stderr:""},logPath:i});return t.ready=!0,n.data??{}}async function tN(e){await to(tl,e,async()=>{await t_(e)})}async function tS(){let e=Array.from(ts.keys());await Promise.allSettled(e.map(async e=>{await tN(e)}))}async function tb(e){await to(tl,e.deviceId,async()=>{await t_(e.deviceId,e)})}async function t_(e,t){let i=t??ts.get(e);if(i){try{await t$(i.device,i.port,{command:"shutdown"},void 0,15e3)}catch{await tM(i.child.pid,"SIGTERM")}try{await Promise.race([i.testPromise,new Promise(e=>setTimeout(e,1e4))])}catch{}await tM(i.child.pid,"SIGKILL"),tq(i.xctestrunPath),tq(i.jsonPath),ts.get(e)===i&&ts.delete(e)}}async function tD(e){await u("xcrun",["simctl","bootstatus",e,"-b"],{allowFailure:!0,timeoutMs:tc})}async function tO(e,t){return await to(tl,e.id,async()=>{var i,r;let n=ts.get(e.id);if(n){if((i=n.child.pid)&&v(i))return n;await t_(e.id,n)}await ("simulator"!==(r=e).kind?Promise.resolve():tD(r.id));let a=await tE(e,t),o=await tj(),{xctestrunPath:s,jsonPath:c}=await tB(a,{AGENT_DEVICE_RUNNER_PORT:String(o)},`session-${e.id}-${o}`),{child:d,wait:u}=l("xcodebuild",["test-without-building","-only-testing","AgentDeviceRunnerUITests/RunnerTests/testCommand","-parallel-testing-enabled","NO","-test-timeouts-enabled","NO",tx(e),"1","-destination-timeout",String(tw),"-xctestrun",s,"-destination",function(e){if("ios"!==e.platform)throw new I("UNSUPPORTED_PLATFORM",`Unsupported platform for iOS runner: ${e.platform}`);return"simulator"===e.kind?`platform=iOS Simulator,id=${e.id}`:`platform=iOS,id=${e.id}`}(e)],{allowFailure:!0,env:{...process.env,AGENT_DEVICE_RUNNER_PORT:String(o)}});d.stdout?.on("data",e=>{tL(e,t.logPath,t.traceLogPath,t.verbose)}),d.stderr?.on("data",e=>{tL(e,t.logPath,t.traceLogPath,t.verbose)});let p={device:e,deviceId:e.id,port:o,xctestrunPath:s,jsonPath:c,testPromise:u,child:d,ready:!1};return ts.set(e.id,p),p})}async function tM(e,t){if(!e||e<=0)return;try{process.kill(e,t)}catch{}let i="SIGTERM"===t?"TERM":"KILL";try{await u("pkill",[`-${i}`,"-P",String(e)],{allowFailure:!0})}catch{}}async function tE(e,t){var a,o;let s,l=(a=e.kind,(s=process.env.AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH?.trim())?i.resolve(s):"simulator"===a?i.join(tg,"derived"):i.join(tg,"derived",a));if(E(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 E(e.AGENT_DEVICE_IOS_ALLOW_OVERRIDE_DERIVED_CLEAN)}(t))throw new I("COMMAND_FAILED","Refusing to clean AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH automatically",{derivedPath:e,hint:"Unset AGENT_DEVICE_IOS_CLEAN_DERIVED, or set AGENT_DEVICE_IOS_ALLOW_OVERRIDE_DERIVED_CLEAN=1 if you trust this path."})}(l);try{A.rmSync(l,{recursive:!0,force:!0})}catch{}}let c=tk(l);if(c)return c;let d=function(){let e=i.dirname(n(import.meta.url)),t=e;for(let e=0;e<6;e+=1){let e=i.join(t,"package.json");if(A.existsSync(e))return t;t=i.dirname(t)}return e}(),u=i.join(d,"ios-runner","AgentDeviceRunner","AgentDeviceRunner.xcodeproj");if(!A.existsSync(u))throw new I("COMMAND_FAILED","iOS runner project not found",{projectPath:u});let p=function(e=process.env,t=!1){if(!t)return[];let i=e.AGENT_DEVICE_IOS_TEAM_ID?.trim()||"",r=e.AGENT_DEVICE_IOS_SIGNING_IDENTITY?.trim()||"",n=e.AGENT_DEVICE_IOS_PROVISIONING_PROFILE?.trim()||"",a=["CODE_SIGN_STYLE=Automatic"];return i&&a.push(`DEVELOPMENT_TEAM=${i}`),r&&a.push(`CODE_SIGN_IDENTITY=${r}`),n&&a.push(`PROVISIONING_PROFILE_SPECIFIER=${n}`),a}(process.env,"device"===e.kind),f="device"===e.kind?["-allowProvisioningUpdates"]:[];try{await r("xcodebuild",["build-for-testing","-project",u,"-scheme","AgentDeviceRunner","-parallel-testing-enabled","NO",tx(e),"1","-destination",function(e){if("ios"!==e.platform)throw new I("UNSUPPORTED_PLATFORM",`Unsupported platform for iOS runner: ${e.platform}`);return"simulator"===e.kind?`platform=iOS Simulator,id=${e.id}`:"generic/platform=iOS"}(e),"-derivedDataPath",l,...f,...p],{onStdoutChunk:e=>{tL(e,t.logPath,t.traceLogPath,t.verbose)},onStderrChunk:e=>{tL(e,t.logPath,t.traceLogPath,t.verbose)}})}catch(a){let e,i,r=a instanceof I?a:new I("COMMAND_FAILED",String(a)),n=(e=(o=r).details?JSON.stringify(o.details):"",(i=`${o.message}
|
|
10
|
+
${e}`.toLowerCase()).includes("requires a development team")?"Configure signing in Xcode or set AGENT_DEVICE_IOS_TEAM_ID for physical-device runs.":i.includes("no profiles for")||i.includes("provisioning profile")?"Install/select a valid iOS provisioning profile, or set AGENT_DEVICE_IOS_PROVISIONING_PROFILE.":i.includes("code signing")?"Enable Automatic Signing in Xcode or provide AGENT_DEVICE_IOS_TEAM_ID and optional AGENT_DEVICE_IOS_SIGNING_IDENTITY.":void 0);throw new I("COMMAND_FAILED","xcodebuild build-for-testing failed",{error:r.message,details:r.details,logPath:t.logPath,hint:n})}let m=tk(l);if(!m)throw new I("COMMAND_FAILED","Failed to locate .xctestrun after build");return m}function tx(e){return"device"===e.kind?"-maximum-concurrent-test-device-destinations":"-maximum-concurrent-test-simulator-destinations"}function tk(e){if(!A.existsSync(e))return null;let t=[],r=[e];for(;r.length>0;){let e=r.pop();for(let n of A.readdirSync(e,{withFileTypes:!0})){let a=i.join(e,n.name);if(n.isDirectory()){r.push(a);continue}if(n.isFile()&&n.name.endsWith(".xctestrun"))try{let e=A.statSync(a);t.push({path:a,mtimeMs:e.mtimeMs})}catch{}}}return 0===t.length?null:(t.sort((e,t)=>t.mtimeMs-e.mtimeMs),t[0]?.path??null)}function tL(e,t,i,r){t&&A.appendFileSync(t,e),i&&A.appendFileSync(i,e),r&&process.stderr.write(e)}function tC(e){if(!(e instanceof I)||"COMMAND_FAILED"!==e.code)return!1;let t=`${e.message??""}`.toLowerCase();return!(t.includes("xcodebuild exited early")||t.includes("device is busy")&&t.includes("connecting"))&&!!(t.includes("runner did not accept connection")||t.includes("fetch failed")||t.includes("econnrefused")||t.includes("socket hang up"))}function tR(e){let{port:t,endpoints:i,logPath:r,lastError:n}=e,a="Runner did not accept connection";return new I("COMMAND_FAILED",a,{port:t,endpoints:i,logPath:r,lastError:n?String(n):void 0,reason:$({error:n,message:a,context:{platform:"ios",phase:"connect"}}),hint:F("IOS_RUNNER_CONNECT_TIMEOUT")})}function tT(e){return!(e instanceof I)||"COMMAND_FAILED"!==e.code||!String(e.message??"").toLowerCase().includes("xcodebuild exited early")}async function tP(e){var t,i;let r,{session:n,port:a,logPath:o}=e,s=await n.testPromise,l="Runner did not accept connection (xcodebuild exited early)",c=$({message:l,stdout:s.stdout,stderr:s.stderr,context:{platform:"ios",phase:"connect"}});return new I("COMMAND_FAILED",l,{port:a,logPath:o,xcodebuild:{exitCode:s.exitCode,stdout:s.stdout,stderr:s.stderr},reason:c,hint:(t=s.stdout,i=s.stderr,(r=`${l}
|
|
12
11
|
${t}
|
|
13
|
-
${i}`.toLowerCase()).includes("device is busy")&&n.includes("connecting")?"Target iOS device is still connecting. Keep it unlocked, wait for device trust/connection to settle, then retry.":x("IOS_RUNNER_CONNECT_TIMEOUT"))})}async function tE(e,t,i,n,r=tt,a){let o=_.fromTimeoutMs(r),s=await tx(e,t,o.remainingMs()),l=null,c=Math.max(1,Math.ceil(r/tn));try{return await O(async({deadline:o})=>{if(o?.isExpired())throw new h("COMMAND_FAILED","Runner connection deadline exceeded",{port:t,timeoutMs:r});if(a&&null!==a.child.exitCode&&void 0!==a.child.exitCode)throw await tM({session:a,port:t,logPath:n});for(let n of("device"===e.kind&&(s=await tx(e,t,o?.remainingMs())),s))try{let e=o?.remainingMs()??r;if(e<=0)throw new h("COMMAND_FAILED","Runner connection deadline exceeded",{port:t,timeoutMs:r});return await tk(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)},Math.min(to,e))}catch(e){l=e}throw new h("COMMAND_FAILED","Runner endpoint probe failed",{port:t,endpoints:s,lastError:l?String(l):void 0})},{maxAttempts:c,baseDelayMs:tr,maxDelayMs:ta,jitter:.2,shouldRetry:tO},{deadline:o,phase:"ios_runner_connect"})}catch(e){l||(l=e)}if("simulator"===e.kind){let r=o.remainingMs();if(r<=0)throw t_({port:t,endpoints:s,logPath:n,lastError:l});let a=await tR(e.id,t,i,r);return new Response(a.body,{status:a.status})}throw t_({port:t,endpoints:s,logPath:n,lastError:l})}async function tx(e,t,i){let n=[`http://127.0.0.1:${t}/command`];if("device"!==e.kind)return n;let r=await tL(e.id,i);return r&&n.unshift(`http://[${r}]:${t}/command`),n}async function tk(e,t,i){let n=new AbortController,r=setTimeout(()=>n.abort(),i);try{return await fetch(e,{...t,signal:n.signal})}finally{clearTimeout(r)}}async function tL(e,t){if("number"==typeof t&&t<=0)return null;let i="number"==typeof t?Math.max(1,Math.min(ts,t)):ts,r=n.join(g.tmpdir(),`agent-device-devicectl-info-${process.pid}-${Date.now()}.json`);try{let t=Math.max(1,Math.ceil(i/1e3)),n=await d("xcrun",["devicectl","device","info","details","--device",e,"--json-output",r,"--timeout",String(t)],{allowFailure:!0,timeoutMs:i});if(0!==n.exitCode||!w.existsSync(r))return null;let a=JSON.parse(w.readFileSync(r,"utf8"));if(a.info?.outcome&&"success"!==a.info.outcome)return null;let o=(a.result?.connectionProperties?.tunnelIPAddress??a.result?.device?.connectionProperties?.tunnelIPAddress)?.trim();return o&&o.length>0?o:null}catch{return null}finally{tP(r)}}async function tR(e,t,i,n){let r=JSON.stringify(i),a=await d("xcrun",["simctl","spawn",e,"/usr/bin/curl","-s","-X","POST","-H","Content-Type: application/json","--data",r,`http://127.0.0.1:${t}/command`],{allowFailure:!0,timeoutMs:n}),o=a.stdout;if(0!==a.exitCode){let e=E({message:"Runner did not accept connection (simctl spawn)",stdout:a.stdout,stderr:a.stderr,context:{platform:"ios",phase:"connect"}});throw new h("COMMAND_FAILED","Runner did not accept connection (simctl spawn)",{port:t,stdout:a.stdout,stderr:a.stderr,exitCode:a.exitCode,reason:e,hint:x(e)})}return{status:200,body:o}}async function tC(){return await new Promise((e,t)=>{let i=I.createServer();i.listen(0,"127.0.0.1",()=>{let n=i.address();i.close(),"object"==typeof n&&n?.port?e(n.port):t(new h("COMMAND_FAILED","Failed to allocate port"))}),i.on("error",t)})}async function tT(e,t,i){let r,a=n.dirname(e),o=i.replace(/[^a-zA-Z0-9._-]/g,"_"),s=n.join(a,`AgentDeviceRunner.env.${o}.json`),l=n.join(a,`AgentDeviceRunner.env.${o}.xctestrun`),c=await d("plutil",["-convert","json","-o","-",e],{allowFailure:!0});if(0!==c.exitCode||!c.stdout.trim())throw new h("COMMAND_FAILED","Failed to read xctestrun plist",{xctestrunPath:e,stderr:c.stderr});try{r=JSON.parse(c.stdout)}catch(t){throw new h("COMMAND_FAILED","Failed to parse xctestrun JSON",{xctestrunPath:e,error:String(t)})}let u=e=>{e.EnvironmentVariables={...e.EnvironmentVariables??{},...t},e.UITestEnvironmentVariables={...e.UITestEnvironmentVariables??{},...t},e.UITargetAppEnvironmentVariables={...e.UITargetAppEnvironmentVariables??{},...t},e.TestingEnvironmentVariables={...e.TestingEnvironmentVariables??{},...t}},p=r.TestConfigurations;if(Array.isArray(p))for(let e of p){if(!e||"object"!=typeof e)continue;let t=e.TestTargets;if(Array.isArray(t))for(let e of t)e&&"object"==typeof e&&u(e)}for(let[e,t]of Object.entries(r))t&&"object"==typeof t&&t.TestBundlePath&&(u(t),r[e]=t);w.writeFileSync(s,JSON.stringify(r,null,2));let f=await d("plutil",["-convert","xml1","-o",l,s],{allowFailure:!0});if(0!==f.exitCode)throw new h("COMMAND_FAILED","Failed to write xctestrun plist",{tmpXctestrunPath:l,stderr:f.stderr});return{xctestrunPath:l,jsonPath:s}}function tP(e){try{w.existsSync(e)&&w.unlinkSync(e)}catch{}}async function t$(e){let t={platform:e.platform,deviceName:e.device,udid:e.udid,serial:e.serial};if("android"===t.platform){await ey();let e=await T();return await y(e,t)}if("ios"===t.platform){let e=await eC();return await y(e,t)}let i=[];try{i.push(...await T())}catch{}try{i.push(...await eC())}catch{}return await y(i,t)}async function tF(e,t,i,r,a){var o,s,l,c,d,p;let f=function(e,t){switch(e.platform){case"android":return{open:(t,i)=>ee(e,t,i?.activity),openDevice:()=>ei(e),close:t=>en(e,t),tap:(t,i)=>es(e,t,i),doubleTap:async(t,i)=>{await es(e,t,i),await es(e,t,i)},swipe:(t,i,n,r,a)=>el(e,t,i,n,r,a),longPress:(t,i,n)=>ep(e,t,i,n),focus:(t,i)=>em(e,t,i),type:t=>ef(e,t),fill:(t,i,n)=>eh(e,t,i,n),scroll:(t,i)=>ew(e,t,i),scrollIntoView:t=>eg(e,t),screenshot:t=>ev(e,t)};case"ios":var i,n;let r;return{open:(t,i)=>eX(e,t,{appBundleId:i?.appBundleId,url:i?.url}),openDevice:()=>eY(e),close:t=>eK(e,t),screenshot:t=>e1(e,t),...(i=e,r={verbose:(n=t).verbose,logPath:n.logPath,traceLogPath:n.traceLogPath},{tap:async(e,t)=>{await td(i,{command:"tap",x:e,y:t,appBundleId:n.appBundleId},r)},doubleTap:async(e,t)=>{await td(i,{command:"tapSeries",x:e,y:t,count:1,intervalMs:0,doubleTap:!0,appBundleId:n.appBundleId},r)},swipe:async(e,t,a,o,s)=>{await td(i,{command:"drag",x:e,y:t,x2:a,y2:o,durationMs:s,appBundleId:n.appBundleId},r)},longPress:async(e,t,a)=>{await td(i,{command:"longPress",x:e,y:t,durationMs:a,appBundleId:n.appBundleId},r)},focus:async(e,t)=>{await td(i,{command:"tap",x:e,y:t,appBundleId:n.appBundleId},r)},type:async e=>{await td(i,{command:"type",text:e,appBundleId:n.appBundleId},r)},fill:async(e,t,a)=>{await td(i,{command:"tap",x:e,y:t,appBundleId:n.appBundleId},r),await td(i,{command:"type",text:a,clearFirst:!0,appBundleId:n.appBundleId},r)},scroll:async(e,t)=>{if(!["up","down","left","right"].includes(e))throw new h("INVALID_ARGS",`Unknown direction: ${e}`);let a=function(e){switch(e){case"up":return"down";case"down":return"up";case"left":return"right";case"right":return"left"}}(e);await td(i,{command:"swipe",direction:a,appBundleId:n.appBundleId},r)},scrollIntoView:async e=>{for(let t=0;t<8;t+=1){let a=await td(i,{command:"findText",text:e,appBundleId:n.appBundleId},r);if(a?.found)return{attempts:t+1};await td(i,{command:"swipe",direction:"up",appBundleId:n.appBundleId},r),await new Promise(e=>setTimeout(e,300))}throw new h("COMMAND_FAILED",`scrollintoview could not find text: ${e}`)}})};default:throw new h("UNSUPPORTED_PLATFORM",`Unsupported platform: ${e.platform}`)}}(e,{appBundleId:a?.appBundleId,verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath});switch(t){case"open":{let t=i[0],n=i[1];if(i.length>2)throw new h("INVALID_ARGS","open accepts at most two arguments: <app|url> [url]");if(!t)return await f.openDevice(),{app:null};if(void 0!==n){if("ios"!==e.platform)throw new h("INVALID_ARGS","open <app> <url> is supported only on iOS");if(F(t))throw new h("INVALID_ARGS","open <app> <url> requires an app target as the first argument");if(!F(n))throw new h("INVALID_ARGS","open <app> <url> requires a valid URL target");return await f.open(t,{activity:a?.activity,appBundleId:a?.appBundleId,url:n}),{app:t,url:n}}return await f.open(t,{activity:a?.activity,appBundleId:a?.appBundleId}),{app:t}}case"close":{let e=i[0];if(!e)return{closed:"session"};return await f.close(e),{app:e}}case"press":{let[t,n]=i.map(Number);if(Number.isNaN(t)||Number.isNaN(n))throw new h("INVALID_ARGS","press requires x y");let r=tU(a?.count??1,"count",1,200),d=tU(a?.intervalMs??0,"interval-ms",0,1e4),u=tU(a?.holdMs??0,"hold-ms",0,1e4),p=tU(a?.jitterPx??0,"jitter-px",0,100),m=a?.doubleTap===!0;if(m&&u>0)throw new h("INVALID_ARGS","double-tap cannot be combined with hold-ms");if(m&&p>0)throw new h("INVALID_ARGS","double-tap cannot be combined with jitter-px");if(o=e,s=r,l=u,c=p,"ios"===o.platform&&s>1&&0===l&&0===c)return await td(e,{command:"tapSeries",x:t,y:n,count:r,intervalMs:d,doubleTap:m,appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{x:t,y:n,count:r,intervalMs:d,holdMs:u,jitterPx:p,doubleTap:m,timingMode:"runner-series"};return await tG(r,d,async e=>{let[i,r]=function(e,t){if(t<=0)return[0,0];let[i,n]=tV[e%tV.length];return[i*t,n*t]}(e,p),a=t+i,o=n+r;m?await f.doubleTap(a,o):u>0?await f.longPress(a,o,u):await f.tap(a,o)}),{x:t,y:n,count:r,intervalMs:d,holdMs:u,jitterPx:p,doubleTap:m}}case"swipe":{let t=Number(i[0]),n=Number(i[1]),r=Number(i[2]),o=Number(i[3]);if([t,n,r,o].some(Number.isNaN))throw new h("INVALID_ARGS","swipe requires x1 y1 x2 y2 [durationMs]");let s=tU(i[4]?Number(i[4]):250,"durationMs",16,1e4),l="ios"===e.platform?60:s,c=tU(a?.count??1,"count",1,200),u=tU(a?.pauseMs??0,"pause-ms",0,1e4),m=a?.pattern??"one-way";if("one-way"!==m&&"ping-pong"!==m)throw new h("INVALID_ARGS",`Invalid pattern: ${m}`);if(d=e,p=c,"ios"===d.platform&&p>1)return await td(e,{command:"dragSeries",x:t,y:n,x2:r,y2:o,durationMs:l,count:c,pauseMs:u,pattern:m,appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{x1:t,y1:n,x2:r,y2:o,durationMs:s,effectiveDurationMs:l,timingMode:"runner-series",count:c,pauseMs:u,pattern:m};return await tG(c,u,async e=>{"ping-pong"===m&&e%2==1?await f.swipe(r,o,t,n,l):await f.swipe(t,n,r,o,l)}),{x1:t,y1:n,x2:r,y2:o,durationMs:s,effectiveDurationMs:l,timingMode:"ios"===e.platform?"safe-normalized":"direct",count:c,pauseMs:u,pattern:m}}case"long-press":{let e=Number(i[0]),t=Number(i[1]),n=i[2]?Number(i[2]):void 0;if(Number.isNaN(e)||Number.isNaN(t))throw new h("INVALID_ARGS","long-press requires x y [durationMs]");return await f.longPress(e,t,n),{x:e,y:t,durationMs:n}}case"focus":{let[e,t]=i.map(Number);if(Number.isNaN(e)||Number.isNaN(t))throw new h("INVALID_ARGS","focus requires x y");return await f.focus(e,t),{x:e,y:t}}case"type":{let e=i.join(" ");if(!e)throw new h("INVALID_ARGS","type requires text");return await f.type(e),{text:e}}case"fill":{let e=Number(i[0]),t=Number(i[1]),n=i.slice(2).join(" ");if(Number.isNaN(e)||Number.isNaN(t)||!n)throw new h("INVALID_ARGS","fill requires x y text");return await f.fill(e,t,n),{x:e,y:t,text:n}}case"scroll":{let e=i[0],t=i[1]?Number(i[1]):void 0;if(!e)throw new h("INVALID_ARGS","scroll requires direction");return await f.scroll(e,t),{direction:e,amount:t}}case"scrollintoview":{let e=i.join(" ").trim();if(!e)throw new h("INVALID_ARGS","scrollintoview requires text");let t=await f.scrollIntoView(e);if(t?.attempts)return{text:e,attempts:t.attempts};return{text:e}}case"pinch":{if("android"===e.platform)throw new h("UNSUPPORTED_OPERATION","Android pinch is not supported in current adb backend; requires instrumentation-based backend.");let t=Number(i[0]),n=i[1]?Number(i[1]):void 0,r=i[2]?Number(i[2]):void 0;if(Number.isNaN(t)||t<=0)throw new h("INVALID_ARGS","pinch requires scale > 0");return await td(e,{command:"pinch",scale:t,x:n,y:r,appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{scale:t,x:n,y:r}}case"screenshot":{let e=i[0]??r??`./screenshot-${Date.now()}.png`;return await u.mkdir(n.dirname(e),{recursive:!0}),await f.screenshot(e),{path:e}}case"back":if("ios"===e.platform)return await td(e,{command:"back",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{action:"back"};return await ec(e),{action:"back"};case"home":if("ios"===e.platform)return await td(e,{command:"home",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{action:"home"};return await ed(e),{action:"home"};case"app-switcher":if("ios"===e.platform)return await td(e,{command:"appSwitcher",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{action:"app-switcher"};return await eu(e),{action:"app-switcher"};case"settings":{let[t,n,r]=i;if("ios"===e.platform)return await e2(e,t,n,r??a?.appBundleId),{setting:t,state:n};return await eI(e,t,n),{setting:t,state:n}}case"snapshot":{if("ios"===e.platform){let t=await td(e,{command:"snapshot",appBundleId:a?.appBundleId,interactiveOnly:a?.snapshotInteractiveOnly,compact:a?.snapshotCompact,depth:a?.snapshotDepth,scope:a?.snapshotScope,raw:a?.snapshotRaw},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),i=t.nodes??[];if(0===i.length&&"simulator"===e.kind)throw new h("COMMAND_FAILED","XCTest snapshot returned 0 nodes on iOS simulator.");return{nodes:i,truncated:t.truncated??!1,backend:"xctest"}}let t=await eA(e,{interactiveOnly:a?.snapshotInteractiveOnly,compact:a?.snapshotCompact,depth:a?.snapshotDepth,scope:a?.snapshotScope,raw:a?.snapshotRaw});return{nodes:t.nodes??[],truncated:t.truncated??!1,backend:"android"}}default:throw new h("INVALID_ARGS",`Unknown command: ${t}`)}}let tV=[[0,0],[1,0],[0,1],[-1,0],[0,-1],[1,1],[-1,1],[1,-1],[-1,-1]];function tU(e,t,i,n){if(!Number.isFinite(e)||!Number.isInteger(e)||e<i||e>n)throw new h("INVALID_ARGS",`${t} must be an integer between ${i} and ${n}`);return e}async function tG(e,t,i){for(let n=0;n<e;n+=1)await i(n),n<e-1&&t>0&&await tj(t)}async function tj(e){await new Promise(t=>setTimeout(t,e))}let tB={alert:{ios:{simulator:!0},android:{}},pinch:{ios:{simulator:!0},android:{}},"app-switcher":{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},apps:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},back:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},boot:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},click:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},close:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},fill:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},find:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},focus:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},get:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},is:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},home:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},"long-press":{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},open:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},reinstall:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},press:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},record:{ios:{simulator:!0},android:{emulator:!0,device:!0,unknown:!0}},screenshot:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},scroll:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},scrollintoview:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},swipe:{ios:{simulator:!0},android:{emulator:!0,device:!0,unknown:!0}},settings:{ios:{simulator:!0},android:{emulator:!0,device:!0,unknown:!0}},snapshot:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},type:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},wait:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}}};function tq(e,t){let i=tB[e];if(!i)return!0;let n=i[t.platform];return!!n&&!0===n[t.kind??"unknown"]}function tW(e){let t=e.result?.text;if("string"==typeof t&&t.trim().length>0)return t;let i=e.positionals??[];return 0===i.length?"":i[0].startsWith("@")?i.length>=3?i.slice(2).join(" ").trim():i.slice(1).join(" ").trim():!(i.length>=3)||Number.isNaN(Number(i[0]))||Number.isNaN(Number(i[1]))?i.slice(1).join(" ").trim():i.slice(2).join(" ").trim()}function tJ(e){let t=new Set,i=[];for(let n of e)t.has(n)||(t.add(n),i.push(n));return i}let tH=/^-?\d+(\.\d+)?$/,tz=new Map([["--count","count"],["--interval-ms","intervalMs"],["--hold-ms","holdMs"],["--jitter-px","jitterPx"]]),tX=new Map([["--count","count"],["--pause-ms","pauseMs"]]);function tY(e){return"click"===e||"press"===e}function tK(e){let t=e.trim();return t.startsWith("@")||tH.test(t)?t:JSON.stringify(t)}function tZ(e,t){let i=t.flags??{};if(tY(t.command)){"number"==typeof i.count&&e.push("--count",String(i.count)),"number"==typeof i.intervalMs&&e.push("--interval-ms",String(i.intervalMs)),"number"==typeof i.holdMs&&e.push("--hold-ms",String(i.holdMs)),"number"==typeof i.jitterPx&&e.push("--jitter-px",String(i.jitterPx)),!0===i.doubleTap&&e.push("--double-tap");return}"swipe"===t.command&&("number"==typeof i.count&&e.push("--count",String(i.count)),"number"==typeof i.pauseMs&&e.push("--pause-ms",String(i.pauseMs)),("one-way"===i.pattern||"ping-pong"===i.pattern)&&e.push("--pattern",i.pattern))}function tQ(e,t){let i=[],n={},r=tY(e)?tz:"swipe"===e?tX:void 0;for(let a=0;a<t.length;a+=1){let o=t[a];if(tY(e)&&"--double-tap"===o){n.doubleTap=!0;continue}let s=r?.get(o);if(s&&a+1<t.length){let e=function(e){if(!e)return null;let t=Number(e);return!Number.isFinite(t)||t<0?null:Math.floor(t)}(t[a+1]);null!==e&&(n[s]=e),a+=1;continue}if("swipe"===e&&"--pattern"===o&&a+1<t.length){let e=t[a+1];("one-way"===e||"ping-pong"===e)&&(n.pattern=e),a+=1;continue}i.push(o)}return{positionals:i,flags:n}}class t0{sessions=new Map;sessionsDir;constructor(e){this.sessionsDir=e}get(e){return this.sessions.get(e)}has(e){return this.sessions.has(e)}set(e,t){this.sessions.set(e,t)}delete(e){return this.sessions.delete(e)}values(){return this.sessions.values()}toArray(){return Array.from(this.sessions.values())}recordAction(e,t){t.flags?.noRecord||(t.flags?.saveScript&&(e.recordSession=!0,"string"==typeof t.flags.saveScript&&(e.saveScriptPath=t0.expandHome(t.flags.saveScript))),e.actions.push({ts:Date.now(),command:t.command,positionals:t.positionals,flags:function(e){if(!e)return{};let{platform:t,device:i,udid:n,serial:r,out:a,verbose:o,snapshotInteractiveOnly:s,snapshotCompact:l,snapshotDepth:c,snapshotScope:d,snapshotRaw:u,relaunch:p,saveScript:f,noRecord:m,count:h,intervalMs:w,holdMs:g,jitterPx:v,doubleTap:I,pauseMs:A,pattern:y}=e;return{platform:t,device:i,udid:n,serial:r,out:a,verbose:o,snapshotInteractiveOnly:s,snapshotCompact:l,snapshotDepth:c,snapshotScope:d,snapshotRaw:u,relaunch:p,saveScript:f,noRecord:m,count:h,intervalMs:w,holdMs:g,jitterPx:v,doubleTap:I,pauseMs:A,pattern:y}}(t.flags),result:t.result}))}writeSessionLog(e){try{if(!e.recordSession)return;let t=this.resolveScriptPath(e),i=n.dirname(t);w.existsSync(i)||w.mkdirSync(i,{recursive:!0});let r=function(e,t){let i=[],n=e.device.name.replace(/"/g,'\\"'),r=e.device.kind?` kind=${e.device.kind}`:"";for(let a of(i.push(`context platform=${e.device.platform} device="${n}"${r} theme=unknown`),t))a.flags?.noRecord||i.push(function(e){let t=[e.command];if(tY(e.command)){let i=e.positionals?.[0];if(i){if(i.startsWith("@")){t.push(tK(i));let n=e.result?.refLabel;return"string"==typeof n&&n.trim().length>0&&t.push(tK(n)),tZ(t,e),t.join(" ")}if(1===e.positionals.length)return t.push(tK(i)),tZ(t,e),t.join(" ")}}if("fill"===e.command){let i=e.positionals?.[0];if(i&&i.startsWith("@")){t.push(tK(i));let n=e.result?.refLabel,r=e.positionals.slice(1).join(" ");return"string"==typeof n&&n.trim().length>0&&t.push(tK(n)),r&&t.push(tK(r)),t.join(" ")}}if("get"===e.command){let i=e.positionals?.[0],n=e.positionals?.[1];if(i&&n){if(t.push(tK(i)),t.push(tK(n)),n.startsWith("@")){let i=e.result?.refLabel;"string"==typeof i&&i.trim().length>0&&t.push(tK(i))}return t.join(" ")}}if("snapshot"===e.command)return e.flags?.snapshotInteractiveOnly&&t.push("-i"),e.flags?.snapshotCompact&&t.push("-c"),"number"==typeof e.flags?.snapshotDepth&&t.push("-d",String(e.flags.snapshotDepth)),e.flags?.snapshotScope&&t.push("-s",tK(e.flags.snapshotScope)),e.flags?.snapshotRaw&&t.push("--raw"),t.join(" ");if("open"===e.command){for(let i of e.positionals??[])t.push(tK(i));return e.flags?.relaunch&&t.push("--relaunch"),t.join(" ")}for(let i of e.positionals??[])t.push(tK(i));return tZ(t,e),t.join(" ")}(a));return`${i.join("\n")}
|
|
14
|
-
`}(e,this.buildOptimizedActions(e));
|
|
15
|
-
${t}`.toLowerCase().includes("timed out waiting for all destinations")?"Xcode destination did not become available in time. Keep device unlocked and retry.":ej)}function t6(e){return e.map((e,t)=>({...e,ref:`e${t+1}`}))}function t9(e){let t=e.trim();return t.startsWith("@")?t.slice(1)||null:t.startsWith("e")?t:null}function t7(e,t){return e.find(e=>e.ref===t)??null}function ie(e){return{x:Math.round(e.x+e.width/2),y:Math.round(e.y+e.height/2)}}function it(e,t){let i=t.toLowerCase();return e.find(e=>{let t=(e.label??"").toLowerCase(),n=(e.value??"").toLowerCase(),r=(e.identifier??"").toLowerCase();return t.includes(i)||n.includes(i)||r.includes(i)})??null}function ii(e,t){let i=[e.label,e.value,e.identifier].map(e=>"string"==typeof e?e.trim():"").find(e=>e&&e.length>0);return i&&ir(i)?i:function(e,t){if(!e.rect)return;let i=e.rect.y+e.rect.height/2,n=null;for(let e of t){if(!e.rect)continue;let t=[e.label,e.value,e.identifier].map(e=>"string"==typeof e?e.trim():"").find(e=>e&&e.length>0);if(!t||!ir(t))continue;let r=Math.abs(e.rect.y+e.rect.height/2-i);(!n||r<n.distance)&&(n={label:t,distance:r})}return n?.label}(e,t)??(i&&ir(i)?i:void 0)}function ir(e){let t=e.trim();return!(!t||/^(true|false)$/i.test(t)||/^\d+$/.test(t))}function ia(e){let t=[],i=[];for(let n of e){let e=n.depth??0;for(;t.length>0&&e<=t[t.length-1];)t.pop();let r=io(n.type??""),a=[n.label,n.value,n.identifier].map(e=>"string"==typeof e?e.trim():"").find(e=>e&&e.length>0),o=!!a&&ir(a);if(("group"===r||"ioscontentgroup"===r)&&!o){t.push(e);continue}let s=Math.max(0,e-t.length);i.push({...n,depth:s})}return i}function io(e){let t=e.trim().replace(/XCUIElementType/gi,"").toLowerCase(),i=Math.max(t.lastIndexOf("."),t.lastIndexOf("/"));return -1!==i&&(t=t.slice(i+1)),t}function is(e,t){let i=io(e);return!i||("android"===t?i.includes("edittext")||i.includes("autocompletetextview"):i.includes("textfield")||i.includes("securetextfield")||i.includes("searchfield")||i.includes("textview")||i.includes("textarea")||"search"===i)}function il(e){return[e.label,e.value,e.identifier].map(e=>"string"==typeof e?e.trim():"").filter(e=>e.length>0)[0]??""}let ic=new Set(["id","role","text","label","value"]),id=new Set(["visible","hidden","editable","selected","enabled","hittable"]),iu=new Set([...ic,...id]);function ip(e){let t=e.trim();if(!t)throw new h("INVALID_ARGS","Selector expression cannot be empty");let i=function(e){let t=[],i="",n=null;for(let r=0;r<e.length;r+=1){let a=e[r];if(('"'===a||"'"===a)&&!ik(e,r)){n?n===a&&(n=null):n=a,i+=a;continue}if(!n&&"|"===a&&"|"===e[r+1]){let n=i.trim();if(!n)throw new h("INVALID_ARGS",`Invalid selector fallback expression: ${e}`);t.push(n),i="",r+=1;continue}i+=a}let r=i.trim();if(!r)throw new h("INVALID_ARGS",`Invalid selector fallback expression: ${e}`);return t.push(r),t}(t);if(0===i.length)throw new h("INVALID_ARGS","Selector expression cannot be empty");return{raw:t,selectors:i.map(e=>(function(e){let t=e.trim();if(!t)throw new h("INVALID_ARGS","Selector segment cannot be empty");let i=function(e){let t=[],i="",n=null;for(let r=0;r<e.length;r+=1){let a=e[r];if(('"'===a||"'"===a)&&!ik(e,r)){n?n===a&&(n=null):n=a,i+=a;continue}if(!n&&/\s/.test(a)){i.trim().length>0&&t.push(i.trim()),i="";continue}i+=a}if(n)throw new h("INVALID_ARGS",`Unclosed quote in selector: ${e}`);return i.trim().length>0&&t.push(i.trim()),t}(t);if(0===i.length)throw new h("INVALID_ARGS",`Invalid selector segment: ${e}`);return{raw:t,terms:i.map(iS)}})(e))}}function im(e){try{return ip(e)}catch{return null}}function ih(e,t,i){let n=i.requireRect??!1,r=i.requireUnique??!0,a=i.disambiguateAmbiguous??!1,o=[];for(let s=0;s<t.selectors.length;s+=1){let l=t.selectors[s],c=function(e,t,i){let n=0,r=null,a=null,o=!1;for(let s of e){if(i.requireRect&&!s.rect||!ib(s,t,i.platform))continue;if(n+=1,r||(r=s),!a){a=s;continue}let e=function(e,t){let i=e.depth??0,n=t.depth??0;if(i!==n)return i>n?1:-1;let r=ix(e),a=ix(t);return r!==a?r<a?1:-1:0}(s,a);if(e>0){a=s,o=!1;continue}0===e&&(o=!0)}return{count:n,firstNode:r,disambiguated:o?null:a}}(e,l,{platform:i.platform,requireRect:n});if(o.push({selector:l.raw,matches:c.count}),0!==c.count&&c.firstNode){if(r&&1!==c.count){if(!a)continue;let e=c.disambiguated;if(!e)continue;return{node:e,selector:l,selectorIndex:s,matches:c.count,diagnostics:o}}return{node:c.firstNode,selector:l,selectorIndex:s,matches:c.count,diagnostics:o}}}return null}function iw(e,t,i){let n=i.requireRect??!1,r=[];for(let a=0;a<t.selectors.length;a+=1){let o=t.selectors[a],s=function(e,t,i){let n=0;for(let r of e)(!i.requireRect||r.rect)&&ib(r,t,i.platform)&&(n+=1);return n}(e,o,{platform:i.platform,requireRect:n});if(r.push({selector:o.raw,matches:s}),s>0)return{selectorIndex:a,selector:o,matches:s,diagnostics:r}}return null}function ig(e,t,i){let n=i.unique??!0;if(0===t.length)return`Selector did not match: ${e.raw}`;let r=t.map(e=>`${e.selector} -> ${e.matches}`).join(", ");return n?`Selector did not resolve uniquely (${r})`:`Selector did not match (${r})`}function iv(e,t={}){if(0===e.length)return null;let i=t.preferTrailingValue??!1,n=0,r=[];for(;n<e.length&&function(e){let t=e.trim();if(!t)return!1;if("||"===t)return!0;let i=t.indexOf("=");if(-1!==i){let e=t.slice(0,i).trim().toLowerCase();return iu.has(e)}return iu.has(t.toLowerCase())}(e[n]);){n+=1;let t=e.slice(0,n).join(" ").trim();t&&im(t)&&r.push(n)}if(0===r.length)return null;let a=r[r.length-1];if(i){for(let t=r.length-1;t>=0;t-=1)if(r[t]<e.length){a=r[t];break}}let o=e.slice(0,a).join(" ").trim();return o?{selectorExpression:o,rest:e.slice(a)}:null}function iI(e){let t=e[0]??"",i=iv(e.slice(1),{preferTrailingValue:"text"===t});return{predicate:t,split:i}}function iA(e){return!0===e.hittable||!!e.rect&&e.rect.width>0&&e.rect.height>0}function iy(e,t){return is(e.type??"",t)&&!1!==e.enabled}function iN(e,t,i={}){let n=[],r=io(e.type??""),a=iE(e.identifier),o=iE(e.label),s=iE(e.value),l=iE(il(e)),c="fill"===i.action;a&&n.push(`id=${iM(a)}`),r&&o&&n.push(c?`role=${iM(r)} label=${iM(o)} editable=true`:`role=${iM(r)} label=${iM(o)}`),o&&n.push(c?`label=${iM(o)} editable=true`:`label=${iM(o)}`),s&&n.push(c?`value=${iM(s)} editable=true`:`value=${iM(s)}`),l&&l!==o&&l!==s&&n.push(c?`text=${iM(l)} editable=true`:`text=${iM(l)}`),r&&c&&!n.some(e=>e.includes("editable=true"))&&n.push(`role=${iM(r)} editable=true`);let d=tJ(n);return 0===d.length&&r&&d.push(c?`role=${iM(r)} editable=true`:`role=${iM(r)}`),0===d.length&&iA(e)&&d.push("visible=true"),d}function iS(e){let t=e.trim();if(!t)throw new h("INVALID_ARGS","Empty selector term");let i=t.indexOf("=");if(-1===i){let i=t.toLowerCase();if(!id.has(i))throw new h("INVALID_ARGS",`Invalid selector term "${e}", expected key=value`);return{key:i,value:!0}}let n=t.slice(0,i).trim().toLowerCase(),r=t.slice(i+1).trim();if(!iu.has(n))throw new h("INVALID_ARGS",`Unknown selector key: ${n}`);if(!r)throw new h("INVALID_ARGS",`Missing selector value for key: ${n}`);if(id.has(n)){let e,t="true"===(e=iD(r).toLowerCase())||"false"!==e&&null;if(null===t)throw new h("INVALID_ARGS",`Invalid boolean value for ${n}: ${r}`);return{key:n,value:t}}return{key:n,value:iD(r)}}function ib(e,t,i){return t.terms.every(t=>(function(e,t,i){switch(t.key){case"id":return i_(e.identifier,String(t.value));case"role":var n,r;return n=e.type,r=String(t.value),function(e){return io(e)}(n??"")===function(e){return io(e)}(r);case"label":return i_(e.label,String(t.value));case"value":return i_(e.value,String(t.value));case"text":{let i=iO(String(t.value));return iO(il(e))===i}case"visible":return iA(e)===!!t.value;case"hidden":return!iA(e)==!!t.value;case"editable":return iy(e,i)===!!t.value;case"selected":return!0===e.selected==!!t.value;case"enabled":return!1!==e.enabled==!!t.value;case"hittable":return!0===e.hittable==!!t.value;default:return!1}})(e,t,i))}function iD(e){let t=e.trim();return t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1).replace(/\\(["'])/g,"$1"):t}function i_(e,t){return iO(e??"")===iO(t)}function iO(e){return e.trim().toLowerCase().replace(/\s+/g," ")}function iM(e){return JSON.stringify(e)}function iE(e){if(!e)return null;let t=e.trim();return t||null}function ix(e){return e.rect?e.rect.width*e.rect.height:1/0}function ik(e,t){let i=0;for(let n=t-1;n>=0&&"\\"===e[n];n-=1)i+=1;return i%2==1}let iL='iOS appstate requires an active session on the target device. Run open first (for example: open --session sim --platform ios --device "<name>" <app>).',iR=["platform","device","udid","serial","verbose","out"];function iC(e,t,i){return t||iT(i)?null:{ok:!1,error:{code:"INVALID_ARGS",message:`${e} requires an active session or an explicit device selector (e.g. --platform ios).`}}}function iT(e){return!!(e?.platform||e?.device||e?.udid||e?.serial)}async function iP(e){let t=iT(e.flags)||!e.session?await e.resolveTargetDeviceFn(e.flags??{}):e.session.device;return!1!==e.ensureReady&&await e.ensureReadyFn(t),t}let i$={ios:async(e,t,i)=>{let{reinstallIosApp:n}=await Promise.resolve().then(()=>({reinstallIosApp:e0}));return await n(e,t,i)},android:async(e,t,i)=>{let{reinstallAndroidApp:n}=await Promise.resolve().then(()=>({reinstallAndroidApp:eo}));return await n(e,t,i)}};async function iF(e,t,i){if("ios"===e.platform&&t)return F(t)?"device"===e.kind?V(i,t):void 0:await iV(e,t)}async function iV(e,t){try{let{resolveIosApp:i}=await Promise.resolve().then(()=>({resolveIosApp:ez}));return await i(e,t)}catch{return}}async function iU(e){let{req:t,sessionName:i,sessionStore:n,ensureReady:r,resolveDevice:a}=e,o=n.get(i),s=t.flags??{};if(!o&&"string"==typeof s?.session&&s.session.trim().length>0)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"ios"===s.platform?`No active session "${i}". Run open with --session ${i} first.`:`No active session "${i}". Run open with --session ${i} first, or omit --session to query by device selector.`}};let l=iC("appstate",o,s);if(l)return l;let c=o?.device.platform==="ios"&&!!o&&(!iT(s)||!(s?.platform&&s.platform!==o.device.platform||s?.udid&&s.udid!==o.device.id||s?.serial&&s.serial!==o.device.id)&&(!s?.device||s.device.trim().toLowerCase()===o.device.name.trim().toLowerCase()));if("ios"===s.platform&&!c)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:iL}};if(c){let e=o.appName??o.appBundleId;return o.appName||o.appBundleId?{ok:!0,data:{platform:"ios",appName:e??"unknown",appBundleId:o.appBundleId,source:"session"}}:{ok:!1,error:{code:"COMMAND_FAILED",message:"No foreground app is tracked for this iOS session. Open an app in the session, then retry appstate."}}}let d=await iP({session:o,flags:s,ensureReadyFn:r,resolveTargetDeviceFn:a,ensureReady:!0});if("ios"===d.platform)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:iL}};let{getAndroidAppState:u}=await Promise.resolve().then(()=>({getAndroidAppState:Z})),p=await u(d);return{ok:!0,data:{platform:"android",package:p.package,activity:p.activity}}}async function iG(e){let{req:t,sessionName:i,logPath:n,sessionStore:r,invoke:a,dispatch:o,ensureReady:s,resolveTargetDevice:l,reinstallOps:c=i$}=e,d=o??tF,u=s??t3,f=l??t$,m=t.command;if("session_list"===m)return{ok:!0,data:{sessions:r.toArray().map(e=>({name:e.name,platform:e.device.platform,device:e.device.name,id:e.device.id,createdAt:e.createdAt}))}};if("devices"===m)try{let e=[];if(t.flags?.platform==="android"){let{listAndroidDevices:t}=await Promise.resolve().then(()=>({listAndroidDevices:T}));e.push(...await t())}else if(t.flags?.platform==="ios"){let{listIosDevices:t}=await Promise.resolve().then(()=>({listIosDevices:eC}));e.push(...await t())}else{let{listAndroidDevices:t}=await Promise.resolve().then(()=>({listAndroidDevices:T})),{listIosDevices:i}=await Promise.resolve().then(()=>({listIosDevices:eC}));try{e.push(...await t())}catch{}try{e.push(...await i())}catch{}}return{ok:!0,data:{devices:e}}}catch(t){let e=p(t);return{ok:!1,error:{code:e.code,message:e.message,details:e.details}}}if("apps"===m){let e=r.get(i),n=t.flags??{},a=iC(m,e,n);if(a)return a;let o=await iP({session:e,flags:n,ensureReadyFn:u,resolveTargetDeviceFn:f,ensureReady:!0});if(!tq("apps",o))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"apps is not supported on this device"}};let s=t.flags?.appsFilter??"all";if("ios"===o.platform){let{listIosApps:e}=await Promise.resolve().then(()=>({listIosApps:e3}));return{ok:!0,data:{apps:(await e(o,s)).map(e=>e.name&&e.name!==e.bundleId?`${e.name} (${e.bundleId})`:e.bundleId)}}}let{listAndroidApps:l}=await Promise.resolve().then(()=>({listAndroidApps:X}));return{ok:!0,data:{apps:(await l(o,s)).map(e=>e.name&&e.name!==e.package?`${e.name} (${e.package})`:e.package)}}}if("boot"===m){let e=r.get(i),n=t.flags??{},a=iC(m,e,n);if(a)return a;let o=await iP({session:e,flags:n,ensureReadyFn:u,resolveTargetDeviceFn:f,ensureReady:!0});return tq("boot",o)?{ok:!0,data:{platform:o.platform,device:o.name,id:o.id,kind:o.kind,booted:!0}}:{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"boot is not supported on this device"}}}if("appstate"===m)return await iU({req:t,sessionName:i,sessionStore:r,ensureReady:u,resolveDevice:f});if("reinstall"===m){let e,n=r.get(i),a=t.flags??{},o=iC(m,n,a);if(o)return o;let s=t.positionals?.[0]?.trim(),l=t.positionals?.[1]?.trim();if(!s||!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"reinstall requires: reinstall <app> <path-to-app-binary>"}};let d=t0.expandHome(l);if(!w.existsSync(d))return{ok:!1,error:{code:"INVALID_ARGS",message:`App binary not found: ${d}`}};let p=await iP({session:n,flags:a,ensureReadyFn:u,resolveTargetDeviceFn:f,ensureReady:!1});if(!tq("reinstall",p))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"reinstall is not supported on this device"}};if("ios"===p.platform){let t=await c.ios(p,s,d);e={platform:"ios",appId:t.bundleId,bundleId:t.bundleId}}else{let t=await c.android(p,s,d);e={platform:"android",appId:t.package,package:t.package}}let h={app:s,appPath:d,...e};return n&&r.recordAction(n,{command:m,positionals:t.positionals??[],flags:t.flags??{},result:h}),{ok:!0,data:h}}if("open"===m){let e=t.flags?.relaunch===!0;if(r.has(i)){let a=r.get(i),o=t.positionals?.[0],s=o??(e?a?.appName:void 0);if(!a||!s)return e?{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch requires an app name or an active session app."}}:{ok:!1,error:{code:"INVALID_ARGS",message:"Session already active. Close it first or pass a new --session name."}};if(e&&F(s))return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch does not support URL targets."}};await u(a.device);let l=await iF(a.device,s,a.appBundleId),c=o?t.positionals??[]:[s];if(e){let e=l??s;await d(a.device,"close",[e],t.flags?.out,{...t1(n,t.flags,l??a.appBundleId,a.trace?.outPath)})}await d(a.device,"open",c,t.flags?.out,{...t1(n,t.flags,l)});let p={...a,appBundleId:l,appName:s,recordSession:a.recordSession||!!t.flags?.saveScript,snapshot:void 0};return r.recordAction(p,{command:m,positionals:c,flags:t.flags??{},result:{session:i,appName:s,appBundleId:l}}),r.set(i,p),{ok:!0,data:{session:i,appName:s,appBundleId:l}}}let a=t.positionals?.[0];if(e&&!a)return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch requires an app argument."}};if(e&&a&&F(a))return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch does not support URL targets."}};let o=await f(t.flags??{}),s=r.toArray().find(e=>e.device.id===o.id);if(s)return{ok:!1,error:{code:"DEVICE_IN_USE",message:`Device is already in use by session "${s.name}".`,details:{session:s.name,deviceId:o.id,deviceName:o.name}}};await u(o);let l=await iF(o,a);if(e&&a){let e=l??a;await d(o,"close",[e],t.flags?.out,{...t1(n,t.flags,l)})}await d(o,"open",t.positionals??[],t.flags?.out,{...t1(n,t.flags,l)});let c={name:i,device:o,createdAt:Date.now(),appBundleId:l,appName:a,recordSession:!!t.flags?.saveScript,actions:[]};return r.recordAction(c,{command:m,positionals:t.positionals??[],flags:t.flags??{},result:{session:i}}),r.set(i,c),{ok:!0,data:{session:i}}}if("replay"===m){let e=t.positionals?.[0];if(!e)return{ok:!1,error:{code:"INVALID_ARGS",message:"replay requires a path"}};try{let o=t0.expandHome(e),s=w.readFileSync(o,"utf8"),l=s.trimStart()[0];if("{"===l||"["===l)return{ok:!1,error:{code:"INVALID_ARGS",message:"replay accepts .ad script files. JSON replay payloads are no longer supported."}};let c=function(e){let t=[];for(let i of e.split(/\r?\n/)){let e=function(e){let t=e.trim();if(0===t.length||t.startsWith("#"))return null;let i=function(e){let t=[],i=0;for(;i<e.length;){for(;i<e.length&&/\s/.test(e[i]);)i+=1;if(i>=e.length)break;if('"'===e[i]){let n=i+1,r=!1;for(;n<e.length;){let t=e[n];if('"'===t&&!r)break;r="\\"===t&&!r,"\\"!==t&&(r=!1),n+=1}if(n>=e.length)throw new h("INVALID_ARGS",`Invalid replay script line: ${e}`);let a=e.slice(i,n+1);t.push(JSON.parse(a)),i=n+1;continue}let n=i;for(;n<e.length&&!/\s/.test(e[n]);)n+=1;t.push(e.slice(i,n)),i=n}return t}(t);if(0===i.length)return null;let[n,...r]=i;if("context"===n)return null;let a={ts:Date.now(),command:n,positionals:[],flags:{}};if("snapshot"===n){a.positionals=[];for(let e=0;e<r.length;e+=1){let t=r[e];if("-i"===t){a.flags.snapshotInteractiveOnly=!0;continue}if("-c"===t){a.flags.snapshotCompact=!0;continue}if("--raw"===t){a.flags.snapshotRaw=!0;continue}if(("-d"===t||"--depth"===t)&&e+1<r.length){let t=Number(r[e+1]);Number.isFinite(t)&&t>=0&&(a.flags.snapshotDepth=Math.floor(t)),e+=1;continue}if(("-s"===t||"--scope"===t)&&e+1<r.length){a.flags.snapshotScope=r[e+1],e+=1;continue}if("--backend"===t&&e+1<r.length){e+=1;continue}}return a}if("open"===n){a.positionals=[];for(let e=0;e<r.length;e+=1){let t=r[e];if("--relaunch"===t){a.flags.relaunch=!0;continue}a.positionals.push(t)}return a}if(tY(n)){let e=tQ(n,r);if(Object.assign(a.flags,e.flags),0===e.positionals.length)return a;let t=e.positionals[0];if(t.startsWith("@"))return a.positionals=[t],e.positionals[1]&&(a.result={refLabel:e.positionals[1]}),a;let i=e.positionals[0],o=e.positionals[1];return iz(i)&&iz(o)&&e.positionals.length>=2?a.positionals=[i,o]:a.positionals=[e.positionals.join(" ")],a}if("fill"===n){if(r.length<2)return a.positionals=r,a;let e=r[0];return e.startsWith("@")?(r.length>=3?(a.positionals=[e,r.slice(2).join(" ")],a.result={refLabel:r[1]}):a.positionals=[e,r[1]],a):(a.positionals=[e,r.slice(1).join(" ")],a)}if("get"===n){if(r.length<2)return a.positionals=r,a;let e=r[0],t=r[1];return t.startsWith("@")?(a.positionals=[e,t],r[2]&&(a.result={refLabel:r[2]})):a.positionals=[e,r.slice(1).join(" ")],a}if("swipe"===n){let e=tQ(n,r);return Object.assign(a.flags,e.flags),a.positionals=e.positionals,a}return a.positionals=r,a}(i);e&&t.push(e)}return t}(s),u=t.flags?.replayUpdate===!0,p=0;for(let e=0;e<c.length;e+=1){let s=c[e];if(!s||"replay"===s.command)continue;let l=await a({token:t.token,session:i,command:s.command,positionals:s.positionals??[],flags:s.flags??{}});if(l.ok)continue;if(!u)return iq(l,s,e,o);let f=await iW({action:s,sessionName:i,logPath:n,sessionStore:r,dispatch:d});if(!f)return iq(l,s,e,o);if(c[e]=f,!(l=await a({token:t.token,session:i,command:f.command,positionals:f.positionals??[],flags:f.flags??{}})).ok)return iq(l,f,e,o);p+=1}if(u&&p>0){let e=r.get(i);!function(e,t,i){let n=[];if(i){let e=i.device.name.replace(/"/g,'\\"'),t=i.device.kind?` kind=${i.device.kind}`:"";n.push(`context platform=${i.device.platform} device="${e}"${t} theme=unknown`)}for(let e of t)n.push(function(e){let t=[e.command];if("snapshot"===e.command)return e.flags?.snapshotInteractiveOnly&&t.push("-i"),e.flags?.snapshotCompact&&t.push("-c"),"number"==typeof e.flags?.snapshotDepth&&t.push("-d",String(e.flags.snapshotDepth)),e.flags?.snapshotScope&&t.push("-s",tK(e.flags.snapshotScope)),e.flags?.snapshotRaw&&t.push("--raw"),t.join(" ");if("open"===e.command){for(let i of e.positionals??[])t.push(tK(i));return e.flags?.relaunch&&t.push("--relaunch"),t.join(" ")}for(let i of e.positionals??[])t.push(tK(i));return tZ(t,e),t.join(" ")}(e));let r=`${n.join("\n")}
|
|
16
|
-
`,a=`${e}.tmp-${process.pid}-${Date.now()}`;w.writeFileSync(a,r),w.renameSync(a,e)}(o,c,e)}return{ok:!0,data:{replayed:c.length,healed:p,session:i}}}catch(t){let e=p(t);return{ok:!1,error:{code:e.code,message:e.message}}}}if("batch"===m)return await ij(t,i,a);if("close"===m){let e=r.get(i);return e?(t.positionals&&t.positionals.length>0&&await d(e.device,"close",t.positionals??[],t.flags?.out,{...t1(n,t.flags,e.appBundleId,e.trace?.outPath)}),"ios"===e.device.platform&&await tm(e.device.id),r.recordAction(e,{command:m,positionals:t.positionals??[],flags:t.flags??{},result:{session:i}}),t.flags?.saveScript&&(e.recordSession=!0),r.writeSessionLog(e),r.delete(i),{ok:!0,data:{session:i}}):{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session"}}}return null}async function ij(e,t,i){let n=e.flags?.batchOnError??"stop";if("stop"!==n)return{ok:!1,error:{code:"INVALID_ARGS",message:`Unsupported batch on-error mode: ${n}.`}};let r=e.flags?.batchMaxSteps??f;if(!Number.isInteger(r)||r<1||r>1e3)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid batch max-steps: ${String(e.flags?.batchMaxSteps)}`}};try{let n=o(e.flags?.batchSteps,r),a=Date.now(),s=[];for(let r=0;r<n.length;r+=1){let a=n[r],o=await iB(e,t,a,i,r+1);if(!o.ok)return{ok:!1,error:{code:o.error.code,message:`Batch failed at step ${o.step} (${a.command}): ${o.error.message}`,details:{...o.error.details??{},step:o.step,command:a.command,positionals:a.positionals,executed:r,total:n.length,partialResults:s}}};s.push(o.result)}return{ok:!0,data:{total:n.length,executed:n.length,totalDurationMs:Date.now()-a,results:s}}}catch(t){let e=p(t);return{ok:!1,error:{code:e.code,message:e.message,details:e.details}}}}async function iB(e,t,i,n,r){let a=Date.now(),o=await n({token:e.token,session:t,command:i.command,positionals:i.positionals,flags:function(e,t){let i={...t??{}};delete i.batchSteps,delete i.batchOnError,delete i.batchMaxSteps;let n=e??{};for(let e of iR)void 0===i[e]&&void 0!==n[e]&&(i[e]=n[e]);return i}(e.flags,i.flags)}),s=Date.now()-a;return o.ok?{ok:!0,step:r,result:{step:r,command:i.command,ok:!0,data:o.data??{},durationMs:s}}:{ok:!1,step:r,error:o.error}}function iq(e,t,i,n){if(e.ok)return e;let r=i+1,a=function(e){let t;return t=(e.positionals??[]).map(e=>tK(e)),[e.command,...t].join(" ")}(t),o={...e.error.details??{},replayPath:n,step:r,action:t.command,positionals:t.positionals??[]};return{ok:!1,error:{code:e.error.code,message:`Replay failed at step ${r} (${a}): ${e.error.message}`,details:o}}}async function iW(e){let{action:t,sessionName:i,logPath:n,sessionStore:r,dispatch:a}=e;if(!(tY(t.command)||["fill","get","is","wait"].includes(t.command)))return null;let o=r.get(i);if(!o)return null;let s=tY(t.command)||"fill"===t.command,l=tY(t.command)||"fill"===t.command||"get"===t.command&&t.positionals?.[0]==="text",c=await iJ(o,t,n,s,a,r);for(let e of function(e){let t=[],i=Array.isArray(e.result?.selectorChain)&&e.result?.selectorChain.every(e=>"string"==typeof e)?e.result.selectorChain:[];if(t.push(...i),tY(e.command)){let i=e.positionals?.[0]??"";i&&!i.startsWith("@")&&t.push(e.positionals.join(" "))}if("fill"===e.command){let i=e.positionals?.[0]??"";i&&!i.startsWith("@")&&Number.isNaN(Number(i))&&t.push(i)}if("get"===e.command){let i=e.positionals?.[1]??"";i&&!i.startsWith("@")&&t.push(e.positionals.slice(1).join(" "))}if("is"===e.command){let{split:i}=iI(e.positionals);i&&t.push(i.selectorExpression)}if("wait"===e.command){let{selectorExpression:i}=iH(e.positionals??[]);i&&t.push(i)}let n="string"==typeof e.result?.refLabel?e.result.refLabel.trim():"";if(n.length>0){let i=JSON.stringify(n);"fill"===e.command?(t.push(`id=${i} editable=true`),t.push(`label=${i} editable=true`),t.push(`text=${i} editable=true`),t.push(`value=${i} editable=true`)):(t.push(`id=${i}`),t.push(`label=${i}`),t.push(`text=${i}`),t.push(`value=${i}`))}return tJ(t).filter(e=>e.trim().length>0)}(t)){let i=im(e);if(!i)continue;let n=ih(c.nodes,i,{platform:o.device.platform,requireRect:s,requireUnique:!0,disambiguateAmbiguous:l});if(!n)continue;let r=iN(n.node,o.device.platform,{action:tY(t.command)?"click":"fill"===t.command?"fill":"get"}).join(" || ");if(tY(t.command))return{...t,positionals:[r]};if("fill"===t.command){let e=tW(t);if(!e)continue;return{...t,positionals:[r,e]}}if("get"===t.command){let e=t.positionals?.[0];if("text"!==e&&"attrs"!==e)continue;return{...t,positionals:[e,r]}}if("is"===t.command){let{predicate:e,split:i}=iI(t.positionals);if(!e)continue;let n=i?.rest.join(" ").trim()??"",a=[e,r];return"text"===e&&n.length>0&&a.push(n),{...t,positionals:a}}if("wait"===t.command){let{selectorTimeout:e}=iH(t.positionals??[]),i=[r];return e&&i.push(e),{...t,positionals:i}}}let d=function(e,t,i){if("get"!==e.command||e.positionals?.[0]!=="text")return null;let n=e.positionals?.[1];if(!n)return null;let r=im(n);if(!r)return null;let a=new Set,o=!1;for(let e of r.selectors)for(let t of e.terms)"role"===t.key&&"string"==typeof t.value&&a.add(io(t.value)),("text"===t.key||"label"===t.key||"value"===t.key)&&"string"==typeof t.value&&/^\d+$/.test(t.value.trim())&&(o=!0);if(!o)return null;let s=t.nodes.filter(e=>{let t=il(e).trim();return!!/^\d+$/.test(t)&&(0===a.size||a.has(io(e.type??"")))});if(0===s.length||1!==tJ(s.map(e=>il(e).trim())).length)return null;let l=s[0];if(!l)return null;let c=iN(l,i.device.platform,{action:"get"});return 0===c.length?null:{...e,positionals:["text",c.join(" || ")]}}(t,c,o);return d||null}async function iJ(e,t,i,n,r,a){let o=await r(e.device,"snapshot",[],t.flags?.out,{...t1(i,{...t.flags??{},snapshotInteractiveOnly:n,snapshotCompact:n},e.appBundleId,e.trace?.outPath)}),s=o?.nodes??[],l={nodes:t6(t.flags?.snapshotRaw?s:ia(s)),truncated:o?.truncated,createdAt:Date.now(),backend:o?.backend};return e.snapshot=l,a.set(e.name,e),l}function iH(e){if(0===e.length)return{selectorExpression:null,selectorTimeout:null};let t=e[e.length-1],i=/^\d+$/.test(t??""),n=iv(i?e.slice(0,-1):e.slice());return!n||n.rest.length>0?{selectorExpression:null,selectorTimeout:null}:{selectorExpression:n.selectorExpression,selectorTimeout:i?t:null}}function iz(e){return!!e&&!Number.isNaN(Number(e))}function iX(e){if(!e)return null;let t=Number(e);return Number.isFinite(t)?t:null}async function iY(e){let{req:t,sessionName:i,logPath:n,sessionStore:r}=e,a=t.command;if("snapshot"===a){let{session:e,device:a}=await iK(r,i,t.flags);if(!tq("snapshot",a))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"snapshot is not supported on this device"}};let o=e?.appBundleId,s=t.flags?.snapshotScope;if(s&&s.trim().startsWith("@")){if(!e?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"Ref scope requires an existing snapshot in session."}};let t=t9(s.trim());if(!t)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref scope: ${s}`}};let i=t7(e.snapshot.nodes,t),n=i?ii(i,e.snapshot.nodes):void 0;if(!n)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${s} not found or has no label`}};s=n}return await iZ(e,a,async()=>{let l=await tF(a,"snapshot",[],t.flags?.out,{...t1(n,{...t.flags,snapshotScope:s},o,e?.trace?.outPath)}),c=l?.nodes??[],d=t6(t.flags?.snapshotRaw?c:ia(c)),u={nodes:d,truncated:l?.truncated,createdAt:Date.now(),backend:l?.backend},p=e?{...e,snapshot:u}:{name:i,device:a,createdAt:Date.now(),appBundleId:o,snapshot:u,actions:[]};return iQ(r,p,t,{nodes:d.length,truncated:l?.truncated??!1}),r.set(i,p),{ok:!0,data:{nodes:d,truncated:l?.truncated??!1,appName:p.appBundleId?p.appName??p.appBundleId:void 0,appBundleId:p.appBundleId}}})}if("wait"===a){let{session:e,device:a}=await iK(r,i,t.flags),o=function(e){if(0===e.length)return null;let t=iX(e[0]);if(null!==t)return{kind:"sleep",durationMs:t};if("text"===e[0]){let t=iX(e[e.length-1]);return{kind:"text",text:(null!==t?e.slice(1,-1).join(" "):e.slice(1).join(" ")).trim(),timeoutMs:t}}if(e[0].startsWith("@")){let t=iX(e[e.length-1]);return{kind:"ref",rawRef:e[0],timeoutMs:t}}let i=iX(e[e.length-1]),n=iv(null!==i?e.slice(0,-1):e.slice());if(n&&0===n.rest.length){let e=im(n.selectorExpression);if(e)return{kind:"selector",selector:e,selectorExpression:n.selectorExpression,timeoutMs:i}}return{kind:"text",text:(null!==i?e.slice(0,-1).join(" "):e.join(" ")).trim(),timeoutMs:i}}(t.positionals??[]);return o?"sleep"===o.kind?(await new Promise(e=>setTimeout(e,o.durationMs)),iQ(r,e,t,{waitedMs:o.durationMs}),{ok:!0,data:{waitedMs:o.durationMs}}):tq("wait",a)?await iZ(e,a,async()=>{let s,l;if("selector"===o.kind){let s=o.timeoutMs??1e4,l=Date.now();for(;Date.now()-l<s;){let s=await tF(a,"snapshot",[],t.flags?.out,{...t1(n,{...t.flags,snapshotInteractiveOnly:!1,snapshotCompact:!1},e?.appBundleId,e?.trace?.outPath)}),c=s?.nodes??[],d=t6(t.flags?.snapshotRaw?c:ia(c));e&&(e.snapshot={nodes:d,truncated:s?.truncated,createdAt:Date.now(),backend:s?.backend},r.set(i,e));let u=iw(d,o.selector,{platform:a.platform});if(u)return iQ(r,e,t,{selector:u.selector.raw,waitedMs:Date.now()-l}),{ok:!0,data:{selector:u.selector.raw,waitedMs:Date.now()-l}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:`wait timed out for selector: ${o.selectorExpression}`}}}if("ref"===o.kind){if(!e?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"Ref wait requires an existing snapshot in session."}};let t=t9(o.rawRef);if(!t)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref: ${o.rawRef}`}};let i=t7(e.snapshot.nodes,t),n=i?ii(i,e.snapshot.nodes):void 0;if(!n)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${o.rawRef} not found or has no label`}};s=n,l=o.timeoutMs}else s=o.text,l=o.timeoutMs;if(!s)return{ok:!1,error:{code:"INVALID_ARGS",message:"wait requires text"}};let c=l??1e4,d=Date.now();for(;Date.now()-d<c;){if("ios"===a.platform){let i=await td(a,{command:"findText",text:s,appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:n,traceLogPath:e?.trace?.outPath});if(i?.found)return iQ(r,e,t,{text:s,waitedMs:Date.now()-d}),{ok:!0,data:{text:s,waitedMs:Date.now()-d}}}else if("android"===a.platform&&it(t6((await eA(a,{scope:s})).nodes??[]),s))return iQ(r,e,t,{text:s,waitedMs:Date.now()-d}),{ok:!0,data:{text:s,waitedMs:Date.now()-d}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:`wait timed out for text: ${s}`}}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"wait is not supported on this device"}}:{ok:!1,error:{code:"INVALID_ARGS",message:"wait requires a duration or text"}}}if("alert"===a){let{session:e,device:a}=await iK(r,i,t.flags),o=(t.positionals?.[0]??"get").toLowerCase();return tq("alert",a)?await iZ(e,a,async()=>{if("wait"===o){let i=iX(t.positionals?.[1])??1e4,o=Date.now();for(;Date.now()-o<i;){try{let i=await td(a,{command:"alert",action:"get",appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:n,traceLogPath:e?.trace?.outPath});return iQ(r,e,t,i),{ok:!0,data:i}}catch{}await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:"alert wait timed out"}}}let i=await td(a,{command:"alert",action:"accept"===o||"dismiss"===o?o:"get",appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:n,traceLogPath:e?.trace?.outPath});return iQ(r,e,t,i),{ok:!0,data:i}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"alert is only supported on iOS simulators"}}}if("settings"===a){let e=t.positionals?.[0],a=t.positionals?.[1];if(!e||!a)return{ok:!1,error:{code:"INVALID_ARGS",message:"settings requires <wifi|airplane|location> <on|off>"}};let{session:o,device:s}=await iK(r,i,t.flags);return tq("settings",s)?await iZ(o,s,async()=>{let i=o?.appBundleId,l=await tF(s,"settings",[e,a,i??""],t.flags?.out,{...t1(n,t.flags,i,o?.trace?.outPath)});return iQ(r,o,t,l??{setting:e,state:a}),{ok:!0,data:l??{setting:e,state:a}}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"settings is not supported on this device"}}}return null}async function iK(e,t,i){let n=e.get(t),r=n?.device??await t$(i??{});return n||await t3(r),{session:n,device:r}}async function iZ(e,t,i){let n=!e&&"ios"===t.platform;try{return await i()}finally{n&&await tm(t.id)}}function iQ(e,t,i,n){t&&e.recordAction(t,{command:i.command,positionals:i.positionals??[],flags:i.flags??{},result:n})}function i0(e,t,i,n={}){let r=i2(i);if(!r)return{matches:[],score:0};let a=0,o=[];for(let i of e){if(n.requireRect&&!i.rect)continue;let e=function(e,t,i){switch(t){case"role":return function(e,t){let i=function(e){let t=e.trim();return t?t=(t.split(".").pop()??t).replace(/XCUIElementType/gi,"").toLowerCase():""}(e??"");return i?i===t?2:+!!i.includes(t):0}(e.type,i);case"label":return i1(e.label,i);case"value":return i1(e.value,i);case"id":return i1(e.identifier,i);default:return Math.max(i1(e.label,i),i1(e.value,i),i1(e.identifier,i))}}(i,t,r);if(!(e<=0)){if(e>a){a=e,o.length=0,o.push(i);continue}e===a&&o.push(i)}}return{matches:o,score:a}}function i1(e,t){let i=i2(e??"");return i?i===t?2:+!!i.includes(t):0}function i2(e){return e.trim().toLowerCase().replace(/\s+/g," ")}async function i3(e){let{req:t,sessionName:i,logPath:n,sessionStore:r,invoke:a}=e,o=t.command;if("find"!==o)return null;let s=t.positionals??[];if(0===s.length)return{ok:!1,error:{code:"INVALID_ARGS",message:"find requires a locator or text"}};let{locator:l,query:c,action:d,value:u,timeoutMs:p}=function(e){let t="any",i=0;["text","label","value","role","id"].includes(e[0])&&(t=e[0],i=1);let n=e[i]??"",r=e.slice(i+1);if(0===r.length)return{locator:t,query:n,action:"click"};let a=r[0].toLowerCase();if("get"===a){let e=r[1]?.toLowerCase();if("text"===e)return{locator:t,query:n,action:"get_text"};if("attrs"===e)return{locator:t,query:n,action:"get_attrs"};throw new h("INVALID_ARGS","find get only supports text or attrs")}if("wait"===a)return{locator:t,query:n,action:"wait",timeoutMs:iX(r[1])??void 0};if("exists"===a)return{locator:t,query:n,action:"exists"};if("click"===a)return{locator:t,query:n,action:"click"};if("focus"===a)return{locator:t,query:n,action:"focus"};if("fill"===a)return{locator:t,query:n,action:"fill",value:r.slice(1).join(" ")};if("type"===a)return{locator:t,query:n,action:"type",value:r.slice(1).join(" ")};throw new h("INVALID_ARGS",`Unsupported find action: ${r[0]}`)}(s);if(!c)return{ok:!1,error:{code:"INVALID_ARGS",message:"find requires a value"}};let f=r.get(i);if(!f&&"exists"!==d&&"wait"!==d&&"get_text"!==d&&"get_attrs"!==d)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let m=f?.device??await t$(t.flags??{});f||await t3(m);let w=f?.appBundleId,g="role"!==l?c:void 0,v="click"===d||"focus"===d||"fill"===d||"type"===d,I=0,A=null,y=async()=>{let e=Date.now();if(A&&e-I<750)return{nodes:A};let a=await tF(m,"snapshot",[],t.flags?.out,{...t1(n,{...t.flags,snapshotScope:g,snapshotInteractiveOnly:v,snapshotCompact:v},w,f?.trace?.outPath)}),o=a?.nodes??[],s=t6(t.flags?.snapshotRaw?o:ia(o));return I=e,A=s,f&&(f.snapshot={nodes:s,truncated:a?.truncated,createdAt:Date.now(),backend:a?.backend},r.set(i,f)),{nodes:s,truncated:a?.truncated,backend:a?.backend}};if("wait"===d){let e=p??1e4,i=Date.now();for(;Date.now()-i<e;){let{nodes:e}=await y();if(i0(e,l,c,{requireRect:!1}).matches[0])return f&&r.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{found:!0,waitedMs:Date.now()-i}}),{ok:!0,data:{found:!0,waitedMs:Date.now()-i}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:"find wait timed out"}}}let{nodes:N}=await y(),S=i0(N,l,c,{requireRect:v});if(v&&S.matches.length>1){let e=S.matches.slice(0,8).map(e=>{let t=il(e)||e.label||e.identifier||e.type||"";return`@${e.ref}${t?`(${t})`:""}`});return{ok:!1,error:{code:"AMBIGUOUS_MATCH",message:`find matched ${S.matches.length} elements for ${l} "${c}". Use a more specific locator or selector.`,details:{locator:l,query:c,matches:S.matches.length,candidates:e}}}}let b=S.matches[0]??null;if(!b)return{ok:!1,error:{code:"COMMAND_FAILED",message:"find did not match any element"}};let D="click"===d||"focus"===d||"fill"===d||"type"===d?function(e,t){if(t.hittable)return t;let i=t,n=new Set;for(;void 0!==i.parentIndex&&!n.has(i.ref);){n.add(i.ref);let t=e[i.parentIndex];if(!t)break;if(t.hittable)return t;i=t}return null}(N,b)??b:b,_=`@${D.ref}`,O={...t.flags??{},noRecord:!0};if("exists"===d)return f&&r.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{found:!0}}),{ok:!0,data:{found:!0}};if("get_text"===d){let e=il(b);return f&&r.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:_,action:"get text",text:e}}),{ok:!0,data:{ref:_,text:e,node:b}}}if("get_attrs"===d)return f&&r.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:_,action:"get attrs"}}),{ok:!0,data:{ref:_,node:b}};if("click"===d){let e=await a({token:t.token,session:i,command:"click",positionals:[_],flags:O});return e.ok&&f&&r.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:_,action:"click"}}),e}if("fill"===d){if(!u)return{ok:!1,error:{code:"INVALID_ARGS",message:"find fill requires text"}};let e=await a({token:t.token,session:i,command:"fill",positionals:[_,u],flags:O});return e.ok&&f&&r.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:_,action:"fill"}}),e}if("focus"===d){let e=b.rect?ie(b.rect):null;if(!e)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};let i=await tF(m,"focus",[String(e.x),String(e.y)],t.flags?.out,{...t1(n,t.flags,f?.appBundleId,f?.trace?.outPath)});return f&&r.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:_,action:"focus"}}),{ok:!0,data:i??{ref:_}}}if("type"===d){if(!u)return{ok:!1,error:{code:"INVALID_ARGS",message:"find type requires text"}};let e=b.rect?ie(b.rect):null;if(!e)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};await tF(m,"focus",[String(e.x),String(e.y)],t.flags?.out,{...t1(n,t.flags,f?.appBundleId,f?.trace?.outPath)});let i=await tF(m,"type",[u],t.flags?.out,{...t1(n,t.flags,f?.appBundleId,f?.trace?.outPath)});return f&&r.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:_,action:"type"}}),{ok:!0,data:i??{ref:_}}}return null}async function i4(e){let{req:t,sessionName:i,sessionStore:r}=e,a=t.command;if("record"===a){let e=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"record requires start|stop"}};let o=r.get(i),s=o?.device??await t$(t.flags??{});o||await t3(s);let c=o??{name:i,device:s,createdAt:Date.now(),actions:[]};if("start"===e){if(c.recording)return{ok:!1,error:{code:"INVALID_ARGS",message:"recording already in progress"}};let e=t.positionals?.[1]??`./recording-${Date.now()}.mp4`,o=n.resolve(e),d=n.dirname(o);if(w.existsSync(d)||w.mkdirSync(d,{recursive:!0}),!tq("record",s))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"record is only supported on iOS simulators"}};if("ios"===s.platform){let{child:e,wait:t}=l("xcrun",["simctl","io",s.id,"recordVideo",o],{allowFailure:!0});c.recording={platform:"ios",outPath:o,child:e,wait:t}}else{let e=`/sdcard/agent-device-recording-${Date.now()}.mp4`,{child:t,wait:i}=l("adb",["-s",s.id,"shell","screenrecord",e],{allowFailure:!0});c.recording={platform:"android",outPath:o,remotePath:e,child:t,wait:i}}return r.set(i,c),r.recordAction(c,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{action:"start"}}),{ok:!0,data:{recording:"started",outPath:e}}}if(!c.recording)return{ok:!1,error:{code:"INVALID_ARGS",message:"no active recording"}};let u=c.recording;u.child.kill("SIGINT");try{await u.wait}catch{}if("android"===u.platform&&u.remotePath)try{await d("adb",["-s",s.id,"pull",u.remotePath,u.outPath],{allowFailure:!0}),await d("adb",["-s",s.id,"shell","rm","-f",u.remotePath],{allowFailure:!0})}catch{}return c.recording=void 0,r.recordAction(c,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:u.outPath}}),{ok:!0,data:{recording:"stopped",outPath:u.outPath}}}if("trace"===a){let e=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"trace requires start|stop"}};let o=r.get(i);if(!o)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session"}};if("start"===e){if(o.trace)return{ok:!1,error:{code:"INVALID_ARGS",message:"trace already in progress"}};let e=t.positionals?.[1]??r.defaultTracePath(o),i=t0.expandHome(e);return w.mkdirSync(n.dirname(i),{recursive:!0}),w.appendFileSync(i,""),o.trace={outPath:i,startedAt:Date.now()},r.recordAction(o,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{action:"start",outPath:i}}),{ok:!0,data:{trace:"started",outPath:i}}}if(!o.trace)return{ok:!1,error:{code:"INVALID_ARGS",message:"no active trace"}};let s=o.trace.outPath;if(t.positionals?.[1]){let e=t0.expandHome(t.positionals[1]);w.mkdirSync(n.dirname(e),{recursive:!0}),w.existsSync(s)?w.renameSync(s,e):w.appendFileSync(e,""),s=e}return o.trace=void 0,r.recordAction(o,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:s}}),{ok:!0,data:{trace:"stopped",outPath:s}}}return null}async function i5(e){let{req:t,sessionName:i,sessionStore:n,contextFromFlags:r}=e,a=e.dispatch??tF,o=t.command;if("press"===o){let e=n.get(i);if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let s=function(e){if(e.length<2)return null;let t=Number(e[0]),i=Number(e[1]);return Number.isFinite(t)&&Number.isFinite(i)?{x:t,y:i}:null}(t.positionals??[]);if(s){let i=await a(e.device,"press",[String(s.x),String(s.y)],t.flags?.out,{...r(t.flags,e.appBundleId,e.trace?.outPath)});return n.recordAction(e,{command:o,positionals:t.positionals??[String(s.x),String(s.y)],flags:t.flags??{},result:i??{x:s.x,y:s.y}}),{ok:!0,data:i??{x:s.x,y:s.y}}}let l="click",c=t.positionals?.[0]??"";if(c.startsWith("@")){let i=i9("press",t.flags);if(i)return i;if(!e.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}};let s=t9(c);if(!s)return{ok:!1,error:{code:"INVALID_ARGS",message:`${o} requires a ref like @e2`}};let d=t7(e.snapshot.nodes,s);if(!d?.rect&&t.positionals.length>1){let i=t.positionals.slice(1).join(" ").trim();i.length>0&&(d=it(e.snapshot.nodes,i))}if(!d?.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${c} not found or has no bounds`}};let u=ii(d,e.snapshot.nodes),p=iN(d,e.device.platform,{action:l}),{x:f,y:m}=ie(d.rect),h=await a(e.device,"press",[String(f),String(m)],t.flags?.out,{...r(t.flags,e.appBundleId,e.trace?.outPath)});return n.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:s,x:f,y:m,refLabel:u,selectorChain:p}}),{ok:!0,data:{...h??{},ref:s,x:f,y:m}}}let d=(t.positionals??[]).join(" ").trim();if(!d)return{ok:!1,error:{code:"INVALID_ARGS",message:`${o} requires @ref, selector expression, or x y coordinates`}};let u=ip(d),p=await i8(e,t.flags,n,r,{interactiveOnly:!0},a),f=ih(p.nodes,u,{platform:e.device.platform,requireRect:!0,requireUnique:!0,disambiguateAmbiguous:!0});if(!f||!f.node.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:ig(u,f?.diagnostics??[],{unique:!0})}};let{x:m,y:h}=ie(f.node.rect),w=await a(e.device,"press",[String(m),String(h)],t.flags?.out,{...r(t.flags,e.appBundleId,e.trace?.outPath)}),g=iN(f.node,e.device.platform,{action:l}),v=ii(f.node,p.nodes);return n.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{x:m,y:h,selector:f.selector.raw,selectorChain:g,refLabel:v}}),{ok:!0,data:{...w??{},selector:f.selector.raw,x:m,y:h}}}if("fill"===o){let e=n.get(i);if(t.positionals?.[0]?.startsWith("@")){let i=i9("fill",t.flags);if(i)return i;if(!e?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}};let s=t9(t.positionals[0]);if(!s)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires a ref like @e2"}};let l=t.positionals.length>=3?t.positionals[1]:"",c=t.positionals.length>=3?t.positionals.slice(2).join(" "):t.positionals.slice(1).join(" ");if(!c)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after ref"}};let d=t7(e.snapshot.nodes,s);if(!d?.rect&&l&&(d=it(e.snapshot.nodes,l)),!d?.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${t.positionals[0]} not found or has no bounds`}};let u=d.type??"",p=u&&!is(u,e.device.platform)?`fill target ${t.positionals[0]} resolved to "${u}", attempting fill anyway.`:void 0,f=ii(d,e.snapshot.nodes),m=iN(d,e.device.platform,{action:"fill"}),{x:h,y:w}=ie(d.rect),g={...await a(e.device,"fill",[String(h),String(w),c],t.flags?.out,{...r(t.flags,e.appBundleId,e.trace?.outPath)})??{ref:s,x:h,y:w}};return p&&(g.warning=p),n.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{...g,refLabel:f,selectorChain:m}}),{ok:!0,data:g}}if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let s=iv(t.positionals??[],{preferTrailingValue:!0});if(s){if(0===s.rest.length)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after selector"}};let i=s.rest.join(" ").trim();if(!i)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after selector"}};let l=ip(s.selectorExpression),c=await i8(e,t.flags,n,r,{interactiveOnly:!0},a),d=ih(c.nodes,l,{platform:e.device.platform,requireRect:!0,requireUnique:!0,disambiguateAmbiguous:!0});if(!d||!d.node.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:ig(l,d?.diagnostics??[],{unique:!0})}};let u=d.node,p=u.type??"",f=p&&!is(p,e.device.platform)?`fill target ${d.selector.raw} resolved to "${p}", attempting fill anyway.`:void 0,{x:m,y:h}=ie(d.node.rect),w=await a(e.device,"fill",[String(m),String(h),i],t.flags?.out,{...r(t.flags,e.appBundleId,e.trace?.outPath)}),g=iN(u,e.device.platform,{action:"fill"}),v={...w??{x:m,y:h,text:i},selector:d.selector.raw,selectorChain:g,refLabel:ii(u,c.nodes)};return f&&(v.warning=f),n.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:v}),{ok:!0,data:v}}return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires x y text, @ref text, or selector text"}}}if("get"===o){let e=t.positionals?.[0];if("text"!==e&&"attrs"!==e)return{ok:!1,error:{code:"INVALID_ARGS",message:"get only supports text or attrs"}};let s=n.get(i);if(!s)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let l=t.positionals?.[1]??"";if(l.startsWith("@")){let i=i9("get",t.flags);if(i)return i;if(!s.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}};let r=t9(l??"");if(!r)return{ok:!1,error:{code:"INVALID_ARGS",message:"get text requires a ref like @e2"}};let a=t7(s.snapshot.nodes,r);if(!a&&t.positionals.length>2){let e=t.positionals.slice(2).join(" ").trim();e.length>0&&(a=it(s.snapshot.nodes,e))}if(!a)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${l} not found`}};let c=iN(a,s.device.platform,{action:"get"});if("attrs"===e)return n.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:r,selectorChain:c}}),{ok:!0,data:{ref:r,node:a}};let d=il(a);return n.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:r,text:d,refLabel:d||void 0,selectorChain:c}}),{ok:!0,data:{ref:r,text:d,node:a}}}let c=t.positionals.slice(1).join(" ").trim();if(!c)return{ok:!1,error:{code:"INVALID_ARGS",message:"get requires @ref or selector expression"}};let d=ip(c),u=ih((await i8(s,t.flags,n,r,{interactiveOnly:!1},a)).nodes,d,{platform:s.device.platform,requireRect:!1,requireUnique:!0,disambiguateAmbiguous:"text"===e});if(!u)return{ok:!1,error:{code:"COMMAND_FAILED",message:ig(d,[],{unique:!0})}};let p=u.node,f=iN(p,s.device.platform,{action:"get"});if("attrs"===e)return n.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{selector:u.selector.raw,selectorChain:f}}),{ok:!0,data:{selector:u.selector.raw,node:p}};let m=il(p);return n.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{text:m,refLabel:m||void 0,selector:u.selector.raw,selectorChain:f}}),{ok:!0,data:{selector:u.selector.raw,text:m,node:p}}}if("is"===o){let e=(t.positionals?.[0]??"").toLowerCase();if(!["visible","hidden","exists","editable","selected","text"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"is requires predicate: visible|hidden|exists|editable|selected|text"}};let s=n.get(i);if(!s)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};if(!tq("is",s.device))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"is is not supported on this device"}};let{split:l}=iI(t.positionals);if(!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"is requires a selector expression"}};let c=l.rest.join(" ").trim();if("text"===e&&!c)return{ok:!1,error:{code:"INVALID_ARGS",message:"is text requires expected text value"}};if("text"!==e&&l.rest.length>0)return{ok:!1,error:{code:"INVALID_ARGS",message:`is ${e} does not accept trailing values`}};let d=ip(l.selectorExpression),u=await i8(s,t.flags,n,r,{interactiveOnly:!1},a);if("exists"===e){let i=iw(u.nodes,d,{platform:s.device.platform});return i?(n.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{predicate:e,selector:i.selector.raw,selectorChain:d.selectors.map(e=>e.raw),pass:!0,matches:i.matches}}),{ok:!0,data:{predicate:e,pass:!0,selector:i.selector.raw,matches:i.matches}}):{ok:!1,error:{code:"COMMAND_FAILED",message:ig(d,[],{unique:!1})}}}let p=ih(u.nodes,d,{platform:s.device.platform,requireUnique:!0});if(!p)return{ok:!1,error:{code:"COMMAND_FAILED",message:ig(d,[],{unique:!0})}};let f=function(e){let{predicate:t,node:i,expectedText:n,platform:r}=e,a=il(i),o=!1;switch(t){case"visible":o=iA(i);break;case"hidden":o=!iA(i);break;case"editable":o=iy(i,r);break;case"selected":o=!0===i.selected;break;case"text":o=a===(n??"")}let s="text"===t?`expected="${n??""}" actual="${a}"`:`actual=${JSON.stringify({visible:iA(i),editable:iy(i,r),selected:!0===i.selected})}`;return{pass:o,actualText:a,details:s}}({predicate:e,node:p.node,expectedText:c,platform:s.device.platform});return f.pass?(n.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{predicate:e,selector:p.selector.raw,selectorChain:d.selectors.map(e=>e.raw),pass:!0,text:"text"===e?f.actualText:void 0}}),{ok:!0,data:{predicate:e,pass:!0,selector:p.selector.raw}}):{ok:!1,error:{code:"COMMAND_FAILED",message:`is ${e} failed for selector ${p.selector.raw}: ${f.details}`}}}return null}async function i8(e,t,i,n,r,a=tF){let o=await a(e.device,"snapshot",[],t?.out,{...n({...t??{},snapshotInteractiveOnly:r.interactiveOnly,snapshotCompact:r.interactiveOnly},e.appBundleId,e.trace?.outPath)}),s=o?.nodes??[];return e.snapshot={nodes:t6(t?.snapshotRaw?s:ia(s)),truncated:o?.truncated,createdAt:Date.now(),backend:o?.backend},i.set(e.name,e),e.snapshot}let i6=[["snapshotDepth","--depth"],["snapshotScope","--scope"],["snapshotRaw","--raw"]];function i9(e,t){let i=function(e){if(!e)return[];let t=[];for(let[i,n]of i6)void 0!==e[i]&&t.push(n);return t}(t);return 0===i.length?null:{ok:!1,error:{code:"INVALID_ARGS",message:`${e} @ref does not support ${i.join(", ")}.`}}}let i7=n.join(g.homedir(),".agent-device"),ne=n.join(i7,"daemon.json"),nt=n.join(i7,"daemon.lock"),ni=n.join(i7,"daemon.log"),nn=new t0(n.join(i7,"sessions")),nr=c(),na=e.randomBytes(24).toString("hex"),no=new Set(["session_list","devices"]),ns=v(process.pid)??void 0;function nl(e,t,i){return t1(ni,e,t,i)}async function nc(e){var t;if(e.token!==na)return{ok:!1,error:{code:"UNAUTHORIZED",message:"Invalid token"}};let i="click"!==(t=e).command?t:{...t,command:"press"},n=i.command,r=function(e,t){var i;let n,r=e.session||"default";if(i=e,"string"==typeof(n=i.flags?.session)&&n.trim().length>0||"default"!==r||t.has(r))return r;let a=t.toArray();return 1===a.length?a[0].name:r}(i,nn),a=nn.get(r);a&&!no.has(n)&&function(e,t){if(!t)return;let i=[],n=e.device;if(t.platform&&t.platform!==n.platform&&i.push(`--platform=${t.platform}`),t.udid&&("ios"!==n.platform||t.udid!==n.id)&&i.push(`--udid=${t.udid}`),t.serial&&("android"!==n.platform||t.serial!==n.id)&&i.push(`--serial=${t.serial}`),0!==i.length){var r;let t,n,a;throw new h("INVALID_ARGS",`Session "${e.name}" is bound to ${(t=(r=e).device.platform,n=r.device.name.trim(),a=r.device.id,`${t} device "${n}" (${a})`)} and cannot be used with ${i.join(", ")}. Use a different --session name or close this session first.`)}}(a,i.flags);let o=await iG({req:i,sessionName:r,logPath:ni,sessionStore:nn,invoke:nc});if(o)return o;let s=await iY({req:i,sessionName:r,logPath:ni,sessionStore:nn});if(s)return s;let l=await i4({req:i,sessionName:r,sessionStore:nn});if(l)return l;let c=await i3({req:i,sessionName:r,logPath:ni,sessionStore:nn,invoke:nc});if(c)return c;let d=await i5({req:i,sessionName:r,sessionStore:nn,contextFromFlags:nl});if(d)return d;let u=nn.get(r);if(!u)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};if(!tq(n,u.device))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:`${n} is not supported on this device`}};let p=await tF(u.device,n,i.positionals??[],i.flags?.out,{...nl(i.flags,u.appBundleId,u.trace?.outPath)});return nn.recordAction(u,{command:n,positionals:i.positionals??[],flags:i.flags??{},result:p??{}}),{ok:!0,data:p??{}}}function nd(){if(!w.existsSync(nt))return null;try{let e=JSON.parse(w.readFileSync(nt,"utf8"));if(!Number.isInteger(e.pid)||e.pid<=0)return null;return e}catch{return null}}!function(){if(!function(){w.existsSync(i7)||w.mkdirSync(i7,{recursive:!0});let e=JSON.stringify({pid:process.pid,version:nr,startedAt:Date.now(),processStartTime:ns},null,2),t=()=>{try{return w.writeFileSync(nt,e,{flag:"wx",mode:384}),!0}catch(e){if("EEXIST"===e.code)return!1;throw e}};if(t())return!0;let i=nd();if(i?.pid&&i.pid!==process.pid&&s(i.pid,i.processStartTime))return!1;try{w.unlinkSync(nt)}catch{}return t()}()){process.stderr.write("Daemon lock is held by another process; exiting.\n"),process.exit(0);return}let e=I.createServer(e=>{let t="";e.setEncoding("utf8"),e.on("data",async i=>{let n=(t+=i).indexOf("\n");for(;-1!==n;){let i,r=t.slice(0,n).trim();if(t=t.slice(n+1),0===r.length){n=t.indexOf("\n");continue}try{let e=JSON.parse(r);i=await nc(e)}catch(t){let e=p(t);i={ok:!1,error:{code:e.code,message:e.message,details:e.details}}}e.write(`${JSON.stringify(i)}
|
|
17
|
-
`),
|
|
18
|
-
`)}});let t=!1,i=async()=>{await new Promise(t=>{try{e.close(()=>t())}catch{t()}})},
|
|
19
|
-
`),
|
|
12
|
+
${i}`.toLowerCase()).includes("device is busy")&&r.includes("connecting")?"Target iOS device is still connecting. Keep it unlocked, wait for device trust/connection to settle, then retry.":F("IOS_RUNNER_CONNECT_TIMEOUT"))})}async function t$(e,t,i,r,n=tc,a){let o=C.fromTimeoutMs(n),s=await tF(e,t,o.remainingMs()),l=null,c=Math.max(1,Math.ceil(n/tu));try{return await R(async({deadline:o})=>{if(o?.isExpired())throw new I("COMMAND_FAILED","Runner connection deadline exceeded",{port:t,timeoutMs:n});if(a&&null!==a.child.exitCode&&void 0!==a.child.exitCode)throw await tP({session:a,port:t,logPath:r});for(let r of("device"===e.kind&&(s=await tF(e,t,o?.remainingMs())),s))try{let e=o?.remainingMs()??n;if(e<=0)throw new I("COMMAND_FAILED","Runner connection deadline exceeded",{port:t,timeoutMs:n});return await tV(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)},Math.min(tm,e))}catch(e){l=e}throw new I("COMMAND_FAILED","Runner endpoint probe failed",{port:t,endpoints:s,lastError:l?String(l):void 0})},{maxAttempts:c,baseDelayMs:tp,maxDelayMs:tf,jitter:.2,shouldRetry:tT},{deadline:o,phase:"ios_runner_connect"})}catch(e){l||(l=e)}if("simulator"===e.kind){let n=o.remainingMs();if(n<=0)throw tR({port:t,endpoints:s,logPath:r,lastError:l});let a=await tG(e.id,t,i,n);return new Response(a.body,{status:a.status})}throw tR({port:t,endpoints:s,logPath:r,lastError:l})}async function tF(e,t,i){let r=[`http://127.0.0.1:${t}/command`];if("device"!==e.kind)return r;let n=await tU(e.id,i);return n&&r.unshift(`http://[${n}]:${t}/command`),r}async function tV(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 tU(e,t){if("number"==typeof t&&t<=0)return null;let r="number"==typeof t?Math.max(1,Math.min(th,t)):th,n=i.join(S.tmpdir(),`agent-device-devicectl-info-${process.pid}-${Date.now()}.json`);try{let t=Math.max(1,Math.ceil(r/1e3)),i=await u("xcrun",["devicectl","device","info","details","--device",e,"--json-output",n,"--timeout",String(t)],{allowFailure:!0,timeoutMs:r});if(0!==i.exitCode||!A.existsSync(n))return null;let a=JSON.parse(A.readFileSync(n,"utf8"));if(a.info?.outcome&&"success"!==a.info.outcome)return null;let o=(a.result?.connectionProperties?.tunnelIPAddress??a.result?.device?.connectionProperties?.tunnelIPAddress)?.trim();return o&&o.length>0?o:null}catch{return null}finally{tq(n)}}async function tG(e,t,i,r){let n=JSON.stringify(i),a=await u("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=$({message:"Runner did not accept connection (simctl spawn)",stdout:a.stdout,stderr:a.stderr,context:{platform:"ios",phase:"connect"}});throw new I("COMMAND_FAILED","Runner did not accept connection (simctl spawn)",{port:t,stdout:a.stdout,stderr:a.stderr,exitCode:a.exitCode,reason:e,hint:F(e)})}return{status:200,body:o}}async function tj(){return await new Promise((e,t)=>{let i=_.createServer();i.listen(0,"127.0.0.1",()=>{let r=i.address();i.close(),"object"==typeof r&&r?.port?e(r.port):t(new I("COMMAND_FAILED","Failed to allocate port"))}),i.on("error",t)})}async function tB(e,t,r){let n,a=i.dirname(e),o=r.replace(/[^a-zA-Z0-9._-]/g,"_"),s=i.join(a,`AgentDeviceRunner.env.${o}.json`),l=i.join(a,`AgentDeviceRunner.env.${o}.xctestrun`),c=await u("plutil",["-convert","json","-o","-",e],{allowFailure:!0});if(0!==c.exitCode||!c.stdout.trim())throw new I("COMMAND_FAILED","Failed to read xctestrun plist",{xctestrunPath:e,stderr:c.stderr});try{n=JSON.parse(c.stdout)}catch(t){throw new I("COMMAND_FAILED","Failed to parse xctestrun JSON",{xctestrunPath:e,error:String(t)})}let d=e=>{e.EnvironmentVariables={...e.EnvironmentVariables??{},...t},e.UITestEnvironmentVariables={...e.UITestEnvironmentVariables??{},...t},e.UITargetAppEnvironmentVariables={...e.UITargetAppEnvironmentVariables??{},...t},e.TestingEnvironmentVariables={...e.TestingEnvironmentVariables??{},...t}},p=n.TestConfigurations;if(Array.isArray(p))for(let e of p){if(!e||"object"!=typeof e)continue;let t=e.TestTargets;if(Array.isArray(t))for(let e of t)e&&"object"==typeof e&&d(e)}for(let[e,t]of Object.entries(n))t&&"object"==typeof t&&t.TestBundlePath&&(d(t),n[e]=t);A.writeFileSync(s,JSON.stringify(n,null,2));let f=await u("plutil",["-convert","xml1","-o",l,s],{allowFailure:!0});if(0!==f.exitCode)throw new I("COMMAND_FAILED","Failed to write xctestrun plist",{tmpXctestrunPath:l,stderr:f.stderr});return{xctestrunPath:l,jsonPath:s}}function tq(e){try{A.existsSync(e)&&A.unlinkSync(e)}catch{}}async function tW(e){return await p("resolve_target_device",async()=>{let t={platform:e.platform,deviceName:e.device,udid:e.udid,serial:e.serial};if("android"===t.platform){await eM();let e=await j();return await O(e,t)}if("ios"===t.platform){let e=await eG();return await O(e,t)}let i=[];try{i.push(...await j())}catch{}try{i.push(...await eG())}catch{}return await O(i,t)},{platform:e.platform})}async function tJ(e,t,r,n,a){let o=function(e,t){switch(e.platform){case"android":return{open:(t,i)=>es(e,t,i?.activity),openDevice:()=>ec(e),close:t=>ed(e,t),tap:(t,i)=>em(e,t,i),doubleTap:async(t,i)=>{await em(e,t,i),await em(e,t,i)},swipe:(t,i,r,n,a)=>eh(e,t,i,r,n,a),longPress:(t,i,r)=>eI(e,t,i,r),focus:(t,i)=>ey(e,t,i),type:t=>eA(e,t),fill:(t,i,r)=>eN(e,t,i,r),scroll:(t,i)=>eS(e,t,i),scrollIntoView:t=>eb(e,t),screenshot:t=>e_(e,t)};case"ios":var i,r;let n;return{open:(t,i)=>e1(e,t,{appBundleId:i?.appBundleId,url:i?.url}),openDevice:()=>e2(e),close:t=>e3(e,t),screenshot:t=>e6(e,t),...(i=e,n={verbose:(r=t).verbose,logPath:r.logPath,traceLogPath:r.traceLogPath},{tap:async(e,t)=>{await tv(i,{command:"tap",x:e,y:t,appBundleId:r.appBundleId},n)},doubleTap:async(e,t)=>{await tv(i,{command:"tapSeries",x:e,y:t,count:1,intervalMs:0,doubleTap:!0,appBundleId:r.appBundleId},n)},swipe:async(e,t,a,o,s)=>{await tv(i,{command:"drag",x:e,y:t,x2:a,y2:o,durationMs:s,appBundleId:r.appBundleId},n)},longPress:async(e,t,a)=>{await tv(i,{command:"longPress",x:e,y:t,durationMs:a,appBundleId:r.appBundleId},n)},focus:async(e,t)=>{await tv(i,{command:"tap",x:e,y:t,appBundleId:r.appBundleId},n)},type:async e=>{await tv(i,{command:"type",text:e,appBundleId:r.appBundleId},n)},fill:async(e,t,a)=>{await tv(i,{command:"tap",x:e,y:t,appBundleId:r.appBundleId},n),await tv(i,{command:"type",text:a,clearFirst:!0,appBundleId:r.appBundleId},n)},scroll:async(e,t)=>{if(!["up","down","left","right"].includes(e))throw new I("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 tv(i,{command:"swipe",direction:a,appBundleId:r.appBundleId},n)},scrollIntoView:async e=>{for(let t=0;t<8;t+=1){let a=await tv(i,{command:"findText",text:e,appBundleId:r.appBundleId},n);if(a?.found)return{attempts:t+1};await tv(i,{command:"swipe",direction:"up",appBundleId:r.appBundleId},n),await new Promise(e=>setTimeout(e,300))}throw new I("COMMAND_FAILED",`scrollintoview could not find text: ${e}`)}})};default:throw new I("UNSUPPORTED_PLATFORM",`Unsupported platform: ${e.platform}`)}}(e,{appBundleId:a?.appBundleId,verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath});return m({level:"debug",phase:"platform_command_prepare",data:{command:t,platform:e.platform,kind:e.kind}}),await p("platform_command",async()=>{var s,l,c,d,u,f;switch(t){case"open":{let t=r[0],i=r[1];if(r.length>2)throw new I("INVALID_ARGS","open accepts at most two arguments: <app|url> [url]");if(!t)return await o.openDevice(),{app:null};if(void 0!==i){if("ios"!==e.platform)throw new I("INVALID_ARGS","open <app> <url> is supported only on iOS");if(W(t))throw new I("INVALID_ARGS","open <app> <url> requires an app target as the first argument");if(!W(i))throw new I("INVALID_ARGS","open <app> <url> requires a valid URL target");return await o.open(t,{activity:a?.activity,appBundleId:a?.appBundleId,url:i}),{app:t,url:i}}return await o.open(t,{activity:a?.activity,appBundleId:a?.appBundleId}),{app:t}}case"close":{let e=r[0];if(!e)return{closed:"session"};return await o.close(e),{app:e}}case"press":{let[t,i]=r.map(Number);if(Number.isNaN(t)||Number.isNaN(i))throw new I("INVALID_ARGS","press requires x y");let n=tz(a?.count??1,"count",1,200),u=tz(a?.intervalMs??0,"interval-ms",0,1e4),p=tz(a?.holdMs??0,"hold-ms",0,1e4),f=tz(a?.jitterPx??0,"jitter-px",0,100),m=a?.doubleTap===!0;if(m&&p>0)throw new I("INVALID_ARGS","double-tap cannot be combined with hold-ms");if(m&&f>0)throw new I("INVALID_ARGS","double-tap cannot be combined with jitter-px");if(s=e,l=n,c=p,d=f,"ios"===s.platform&&l>1&&0===c&&0===d)return await tv(e,{command:"tapSeries",x:t,y:i,count:n,intervalMs:u,doubleTap:m,appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{x:t,y:i,count:n,intervalMs:u,holdMs:p,jitterPx:f,doubleTap:m,timingMode:"runner-series"};return await tX(n,u,async e=>{let[r,n]=function(e,t){if(t<=0)return[0,0];let[i,r]=tH[e%tH.length];return[i*t,r*t]}(e,f),a=t+r,s=i+n;m?await o.doubleTap(a,s):p>0?await o.longPress(a,s,p):await o.tap(a,s)}),{x:t,y:i,count:n,intervalMs:u,holdMs:p,jitterPx:f,doubleTap:m}}case"swipe":{let t=Number(r[0]),i=Number(r[1]),n=Number(r[2]),s=Number(r[3]);if([t,i,n,s].some(Number.isNaN))throw new I("INVALID_ARGS","swipe requires x1 y1 x2 y2 [durationMs]");let l=tz(r[4]?Number(r[4]):250,"durationMs",16,1e4),c="ios"===e.platform?60:l,d=tz(a?.count??1,"count",1,200),p=tz(a?.pauseMs??0,"pause-ms",0,1e4),m=a?.pattern??"one-way";if("one-way"!==m&&"ping-pong"!==m)throw new I("INVALID_ARGS",`Invalid pattern: ${m}`);if(u=e,f=d,"ios"===u.platform&&f>1)return await tv(e,{command:"dragSeries",x:t,y:i,x2:n,y2:s,durationMs:c,count:d,pauseMs:p,pattern:m,appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{x1:t,y1:i,x2:n,y2:s,durationMs:l,effectiveDurationMs:c,timingMode:"runner-series",count:d,pauseMs:p,pattern:m};return await tX(d,p,async e=>{"ping-pong"===m&&e%2==1?await o.swipe(n,s,t,i,c):await o.swipe(t,i,n,s,c)}),{x1:t,y1:i,x2:n,y2:s,durationMs:l,effectiveDurationMs:c,timingMode:"ios"===e.platform?"safe-normalized":"direct",count:d,pauseMs:p,pattern:m}}case"long-press":{let e=Number(r[0]),t=Number(r[1]),i=r[2]?Number(r[2]):void 0;if(Number.isNaN(e)||Number.isNaN(t))throw new I("INVALID_ARGS","long-press requires x y [durationMs]");return await o.longPress(e,t,i),{x:e,y:t,durationMs:i}}case"focus":{let[e,t]=r.map(Number);if(Number.isNaN(e)||Number.isNaN(t))throw new I("INVALID_ARGS","focus requires x y");return await o.focus(e,t),{x:e,y:t}}case"type":{let e=r.join(" ");if(!e)throw new I("INVALID_ARGS","type requires text");return await o.type(e),{text:e}}case"fill":{let e=Number(r[0]),t=Number(r[1]),i=r.slice(2).join(" ");if(Number.isNaN(e)||Number.isNaN(t)||!i)throw new I("INVALID_ARGS","fill requires x y text");return await o.fill(e,t,i),{x:e,y:t,text:i}}case"scroll":{let e=r[0],t=r[1]?Number(r[1]):void 0;if(!e)throw new I("INVALID_ARGS","scroll requires direction");return await o.scroll(e,t),{direction:e,amount:t}}case"scrollintoview":{let e=r.join(" ").trim();if(!e)throw new I("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 I("UNSUPPORTED_OPERATION","Android pinch is not supported in current adb backend; requires instrumentation-based backend.");let t=Number(r[0]),i=r[1]?Number(r[1]):void 0,n=r[2]?Number(r[2]):void 0;if(Number.isNaN(t)||t<=0)throw new I("INVALID_ARGS","pinch requires scale > 0");return await tv(e,{command:"pinch",scale:t,x:i,y:n,appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{scale:t,x:i,y:n}}case"screenshot":{let e=r[0]??n??`./screenshot-${Date.now()}.png`;return await h.mkdir(i.dirname(e),{recursive:!0}),await o.screenshot(e),{path:e}}case"back":if("ios"===e.platform)return await tv(e,{command:"back",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{action:"back"};return await ew(e),{action:"back"};case"home":if("ios"===e.platform)return await tv(e,{command:"home",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{action:"home"};return await eg(e),{action:"home"};case"app-switcher":if("ios"===e.platform)return await tv(e,{command:"appSwitcher",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{action:"app-switcher"};return await ev(e),{action:"app-switcher"};case"settings":{let[t,i,n]=r;if(m({level:"debug",phase:"settings_apply",data:{setting:t,state:i,platform:e.platform}}),"ios"===e.platform)return await e7(e,t,i,n??a?.appBundleId),{setting:t,state:i};return await eD(e,t,i),{setting:t,state:i}}case"snapshot":{if("ios"===e.platform){let t=await p("snapshot_capture",async()=>await tv(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}),{backend:"xctest"}),i=t.nodes??[];if(0===i.length&&"simulator"===e.kind)throw new I("COMMAND_FAILED","XCTest snapshot returned 0 nodes on iOS simulator.");return{nodes:i,truncated:t.truncated??!1,backend:"xctest"}}let t=await p("snapshot_capture",async()=>await eO(e,{interactiveOnly:a?.snapshotInteractiveOnly,compact:a?.snapshotCompact,depth:a?.snapshotDepth,scope:a?.snapshotScope,raw:a?.snapshotRaw}),{backend:"android"});return{nodes:t.nodes??[],truncated:t.truncated??!1,backend:"android"}}default:throw new I("INVALID_ARGS",`Unknown command: ${t}`)}},{command:t,platform:e.platform})}let tH=[[0,0],[1,0],[0,1],[-1,0],[0,-1],[1,1],[-1,1],[1,-1],[-1,-1]];function tz(e,t,i,r){if(!Number.isFinite(e)||!Number.isInteger(e)||e<i||e>r)throw new I("INVALID_ARGS",`${t} must be an integer between ${i} and ${r}`);return e}async function tX(e,t,i){for(let r=0;r<e;r+=1)await i(r),r<e-1&&t>0&&await tY(t)}async function tY(e){await new Promise(t=>setTimeout(t,e))}let tK={alert:{ios:{simulator:!0},android:{}},pinch:{ios:{simulator:!0},android:{}},"app-switcher":{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},apps:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},back:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},boot:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},click:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},close:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},fill:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},find:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},focus:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},get:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},is:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},home:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},"long-press":{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},open:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},reinstall:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},press:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},record:{ios:{simulator:!0},android:{emulator:!0,device:!0,unknown:!0}},screenshot:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},scroll:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},scrollintoview:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},swipe:{ios:{simulator:!0},android:{emulator:!0,device:!0,unknown:!0}},settings:{ios:{simulator:!0},android:{emulator:!0,device:!0,unknown:!0}},snapshot:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},type:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},wait:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}}};function tZ(e,t){let i=tK[e];if(!i)return!0;let r=i[t.platform];return!!r&&!0===r[t.kind??"unknown"]}function tQ(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 t0(e){let t=new Set,i=[];for(let r of e)t.has(r)||(t.add(r),i.push(r));return i}let t1=/^-?\d+(\.\d+)?$/,t2=new Map([["--count","count"],["--interval-ms","intervalMs"],["--hold-ms","holdMs"],["--jitter-px","jitterPx"]]),t3=new Map([["--count","count"],["--pause-ms","pauseMs"]]);function t4(e){return"click"===e||"press"===e}function t5(e){let t=e.trim();return t.startsWith("@")||t1.test(t)?t:JSON.stringify(t)}function t8(e,t){let i=t.flags??{};if(t4(t.command)){"number"==typeof i.count&&e.push("--count",String(i.count)),"number"==typeof i.intervalMs&&e.push("--interval-ms",String(i.intervalMs)),"number"==typeof i.holdMs&&e.push("--hold-ms",String(i.holdMs)),"number"==typeof i.jitterPx&&e.push("--jitter-px",String(i.jitterPx)),!0===i.doubleTap&&e.push("--double-tap");return}"swipe"===t.command&&("number"==typeof i.count&&e.push("--count",String(i.count)),"number"==typeof i.pauseMs&&e.push("--pause-ms",String(i.pauseMs)),("one-way"===i.pattern||"ping-pong"===i.pattern)&&e.push("--pattern",i.pattern))}function t6(e,t){let i=[],r={},n=t4(e)?t2:"swipe"===e?t3:void 0;for(let a=0;a<t.length;a+=1){let o=t[a];if(t4(e)&&"--double-tap"===o){r.doubleTap=!0;continue}let s=n?.get(o);if(s&&a+1<t.length){let e=function(e){if(!e)return null;let t=Number(e);return!Number.isFinite(t)||t<0?null:Math.floor(t)}(t[a+1]);null!==e&&(r[s]=e),a+=1;continue}if("swipe"===e&&"--pattern"===o&&a+1<t.length){let e=t[a+1];("one-way"===e||"ping-pong"===e)&&(r.pattern=e),a+=1;continue}i.push(o)}return{positionals:i,flags:r}}class t7{sessions=new Map;sessionsDir;constructor(e){this.sessionsDir=e}get(e){return this.sessions.get(e)}has(e){return this.sessions.has(e)}set(e,t){this.sessions.set(e,t)}delete(e){return this.sessions.delete(e)}values(){return this.sessions.values()}toArray(){return Array.from(this.sessions.values())}recordAction(e,t){t.flags?.noRecord||(t.flags?.saveScript&&(e.recordSession=!0,"string"==typeof t.flags.saveScript&&(e.saveScriptPath=t7.expandHome(t.flags.saveScript))),e.actions.push({ts:Date.now(),command:t.command,positionals:t.positionals,flags:function(e){if(!e)return{};let{platform:t,device:i,udid:r,serial:n,out:a,verbose:o,snapshotInteractiveOnly:s,snapshotCompact:l,snapshotDepth:c,snapshotScope:d,snapshotRaw:u,relaunch:p,saveScript:f,noRecord:m,count:h,intervalMs:w,holdMs:g,jitterPx:v,doubleTap:I,pauseMs:A,pattern:y}=e;return{platform:t,device:i,udid:r,serial:n,out:a,verbose:o,snapshotInteractiveOnly:s,snapshotCompact:l,snapshotDepth:c,snapshotScope:d,snapshotRaw:u,relaunch:p,saveScript:f,noRecord:m,count:h,intervalMs:w,holdMs:g,jitterPx:v,doubleTap:I,pauseMs:A,pattern:y}}(t.flags),result:t.result}),m({level:"debug",phase:"record_action",data:{command:t.command,session:e.name}}))}writeSessionLog(e){try{if(!e.recordSession)return;let t=this.resolveScriptPath(e),r=i.dirname(t);A.existsSync(r)||A.mkdirSync(r,{recursive:!0});let n=function(e,t){let i=[],r=e.device.name.replace(/"/g,'\\"'),n=e.device.kind?` kind=${e.device.kind}`:"";for(let a of(i.push(`context platform=${e.device.platform} device="${r}"${n} theme=unknown`),t))a.flags?.noRecord||i.push(function(e){let t=[e.command];if(t4(e.command)){let i=e.positionals?.[0];if(i){if(i.startsWith("@")){t.push(t5(i));let r=e.result?.refLabel;return"string"==typeof r&&r.trim().length>0&&t.push(t5(r)),t8(t,e),t.join(" ")}if(1===e.positionals.length)return t.push(t5(i)),t8(t,e),t.join(" ")}}if("fill"===e.command){let i=e.positionals?.[0];if(i&&i.startsWith("@")){t.push(t5(i));let r=e.result?.refLabel,n=e.positionals.slice(1).join(" ");return"string"==typeof r&&r.trim().length>0&&t.push(t5(r)),n&&t.push(t5(n)),t.join(" ")}}if("get"===e.command){let i=e.positionals?.[0],r=e.positionals?.[1];if(i&&r){if(t.push(t5(i)),t.push(t5(r)),r.startsWith("@")){let i=e.result?.refLabel;"string"==typeof i&&i.trim().length>0&&t.push(t5(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",t5(e.flags.snapshotScope)),e.flags?.snapshotRaw&&t.push("--raw"),t.join(" ");if("open"===e.command){for(let i of e.positionals??[])t.push(t5(i));return e.flags?.relaunch&&t.push("--relaunch"),t.join(" ")}for(let i of e.positionals??[])t.push(t5(i));return t8(t,e),t.join(" ")}(a));return`${i.join("\n")}
|
|
13
|
+
`}(e,this.buildOptimizedActions(e));A.writeFileSync(t,n)}catch{}}defaultTracePath(e){let t=e.name.replace(/[^a-zA-Z0-9._-]/g,"_"),r=new Date().toISOString().replace(/[:.]/g,"-");return i.join(this.sessionsDir,`${t}-${r}.trace.log`)}static expandHome(e){return e.startsWith("~/")?i.join(S.homedir(),e.slice(2)):i.resolve(e)}resolveScriptPath(e){if(e.saveScriptPath)return t7.expandHome(e.saveScriptPath);A.existsSync(this.sessionsDir)||A.mkdirSync(this.sessionsDir,{recursive:!0});let t=e.name.replace(/[^a-zA-Z0-9._-]/g,"_"),r=new Date(e.createdAt).toISOString().replace(/[:.]/g,"-");return i.join(this.sessionsDir,`${t}-${r}.ad`)}buildOptimizedActions(e){let t=[];for(let i of e.actions){if("snapshot"===i.command)continue;let r=Array.isArray(i.result?.selectorChain)&&i.result?.selectorChain.every(e=>"string"==typeof e)?i.result.selectorChain:[];if(r.length>0&&(t4(i.command)||"fill"===i.command||"get"===i.command)){let e=r.join(" || ");if(t4(i.command)){t.push({...i,positionals:[e]});continue}if("fill"===i.command){let r=tQ(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(t4(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}}function t9(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,count:t?.count,intervalMs:t?.intervalMs,holdMs:t?.holdMs,jitterPx:t?.jitterPx,doubleTap:t?.doubleTap,pauseMs:t?.pauseMs,pattern:t?.pattern}}let ie=eV(process.env.AGENT_DEVICE_IOS_DEVICE_READY_TIMEOUT_MS,15e3,1e3);async function it(e){if("ios"===e.platform){if("simulator"===e.kind){let{ensureBootedSimulator:t}=await Promise.resolve().then(()=>({ensureBootedSimulator:eY}));await t(e);return}if("device"===e.kind)return void await ii(e.id)}if("android"===e.platform){let{waitForAndroidBoot:t}=await Promise.resolve().then(()=>({waitForAndroidBoot:q}));await t(e.id)}}async function ii(e){let t=i.join(S.tmpdir(),`agent-device-ready-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`),r=Math.max(1,Math.ceil(ie/1e3));try{let i=await u("xcrun",["devicectl","device","info","details","--device",e,"--json-output",t,"--timeout",String(r)],{allowFailure:!0,timeoutMs:ie+3e3}),n=String(i.stdout??""),a=String(i.stderr??""),o=await ir(t);if(0===i.exitCode){if(!o.parsed)throw new I("COMMAND_FAILED","iOS device readiness probe failed",{kind:"probe_inconclusive",deviceId:e,stdout:n,stderr:a,hint:"CoreDevice returned success but readiness JSON output was missing or invalid. Retry; if it persists restart Xcode and the iOS device."});let t=o?.tunnelState?.toLowerCase();if("connecting"===t)throw new I("COMMAND_FAILED","iOS device is not ready for automation",{kind:"not_ready",deviceId:e,tunnelState:t,hint:"Device tunnel is still connecting. Keep the device unlocked and connected by cable until it is fully available in Xcode Devices, then retry."});return}throw new I("COMMAND_FAILED","iOS device is not ready for automation",{kind:"not_ready",deviceId:e,stdout:n,stderr:a,exitCode:i.exitCode,tunnelState:o?.tunnelState,hint:ia(n,a)})}catch(t){if(t instanceof I&&"COMMAND_FAILED"===t.code){if("not_ready"===("string"==typeof t.details?.kind?t.details.kind:""))throw t;let i=t.details??{},r=String(i.stdout??""),n=String(i.stderr??""),a=Number(i.timeoutMs??ie),o=`CoreDevice did not respond within ${a}ms. Keep the device unlocked and trusted, then retry; if it persists restart Xcode and the iOS device.`;throw new I("COMMAND_FAILED","iOS device readiness probe failed",{deviceId:e,cause:t.message,timeoutMs:a,stdout:r,stderr:n,hint:r||n?ia(r,n):o},t)}throw new I("COMMAND_FAILED","iOS device readiness probe failed",{deviceId:e,hint:"Reconnect the device, keep it unlocked, and retry."},t instanceof Error?t:void 0)}finally{await h.rm(t,{force:!0}).catch(()=>{})}}async function ir(e){try{let t=await h.readFile(e,"utf8"),i=JSON.parse(t),r=function(e){let t=e?.result;if(!t||"object"!=typeof t)return{};let i=t.connectionProperties?.tunnelState,r=t.device?.connectionProperties?.tunnelState,n="string"==typeof i?i:"string"==typeof r?r:void 0;return n?{tunnelState:n}:{}}(i);return{parsed:!0,tunnelState:r.tunnelState}}catch{return{parsed:!1}}}function ia(e,t){let i=eX(e,t);return i||(`${e}
|
|
14
|
+
${t}`.toLowerCase().includes("timed out waiting for all destinations")?"Xcode destination did not become available in time. Keep device unlocked and retry.":ez)}function io(e){return e.map((e,t)=>({...e,ref:`e${t+1}`}))}function is(e){let t=e.trim();return t.startsWith("@")?t.slice(1)||null:t.startsWith("e")?t:null}function il(e,t){return e.find(e=>e.ref===t)??null}function ic(e){return{x:Math.round(e.x+e.width/2),y:Math.round(e.y+e.height/2)}}function id(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 iu(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&&ip(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||!ip(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&&ip(i)?i:void 0)}function ip(e){let t=e.trim();return!(!t||/^(true|false)$/i.test(t)||/^\d+$/.test(t))}function im(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=ih(r.type??""),a=[r.label,r.value,r.identifier].map(e=>"string"==typeof e?e.trim():"").find(e=>e&&e.length>0),o=!!a&&ip(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 ih(e){let t=e.trim().replace(/XCUIElementType/gi,"").toLowerCase(),i=Math.max(t.lastIndexOf("."),t.lastIndexOf("/"));return -1!==i&&(t=t.slice(i+1)),t}function iw(e,t){let i=ih(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 ig(e){return[e.label,e.value,e.identifier].map(e=>"string"==typeof e?e.trim():"").filter(e=>e.length>0)[0]??""}let iv=new Set(["id","role","text","label","value"]),iI=new Set(["visible","hidden","editable","selected","enabled","hittable"]),iA=new Set([...iv,...iI]);function iy(e){let t=e.trim();if(!t)throw new I("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)&&!iV(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 I("INVALID_ARGS",`Invalid selector fallback expression: ${e}`);t.push(r),i="",n+=1;continue}i+=a}let n=i.trim();if(!n)throw new I("INVALID_ARGS",`Invalid selector fallback expression: ${e}`);return t.push(n),t}(t);if(0===i.length)throw new I("INVALID_ARGS","Selector expression cannot be empty");return{raw:t,selectors:i.map(e=>(function(e){let t=e.trim();if(!t)throw new I("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)&&!iV(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 I("INVALID_ARGS",`Unclosed quote in selector: ${e}`);return i.trim().length>0&&t.push(i.trim()),t}(t);if(0===i.length)throw new I("INVALID_ARGS",`Invalid selector segment: ${e}`);return{raw:t,terms:i.map(ik)}})(e))}}function iN(e){try{return iy(e)}catch{return null}}function iS(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||!iL(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=iF(e),a=iF(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 ib(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)&&iL(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 i_(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 iD(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 iA.has(e)}return iA.has(t.toLowerCase())}(e[r]);){r+=1;let t=e.slice(0,r).join(" ").trim();t&&iN(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 iO(e){let t=e[0]??"",i=iD(e.slice(1),{preferTrailingValue:"text"===t});return{predicate:t,split:i}}function iM(e){return!0===e.hittable||!!e.rect&&e.rect.width>0&&e.rect.height>0}function iE(e,t){return iw(e.type??"",t)&&!1!==e.enabled}function ix(e,t,i={}){let r=[],n=ih(e.type??""),a=i$(e.identifier),o=i$(e.label),s=i$(e.value),l=i$(ig(e)),c="fill"===i.action;a&&r.push(`id=${iP(a)}`),n&&o&&r.push(c?`role=${iP(n)} label=${iP(o)} editable=true`:`role=${iP(n)} label=${iP(o)}`),o&&r.push(c?`label=${iP(o)} editable=true`:`label=${iP(o)}`),s&&r.push(c?`value=${iP(s)} editable=true`:`value=${iP(s)}`),l&&l!==o&&l!==s&&r.push(c?`text=${iP(l)} editable=true`:`text=${iP(l)}`),n&&c&&!r.some(e=>e.includes("editable=true"))&&r.push(`role=${iP(n)} editable=true`);let d=t0(r);return 0===d.length&&n&&d.push(c?`role=${iP(n)} editable=true`:`role=${iP(n)}`),0===d.length&&iM(e)&&d.push("visible=true"),d}function ik(e){let t=e.trim();if(!t)throw new I("INVALID_ARGS","Empty selector term");let i=t.indexOf("=");if(-1===i){let i=t.toLowerCase();if(!iI.has(i))throw new I("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(!iA.has(r))throw new I("INVALID_ARGS",`Unknown selector key: ${r}`);if(!n)throw new I("INVALID_ARGS",`Missing selector value for key: ${r}`);if(iI.has(r)){let e,t="true"===(e=iC(n).toLowerCase())||"false"!==e&&null;if(null===t)throw new I("INVALID_ARGS",`Invalid boolean value for ${r}: ${n}`);return{key:r,value:t}}return{key:r,value:iC(n)}}function iL(e,t,i){return t.terms.every(t=>(function(e,t,i){switch(t.key){case"id":return iR(e.identifier,String(t.value));case"role":var r,n;return r=e.type,n=String(t.value),function(e){return ih(e)}(r??"")===function(e){return ih(e)}(n);case"label":return iR(e.label,String(t.value));case"value":return iR(e.value,String(t.value));case"text":{let i=iT(String(t.value));return iT(ig(e))===i}case"visible":return iM(e)===!!t.value;case"hidden":return!iM(e)==!!t.value;case"editable":return iE(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 iC(e){let t=e.trim();return t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1).replace(/\\(["'])/g,"$1"):t}function iR(e,t){return iT(e??"")===iT(t)}function iT(e){return e.trim().toLowerCase().replace(/\s+/g," ")}function iP(e){return JSON.stringify(e)}function i$(e){if(!e)return null;let t=e.trim();return t||null}function iF(e){return e.rect?e.rect.width*e.rect.height:1/0}function iV(e,t){let i=0;for(let r=t-1;r>=0&&"\\"===e[r];r-=1)i+=1;return i%2==1}let iU='iOS appstate requires an active session on the target device. Run open first (for example: open --session sim --platform ios --device "<name>" <app>).',iG=["platform","device","udid","serial","verbose","out"];function ij(e,t,i){return t||iB(i)?null:{ok:!1,error:{code:"INVALID_ARGS",message:`${e} requires an active session or an explicit device selector (e.g. --platform ios).`}}}function iB(e){return!!(e?.platform||e?.device||e?.udid||e?.serial)}async function iq(e){let t=iB(e.flags)||!e.session?await e.resolveTargetDeviceFn(e.flags??{}):e.session.device;return!1!==e.ensureReady&&await e.ensureReadyFn(t),t}let iW={ios:async(e,t,i)=>{let{reinstallIosApp:r}=await Promise.resolve().then(()=>({reinstallIosApp:e8}));return await r(e,t,i)},android:async(e,t,i)=>{let{reinstallAndroidApp:r}=await Promise.resolve().then(()=>({reinstallAndroidApp:ef}));return await r(e,t,i)}};async function iJ(e,t,i){if("ios"===e.platform&&t)return W(t)?"device"===e.kind?J(i,t):void 0:await iH(e,t)}async function iH(e,t){try{let{resolveIosApp:i}=await Promise.resolve().then(()=>({resolveIosApp:e0}));return await i(e,t)}catch{return}}async function iz(e){let{req:t,sessionName:i,sessionStore:r,ensureReady:n,resolveDevice:a}=e,o=r.get(i),s=t.flags??{};if(!o&&"string"==typeof s?.session&&s.session.trim().length>0)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"ios"===s.platform?`No active session "${i}". Run open with --session ${i} first.`:`No active session "${i}". Run open with --session ${i} first, or omit --session to query by device selector.`}};let l=ij("appstate",o,s);if(l)return l;let c=o?.device.platform==="ios"&&!!o&&(!iB(s)||!(s?.platform&&s.platform!==o.device.platform||s?.udid&&s.udid!==o.device.id||s?.serial&&s.serial!==o.device.id)&&(!s?.device||s.device.trim().toLowerCase()===o.device.name.trim().toLowerCase()));if("ios"===s.platform&&!c)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:iU}};if(c){let e=o.appName??o.appBundleId;return o.appName||o.appBundleId?{ok:!0,data:{platform:"ios",appName:e??"unknown",appBundleId:o.appBundleId,source:"session"}}:{ok:!1,error:{code:"COMMAND_FAILED",message:"No foreground app is tracked for this iOS session. Open an app in the session, then retry appstate."}}}let d=await iq({session:o,flags:s,ensureReadyFn:n,resolveTargetDeviceFn:a,ensureReady:!0});if("ios"===d.platform)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:iU}};let{getAndroidAppState:u}=await Promise.resolve().then(()=>({getAndroidAppState:ea})),p=await u(d);return{ok:!0,data:{platform:"android",package:p.package,activity:p.activity}}}async function iX(e){let{req:t,sessionName:i,logPath:r,sessionStore:n,invoke:a,dispatch:o,ensureReady:s,resolveTargetDevice:l,reinstallOps:c=iW}=e,d=o??tJ,u=s??it,p=l??tW,f=t.command;if("session_list"===f)return{ok:!0,data:{sessions:n.toArray().map(e=>({name:e.name,platform:e.device.platform,device:e.device.name,id:e.device.id,createdAt:e.createdAt}))}};if("devices"===f)try{let e=[];if(t.flags?.platform==="android"){let{listAndroidDevices:t}=await Promise.resolve().then(()=>({listAndroidDevices:j}));e.push(...await t())}else if(t.flags?.platform==="ios"){let{listIosDevices:t}=await Promise.resolve().then(()=>({listIosDevices:eG}));e.push(...await t())}else{let{listAndroidDevices:t}=await Promise.resolve().then(()=>({listAndroidDevices:j})),{listIosDevices:i}=await Promise.resolve().then(()=>({listIosDevices:eG}));try{e.push(...await t())}catch{}try{e.push(...await i())}catch{}}return{ok:!0,data:{devices:e}}}catch(t){let e=w(t);return{ok:!1,error:{code:e.code,message:e.message,details:e.details}}}if("apps"===f){let e=n.get(i),r=t.flags??{},a=ij(f,e,r);if(a)return a;let o=await iq({session:e,flags:r,ensureReadyFn:u,resolveTargetDeviceFn:p,ensureReady:!0});if(!tZ("apps",o))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"apps is not supported on this device"}};let s=t.flags?.appsFilter??"all";if("ios"===o.platform){let{listIosApps:e}=await Promise.resolve().then(()=>({listIosApps:e9}));return{ok:!0,data:{apps:(await e(o,s)).map(e=>e.name&&e.name!==e.bundleId?`${e.name} (${e.bundleId})`:e.bundleId)}}}let{listAndroidApps:l}=await Promise.resolve().then(()=>({listAndroidApps:ei}));return{ok:!0,data:{apps:(await l(o,s)).map(e=>e.name&&e.name!==e.package?`${e.name} (${e.package})`:e.package)}}}if("boot"===f){let e=n.get(i),r=t.flags??{},a=ij(f,e,r);if(a)return a;let o=await iq({session:e,flags:r,ensureReadyFn:u,resolveTargetDeviceFn:p,ensureReady:!0});return tZ("boot",o)?{ok:!0,data:{platform:o.platform,device:o.name,id:o.id,kind:o.kind,booted:!0}}:{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"boot is not supported on this device"}}}if("appstate"===f)return await iz({req:t,sessionName:i,sessionStore:n,ensureReady:u,resolveDevice:p});if("reinstall"===f){let e,r=n.get(i),a=t.flags??{},o=ij(f,r,a);if(o)return o;let s=t.positionals?.[0]?.trim(),l=t.positionals?.[1]?.trim();if(!s||!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"reinstall requires: reinstall <app> <path-to-app-binary>"}};let d=t7.expandHome(l);if(!A.existsSync(d))return{ok:!1,error:{code:"INVALID_ARGS",message:`App binary not found: ${d}`}};let m=await iq({session:r,flags:a,ensureReadyFn:u,resolveTargetDeviceFn:p,ensureReady:!1});if(!tZ("reinstall",m))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"reinstall is not supported on this device"}};if("ios"===m.platform){let t=await c.ios(m,s,d);e={platform:"ios",appId:t.bundleId,bundleId:t.bundleId}}else{let t=await c.android(m,s,d);e={platform:"android",appId:t.package,package:t.package}}let h={app:s,appPath:d,...e};return r&&n.recordAction(r,{command:f,positionals:t.positionals??[],flags:t.flags??{},result:h}),{ok:!0,data:h}}if("open"===f){let e=t.flags?.relaunch===!0;if(n.has(i)){let a=n.get(i),o=t.positionals?.[0],s=o??(e?a?.appName:void 0);if(!a||!s)return e?{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch requires an app name or an active session app."}}:{ok:!1,error:{code:"INVALID_ARGS",message:"Session already active. Close it first or pass a new --session name."}};if(e&&W(s))return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch does not support URL targets."}};await u(a.device);let l=await iJ(a.device,s,a.appBundleId),c=o?t.positionals??[]:[s];if(e){let e=l??s;await d(a.device,"close",[e],t.flags?.out,{...t9(r,t.flags,l??a.appBundleId,a.trace?.outPath)})}await d(a.device,"open",c,t.flags?.out,{...t9(r,t.flags,l)});let p={...a,appBundleId:l,appName:s,recordSession:a.recordSession||!!t.flags?.saveScript,snapshot:void 0};return n.recordAction(p,{command:f,positionals:c,flags:t.flags??{},result:{session:i,appName:s,appBundleId:l}}),n.set(i,p),{ok:!0,data:{session:i,appName:s,appBundleId:l}}}let a=t.positionals?.[0];if(e&&!a)return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch requires an app argument."}};if(e&&a&&W(a))return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch does not support URL targets."}};let o=await p(t.flags??{}),s=n.toArray().find(e=>e.device.id===o.id);if(s)return{ok:!1,error:{code:"DEVICE_IN_USE",message:`Device is already in use by session "${s.name}".`,details:{session:s.name,deviceId:o.id,deviceName:o.name}}};await u(o);let l=await iJ(o,a);if(e&&a){let e=l??a;await d(o,"close",[e],t.flags?.out,{...t9(r,t.flags,l)})}await d(o,"open",t.positionals??[],t.flags?.out,{...t9(r,t.flags,l)});let c={name:i,device:o,createdAt:Date.now(),appBundleId:l,appName:a,recordSession:!!t.flags?.saveScript,actions:[]};return n.recordAction(c,{command:f,positionals:t.positionals??[],flags:t.flags??{},result:{session:i}}),n.set(i,c),{ok:!0,data:{session:i}}}if("replay"===f){let e=t.positionals?.[0];if(!e)return{ok:!1,error:{code:"INVALID_ARGS",message:"replay requires a path"}};try{let o=t7.expandHome(e),s=A.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 I("INVALID_ARGS",`Invalid replay script line: ${e}`);let a=e.slice(i,r+1);t.push(JSON.parse(a)),i=r+1;continue}let r=i;for(;r<e.length&&!/\s/.test(e[r]);)r+=1;t.push(e.slice(i,r)),i=r}return t}(t);if(0===i.length)return null;let[r,...n]=i;if("context"===r)return null;let a={ts:Date.now(),command:r,positionals:[],flags:{}};if("snapshot"===r){a.positionals=[];for(let e=0;e<n.length;e+=1){let t=n[e];if("-i"===t){a.flags.snapshotInteractiveOnly=!0;continue}if("-c"===t){a.flags.snapshotCompact=!0;continue}if("--raw"===t){a.flags.snapshotRaw=!0;continue}if(("-d"===t||"--depth"===t)&&e+1<n.length){let t=Number(n[e+1]);Number.isFinite(t)&&t>=0&&(a.flags.snapshotDepth=Math.floor(t)),e+=1;continue}if(("-s"===t||"--scope"===t)&&e+1<n.length){a.flags.snapshotScope=n[e+1],e+=1;continue}if("--backend"===t&&e+1<n.length){e+=1;continue}}return a}if("open"===r){a.positionals=[];for(let e=0;e<n.length;e+=1){let t=n[e];if("--relaunch"===t){a.flags.relaunch=!0;continue}a.positionals.push(t)}return a}if(t4(r)){let e=t6(r,n);if(Object.assign(a.flags,e.flags),0===e.positionals.length)return a;let t=e.positionals[0];if(t.startsWith("@"))return a.positionals=[t],e.positionals[1]&&(a.result={refLabel:e.positionals[1]}),a;let i=e.positionals[0],o=e.positionals[1];return i2(i)&&i2(o)&&e.positionals.length>=2?a.positionals=[i,o]:a.positionals=[e.positionals.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}if("swipe"===r){let e=t6(r,n);return Object.assign(a.flags,e.flags),a.positionals=e.positionals,a}return a.positionals=n,a}(i);e&&t.push(e)}return t}(s),u=t.flags?.replayUpdate===!0,p=0;for(let e=0;e<c.length;e+=1){let s=c[e];if(!s||"replay"===s.command)continue;let l=await a({token:t.token,session:i,command:s.command,positionals:s.positionals??[],flags:s.flags??{}});if(l.ok)continue;if(!u)return iZ(l,s,e,o);let f=await iQ({action:s,sessionName:i,logPath:r,sessionStore:n,dispatch:d});if(!f)return iZ(l,s,e,o);if(c[e]=f,!(l=await a({token:t.token,session:i,command:f.command,positionals:f.positionals??[],flags:f.flags??{}})).ok)return iZ(l,f,e,o);p+=1}if(u&&p>0){let e=n.get(i);!function(e,t,i){let r=[];if(i){let e=i.device.name.replace(/"/g,'\\"'),t=i.device.kind?` kind=${i.device.kind}`:"";r.push(`context platform=${i.device.platform} device="${e}"${t} theme=unknown`)}for(let e of t)r.push(function(e){let t=[e.command];if("snapshot"===e.command)return e.flags?.snapshotInteractiveOnly&&t.push("-i"),e.flags?.snapshotCompact&&t.push("-c"),"number"==typeof e.flags?.snapshotDepth&&t.push("-d",String(e.flags.snapshotDepth)),e.flags?.snapshotScope&&t.push("-s",t5(e.flags.snapshotScope)),e.flags?.snapshotRaw&&t.push("--raw"),t.join(" ");if("open"===e.command){for(let i of e.positionals??[])t.push(t5(i));return e.flags?.relaunch&&t.push("--relaunch"),t.join(" ")}for(let i of e.positionals??[])t.push(t5(i));return t8(t,e),t.join(" ")}(e));let n=`${r.join("\n")}
|
|
15
|
+
`,a=`${e}.tmp-${process.pid}-${Date.now()}`;A.writeFileSync(a,n),A.renameSync(a,e)}(o,c,e)}return{ok:!0,data:{replayed:c.length,healed:p,session:i}}}catch(t){let e=w(t);return{ok:!1,error:{code:e.code,message:e.message}}}}if("batch"===f)return await iY(t,i,a);if("close"===f){let e=n.get(i);return e?(t.positionals&&t.positionals.length>0&&await d(e.device,"close",t.positionals??[],t.flags?.out,{...t9(r,t.flags,e.appBundleId,e.trace?.outPath)}),"ios"===e.device.platform&&await tN(e.device.id),n.recordAction(e,{command:f,positionals:t.positionals??[],flags:t.flags??{},result:{session:i}}),t.flags?.saveScript&&(e.recordSession=!0),n.writeSessionLog(e),n.delete(i),{ok:!0,data:{session:i}}):{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session"}}}return null}async function iY(e,t,i){let r=e.flags?.batchOnError??"stop";if("stop"!==r)return{ok:!1,error:{code:"INVALID_ARGS",message:`Unsupported batch on-error mode: ${r}.`}};let n=e.flags?.batchMaxSteps??g;if(!Number.isInteger(n)||n<1||n>1e3)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid batch max-steps: ${String(e.flags?.batchMaxSteps)}`}};try{let r=s(e.flags?.batchSteps,n),a=Date.now(),o=[];for(let n=0;n<r.length;n+=1){let a=r[n],s=await iK(e,t,a,i,n+1);if(!s.ok)return{ok:!1,error:{code:s.error.code,message:`Batch failed at step ${s.step} (${a.command}): ${s.error.message}`,hint:s.error.hint,diagnosticId:s.error.diagnosticId,logPath:s.error.logPath,details:{...s.error.details??{},step:s.step,command:a.command,positionals:a.positionals,executed:n,total:r.length,partialResults:o}}};o.push(s.result)}return{ok:!0,data:{total:r.length,executed:r.length,totalDurationMs:Date.now()-a,results:o}}}catch(t){let e=w(t);return{ok:!1,error:{code:e.code,message:e.message,details:e.details}}}}async function iK(e,t,i,r,n){let a=Date.now(),o=await r({token:e.token,session:t,command:i.command,positionals:i.positionals,flags:function(e,t){let i={...t??{}};delete i.batchSteps,delete i.batchOnError,delete i.batchMaxSteps;let r=e??{};for(let e of iG)void 0===i[e]&&void 0!==r[e]&&(i[e]=r[e]);return i}(e.flags,i.flags),meta:e.meta}),s=Date.now()-a;return o.ok?{ok:!0,step:n,result:{step:n,command:i.command,ok:!0,data:o.data??{},durationMs:s}}:{ok:!1,step:n,error:o.error}}function iZ(e,t,i,r){if(e.ok)return e;let n=i+1,a=function(e){let t;return t=(e.positionals??[]).map(e=>t5(e)),[e.command,...t].join(" ")}(t),o={...e.error.details??{},replayPath:r,step:n,action:t.command,positionals:t.positionals??[]};return{ok:!1,error:{code:e.error.code,message:`Replay failed at step ${n} (${a}): ${e.error.message}`,hint:e.error.hint,diagnosticId:e.error.diagnosticId,logPath:e.error.logPath,details:o}}}async function iQ(e){let{action:t,sessionName:i,logPath:r,sessionStore:n,dispatch:a}=e;if(!(t4(t.command)||["fill","get","is","wait"].includes(t.command)))return null;let o=n.get(i);if(!o)return null;let s=t4(t.command)||"fill"===t.command,l=t4(t.command)||"fill"===t.command||"get"===t.command&&t.positionals?.[0]==="text",c=await i0(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),t4(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}=iO(e.positionals);i&&t.push(i.selectorExpression)}if("wait"===e.command){let{selectorExpression:i}=i1(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 t0(t).filter(e=>e.trim().length>0)}(t)){let i=iN(e);if(!i)continue;let r=iS(c.nodes,i,{platform:o.device.platform,requireRect:s,requireUnique:!0,disambiguateAmbiguous:l});if(!r)continue;let n=ix(r.node,o.device.platform,{action:t4(t.command)?"click":"fill"===t.command?"fill":"get"}).join(" || ");if(t4(t.command))return{...t,positionals:[n]};if("fill"===t.command){let e=tQ(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}=iO(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}=i1(t.positionals??[]),i=[n];return e&&i.push(e),{...t,positionals:i}}}let d=function(e,t,i){if("get"!==e.command||e.positionals?.[0]!=="text")return null;let r=e.positionals?.[1];if(!r)return null;let n=iN(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(ih(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=ig(e).trim();return!!/^\d+$/.test(t)&&(0===a.size||a.has(ih(e.type??"")))});if(0===s.length||1!==t0(s.map(e=>ig(e).trim())).length)return null;let l=s[0];if(!l)return null;let c=ix(l,i.device.platform,{action:"get"});return 0===c.length?null:{...e,positionals:["text",c.join(" || ")]}}(t,c,o);return d||null}async function i0(e,t,i,r,n,a){let o=await n(e.device,"snapshot",[],t.flags?.out,{...t9(i,{...t.flags??{},snapshotInteractiveOnly:r,snapshotCompact:r},e.appBundleId,e.trace?.outPath)}),s=o?.nodes??[],l={nodes:io(t.flags?.snapshotRaw?s:im(s)),truncated:o?.truncated,createdAt:Date.now(),backend:o?.backend};return e.snapshot=l,a.set(e.name,e),l}function i1(e){if(0===e.length)return{selectorExpression:null,selectorTimeout:null};let t=e[e.length-1],i=/^\d+$/.test(t??""),r=iD(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 i2(e){return!!e&&!Number.isNaN(Number(e))}function i3(e){if(!e)return null;let t=Number(e);return Number.isFinite(t)?t:null}async function i4(e){let{req:t,sessionName:i,logPath:r,sessionStore:n}=e,a=t.command;if("snapshot"===a){let{session:e,device:a}=await i5(n,i,t.flags);if(!tZ("snapshot",a))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"snapshot is not supported on this device"}};let o=e?.appBundleId,s=t.flags?.snapshotScope;if(s&&s.trim().startsWith("@")){if(!e?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"Ref scope requires an existing snapshot in session."}};let t=is(s.trim());if(!t)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref scope: ${s}`}};let i=il(e.snapshot.nodes,t),r=i?iu(i,e.snapshot.nodes):void 0;if(!r)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${s} not found or has no label`}};s=r}return await i8(e,a,async()=>{let l=await tJ(a,"snapshot",[],t.flags?.out,{...t9(r,{...t.flags,snapshotScope:s},o,e?.trace?.outPath)}),c=l?.nodes??[],d=io(t.flags?.snapshotRaw?c:im(c)),u={nodes:d,truncated:l?.truncated,createdAt:Date.now(),backend:l?.backend},p=e?{...e,snapshot:u}:{name:i,device:a,createdAt:Date.now(),appBundleId:o,snapshot:u,actions:[]};return i6(n,p,t,{nodes:d.length,truncated:l?.truncated??!1}),n.set(i,p),{ok:!0,data:{nodes:d,truncated:l?.truncated??!1,appName:p.appBundleId?p.appName??p.appBundleId:void 0,appBundleId:p.appBundleId}}})}if("wait"===a){let{session:e,device:a}=await i5(n,i,t.flags),o=function(e){if(0===e.length)return null;let t=i3(e[0]);if(null!==t)return{kind:"sleep",durationMs:t};if("text"===e[0]){let t=i3(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=i3(e[e.length-1]);return{kind:"ref",rawRef:e[0],timeoutMs:t}}let i=i3(e[e.length-1]),r=iD(null!==i?e.slice(0,-1):e.slice());if(r&&0===r.rest.length){let e=iN(r.selectorExpression);if(e)return{kind:"selector",selector:e,selectorExpression:r.selectorExpression,timeoutMs:i}}return{kind:"text",text:(null!==i?e.slice(0,-1).join(" "):e.join(" ")).trim(),timeoutMs:i}}(t.positionals??[]);return o?"sleep"===o.kind?(await new Promise(e=>setTimeout(e,o.durationMs)),i6(n,e,t,{waitedMs:o.durationMs}),{ok:!0,data:{waitedMs:o.durationMs}}):tZ("wait",a)?await i8(e,a,async()=>{let s,l;if("selector"===o.kind){let s=o.timeoutMs??1e4,l=Date.now();for(;Date.now()-l<s;){let s=await tJ(a,"snapshot",[],t.flags?.out,{...t9(r,{...t.flags,snapshotInteractiveOnly:!1,snapshotCompact:!1},e?.appBundleId,e?.trace?.outPath)}),c=s?.nodes??[],d=io(t.flags?.snapshotRaw?c:im(c));e&&(e.snapshot={nodes:d,truncated:s?.truncated,createdAt:Date.now(),backend:s?.backend},n.set(i,e));let u=ib(d,o.selector,{platform:a.platform});if(u)return i6(n,e,t,{selector:u.selector.raw,waitedMs:Date.now()-l}),{ok:!0,data:{selector:u.selector.raw,waitedMs:Date.now()-l}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:`wait timed out for selector: ${o.selectorExpression}`}}}if("ref"===o.kind){if(!e?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"Ref wait requires an existing snapshot in session."}};let t=is(o.rawRef);if(!t)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref: ${o.rawRef}`}};let i=il(e.snapshot.nodes,t),r=i?iu(i,e.snapshot.nodes):void 0;if(!r)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${o.rawRef} not found or has no label`}};s=r,l=o.timeoutMs}else s=o.text,l=o.timeoutMs;if(!s)return{ok:!1,error:{code:"INVALID_ARGS",message:"wait requires text"}};let c=l??1e4,d=Date.now();for(;Date.now()-d<c;){if("ios"===a.platform){let i=await tv(a,{command:"findText",text:s,appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:r,traceLogPath:e?.trace?.outPath});if(i?.found)return i6(n,e,t,{text:s,waitedMs:Date.now()-d}),{ok:!0,data:{text:s,waitedMs:Date.now()-d}}}else if("android"===a.platform&&id(io((await eO(a,{scope:s})).nodes??[]),s))return i6(n,e,t,{text:s,waitedMs:Date.now()-d}),{ok:!0,data:{text:s,waitedMs:Date.now()-d}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:`wait timed out for text: ${s}`}}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"wait is not supported on this device"}}:{ok:!1,error:{code:"INVALID_ARGS",message:"wait requires a duration or text"}}}if("alert"===a){let{session:e,device:a}=await i5(n,i,t.flags),o=(t.positionals?.[0]??"get").toLowerCase();return tZ("alert",a)?await i8(e,a,async()=>{if("wait"===o){let i=i3(t.positionals?.[1])??1e4,o=Date.now();for(;Date.now()-o<i;){try{let i=await tv(a,{command:"alert",action:"get",appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:r,traceLogPath:e?.trace?.outPath});return i6(n,e,t,i),{ok:!0,data:i}}catch{}await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:"alert wait timed out"}}}let i=await tv(a,{command:"alert",action:"accept"===o||"dismiss"===o?o:"get",appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:r,traceLogPath:e?.trace?.outPath});return i6(n,e,t,i),{ok:!0,data:i}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"alert is only supported on iOS simulators"}}}if("settings"===a){let e=t.positionals?.[0],a=t.positionals?.[1];if(!e||!a)return{ok:!1,error:{code:"INVALID_ARGS",message:"settings requires <wifi|airplane|location> <on|off> or faceid <match|nonmatch|enroll|unenroll>"}};let{session:o,device:s}=await i5(n,i,t.flags);return tZ("settings",s)?await i8(o,s,async()=>{let i=o?.appBundleId,l=await tJ(s,"settings",[e,a,i??""],t.flags?.out,{...t9(r,t.flags,i,o?.trace?.outPath)});return i6(n,o,t,l??{setting:e,state:a}),{ok:!0,data:l??{setting:e,state:a}}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"settings is not supported on this device"}}}return null}async function i5(e,t,i){let r=e.get(t),n=r?.device??await tW(i??{});return r||await it(n),{session:r,device:n}}async function i8(e,t,i){let r=!e&&"ios"===t.platform;try{return await i()}finally{r&&await tN(t.id)}}function i6(e,t,i,r){t&&e.recordAction(t,{command:i.command,positionals:i.positionals??[],flags:i.flags??{},result:r})}function i7(e,t,i,r={}){let n=re(i);if(!n)return{matches:[],score:0};let a=0,o=[];for(let i of e){if(r.requireRect&&!i.rect)continue;let e=function(e,t,i){switch(t){case"role":return function(e,t){let i=function(e){let t=e.trim();return t?t=(t.split(".").pop()??t).replace(/XCUIElementType/gi,"").toLowerCase():""}(e??"");return i?i===t?2:+!!i.includes(t):0}(e.type,i);case"label":return i9(e.label,i);case"value":return i9(e.value,i);case"id":return i9(e.identifier,i);default:return Math.max(i9(e.label,i),i9(e.value,i),i9(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 i9(e,t){let i=re(e??"");return i?i===t?2:+!!i.includes(t):0}function re(e){return e.trim().toLowerCase().replace(/\s+/g," ")}async function rt(e){let{req:t,sessionName:i,logPath:r,sessionStore:n,invoke:a}=e,o=t.command;if("find"!==o)return null;let s=t.positionals??[];if(0===s.length)return{ok:!1,error:{code:"INVALID_ARGS",message:"find requires a locator or text"}};let{locator:l,query:c,action:d,value:u,timeoutMs:p}=function(e){let t="any",i=0;["text","label","value","role","id"].includes(e[0])&&(t=e[0],i=1);let r=e[i]??"",n=e.slice(i+1);if(0===n.length)return{locator:t,query:r,action:"click"};let a=n[0].toLowerCase();if("get"===a){let e=n[1]?.toLowerCase();if("text"===e)return{locator:t,query:r,action:"get_text"};if("attrs"===e)return{locator:t,query:r,action:"get_attrs"};throw new I("INVALID_ARGS","find get only supports text or attrs")}if("wait"===a)return{locator:t,query:r,action:"wait",timeoutMs:i3(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 I("INVALID_ARGS",`Unsupported find action: ${n[0]}`)}(s);if(!c)return{ok:!1,error:{code:"INVALID_ARGS",message:"find requires a value"}};let f=n.get(i);if(!f&&"exists"!==d&&"wait"!==d&&"get_text"!==d&&"get_attrs"!==d)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let m=f?.device??await tW(t.flags??{});f||await it(m);let h=f?.appBundleId,w="role"!==l?c:void 0,g="click"===d||"focus"===d||"fill"===d||"type"===d,v=0,A=null,y=async()=>{let e=Date.now();if(A&&e-v<750)return{nodes:A};let a=await tJ(m,"snapshot",[],t.flags?.out,{...t9(r,{...t.flags,snapshotScope:w,snapshotInteractiveOnly:g,snapshotCompact:g},h,f?.trace?.outPath)}),o=a?.nodes??[],s=io(t.flags?.snapshotRaw?o:im(o));return v=e,A=s,f&&(f.snapshot={nodes:s,truncated:a?.truncated,createdAt:Date.now(),backend:a?.backend},n.set(i,f)),{nodes:s,truncated:a?.truncated,backend:a?.backend}};if("wait"===d){let e=p??1e4,i=Date.now();for(;Date.now()-i<e;){let{nodes:e}=await y();if(i7(e,l,c,{requireRect:!1}).matches[0])return f&&n.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{found:!0,waitedMs:Date.now()-i}}),{ok:!0,data:{found:!0,waitedMs:Date.now()-i}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:"find wait timed out"}}}let{nodes:N}=await y(),S=i7(N,l,c,{requireRect:g});if(g&&S.matches.length>1){let e=S.matches.slice(0,8).map(e=>{let t=ig(e)||e.label||e.identifier||e.type||"";return`@${e.ref}${t?`(${t})`:""}`});return{ok:!1,error:{code:"AMBIGUOUS_MATCH",message:`find matched ${S.matches.length} elements for ${l} "${c}". Use a more specific locator or selector.`,details:{locator:l,query:c,matches:S.matches.length,candidates:e}}}}let b=S.matches[0]??null;if(!b)return{ok:!1,error:{code:"COMMAND_FAILED",message:"find did not match any element"}};let _="click"===d||"focus"===d||"fill"===d||"type"===d?function(e,t){if(t.hittable)return t;let i=t,r=new Set;for(;void 0!==i.parentIndex&&!r.has(i.ref);){r.add(i.ref);let t=e[i.parentIndex];if(!t)break;if(t.hittable)return t;i=t}return null}(N,b)??b:b,D=`@${_.ref}`,O={...t.flags??{},noRecord:!0};if("exists"===d)return f&&n.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{found:!0}}),{ok:!0,data:{found:!0}};if("get_text"===d){let e=ig(b);return f&&n.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"get text",text:e}}),{ok:!0,data:{ref:D,text:e,node:b}}}if("get_attrs"===d)return f&&n.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"get attrs"}}),{ok:!0,data:{ref:D,node:b}};if("click"===d){let e=await a({token:t.token,session:i,command:"click",positionals:[D],flags:O});return e.ok&&f&&n.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"click"}}),e}if("fill"===d){if(!u)return{ok:!1,error:{code:"INVALID_ARGS",message:"find fill requires text"}};let e=await a({token:t.token,session:i,command:"fill",positionals:[D,u],flags:O});return e.ok&&f&&n.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"fill"}}),e}if("focus"===d){let e=b.rect?ic(b.rect):null;if(!e)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};let i=await tJ(m,"focus",[String(e.x),String(e.y)],t.flags?.out,{...t9(r,t.flags,f?.appBundleId,f?.trace?.outPath)});return f&&n.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"focus"}}),{ok:!0,data:i??{ref:D}}}if("type"===d){if(!u)return{ok:!1,error:{code:"INVALID_ARGS",message:"find type requires text"}};let e=b.rect?ic(b.rect):null;if(!e)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};await tJ(m,"focus",[String(e.x),String(e.y)],t.flags?.out,{...t9(r,t.flags,f?.appBundleId,f?.trace?.outPath)});let i=await tJ(m,"type",[u],t.flags?.out,{...t9(r,t.flags,f?.appBundleId,f?.trace?.outPath)});return f&&n.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"type"}}),{ok:!0,data:i??{ref:D}}}return null}async function ri(e){let{req:t,sessionName:r,sessionStore:n}=e,a=t.command;if("record"===a){let e=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"record requires start|stop"}};let o=n.get(r),s=o?.device??await tW(t.flags??{});o||await it(s);let c=o??{name:r,device:s,createdAt:Date.now(),actions:[]};if("start"===e){if(c.recording)return{ok:!1,error:{code:"INVALID_ARGS",message:"recording already in progress"}};let e=t.positionals?.[1]??`./recording-${Date.now()}.mp4`,o=i.resolve(e),d=i.dirname(o);if(A.existsSync(d)||A.mkdirSync(d,{recursive:!0}),!tZ("record",s))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"record is only supported on iOS simulators"}};if("ios"===s.platform){let{child:e,wait:t}=l("xcrun",["simctl","io",s.id,"recordVideo",o],{allowFailure:!0});c.recording={platform:"ios",outPath:o,child:e,wait:t}}else{let e=`/sdcard/agent-device-recording-${Date.now()}.mp4`,{child:t,wait:i}=l("adb",["-s",s.id,"shell","screenrecord",e],{allowFailure:!0});c.recording={platform:"android",outPath:o,remotePath:e,child:t,wait:i}}return n.set(r,c),n.recordAction(c,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{action:"start"}}),{ok:!0,data:{recording:"started",outPath:e}}}if(!c.recording)return{ok:!1,error:{code:"INVALID_ARGS",message:"no active recording"}};let d=c.recording;d.child.kill("SIGINT");try{await d.wait}catch{}if("android"===d.platform&&d.remotePath)try{await u("adb",["-s",s.id,"pull",d.remotePath,d.outPath],{allowFailure:!0}),await u("adb",["-s",s.id,"shell","rm","-f",d.remotePath],{allowFailure:!0})}catch{}return c.recording=void 0,n.recordAction(c,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:d.outPath}}),{ok:!0,data:{recording:"stopped",outPath:d.outPath}}}if("trace"===a){let e=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"trace requires start|stop"}};let o=n.get(r);if(!o)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session"}};if("start"===e){if(o.trace)return{ok:!1,error:{code:"INVALID_ARGS",message:"trace already in progress"}};let e=t.positionals?.[1]??n.defaultTracePath(o),r=t7.expandHome(e);return A.mkdirSync(i.dirname(r),{recursive:!0}),A.appendFileSync(r,""),o.trace={outPath:r,startedAt:Date.now()},n.recordAction(o,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{action:"start",outPath:r}}),{ok:!0,data:{trace:"started",outPath:r}}}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=t7.expandHome(t.positionals[1]);A.mkdirSync(i.dirname(e),{recursive:!0}),A.existsSync(s)?A.renameSync(s,e):A.appendFileSync(e,""),s=e}return o.trace=void 0,n.recordAction(o,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:s}}),{ok:!0,data:{trace:"stopped",outPath:s}}}return null}async function rr(e){let{req:t,sessionName:i,sessionStore:r,contextFromFlags:n}=e,a=e.dispatch??tJ,o=t.command;if("press"===o){let e=r.get(i);if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let s=function(e){if(e.length<2)return null;let t=Number(e[0]),i=Number(e[1]);return Number.isFinite(t)&&Number.isFinite(i)?{x:t,y:i}:null}(t.positionals??[]);if(s){let i=await a(e.device,"press",[String(s.x),String(s.y)],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)});return r.recordAction(e,{command:o,positionals:t.positionals??[String(s.x),String(s.y)],flags:t.flags??{},result:i??{x:s.x,y:s.y}}),{ok:!0,data:i??{x:s.x,y:s.y}}}let l="click",c=t.positionals?.[0]??"";if(c.startsWith("@")){let i=ro("press",t.flags);if(i)return i;if(!e.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}};let s=is(c);if(!s)return{ok:!1,error:{code:"INVALID_ARGS",message:`${o} requires a ref like @e2`}};let d=il(e.snapshot.nodes,s);if(!d?.rect&&t.positionals.length>1){let i=t.positionals.slice(1).join(" ").trim();i.length>0&&(d=id(e.snapshot.nodes,i))}if(!d?.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${c} not found or has no bounds`}};let u=iu(d,e.snapshot.nodes),p=ix(d,e.device.platform,{action:l}),{x:f,y:m}=ic(d.rect),h=await a(e.device,"press",[String(f),String(m)],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)});return r.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:s,x:f,y:m,refLabel:u,selectorChain:p}}),{ok:!0,data:{...h??{},ref:s,x:f,y:m}}}let d=(t.positionals??[]).join(" ").trim();if(!d)return{ok:!1,error:{code:"INVALID_ARGS",message:`${o} requires @ref, selector expression, or x y coordinates`}};let u=iy(d),f=await rn(e,t.flags,r,n,{interactiveOnly:!0},a),m=await p("selector_resolve",()=>iS(f.nodes,u,{platform:e.device.platform,requireRect:!0,requireUnique:!0,disambiguateAmbiguous:!0}),{command:o});if(!m||!m.node.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:i_(u,m?.diagnostics??[],{unique:!0})}};let{x:h,y:w}=ic(m.node.rect),g=await a(e.device,"press",[String(h),String(w)],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)}),v=ix(m.node,e.device.platform,{action:l}),I=iu(m.node,f.nodes);return r.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{x:h,y:w,selector:m.selector.raw,selectorChain:v,refLabel:I}}),{ok:!0,data:{...g??{},selector:m.selector.raw,x:h,y:w}}}if("fill"===o){let e=r.get(i);if(t.positionals?.[0]?.startsWith("@")){let i=ro("fill",t.flags);if(i)return i;if(!e?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}};let s=is(t.positionals[0]);if(!s)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires a ref like @e2"}};let l=t.positionals.length>=3?t.positionals[1]:"",c=t.positionals.length>=3?t.positionals.slice(2).join(" "):t.positionals.slice(1).join(" ");if(!c)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after ref"}};let d=il(e.snapshot.nodes,s);if(!d?.rect&&l&&(d=id(e.snapshot.nodes,l)),!d?.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${t.positionals[0]} not found or has no bounds`}};let u=d.type??"",p=u&&!iw(u,e.device.platform)?`fill target ${t.positionals[0]} resolved to "${u}", attempting fill anyway.`:void 0,f=iu(d,e.snapshot.nodes),m=ix(d,e.device.platform,{action:"fill"}),{x:h,y:w}=ic(d.rect),g={...await a(e.device,"fill",[String(h),String(w),c],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)})??{ref:s,x:h,y:w}};return p&&(g.warning=p),r.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{...g,refLabel:f,selectorChain:m}}),{ok:!0,data:g}}if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let s=iD(t.positionals??[],{preferTrailingValue:!0});if(s){if(0===s.rest.length)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after selector"}};let i=s.rest.join(" ").trim();if(!i)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after selector"}};let l=iy(s.selectorExpression),c=await rn(e,t.flags,r,n,{interactiveOnly:!0},a),d=await p("selector_resolve",()=>iS(c.nodes,l,{platform:e.device.platform,requireRect:!0,requireUnique:!0,disambiguateAmbiguous:!0}),{command:o});if(!d||!d.node.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:i_(l,d?.diagnostics??[],{unique:!0})}};let u=d.node,f=u.type??"",m=f&&!iw(f,e.device.platform)?`fill target ${d.selector.raw} resolved to "${f}", attempting fill anyway.`:void 0,{x:h,y:w}=ic(d.node.rect),g=await a(e.device,"fill",[String(h),String(w),i],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)}),v=ix(u,e.device.platform,{action:"fill"}),I={...g??{x:h,y:w,text:i},selector:d.selector.raw,selectorChain:v,refLabel:iu(u,c.nodes)};return m&&(I.warning=m),r.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:I}),{ok:!0,data:I}}return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires x y text, @ref text, or selector text"}}}if("get"===o){let e=t.positionals?.[0];if("text"!==e&&"attrs"!==e)return{ok:!1,error:{code:"INVALID_ARGS",message:"get only supports text or attrs"}};let s=r.get(i);if(!s)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let l=t.positionals?.[1]??"";if(l.startsWith("@")){let i=ro("get",t.flags);if(i)return i;if(!s.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}};let n=is(l??"");if(!n)return{ok:!1,error:{code:"INVALID_ARGS",message:"get text requires a ref like @e2"}};let a=il(s.snapshot.nodes,n);if(!a&&t.positionals.length>2){let e=t.positionals.slice(2).join(" ").trim();e.length>0&&(a=id(s.snapshot.nodes,e))}if(!a)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${l} not found`}};let c=ix(a,s.device.platform,{action:"get"});if("attrs"===e)return r.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:n,selectorChain:c}}),{ok:!0,data:{ref:n,node:a}};let d=ig(a);return r.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:n,text:d,refLabel:d||void 0,selectorChain:c}}),{ok:!0,data:{ref:n,text:d,node:a}}}let c=t.positionals.slice(1).join(" ").trim();if(!c)return{ok:!1,error:{code:"INVALID_ARGS",message:"get requires @ref or selector expression"}};let d=iy(c),u=await rn(s,t.flags,r,n,{interactiveOnly:!1},a),f=await p("selector_resolve",()=>iS(u.nodes,d,{platform:s.device.platform,requireRect:!1,requireUnique:!0,disambiguateAmbiguous:"text"===e}),{command:o});if(!f)return{ok:!1,error:{code:"COMMAND_FAILED",message:i_(d,[],{unique:!0})}};let m=f.node,h=ix(m,s.device.platform,{action:"get"});if("attrs"===e)return r.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{selector:f.selector.raw,selectorChain:h}}),{ok:!0,data:{selector:f.selector.raw,node:m}};let w=ig(m);return r.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{text:w,refLabel:w||void 0,selector:f.selector.raw,selectorChain:h}}),{ok:!0,data:{selector:f.selector.raw,text:w,node:m}}}if("is"===o){let e=(t.positionals?.[0]??"").toLowerCase();if(!["visible","hidden","exists","editable","selected","text"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"is requires predicate: visible|hidden|exists|editable|selected|text"}};let s=r.get(i);if(!s)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};if(!tZ("is",s.device))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"is is not supported on this device"}};let{split:l}=iO(t.positionals);if(!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"is requires a selector expression"}};let c=l.rest.join(" ").trim();if("text"===e&&!c)return{ok:!1,error:{code:"INVALID_ARGS",message:"is text requires expected text value"}};if("text"!==e&&l.rest.length>0)return{ok:!1,error:{code:"INVALID_ARGS",message:`is ${e} does not accept trailing values`}};let d=iy(l.selectorExpression),u=await rn(s,t.flags,r,n,{interactiveOnly:!1},a);if("exists"===e){let i=ib(u.nodes,d,{platform:s.device.platform});return i?(r.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{predicate:e,selector:i.selector.raw,selectorChain:d.selectors.map(e=>e.raw),pass:!0,matches:i.matches}}),{ok:!0,data:{predicate:e,pass:!0,selector:i.selector.raw,matches:i.matches}}):{ok:!1,error:{code:"COMMAND_FAILED",message:i_(d,[],{unique:!1})}}}let f=await p("selector_resolve",()=>iS(u.nodes,d,{platform:s.device.platform,requireUnique:!0}),{command:"is",predicate:e});if(!f)return{ok:!1,error:{code:"COMMAND_FAILED",message:i_(d,[],{unique:!0})}};let m=function(e){let{predicate:t,node:i,expectedText:r,platform:n}=e,a=ig(i),o=!1;switch(t){case"visible":o=iM(i);break;case"hidden":o=!iM(i);break;case"editable":o=iE(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:iM(i),editable:iE(i,n),selected:!0===i.selected})}`;return{pass:o,actualText:a,details:s}}({predicate:e,node:f.node,expectedText:c,platform:s.device.platform});return m.pass?(r.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{predicate:e,selector:f.selector.raw,selectorChain:d.selectors.map(e=>e.raw),pass:!0,text:"text"===e?m.actualText:void 0}}),{ok:!0,data:{predicate:e,pass:!0,selector:f.selector.raw}}):{ok:!1,error:{code:"COMMAND_FAILED",message:`is ${e} failed for selector ${f.selector.raw}: ${m.details}`}}}return null}async function rn(e,t,i,r,n,a=tJ){let o=await a(e.device,"snapshot",[],t?.out,{...r({...t??{},snapshotInteractiveOnly:n.interactiveOnly,snapshotCompact:n.interactiveOnly},e.appBundleId,e.trace?.outPath)}),s=o?.nodes??[];return e.snapshot={nodes:io(t?.snapshotRaw?s:im(s)),truncated:o?.truncated,createdAt:Date.now(),backend:o?.backend},i.set(e.name,e),e.snapshot}let ra=[["snapshotDepth","--depth"],["snapshotScope","--scope"],["snapshotRaw","--raw"]];function ro(e,t){let i=function(e){if(!e)return[];let t=[];for(let[i,r]of ra)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 rs=i.join(S.homedir(),".agent-device"),rl=i.join(rs,"daemon.json"),rc=i.join(rs,"daemon.lock"),rd=i.join(rs,"daemon.log"),ru=new t7(i.join(rs,"sessions")),rp=f(),rf=c.randomBytes(24).toString("hex"),rm=new Set(["session_list","devices"]),rh=b(process.pid)??void 0;function rw(e,t,i){return t9(rd,e,t,i)}async function rg(e){let t=!!(e.meta?.debug||e.flags?.verbose);return await y({session:e.session,requestId:e.meta?.requestId,command:e.command,debug:t,logPath:rd},async()=>{if(e.token!==rf)return{ok:!1,error:a(new I("UNAUTHORIZED","Invalid token"))};m({level:"info",phase:"request_start",data:{session:e.session,command:e.command}});try{var t;let i=(t=e,"click"!==t.command?t:{...t,command:"press"}),r=i.command,n=function(e,t){var i;let r,n=e.session||"default";if(i=e,r=i.flags?.session,"string"==typeof r&&r.trim().length>0||"default"!==n||t.has(n))return n;let a=t.toArray();return 1===a.length?a[0].name:n}(i,ru),a=ru.get(n);a&&!rm.has(r)&&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 I("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.`)}}(a,i.flags);let o=await iX({req:i,sessionName:n,logPath:rd,sessionStore:ru,invoke:rg});if(o)return rv(o);let s=await i4({req:i,sessionName:n,logPath:rd,sessionStore:ru});if(s)return rv(s);let l=await ri({req:e,sessionName:n,sessionStore:ru});if(l)return rv(l);let c=await rt({req:i,sessionName:n,logPath:rd,sessionStore:ru,invoke:rg});if(c)return rv(c);let d=await rr({req:i,sessionName:n,sessionStore:ru,contextFromFlags:rw});if(d)return rv(d);let u=ru.get(n);if(!u)return rv({ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}});if(!tZ(r,u.device))return rv({ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:`${r} is not supported on this device`}});let p=await tJ(u.device,r,i.positionals??[],i.flags?.out,{...rw(i.flags,u.appBundleId,u.trace?.outPath)});return ru.recordAction(u,{command:r,positionals:i.positionals??[],flags:i.flags??{},result:p??{}}),rv({ok:!0,data:p??{}})}catch(i){m({level:"error",phase:"request_failed",data:{error:i instanceof Error?i.message:String(i)}});let e=d(),t=N({force:!0})??void 0;return{ok:!1,error:a(i,{diagnosticId:e.diagnosticId,logPath:t})}}})}function rv(e){let t=d();if(!e.ok){m({level:"error",phase:"request_failed",data:{code:e.error.code,message:e.error.message}});let i=N({force:!0})??void 0;return{ok:!1,error:a(new I(e.error.code,e.error.message,{...e.error.details??{},hint:e.error.hint,diagnosticId:e.error.diagnosticId,logPath:e.error.logPath}),{diagnosticId:t.diagnosticId,logPath:i})}}return m({level:"info",phase:"request_success"}),N(),e}function rI(){if(!A.existsSync(rc))return null;try{let e=JSON.parse(A.readFileSync(rc,"utf8"));if(!Number.isInteger(e.pid)||e.pid<=0)return null;return e}catch{return null}}!function(){if(!function(){A.existsSync(rs)||A.mkdirSync(rs,{recursive:!0});let e=JSON.stringify({pid:process.pid,version:rp,startedAt:Date.now(),processStartTime:rh},null,2),t=()=>{try{return A.writeFileSync(rc,e,{flag:"wx",mode:384}),!0}catch(e){if("EEXIST"===e.code)return!1;throw e}};if(t())return!0;let i=rI();if(i?.pid&&i.pid!==process.pid&&o(i.pid,i.processStartTime))return!1;try{A.unlinkSync(rc)}catch{}return t()}()){process.stderr.write("Daemon lock is held by another process; exiting.\n"),process.exit(0);return}let e=_.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 rg(e)}catch(e){i={ok:!1,error:a(e)}}e.write(`${JSON.stringify(i)}
|
|
16
|
+
`),r=t.indexOf("\n")}})});e.listen(0,"127.0.0.1",()=>{let t=e.address();if("object"==typeof t&&t?.port){var i;i=t.port,A.existsSync(rs)||A.mkdirSync(rs,{recursive:!0}),A.writeFileSync(rd,""),A.writeFileSync(rl,JSON.stringify({port:i,token:rf,pid:process.pid,version:rp,processStartTime:rh},null,2),{mode:384}),process.stdout.write(`AGENT_DEVICE_DAEMON_PORT=${t.port}
|
|
17
|
+
`)}});let t=!1,i=async()=>{await new Promise(t=>{try{e.close(()=>t())}catch{t()}})},r=async()=>{if(t)return;for(let e of(t=!0,await i(),ru.toArray()))ru.writeSessionLog(e);await tS(),A.existsSync(rl)&&A.unlinkSync(rl);let e=rI();if(!e||e.pid===process.pid)try{A.existsSync(rc)&&A.unlinkSync(rc)}catch{}process.exit(0)};process.on("SIGINT",()=>{r()}),process.on("SIGTERM",()=>{r()}),process.on("SIGHUP",()=>{r()}),process.on("uncaughtException",e=>{let t=e instanceof I?e:w(e);process.stderr.write(`Daemon error: ${t.message}
|
|
18
|
+
`),r()})}();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-device",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"description": "Unified control plane for physical and virtual devices via an agent-driven CLI.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Callstack",
|
|
@@ -17,12 +17,11 @@
|
|
|
17
17
|
"clean:daemon": "rm -f ~/.agent-device/daemon.json && rm -f ~/.agent-device/daemon.lock",
|
|
18
18
|
"build:node": "pnpm build && pnpm clean:daemon",
|
|
19
19
|
"build:xcuitest": "AGENT_DEVICE_IOS_CLEAN_DERIVED=1 xcodebuild build-for-testing -project ios-runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj -scheme AgentDeviceRunner -destination \"generic/platform=iOS Simulator\" -derivedDataPath ~/.agent-device/ios-runner/derived",
|
|
20
|
-
"build:clis": "pnpm build:node",
|
|
21
20
|
"build:all": "pnpm build:node && pnpm build:xcuitest",
|
|
22
21
|
"ad": "node bin/agent-device.mjs",
|
|
23
22
|
"format": "prettier --write .",
|
|
24
|
-
"prepublishOnly": "pnpm build:
|
|
25
|
-
"prepack": "pnpm build:
|
|
23
|
+
"prepublishOnly": "pnpm build:all",
|
|
24
|
+
"prepack": "pnpm build:all",
|
|
26
25
|
"typecheck": "tsc -p tsconfig.json",
|
|
27
26
|
"test": "node --test",
|
|
28
27
|
"test:unit": "node --test src/__tests__/*.test.ts src/core/__tests__/*.test.ts src/daemon/__tests__/*.test.ts src/daemon/handlers/__tests__/*.test.ts src/platforms/**/__tests__/*.test.ts src/utils/**/__tests__/*.test.ts",
|
|
@@ -90,11 +90,17 @@ agent-device settings airplane on
|
|
|
90
90
|
agent-device settings airplane off
|
|
91
91
|
agent-device settings location on
|
|
92
92
|
agent-device settings location off
|
|
93
|
+
agent-device settings faceid match
|
|
94
|
+
agent-device settings faceid nonmatch
|
|
95
|
+
agent-device settings faceid enroll
|
|
96
|
+
agent-device settings faceid unenroll
|
|
93
97
|
```
|
|
94
98
|
|
|
95
99
|
Note: iOS wifi/airplane toggles status bar indicators, not actual network state.
|
|
96
100
|
Airplane off clears status bar overrides.
|
|
97
101
|
iOS settings helpers are simulator-only.
|
|
102
|
+
Use `match`/`nonmatch` as the canonical command values.
|
|
103
|
+
Think of them as validate/invalidate outcomes when describing intent.
|
|
98
104
|
|
|
99
105
|
### App state
|
|
100
106
|
|
package/dist/src/50.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import t,{promises as e}from"node:fs";import r from"node:path";import{fileURLToPath as n,pathToFileURL as o}from"node:url";import{spawn as i,spawnSync as s}from"node:child_process";class a extends Error{code;details;cause;constructor(t,e,r,n){super(e),this.code=t,this.details=r,this.cause=n}}function u(t){return t instanceof a?t:t instanceof Error?new a("UNKNOWN",t.message,void 0,t):new a("UNKNOWN","Unknown error",{err:t})}function d(){try{let e=l();return JSON.parse(t.readFileSync(r.join(e,"package.json"),"utf8")).version??"0.0.0"}catch{return"0.0.0"}}function l(){let e=r.dirname(n(import.meta.url)),o=e;for(let e=0;e<6;e+=1){let e=r.join(o,"package.json");if(t.existsSync(e))return o;o=r.dirname(o)}return e}async function c(t,e,r={}){return new Promise((n,o)=>{let s=i(t,e,{cwd:r.cwd,env:r.env,stdio:["pipe","pipe","pipe"]}),u="",d=r.binaryStdout?Buffer.alloc(0):void 0,l="",c=!1,f=A(r.timeoutMs),p=f?setTimeout(()=>{c=!0,s.kill("SIGKILL")},f):null;r.binaryStdout||s.stdout.setEncoding("utf8"),s.stderr.setEncoding("utf8"),void 0!==r.stdin&&s.stdin.write(r.stdin),s.stdin.end(),s.stdout.on("data",t=>{r.binaryStdout?d=Buffer.concat([d??Buffer.alloc(0),Buffer.isBuffer(t)?t:Buffer.from(t)]):u+=t}),s.stderr.on("data",t=>{l+=t}),s.on("error",r=>{(p&&clearTimeout(p),"ENOENT"===r.code)?o(new a("TOOL_MISSING",`${t} not found in PATH`,{cmd:t},r)):o(new a("COMMAND_FAILED",`Failed to run ${t}`,{cmd:t,args:e},r))}),s.on("close",i=>{p&&clearTimeout(p);let s=i??1;c&&f?o(new a("COMMAND_FAILED",`${t} timed out after ${f}ms`,{cmd:t,args:e,stdout:u,stderr:l,exitCode:s,timeoutMs:f})):0===s||r.allowFailure?n({stdout:u,stderr:l,exitCode:s,stdoutBuffer:d}):o(new a("COMMAND_FAILED",`${t} exited with code ${s}`,{cmd:t,args:e,stdout:u,stderr:l,exitCode:s}))})})}async function f(t){try{var e;let{shell:r,args:n}=(e=t,"win32"===process.platform?{shell:"cmd.exe",args:["/c","where",e]}:{shell:"bash",args:["-lc",`command -v ${e}`]}),o=await c(r,n,{allowFailure:!0});return 0===o.exitCode&&o.stdout.trim().length>0}catch{return!1}}function p(t,e,r={}){let n=s(t,e,{cwd:r.cwd,env:r.env,stdio:["pipe","pipe","pipe"],encoding:r.binaryStdout?void 0:"utf8",input:r.stdin,timeout:A(r.timeoutMs)});if(n.error){let o=n.error.code;if("ETIMEDOUT"===o)throw new a("COMMAND_FAILED",`${t} timed out after ${A(r.timeoutMs)}ms`,{cmd:t,args:e,timeoutMs:A(r.timeoutMs)},n.error);if("ENOENT"===o)throw new a("TOOL_MISSING",`${t} not found in PATH`,{cmd:t},n.error);throw new a("COMMAND_FAILED",`Failed to run ${t}`,{cmd:t,args:e},n.error)}let o=r.binaryStdout?Buffer.isBuffer(n.stdout)?n.stdout:Buffer.from(n.stdout??""):void 0,i=r.binaryStdout?"":"string"==typeof n.stdout?n.stdout:(n.stdout??"").toString(),u="string"==typeof n.stderr?n.stderr:(n.stderr??"").toString(),d=n.status??1;if(0!==d&&!r.allowFailure)throw new a("COMMAND_FAILED",`${t} exited with code ${d}`,{cmd:t,args:e,stdout:i,stderr:u,exitCode:d});return{stdout:i,stderr:u,exitCode:d,stdoutBuffer:o}}function m(t,e,r={}){i(t,e,{cwd:r.cwd,env:r.env,stdio:"ignore",detached:!0}).unref()}async function w(t,e,r={}){return new Promise((n,o)=>{let s=i(t,e,{cwd:r.cwd,env:r.env,stdio:["pipe","pipe","pipe"]}),u="",d="",l=r.binaryStdout?Buffer.alloc(0):void 0;r.binaryStdout||s.stdout.setEncoding("utf8"),s.stderr.setEncoding("utf8"),void 0!==r.stdin&&s.stdin.write(r.stdin),s.stdin.end(),s.stdout.on("data",t=>{if(r.binaryStdout){l=Buffer.concat([l??Buffer.alloc(0),Buffer.isBuffer(t)?t:Buffer.from(t)]);return}let e=String(t);u+=e,r.onStdoutChunk?.(e)}),s.stderr.on("data",t=>{let e=String(t);d+=e,r.onStderrChunk?.(e)}),s.on("error",r=>{"ENOENT"===r.code?o(new a("TOOL_MISSING",`${t} not found in PATH`,{cmd:t},r)):o(new a("COMMAND_FAILED",`Failed to run ${t}`,{cmd:t,args:e},r))}),s.on("close",i=>{let s=i??1;0===s||r.allowFailure?n({stdout:u,stderr:d,exitCode:s,stdoutBuffer:l}):o(new a("COMMAND_FAILED",`${t} exited with code ${s}`,{cmd:t,args:e,stdout:u,stderr:d,exitCode:s}))})})}function h(t,e,r={}){let n=i(t,e,{cwd:r.cwd,env:r.env,stdio:["ignore","pipe","pipe"]}),o="",s="";n.stdout.setEncoding("utf8"),n.stderr.setEncoding("utf8"),n.stdout.on("data",t=>{o+=t}),n.stderr.on("data",t=>{s+=t});let u=new Promise((i,u)=>{n.on("error",r=>{"ENOENT"===r.code?u(new a("TOOL_MISSING",`${t} not found in PATH`,{cmd:t},r)):u(new a("COMMAND_FAILED",`Failed to run ${t}`,{cmd:t,args:e},r))}),n.on("close",n=>{let d=n??1;0===d||r.allowFailure?i({stdout:o,stderr:s,exitCode:d}):u(new a("COMMAND_FAILED",`${t} exited with code ${d}`,{cmd:t,args:e,stdout:o,stderr:s,exitCode:d}))})});return{child:n,wait:u}}function A(t){if(!Number.isFinite(t))return;let e=Math.floor(t);if(!(e<=0))return e}let S=[/(^|[\/\s"'=])dist\/src\/daemon\.js($|[\s"'])/,/(^|[\/\s"'=])src\/daemon\.ts($|[\s"'])/];function I(t){if(!Number.isInteger(t)||t<=0)return!1;try{return process.kill(t,0),!0}catch(t){return"EPERM"===t.code}}function g(t){if(!Number.isInteger(t)||t<=0)return null;try{let e=p("ps",["-p",String(t),"-o","lstart="],{allowFailure:!0,timeoutMs:1e3});if(0!==e.exitCode)return null;let r=e.stdout.trim();return r.length>0?r:null}catch{return null}}function N(t,e){let r;if(!I(t))return!1;if(e){let r=g(t);if(!r||r!==e)return!1}let n=function(t){if(!Number.isInteger(t)||t<=0)return null;try{let e=p("ps",["-p",String(t),"-o","command="],{allowFailure:!0,timeoutMs:1e3});if(0!==e.exitCode)return null;let r=e.stdout.trim();return r.length>0?r:null}catch{return null}}(t);return!!n&&!!(r=n.toLowerCase().replaceAll("\\","/")).includes("agent-device")&&S.some(t=>t.test(r))}function y(t,e){try{return process.kill(t,e),!0}catch(e){let t=e.code;if("ESRCH"===t||"EPERM"===t)return!1;throw e}}async function M(t,e){if(!I(t))return!0;let r=Date.now();for(;Date.now()-r<e;)if(await new Promise(t=>setTimeout(t,50)),!I(t))return!0;return!I(t)}async function E(t,e){!N(t,e.expectedStartTime)||!y(t,"SIGTERM")||await M(t,e.termTimeoutMs)||y(t,"SIGKILL")&&await M(t,e.killTimeoutMs)}let D=100,L=new Set(["batch","replay"]);function $(t){let e;try{e=JSON.parse(t)}catch{throw new a("INVALID_ARGS","Batch steps must be valid JSON.")}if(!Array.isArray(e)||0===e.length)throw new a("INVALID_ARGS","Batch steps must be a non-empty JSON array.");return e}function _(t,e){if(!Array.isArray(t)||0===t.length)throw new a("INVALID_ARGS","batch requires a non-empty batchSteps array.");if(t.length>e)throw new a("INVALID_ARGS",`batch has ${t.length} steps; max allowed is ${e}.`);let r=[];for(let e=0;e<t.length;e+=1){let n=t[e];if(!n||"object"!=typeof n)throw new a("INVALID_ARGS",`Invalid batch step at index ${e}.`);let o="string"==typeof n.command?n.command.trim().toLowerCase():"";if(!o)throw new a("INVALID_ARGS",`Batch step ${e+1} requires command.`);if(L.has(o))throw new a("INVALID_ARGS",`Batch step ${e+1} cannot run ${o}.`);if(void 0!==n.positionals&&!Array.isArray(n.positionals))throw new a("INVALID_ARGS",`Batch step ${e+1} positionals must be an array.`);let i=n.positionals??[];if(i.some(t=>"string"!=typeof t))throw new a("INVALID_ARGS",`Batch step ${e+1} positionals must contain only strings.`);if(void 0!==n.flags&&("object"!=typeof n.flags||Array.isArray(n.flags)||!n.flags))throw new a("INVALID_ARGS",`Batch step ${e+1} flags must be an object.`);r.push({command:o,positionals:i,flags:n.flags??{}})}return r}export{default as node_net}from"node:net";export{default as node_os}from"node:os";export{a as AppError,D as DEFAULT_BATCH_MAX_STEPS,u as asAppError,n as fileURLToPath,l as findProjectRoot,N as isAgentDeviceDaemonProcess,I as isProcessAlive,t as node_fs,r as node_path,$ as parseBatchStepsJson,o as pathToFileURL,e as promises,g as readProcessStartTime,d as readVersion,c as runCmd,h as runCmdBackground,m as runCmdDetached,w as runCmdStreaming,E as stopProcessForTakeover,_ as validateAndNormalizeBatchSteps,f as whichCmd};
|