agent-device 0.11.4 → 0.11.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -3
- package/dist/src/168.js +3 -0
- package/dist/src/bin.js +70 -67
- package/dist/src/daemon.js +45 -43
- package/dist/src/index.d.ts +14 -0
- package/dist/src/index.js +1 -1
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +16 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +21 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +1 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +9 -1
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +63 -17
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SystemModal.swift +4 -1
- package/ios-runner/README.md +1 -1
- package/ios-runner/RUNNER_PROTOCOL.md +4 -0
- package/package.json +1 -1
- package/skills/agent-device/SKILL.md +2 -1
- package/skills/agent-device/references/debugging.md +5 -1
- package/skills/agent-device/references/exploration.md +22 -13
- package/skills/agent-device/references/verification.md +5 -4
- package/dist/src/916.js +0 -3
|
@@ -42,19 +42,21 @@ Open this file when the app or screen is already running and you need to discove
|
|
|
42
42
|
- `wait`
|
|
43
43
|
- `keyboard dismiss` when the keyboard obscures the next target
|
|
44
44
|
|
|
45
|
-
##
|
|
45
|
+
## Common mistakes to avoid
|
|
46
46
|
|
|
47
|
-
Do not treat `@ref` values as durable after navigation or dynamic updates. Re-snapshot after the UI changes, and switch to selectors when the flow must stay stable.
|
|
47
|
+
**Stale refs.** Do not treat `@ref` values as durable after navigation or dynamic updates. Re-snapshot after the UI changes, and switch to selectors when the flow must stay stable.
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
**Android AX tree lag.** After submits, route changes, or composer transitions, the accessibility tree can lag behind the visible UI. If `snapshot -i` and `screenshot` disagree:
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
1. Trust the screenshot as visual truth.
|
|
52
|
+
2. Take one fresh `snapshot -i`. Android retries briefly after navigation-sensitive actions.
|
|
53
|
+
3. If the tree still disagrees with the screenshot, wait briefly, then take one more fresh snapshot. Do not loop snapshots immediately.
|
|
52
54
|
|
|
53
|
-
|
|
55
|
+
**React Native dev overlays.** In dev or debug builds, warning or error overlays can block taps, change focus, or hide the real UI. Check for them near app open and after major transitions.
|
|
54
56
|
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
57
|
+
- Not blocking the task: dismiss and continue.
|
|
58
|
+
- Blocking or recurring: switch to [debugging.md](debugging.md) and collect evidence.
|
|
59
|
+
- Seen at any point: mention in the final summary even if dismissed.
|
|
58
60
|
|
|
59
61
|
## Common example loops
|
|
60
62
|
|
|
@@ -85,8 +87,10 @@ agent-device close
|
|
|
85
87
|
|
|
86
88
|
- Use plain `snapshot` when you only need to verify whether visible text or structure is on screen.
|
|
87
89
|
- Use `snapshot -i` when you need refs such as `@e3` for interactive exploration or for an intended interaction.
|
|
90
|
+
- On iOS and Android, default snapshot output is visible-first. Off-screen interactive content is surfaced as discovery hints (including inline scroll/list hidden-content hints when known), not shown as directly tappable refs.
|
|
88
91
|
- Treat large text-surface lines in `snapshot -i` as discovery output. If a node shows preview or truncation metadata, use `get text @ref` only after you have already decided that `snapshot -i` is needed for that surface.
|
|
89
92
|
- Use `snapshot -i -s "Camera"` or `snapshot -i -s @e3` when you want a smaller, scoped result.
|
|
93
|
+
- If `snapshot -i -s "<query>"` returns 0 nodes, the scope did not match the current screen. Widen the query or re-check the screen state instead of assuming the command silently fell back to the full tree.
|
|
90
94
|
- If `snapshot -i` returns 0 nodes but the screen is visibly populated, treat `screenshot` as visual truth, wait briefly, then re-run `snapshot -i` once before escalating.
|
|
91
95
|
- If `snapshot -i -d <n>` says the interactive output is empty at that depth, retry without `-d` instead of taking more shallow snapshots.
|
|
92
96
|
|
|
@@ -105,12 +109,15 @@ App: com.apple.Preferences
|
|
|
105
109
|
@e1 [ioscontentgroup]
|
|
106
110
|
@e2 [button] "Camera"
|
|
107
111
|
@e3 [button] "Privacy & Security"
|
|
112
|
+
[off-screen below] 2 interactive items: "Location Services", "Battery"
|
|
108
113
|
```
|
|
109
114
|
|
|
110
115
|
## Refs vs selectors
|
|
111
116
|
|
|
112
117
|
- Use refs for discovery, debugging, and short local loops.
|
|
113
118
|
- Use `scrollintoview @ref` when the target is already known from the current snapshot and you want the command to re-snapshot after each swipe until the element reaches the viewport safe band.
|
|
119
|
+
- If `scrollintoview @ref` succeeds, prefer the returned `currentRef` for the next action.
|
|
120
|
+
- Visible-first off-screen summaries are intentionally compact. If you need the full off-screen tree instead of a short summary, retry with `snapshot --raw`.
|
|
114
121
|
- Cap long searches with `--max-scrolls <n>` when the list may be unbounded or the target may not exist.
|
|
115
122
|
- Use selectors for deterministic scripts, assertions, and replay-friendly actions.
|
|
116
123
|
- Prefer selector or `@ref` targeting over raw coordinates.
|
|
@@ -129,10 +136,11 @@ agent-device is visible 'id="camera_settings_anchor"'
|
|
|
129
136
|
|
|
130
137
|
When `press @ref` fails:
|
|
131
138
|
|
|
132
|
-
1.
|
|
133
|
-
2.
|
|
134
|
-
3.
|
|
135
|
-
4.
|
|
139
|
+
1. If the error says the ref is off-screen, run `scrollintoview @ref` and reuse the returned `currentRef` or take one fresh snapshot.
|
|
140
|
+
2. Re-snapshot if the UI may have changed.
|
|
141
|
+
3. Retry `press @ref` or a selector-based `press`.
|
|
142
|
+
4. If `screenshot --overlay-refs --json` returned a reliable `overlayRefs[].center`, use `agent-device press <x> <y>`.
|
|
143
|
+
5. Use an external vision-based tap tool only after semantic and coordinate targeting fail.
|
|
136
144
|
|
|
137
145
|
- Prefer `@ref` over coordinates.
|
|
138
146
|
- Do not guess coordinates from the image when structured `center` is available.
|
|
@@ -173,6 +181,7 @@ Use this rule of thumb:
|
|
|
173
181
|
- Use `is` for assertions.
|
|
174
182
|
- Use `wait` when the UI needs time to settle after a mutation.
|
|
175
183
|
- Use `find "<query>" click --json` when you need search-driven targeting plus matched-target metadata.
|
|
184
|
+
- Use `find "<query>" click --first` or `--last` when ambiguous matches are expected and you want the first or last occurrence without falling back to raw coordinates.
|
|
176
185
|
- If you are forced onto raw coordinates, open [coordinate-system.md](coordinate-system.md) first.
|
|
177
186
|
|
|
178
187
|
Example:
|
|
@@ -211,7 +220,7 @@ Avoid this escalation path for visible-text questions:
|
|
|
211
220
|
|
|
212
221
|
- Do not jump from `snapshot -i` to `get text @ref`, then to web search, then to typing into a search box just to force the app to reveal the answer.
|
|
213
222
|
- Start with `snapshot`. If the text is not visible or exposed, report that directly.
|
|
214
|
-
- After Android submit or navigation-heavy actions
|
|
223
|
+
- After Android submit or navigation-heavy actions when the UI looks wrong: `screenshot` first, then `snapshot -i`.
|
|
215
224
|
|
|
216
225
|
Canonical QA loop:
|
|
217
226
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## When to open this file
|
|
4
4
|
|
|
5
|
-
Open this file when the task needs evidence, regression checks, replay maintenance, or
|
|
5
|
+
Open this file when the task needs evidence, regression checks, replay maintenance, or session performance measurements after the main interaction flow is already working.
|
|
6
6
|
|
|
7
7
|
## Main commands to reach for first
|
|
8
8
|
|
|
@@ -97,13 +97,14 @@ agent-device test ./smoke --platform android
|
|
|
97
97
|
|
|
98
98
|
## Performance checks
|
|
99
99
|
|
|
100
|
-
Use `perf --json` or `metrics --json` when you need
|
|
100
|
+
Use `perf --json` or `metrics --json` when you need session performance data for the active session.
|
|
101
101
|
|
|
102
102
|
```bash
|
|
103
103
|
agent-device open Settings --platform ios
|
|
104
104
|
agent-device perf --json
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
-
-
|
|
107
|
+
- `startup` is command round-trip timing around `open`.
|
|
108
108
|
- It is not true first-frame or first-interactive telemetry.
|
|
109
|
-
-
|
|
109
|
+
- Android app sessions also expose `memory` (`dumpsys meminfo`) and `cpu` (`dumpsys cpuinfo`) snapshots when the session has an app package context.
|
|
110
|
+
- iOS still reports `fps`, `memory`, and `cpu` as unavailable placeholders.
|
package/dist/src/916.js
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{AsyncLocalStorage as e}from"node:async_hooks";import t from"node:crypto";import r from"node:fs";import n from"node:os";import i from"node:path";import{fileURLToPath as a}from"node:url";import{spawn as o,spawnSync as s}from"node:child_process";import{PNG as l}from"pngjs";let u=new e,c=/(token|secret|password|authorization|cookie|api[_-]?key|access[_-]?key|private[_-]?key)/i,d=/(bearer\s+[a-z0-9._-]+|(?:api[_-]?key|token|secret|password)\s*[=:]\s*\S+)/i;function p(){return t.randomBytes(8).toString("hex")}async function f(e,r){let n={...e,diagnosticId:`${Date.now().toString(36)}-${t.randomBytes(4).toString("hex")}`,events:[]};return await u.run(n,r)}function m(){let e=u.getStore();return e?{diagnosticId:e.diagnosticId,requestId:e.requestId,session:e.session,command:e.command,debug:e.debug}:{}}function h(e){let t=u.getStore();if(!t)return;let n={ts:new Date().toISOString(),level:e.level??"info",phase:e.phase,session:t.session,requestId:t.requestId,command:t.command,durationMs:e.durationMs,data:e.data?S(e.data):void 0};if(t.events.push(n),!t.debug)return;let i=`[agent-device][diag] ${JSON.stringify(n)}
|
|
2
|
-
`;try{t.logPath&&r.appendFile(t.logPath,i,()=>{}),t.traceLogPath&&r.appendFile(t.traceLogPath,i,()=>{}),t.logPath||t.traceLogPath||process.stderr.write(i)}catch{}}async function g(e,t,r){let n=Date.now();try{let i=await t();return h({level:"info",phase:e,durationMs:Date.now()-n,data:r}),i}catch(t){throw h({level:"error",phase:e,durationMs:Date.now()-n,data:{...r??{},error:t instanceof Error?t.message:String(t)}}),t}}function w(e={}){let t=u.getStore();if(!t||!e.force&&!t.debug||0===t.events.length)return null;try{let e=(t.session??"default").replace(/[^a-zA-Z0-9._-]/g,"_"),a=new Date().toISOString().slice(0,10),o=i.join(n.homedir(),".agent-device","logs",e,a);r.mkdirSync(o,{recursive:!0});let s=new Date().toISOString().replace(/[:.]/g,"-"),l=i.join(o,`${s}-${t.diagnosticId}.ndjson`),u=t.events.map(e=>JSON.stringify(S(e)));return r.writeFileSync(l,`${u.join("\n")}
|
|
3
|
-
`),t.events=[],l}catch{return null}}function S(e){return function e(t,r,n){if(null==t)return t;if("string"==typeof t){var i=t,a=n;let e=i.trim();if(!e)return i;if(a&&c.test(a)||d.test(e))return"[REDACTED]";let r=function(e){try{let t=new URL(e);return t.search&&(t.search="?REDACTED"),(t.username||t.password)&&(t.username="REDACTED",t.password="REDACTED"),t.toString()}catch{return null}}(e);return r||(e.length>400?`${e.slice(0,200)}...<truncated>`:e)}if("object"!=typeof t)return t;if(r.has(t))return"[Circular]";if(r.add(t),Array.isArray(t))return t.map(t=>e(t,r));let o={};for(let[n,i]of Object.entries(t)){if(c.test(n)){o[n]="[REDACTED]";continue}o[n]=e(i,r,n)}return o}(e,new WeakSet)}class I extends Error{code;details;cause;constructor(e,t,r,n){super(t),this.code=e,this.details=r,this.cause=n}}function v(e){return e instanceof I?e:e instanceof Error?new I("UNKNOWN",e.message,void 0,e):new I("UNKNOWN","Unknown error",{err:e})}function A(e,t={}){let r=v(e),n=r.details?S(r.details):void 0,i=n&&"string"==typeof n.hint?n.hint:void 0,a=(n&&"string"==typeof n.diagnosticId?n.diagnosticId:void 0)??t.diagnosticId,o=(n&&"string"==typeof n.logPath?n.logPath:void 0)??t.logPath,s=i??function(e){switch(e){case"INVALID_ARGS":return"Check command arguments and run --help for usage examples.";case"SESSION_NOT_FOUND":return"Run open first or pass an explicit device selector.";case"TOOL_MISSING":return"Install required platform tooling and ensure it is available in PATH.";case"DEVICE_NOT_FOUND":return"Verify the target device is booted/connected and selectors match.";case"APP_NOT_INSTALLED":return"Run apps to discover the exact installed package or bundle id, or install the app before open.";case"UNSUPPORTED_OPERATION":return"This command is not available for the selected platform/device.";case"COMMAND_FAILED":default:return"Retry with --debug and inspect diagnostics log for details.";case"UNAUTHORIZED":return"Refresh daemon metadata and retry the command."}}(r.code),l=function(e){if(!e)return;let t={...e};return delete t.hint,delete t.diagnosticId,delete t.logPath,Object.keys(t).length>0?t:void 0}(n),u=function(e,t,r){if("COMMAND_FAILED"!==e||r?.processExitError!==!0)return t;let n=function(e){let t=[/^an error was encountered processing the command/i,/^underlying error\b/i,/^simulator device failed to complete the requested operation/i];for(let r of e.split("\n")){let e=r.trim();if(e&&!t.some(t=>t.test(e)))return e.length>200?`${e.slice(0,200)}...`:e}return null}("string"==typeof r?.stderr?r.stderr:"");return n||t}(r.code,r.message,n);return{code:r.code,message:u,hint:s,diagnosticId:a,logPath:o,details:l}}let y="<wifi|airplane|location> <on|off>",b="appearance <light|dark|toggle>",$="faceid <match|nonmatch|enroll|unenroll>",E="touchid <match|nonmatch|enroll|unenroll>",N="fingerprint <match|nonmatch>",D="permission <grant|deny|reset> <camera|microphone|photos|contacts|contacts-limited|notifications|calendar|location|location-always|media-library|motion|reminders|siri> [full|limited]",x="permission <grant|reset> <accessibility|screen-recording|input-monitoring>",T=`macOS supports only settings ${b} and settings ${x}. wifi|airplane|location remain unsupported on macOS.`,_=`settings ${y} | settings ${b} | settings ${$} | settings ${E} | settings ${N} | settings ${D} | settings ${x}`,O=`settings requires ${y}, ${b}, ${$}, ${E}, ${N}, ${D}, or ${x}`;function L(e){let t=e.trim().toLowerCase();return"appearance"===t||"permission"===t}function M(e){return`Unsupported macOS setting: ${e}. ${T}`}let C=["app","frontmost-app","desktop","menubar"];function k(e){let t=e?.trim().toLowerCase();if("app"===t||"frontmost-app"===t||"desktop"===t||"menubar"===t)return t;throw new I("INVALID_ARGS",`Invalid surface: ${e}. Use ${C.join("|")}.`)}function P(e){var t;let r,n=R(e.label),i=R(e.value),a=R(e.identifier),o=(t=a)&&!/^[\w.]+:id\/[\w.-]+$/i.test(t)&&!/^_?NS:\d+$/i.test(t)?a:"";return(r=F(e.type??"")).includes("textfield")||r.includes("securetextfield")||r.includes("searchfield")||r.includes("edittext")||r.includes("textview")||r.includes("textarea")?i||n||o:n||i||o}function R(e){return"string"==typeof e?e.trim():""}function F(e){let t=e.trim().replace(/XCUIElementType/gi,"").replace(/^AX/,"").toLowerCase(),r=Math.max(t.lastIndexOf("."),t.lastIndexOf("/"));return -1!==r&&(t=t.slice(r+1)),t}function B(e,t={}){let r=[],n=[];for(let i of e){let e=i.depth??0,a=i.label?.trim()||i.value?.trim()||i.identifier?.trim()||"",o=z(i.type??"Element");if("group"===o&&!a)continue;for(;r.length>0&&e<=r[r.length-1];)r.pop();let s=r.length;r.push(e),n.push({node:i,depth:s,type:o,text:j(i,s,!1,o,t)})}return n}function j(e,t,r,n,i={}){var a,o,s,l,u;let c,d,p=n??z(e.type??"Element"),f=(c=P(e),{text:c,isLargeSurface:d=function(e,t){if("text-view"===t||"text-field"===t||"search"===t)return!0;let r=F(e.type??""),n=`${e.role??""} ${e.subrole??""}`.toLowerCase();return r.includes("textview")||r.includes("textarea")||r.includes("textfield")||r.includes("securetextfield")||r.includes("searchfield")||r.includes("edittext")||n.includes("text area")||n.includes("text field")}(e,p),shouldSummarize:d&&!!(a=c)&&(a.length>80||/[\r\n]/.test(a))}),m=(o=e,s=p,l=i,u=f,l.summarizeTextSurfaces&&u.shouldSummarize&&function(e,t,r){let n=R(e.label);if(n&&n!==r)return n;let i=R(e.identifier);if(i&&!V(i)&&i!==r)return i;switch(t){case"text":case"text-view":return"Text view";case"text-field":return"Text field";case"search":return"Search field";default:return""}}(o,s,u.text)||G(o,s)),h=" ".repeat(t),g=e.ref?`@${e.ref}`:"",w=(function(e,t,r,n){let i,a=[];if(!1===e.enabled&&a.push("disabled"),!r.summarizeTextSurfaces||(!0===e.selected&&a.push("selected"),U(t)&&a.push("editable"),function(e,t){if("scroll-area"===t)return!0;let r=(e.type??"").toLowerCase(),n=`${e.role??""} ${e.subrole??""}`.toLowerCase();return r.includes("scroll")||n.includes("scroll")}(e,t)&&a.push("scrollable"),!n.shouldSummarize))return a;return a.push(`preview:"${((i=n.text.replace(/\s+/g," ").trim()).length<=48?i:`${i.slice(0,45)}...`).replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`),a.push("truncated"),[...new Set(a)]})(e,p,i,f).map(e=>` [${e}]`).join(""),S=m?` "${m}"`:"";return r?`${h}${g} [${p}]${w}`.trimEnd():`${h}${g} [${p}]${S}${w}`.trimEnd()}function G(e,t){let r=e.label?.trim(),n=e.value?.trim();if(U(t)){if(n)return n;if(r)return r}else if(r)return r;if(n)return n;let i=e.identifier?.trim();return!i||V(i)&&("group"===t||"image"===t||"list"===t||"collection"===t)?"":i}function z(e){let t=e.replace(/XCUIElementType/gi,"").toLowerCase(),r=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 r?"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"}}function U(e){return"text-field"===e||"text-view"===e||"search"===e}function V(e){return/^[\w.]+:id\/[\w.-]+$/i.test(e)}function q(){try{let e=H();return JSON.parse(r.readFileSync(i.join(e,"package.json"),"utf8")).version??"0.0.0"}catch{return"0.0.0"}}function H(){let e=i.dirname(a(import.meta.url)),t=e;for(let e=0;e<6;e+=1){let e=i.join(t,"package.json");if(r.existsSync(e))return t;t=i.dirname(t)}return e}function J(e){return e?{message:e}:{}}function W(e,t){return t?{...e,message:t}:e}function K(e){return"string"==typeof e?.message&&e.message.length>0?e.message:null}async function X(e,t,r={}){return new Promise((n,i)=>{let a=o(e,t,{cwd:r.cwd,env:r.env,stdio:["pipe","pipe","pipe"],detached:r.detached}),s="",l=r.binaryStdout?Buffer.alloc(0):void 0,u="",c=!1,d=er(r.timeoutMs),p=d?setTimeout(()=>{c=!0,a.kill("SIGKILL")},d):null;r.binaryStdout||a.stdout.setEncoding("utf8"),a.stderr.setEncoding("utf8"),void 0!==r.stdin&&a.stdin.write(r.stdin),a.stdin.end(),a.stdout.on("data",e=>{r.binaryStdout?l=Buffer.concat([l??Buffer.alloc(0),Buffer.isBuffer(e)?e:Buffer.from(e)]):s+=e}),a.stderr.on("data",e=>{u+=e}),a.on("error",r=>{(p&&clearTimeout(p),"ENOENT"===r.code)?i(new I("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},r)):i(new I("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},r))}),a.on("close",a=>{p&&clearTimeout(p);let o=a??1;c&&d?i(new I("COMMAND_FAILED",`${e} timed out after ${d}ms`,{cmd:e,args:t,stdout:s,stderr:u,exitCode:o,timeoutMs:d})):0===o||r.allowFailure?n({stdout:s,stderr:u,exitCode:o,stdoutBuffer:l}):i(new I("COMMAND_FAILED",`${e} exited with code ${o}`,{cmd:e,args:t,stdout:s,stderr:u,exitCode:o,processExitError:!0}))})})}async function Z(e){try{var t;let{shell:r,args:n}=(t=e,"win32"===process.platform?{shell:"cmd.exe",args:["/c","where",t]}:{shell:"bash",args:["-lc",`command -v ${t}`]}),i=await X(r,n,{allowFailure:!0});return 0===i.exitCode&&i.stdout.trim().length>0}catch{return!1}}function Q(e,t,r={}){let n=s(e,t,{cwd:r.cwd,env:r.env,stdio:["pipe","pipe","pipe"],encoding:r.binaryStdout?void 0:"utf8",input:r.stdin,timeout:er(r.timeoutMs)});if(n.error){let i=n.error.code;if("ETIMEDOUT"===i)throw new I("COMMAND_FAILED",`${e} timed out after ${er(r.timeoutMs)}ms`,{cmd:e,args:t,timeoutMs:er(r.timeoutMs)},n.error);if("ENOENT"===i)throw new I("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},n.error);throw new I("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},n.error)}let i=r.binaryStdout?Buffer.isBuffer(n.stdout)?n.stdout:Buffer.from(n.stdout??""):void 0,a=r.binaryStdout?"":"string"==typeof n.stdout?n.stdout:(n.stdout??"").toString(),o="string"==typeof n.stderr?n.stderr:(n.stderr??"").toString(),l=n.status??1;if(0!==l&&!r.allowFailure)throw new I("COMMAND_FAILED",`${e} exited with code ${l}`,{cmd:e,args:t,stdout:a,stderr:o,exitCode:l,processExitError:!0});return{stdout:a,stderr:o,exitCode:l,stdoutBuffer:i}}function Y(e,t,r={}){let n=o(e,t,{cwd:r.cwd,env:r.env,stdio:r.stdio??"ignore",detached:!0});return n.unref(),n.pid??0}async function ee(e,t,r={}){return new Promise((n,i)=>{let a=o(e,t,{cwd:r.cwd,env:r.env,stdio:["pipe","pipe","pipe"],detached:r.detached});r.onSpawn?.(a);let s="",l="",u=r.binaryStdout?Buffer.alloc(0):void 0,c=!1,d=er(r.timeoutMs),p=d?setTimeout(()=>{c=!0,a.kill("SIGKILL")},d):null;r.binaryStdout||a.stdout.setEncoding("utf8"),a.stderr.setEncoding("utf8"),void 0!==r.stdin&&a.stdin.write(r.stdin),a.stdin.end(),a.stdout.on("data",e=>{if(r.binaryStdout){u=Buffer.concat([u??Buffer.alloc(0),Buffer.isBuffer(e)?e:Buffer.from(e)]);return}let t=String(e);s+=t,r.onStdoutChunk?.(t)}),a.stderr.on("data",e=>{let t=String(e);l+=t,r.onStderrChunk?.(t)}),a.on("error",r=>{(p&&clearTimeout(p),"ENOENT"===r.code)?i(new I("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},r)):i(new I("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},r))}),a.on("close",a=>{p&&clearTimeout(p);let o=a??1;c&&d?i(new I("COMMAND_FAILED",`${e} timed out after ${d}ms`,{cmd:e,args:t,stdout:s,stderr:l,exitCode:o,timeoutMs:d})):0===o||r.allowFailure?n({stdout:s,stderr:l,exitCode:o,stdoutBuffer:u}):i(new I("COMMAND_FAILED",`${e} exited with code ${o}`,{cmd:e,args:t,stdout:s,stderr:l,exitCode:o,processExitError:!0}))})})}function et(e,t,r={}){let n=o(e,t,{cwd:r.cwd,env:r.env,stdio:["ignore","pipe","pipe"],detached:r.detached}),i="",a="";n.stdout.setEncoding("utf8"),n.stderr.setEncoding("utf8"),n.stdout.on("data",e=>{i+=e}),n.stderr.on("data",e=>{a+=e});let s=new Promise((o,s)=>{n.on("error",r=>{"ENOENT"===r.code?s(new I("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},r)):s(new I("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},r))}),n.on("close",n=>{let l=n??1;0===l||r.allowFailure?o({stdout:i,stderr:a,exitCode:l}):s(new I("COMMAND_FAILED",`${e} exited with code ${l}`,{cmd:e,args:t,stdout:i,stderr:a,exitCode:l,processExitError:!0}))})});return{child:n,wait:s}}function er(e){if(!Number.isFinite(e))return;let t=Math.floor(e);if(!(t<=0))return t}let en=[/(^|[/\s"'=])dist\/src\/daemon\.js($|[\s"'])/,/(^|[/\s"'=])src\/daemon\.ts($|[\s"'])/];function ei(e){if(!Number.isInteger(e)||e<=0)return!1;try{return process.kill(e,0),!0}catch(e){return"EPERM"===e.code}}function ea(e){if(!Number.isInteger(e)||e<=0)return null;try{let t=Q("ps",["-p",String(e),"-o","lstart="],{allowFailure:!0,timeoutMs:1e3});if(0!==t.exitCode)return null;let r=t.stdout.trim();return r.length>0?r:null}catch{return null}}function eo(e){if(!Number.isInteger(e)||e<=0)return null;try{let t=Q("ps",["-p",String(e),"-o","command="],{allowFailure:!0,timeoutMs:1e3});if(0!==t.exitCode)return null;let r=t.stdout.trim();return r.length>0?r:null}catch{return null}}function es(e,t){let r;if(!ei(e))return!1;if(t){let r=ea(e);if(!r||r!==t)return!1}let n=eo(e);return!!n&&!!(r=n.toLowerCase().replaceAll("\\","/")).includes("agent-device")&&en.some(e=>e.test(r))}function el(e,t){try{return process.kill(e,t),!0}catch(t){let e=t.code;if("ESRCH"===e||"EPERM"===e)return!1;throw t}}async function eu(e,t){if(!ei(e))return!0;let r=Date.now();for(;Date.now()-r<t;)if(await new Promise(e=>setTimeout(e,50)),!ei(e))return!0;return!ei(e)}async function ec(e,t){!es(e,t.expectedStartTime)||!el(e,"SIGTERM")||await eu(e,t.termTimeoutMs)||el(e,"SIGKILL")&&await eu(e,t.killTimeoutMs)}function ed(e){return e?.HOME?.trim()||n.homedir()}function ep(e,t={}){return"~"===e?ed(t.env):e.startsWith("~/")?i.join(ed(t.env),e.slice(2)):e}function ef(e,t={}){let r=ep(e,t);return i.isAbsolute(r)?r:i.resolve(t.cwd??process.cwd(),r)}function em(e){let t,r=(t=(e??"").trim())?ef(t):i.join(ep("~"),".agent-device");return{baseDir:r,infoPath:i.join(r,"daemon.json"),lockPath:i.join(r,"daemon.lock"),logPath:i.join(r,"daemon.log"),sessionsDir:i.join(r,"sessions")}}function eh(e){let t=(e??"").trim().toLowerCase();return"http"===t?"http":"dual"===t?"dual":"socket"}function eg(e){let t=(e??"").trim().toLowerCase();return"auto"===t?"auto":"socket"===t?"socket":"http"===t?"http":"auto"}function ew(e){return"tenant"===(e??"").trim().toLowerCase()?"tenant":"none"}function eS(e){if(!e)return;let t=e.trim();if(t&&/^[a-zA-Z0-9._-]{1,128}$/.test(t))return t}let eI=100,ev=new Set(["batch","replay"]),eA=new Set(["command","positionals","flags","runtime"]);function ey(e){let t;try{t=JSON.parse(e)}catch{throw new I("INVALID_ARGS","Batch steps must be valid JSON.")}if(!Array.isArray(t)||0===t.length)throw new I("INVALID_ARGS","Batch steps must be a non-empty JSON array.");return t}function eb(e,t){if(!Array.isArray(e)||0===e.length)throw new I("INVALID_ARGS","batch requires a non-empty batchSteps array.");if(e.length>t)throw new I("INVALID_ARGS",`batch has ${e.length} steps; max allowed is ${t}.`);let r=[];for(let t=0;t<e.length;t+=1){let n=e[t];if(!n||"object"!=typeof n)throw new I("INVALID_ARGS",`Invalid batch step at index ${t}.`);let i=Object.keys(n).filter(e=>!eA.has(e));if(i.length>0){let e=i.map(e=>`"${e}"`).join(", ");throw new I("INVALID_ARGS",`Batch step ${t+1} has unknown field(s): ${e}. Allowed fields: command, positionals, flags, runtime.`)}let a="string"==typeof n.command?n.command.trim().toLowerCase():"";if(!a)throw new I("INVALID_ARGS",`Batch step ${t+1} requires command.`);if(ev.has(a))throw new I("INVALID_ARGS",`Batch step ${t+1} cannot run ${a}.`);if(void 0!==n.positionals&&!Array.isArray(n.positionals))throw new I("INVALID_ARGS",`Batch step ${t+1} positionals must be an array.`);let o=n.positionals??[];if(o.some(e=>"string"!=typeof e))throw new I("INVALID_ARGS",`Batch step ${t+1} positionals must contain only strings.`);if(void 0!==n.flags&&("object"!=typeof n.flags||Array.isArray(n.flags)||!n.flags))throw new I("INVALID_ARGS",`Batch step ${t+1} flags must be an object.`);if(void 0!==n.runtime&&("object"!=typeof n.runtime||Array.isArray(n.runtime)||!n.runtime))throw new I("INVALID_ARGS",`Batch step ${t+1} runtime must be an object.`);r.push({command:a,positionals:o,flags:n.flags??{},runtime:n.runtime})}return r}function e$(e){let t=e.appId??e.bundleId??e.packageName;return{session:e.session,appId:t,appBundleId:e.bundleId,package:e.packageName}}function eE(e,t,r){return{deviceId:t,deviceName:r,..."android"===e?{serial:t}:"ios"===e?{udid:t}:{}}}function eN(e,t={}){let r=t.includeAndroidSerial??!0;return{platform:e.platform,target:e.target,device:e.name,id:e.id,..."ios"===e.platform?{device_udid:e.ios?.udid??e.id,ios_simulator_device_set:e.ios?.simulatorSetPath??null}:{},..."android"===e.platform&&r?{serial:e.android?.serial??e.id}:{}}}function eD(e){return{name:e.name,...eN(e.device,{includeAndroidSerial:!1}),createdAt:e.createdAt}}function ex(e){return{platform:e.platform,id:e.id,name:e.name,kind:e.kind,target:e.target,..."boolean"==typeof e.booted?{booted:e.booted}:{}}}function eT(e){let t=e.created?"Created":"Reused",r=e.booted?" (booted)":"";return W({udid:e.udid,device:e.device,runtime:e.runtime,ios_simulator_device_set:e.iosSimulatorDeviceSet??null,created:e.created,booted:e.booted},`${t}: ${e.device} ${e.udid}${r}`)}function e_(e){return e.bundleId??e.package??e.app}function eO(e){return W({app:e.app,appPath:e.appPath,platform:e.platform,...e.appId?{appId:e.appId}:{},...e.bundleId?{bundleId:e.bundleId}:{},...e.package?{package:e.package}:{}},`Installed: ${e_(e)}`)}function eL(e){return e.appName??e.bundleId??e.packageName??e.launchTarget}function eM(e){return W({launchTarget:e.launchTarget,...e.appName?{appName:e.appName}:{},...e.appId?{appId:e.appId}:{},...e.bundleId?{bundleId:e.bundleId}:{},...e.packageName?{package:e.packageName}:{},...e.installablePath?{installablePath:e.installablePath}:{},...e.archivePath?{archivePath:e.archivePath}:{},...e.materializationId?{materializationId:e.materializationId}:{},...e.materializationExpiresAt?{materializationExpiresAt:e.materializationExpiresAt}:{}},`Installed: ${eL(e)}`)}function eC(e){let t=e.appName??e.appBundleId??e.session;return W({session:e.session,...e.appName?{appName:e.appName}:{},...e.appBundleId?{appBundleId:e.appBundleId}:{},...e.startup?{startup:e.startup}:{},...e.runtime?{runtime:e.runtime}:{},...e.device?eN(e.device):{}},t?`Opened: ${t}`:"Opened")}function ek(e){return{session:e.session,...e.shutdown?{shutdown:e.shutdown}:{},...J(e.session?`Closed: ${e.session}`:"Closed")}}function eP(e){return{nodes:e.nodes,truncated:e.truncated,...e.appName?{appName:e.appName}:{},...e.appBundleId?{appBundleId:e.appBundleId}:{},...e.warnings&&e.warnings.length>0?{warnings:e.warnings}:{}}}function eR(e,t){try{return l.sync.read(e)}catch(e){throw new I("COMMAND_FAILED",`Failed to decode ${t} as PNG`,{label:t,reason:e instanceof Error?e.message:String(e)})}}export{I as AppError,eI as DEFAULT_BATCH_MAX_STEPS,C as SESSION_SURFACES,O as SETTINGS_INVALID_ARGS_MESSAGE,_ as SETTINGS_USAGE_OVERRIDE,v as asAppError,e$ as buildAppIdentifiers,eE as buildDeviceIdentifiers,B as buildSnapshotDisplayLines,p as createRequestId,eR as decodePng,G as displayLabel,h as emitDiagnostic,ep as expandUserHomePath,P as extractReadableText,H as findProjectRoot,w as flushDiagnosticsToSessionFile,z as formatRole,j as formatSnapshotLine,m as getDiagnosticsMeta,M as getUnsupportedMacOsSettingMessage,es as isAgentDeviceDaemonProcess,L as isMacOsSettingSupported,ei as isProcessAlive,A as normalizeError,eS as normalizeTenantId,ey as parseBatchStepsJson,k as parseSessionSurface,K as readCommandMessage,eo as readProcessCommand,ea as readProcessStartTime,q as readVersion,em as resolveDaemonPaths,eh as resolveDaemonServerMode,eg as resolveDaemonTransportPreference,e_ as resolveDeployResultTarget,eL as resolveInstallFromSourceResultTarget,ew as resolveSessionIsolationMode,ef as resolveUserPath,X as runCmd,et as runCmdBackground,Y as runCmdDetached,ee as runCmdStreaming,Q as runCmdSync,ek as serializeCloseResult,eO as serializeDeployResult,ex as serializeDevice,eT as serializeEnsureSimulatorResult,eM as serializeInstallFromSourceResult,eC as serializeOpenResult,eD as serializeSessionListEntry,eP as serializeSnapshotResult,ec as stopProcessForTakeover,J as successText,eb as validateAndNormalizeBatchSteps,Z as whichCmd,g as withDiagnosticTimer,f as withDiagnosticsScope,W as withSuccessText};
|