agent-device 0.5.4 → 0.5.5
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/dist/src/bin.js +12 -12
- package/dist/src/daemon.js +2 -2
- package/package.json +1 -1
package/dist/src/bin.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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]
|
|
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 c,emitDiagnostic as p,asAppError as d,pathToFileURL as u,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
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}
|
|
@@ -12,8 +12,8 @@ ${r}
|
|
|
12
12
|
`),s.diagnosticId&&process.stderr.write(`Diagnostic ID: ${s.diagnosticId}
|
|
13
13
|
`),s.logPath&&process.stderr.write(`Diagnostics Log: ${s.logPath}
|
|
14
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}]`:"",
|
|
16
|
-
`)}),i=setTimeout(()=>{r.destroy(),
|
|
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}]`:"",c=r?` "${r}"`:"";return s?`${i}${o} [${a}]${l}`.trimEnd():`${i}${o} [${a}]${c}${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 c("daemon_startup",async()=>await U(),{requestId:s,session:t.session}),i={...t,token:r.token,meta:{requestId:s,debug:a,cwd:t.meta?.cwd}};return p({level:"info",phase:"daemon_request_prepare",data:{requestId:s,command:t.command,session:t.session}}),await c("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(),p({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),p({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(),c=r.includes("--debug")||r.includes("--verbose")||r.includes("-v"),u=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:c},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("--"),c=s.startsWith("-")&&s.length>1;if(!o&&!c){r?i.push(s):r=s;continue}let[p,d]=o?_(s):[s,void 0],u=A.get(p);if(!u){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: ${p}`)}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}}(u,p,d,e[t+1]);m.consumeNext&&(t+=1),a[u.key]=m.value,n.push({key:u.key,token:p})}let c=D(r),p=new Set([...$,...c?.allowedFlags??[]]),d=n.filter(e=>!p.has(e.key));if(d.length>0){var u,m;let e=(u=r,m=d.map(e=>e.token),u?1===m.length?`Flag ${m[0]} is not supported for command ${u}.`:`Flags ${m.join(", ")} are not supported for command ${u}.`: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.`),d))delete a[t.key]}if(c?.defaults)for(let[e,t]of Object.entries(c.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){p({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});u?M({success:!1,error:e}):C(e,{showDetails:c}),process.exit(1);return}for(let e of y.warnings)process.stderr.write(`Warning: ${e}
|
|
17
17
|
`);y.flags.version&&(process.stdout.write(`${o()}
|
|
18
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
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}
|
|
@@ -26,16 +26,16 @@ Usage:
|
|
|
26
26
|
${i.join("\n\n")}
|
|
27
27
|
`}(e);t&&(process.stdout.write(t),process.exit(0)),C(new g("INVALID_ARGS",`Unknown command: ${e}`)),process.stdout.write(`${L}
|
|
28
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`:""}
|
|
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,cwd:process.cwd()}});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
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)":""}`,
|
|
32
|
-
`:"";if(0===a.length)return`${
|
|
33
|
-
`;if(t.raw){let e=a.map(e=>JSON.stringify(e));return`${
|
|
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)":""}`,c=n.length>0?`${n.join("\n")}
|
|
32
|
+
`:"";if(0===a.length)return`${c}${l}
|
|
33
|
+
`;if(t.raw){let e=a.map(e=>JSON.stringify(e));return`${c}${l}
|
|
34
34
|
${e.join("\n")}
|
|
35
|
-
`}if(t.flatten){let e=a.map(e=>T(e,0,!1));return`${
|
|
35
|
+
`}if(t.flatten){let e=a.map(e=>T(e,0,!1));return`${c}${l}
|
|
36
36
|
${e.join("\n")}
|
|
37
|
-
`}let
|
|
38
|
-
${
|
|
37
|
+
`}let p=[],d=[];for(let e of a){let t=e.depth??0;for(;p.length>0&&t<=p[p.length-1];)p.pop();let s=e.label?.trim()||e.value?.trim()||e.identifier?.trim()||"",a="group"===P(e.type??"Element")&&!s;a&&p.push(t);let r=a?t:Math.max(0,t-p.length);d.push(T(e,r,a))}return`${c}${l}
|
|
38
|
+
${d.join("\n")}
|
|
39
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
40
|
`),V&&V();return}if("attrs"===e){let e=t.data?.node??{};process.stdout.write(`${JSON.stringify(e,null,2)}
|
|
41
41
|
`),V&&V();return}}if("find"===x){let e=t.data;if("string"==typeof e?.text){process.stdout.write(`${e.text}
|
|
@@ -51,7 +51,7 @@ ${u.join("\n")}
|
|
|
51
51
|
`),r&&process.stdout.write(`Source: ${r}
|
|
52
52
|
`),V&&V();return}if("android"===t){process.stdout.write(`Foreground app: ${i??"unknown"}
|
|
53
53
|
`),o&&process.stdout.write(`Activity: ${o}
|
|
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=
|
|
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=d(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(`
|
|
55
55
|
[daemon log]
|
|
56
56
|
${s}
|
|
57
|
-
`)}}catch{}V&&V(),process.exit(1)}})}
|
|
57
|
+
`)}}catch{}V&&V(),process.exit(1)}})}u(process.argv[1]??"").href===import.meta.url&&ea(process.argv.slice(2)).catch(e=>{C(a(d(e)),{showDetails:!0}),process.exit(1)}),ea(process.argv.slice(2));
|
package/dist/src/daemon.js
CHANGED
|
@@ -10,8 +10,8 @@ ${r.stderr}`.toLowerCase()))throw new I("COMMAND_FAILED",`simctl uninstall faile
|
|
|
10
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}
|
|
11
11
|
${t}
|
|
12
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"],ij=["platform","device","udid","serial","verbose","out"];function iB(e,t,i){return t||iq(i)?null:{ok:!1,error:{code:"INVALID_ARGS",message:`${e} requires an active session or an explicit device selector (e.g. --platform ios).`}}}function iq(e){return!!(e?.platform||e?.device||e?.udid||e?.serial)}async function iW(e){let t=iq(e.flags)||!e.session?await e.resolveTargetDeviceFn(e.flags??{}):e.session.device;return!1!==e.ensureReady&&await e.ensureReadyFn(t),t}let iJ={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 iH(e,t,i){if("ios"===e.platform&&t)return W(t)?"device"===e.kind?J(i,t):void 0:await iz(e,t)}async function iz(e,t){try{let{resolveIosApp:i}=await Promise.resolve().then(()=>({resolveIosApp:e0}));return await i(e,t)}catch{return}}async function iX(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=iB("appstate",o,s);if(l)return l;let c=o?.device.platform==="ios"&&!!o&&(!iq(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 iW({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 iY(e){let{req:t,sessionName:i,logPath:r,sessionStore:n,invoke:a,dispatch:o,ensureReady:s,resolveTargetDevice:l,reinstallOps:c=iJ}=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=iB(f,e,r);if(a)return a;let o=await iW({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=iB(f,e,r);if(a)return a;let o=await iW({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 iX({req:t,sessionName:i,sessionStore:n,ensureReady:u,resolveDevice:p});if("reinstall"===f){let e,r=n.get(i),a=t.flags??{},o=iB(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 iW({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 iH(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 iH(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 i4(i)&&i4(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:i0(t.flags,s.flags),meta:t.meta});if(l.ok)continue;if(!u)return iQ(l,s,e,o);let f=await i1({action:s,sessionName:i,logPath:r,sessionStore:n,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:i0(t.flags,f.flags),meta:t.meta})).ok)return iQ(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")}
|
|
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,t){return e.startsWith("~/")?i.join(S.homedir(),e.slice(2)):t&&!i.isAbsolute(e)?i.resolve(t,e):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"],ij=["platform","device","udid","serial","verbose","out"];function iB(e,t,i){return t||iq(i)?null:{ok:!1,error:{code:"INVALID_ARGS",message:`${e} requires an active session or an explicit device selector (e.g. --platform ios).`}}}function iq(e){return!!(e?.platform||e?.device||e?.udid||e?.serial)}async function iW(e){let t=iq(e.flags)||!e.session?await e.resolveTargetDeviceFn(e.flags??{}):e.session.device;return!1!==e.ensureReady&&await e.ensureReadyFn(t),t}let iJ={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 iH(e,t,i){if("ios"===e.platform&&t)return W(t)?"device"===e.kind?J(i,t):void 0:await iz(e,t)}async function iz(e,t){try{let{resolveIosApp:i}=await Promise.resolve().then(()=>({resolveIosApp:e0}));return await i(e,t)}catch{return}}async function iX(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=iB("appstate",o,s);if(l)return l;let c=o?.device.platform==="ios"&&!!o&&(!iq(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 iW({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 iY(e){let{req:t,sessionName:i,logPath:r,sessionStore:n,invoke:a,dispatch:o,ensureReady:s,resolveTargetDevice:l,reinstallOps:c=iJ}=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=iB(f,e,r);if(a)return a;let o=await iW({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=iB(f,e,r);if(a)return a;let o=await iW({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 iX({req:t,sessionName:i,sessionStore:n,ensureReady:u,resolveDevice:p});if("reinstall"===f){let e,r=n.get(i),a=t.flags??{},o=iB(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 iW({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 iH(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 iH(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,t.meta?.cwd),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 i4(i)&&i4(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:i0(t.flags,s.flags),meta:t.meta});if(l.ok)continue;if(!u)return iQ(l,s,e,o);let f=await i1({action:s,sessionName:i,logPath:r,sessionStore:n,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:i0(t.flags,f.flags),meta:t.meta})).ok)return iQ(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
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 iK(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 iK(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 iZ(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 iZ(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 iQ(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}}}function i0(e,t){let i={...t??{}},r=e??{};for(let e of ij)void 0===i[e]&&void 0!==r[e]&&(i[e]=r[e]);return i}async function i1(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 i2(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}=i3(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}=i3(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 i2(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 i3(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 i4(e){return!!e&&!Number.isNaN(Number(e))}function i5(e){if(!e)return null;let t=Number(e);return Number.isFinite(t)?t:null}async function i8(e){let{req:t,sessionName:i,logPath:r,sessionStore:n}=e,a=t.command;if("snapshot"===a){let{session:e,device:a}=await i6(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 i7(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 i9(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 i6(n,i,t.flags),o=function(e){if(0===e.length)return null;let t=i5(e[0]);if(null!==t)return{kind:"sleep",durationMs:t};if("text"===e[0]){let t=i5(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=i5(e[e.length-1]);return{kind:"ref",rawRef:e[0],timeoutMs:t}}let i=i5(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)),i9(n,e,t,{waitedMs:o.durationMs}),{ok:!0,data:{waitedMs:o.durationMs}}):tZ("wait",a)?await i7(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 i9(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 i9(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 i9(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 i6(n,i,t.flags),o=(t.positionals?.[0]??"get").toLowerCase();return tZ("alert",a)?await i7(e,a,async()=>{if("wait"===o){let i=i5(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 i9(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 i9(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 i6(n,i,t.flags);return tZ("settings",s)?await i7(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 i9(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 i6(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 i7(e,t,i){let r=!e&&"ios"===t.platform;try{return await i()}finally{r&&await tN(t.id)}}function i9(e,t,i,r){t&&e.recordAction(t,{command:i.command,positionals:i.positionals??[],flags:i.flags??{},result:r})}function re(e,t,i,r={}){let n=ri(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 rt(e.label,i);case"value":return rt(e.value,i);case"id":return rt(e.identifier,i);default:return Math.max(rt(e.label,i),rt(e.value,i),rt(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 rt(e,t){let i=ri(e??"");return i?i===t?2:+!!i.includes(t):0}function ri(e){return e.trim().toLowerCase().replace(/\s+/g," ")}async function rr(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:i5(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(re(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=re(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 rn(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 ra(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=rl("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 ro(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=rl("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 ro(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=rl("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 ro(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 ro(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 ro(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 rs=[["snapshotDepth","--depth"],["snapshotScope","--scope"],["snapshotRaw","--raw"]];function rl(e,t){let i=function(e){if(!e)return[];let t=[];for(let[i,r]of rs)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 rc=i.join(S.homedir(),".agent-device"),rd=i.join(rc,"daemon.json"),ru=i.join(rc,"daemon.lock"),rp=i.join(rc,"daemon.log"),rf=new t7(i.join(rc,"sessions")),rm=f(),rh=c.randomBytes(24).toString("hex"),rw=new Set(["session_list","devices"]),rg=b(process.pid)??void 0;function rv(e,t,i){return t9(rp,e,t,i)}async function rI(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:rp},async()=>{if(e.token!==rh)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,rf),a=rf.get(n);a&&!rw.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}`),t.device&&t.device.trim().toLowerCase()!==r.name.trim().toLowerCase()&&i.push(`--device=${t.device}`),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 iY({req:i,sessionName:n,logPath:rp,sessionStore:rf,invoke:rI});if(o)return rA(o);let s=await i8({req:i,sessionName:n,logPath:rp,sessionStore:rf});if(s)return rA(s);let l=await rn({req:e,sessionName:n,sessionStore:rf});if(l)return rA(l);let c=await rr({req:i,sessionName:n,logPath:rp,sessionStore:rf,invoke:rI});if(c)return rA(c);let d=await ra({req:i,sessionName:n,sessionStore:rf,contextFromFlags:rv});if(d)return rA(d);let u=rf.get(n);if(!u)return rA({ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}});if(!tZ(r,u.device))return rA({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,{...rv(i.flags,u.appBundleId,u.trace?.outPath)});return rf.recordAction(u,{command:r,positionals:i.positionals??[],flags:i.flags??{},result:p??{}}),rA({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 rA(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 ry(){if(!A.existsSync(ru))return null;try{let e=JSON.parse(A.readFileSync(ru,"utf8"));if(!Number.isInteger(e.pid)||e.pid<=0)return null;return e}catch{return null}}!function(){if(!function(){A.existsSync(rc)||A.mkdirSync(rc,{recursive:!0});let e=JSON.stringify({pid:process.pid,version:rm,startedAt:Date.now(),processStartTime:rg},null,2),t=()=>{try{return A.writeFileSync(ru,e,{flag:"wx",mode:384}),!0}catch(e){if("EEXIST"===e.code)return!1;throw e}};if(t())return!0;let i=ry();if(i?.pid&&i.pid!==process.pid&&o(i.pid,i.processStartTime))return!1;try{A.unlinkSync(ru)}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 rI(e)}catch(e){i={ok:!1,error:a(e)}}e.write(`${JSON.stringify(i)}
|
|
16
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(rc)||A.mkdirSync(rc,{recursive:!0}),A.writeFileSync(rp,""),A.writeFileSync(rd,JSON.stringify({port:i,token:rh,pid:process.pid,version:rm,processStartTime:rg},null,2),{mode:384}),process.stdout.write(`AGENT_DEVICE_DAEMON_PORT=${t.port}
|
|
17
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(),rf.toArray()))rf.writeSessionLog(e);await tS(),A.existsSync(rd)&&A.unlinkSync(rd);let e=ry();if(!e||e.pid===process.pid)try{A.existsSync(ru)&&A.unlinkSync(ru)}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}
|