agent-device 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -3
- package/dist/src/274.js +1 -1
- package/dist/src/bin.js +25 -22
- package/dist/src/daemon.js +15 -11
- package/package.json +1 -1
- package/skills/agent-device/SKILL.md +8 -1
- package/src/cli.ts +7 -0
- package/src/core/__tests__/capabilities.test.ts +2 -0
- package/src/core/capabilities.ts +2 -0
- package/src/daemon/handlers/__tests__/replay-heal.test.ts +5 -0
- package/src/daemon/handlers/__tests__/session-reinstall.test.ts +219 -0
- package/src/daemon/handlers/__tests__/session.test.ts +122 -0
- package/src/daemon/handlers/find.ts +23 -3
- package/src/daemon/handlers/session.ts +175 -10
- package/src/platforms/__tests__/boot-diagnostics.test.ts +37 -8
- package/src/platforms/android/__tests__/index.test.ts +17 -0
- package/src/platforms/android/devices.ts +47 -14
- package/src/platforms/android/index.ts +101 -14
- package/src/platforms/boot-diagnostics.ts +78 -17
- package/src/platforms/ios/index.ts +76 -9
- package/src/platforms/ios/runner-client.ts +19 -1
- package/src/utils/__tests__/exec.test.ts +16 -0
- package/src/utils/__tests__/finders.test.ts +34 -0
- package/src/utils/__tests__/retry.test.ts +17 -0
- package/src/utils/args.ts +2 -0
- package/src/utils/exec.ts +39 -0
- package/src/utils/finders.ts +27 -9
- package/src/utils/retry.ts +72 -2
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ The project is in early development and considered experimental. Pull requests a
|
|
|
14
14
|
|
|
15
15
|
## Features
|
|
16
16
|
- Platforms: iOS (simulator + limited device support) and Android (emulator + device).
|
|
17
|
-
- Core commands: `open`, `back`, `home`, `app-switcher`, `press`, `long-press`, `focus`, `type`, `fill`, `scroll`, `scrollintoview`, `wait`, `alert`, `screenshot`, `close`.
|
|
17
|
+
- Core commands: `open`, `back`, `home`, `app-switcher`, `press`, `long-press`, `focus`, `type`, `fill`, `scroll`, `scrollintoview`, `wait`, `alert`, `screenshot`, `close`, `reinstall`.
|
|
18
18
|
- Inspection commands: `snapshot` (accessibility tree).
|
|
19
19
|
- Device tooling: `adb` (Android), `simctl`/`devicectl` (iOS via Xcode).
|
|
20
20
|
- Minimal dependencies; TypeScript executed directly on Node 22+ (no build step).
|
|
@@ -75,7 +75,7 @@ Coordinates:
|
|
|
75
75
|
- X increases to the right, Y increases downward.
|
|
76
76
|
|
|
77
77
|
## Command Index
|
|
78
|
-
- `open`, `close`, `home`, `back`, `app-switcher`
|
|
78
|
+
- `boot`, `open`, `close`, `reinstall`, `home`, `back`, `app-switcher`
|
|
79
79
|
- `snapshot`, `find`, `get`
|
|
80
80
|
- `click`, `focus`, `type`, `fill`, `press`, `long-press`, `scroll`, `scrollintoview`, `is`
|
|
81
81
|
- `alert`, `wait`, `screenshot`
|
|
@@ -123,6 +123,13 @@ Sessions:
|
|
|
123
123
|
- Session scripts are written to `~/.agent-device/sessions/<session>-<timestamp>.ad` when recording is enabled with `--save-script`.
|
|
124
124
|
- Deterministic replay is `.ad`-based; use `replay --update` (`-u`) to update selector drift and rewrite the replay file in place.
|
|
125
125
|
|
|
126
|
+
Navigation helpers:
|
|
127
|
+
- `boot --platform ios|android` ensures the target is ready without launching an app.
|
|
128
|
+
- Use `boot` mainly when starting a new session and `open` fails because no booted simulator/emulator is available.
|
|
129
|
+
- `open [app]` already boots/activates the selected target when needed.
|
|
130
|
+
- `reinstall <app> <path>` uninstalls and installs the app binary in one command (Android + iOS simulator in v1).
|
|
131
|
+
- `reinstall` accepts package/bundle id style app names and supports `~` in paths.
|
|
132
|
+
|
|
126
133
|
Find (semantic):
|
|
127
134
|
- `find <text> <action> [value]` finds by any text (label/value/identifier) using a scoped snapshot.
|
|
128
135
|
- `find text|label|value|role|id <value> <action> [value]` for specific locators.
|
|
@@ -186,8 +193,10 @@ App state:
|
|
|
186
193
|
|
|
187
194
|
Boot diagnostics:
|
|
188
195
|
- Boot failures include normalized reason codes in `error.details.reason` (JSON mode) and verbose logs.
|
|
189
|
-
- Reason codes: `
|
|
196
|
+
- Reason codes: `IOS_BOOT_TIMEOUT`, `IOS_RUNNER_CONNECT_TIMEOUT`, `ANDROID_BOOT_TIMEOUT`, `ADB_TRANSPORT_UNAVAILABLE`, `CI_RESOURCE_STARVATION_SUSPECTED`, `BOOT_COMMAND_FAILED`, `UNKNOWN`.
|
|
190
197
|
- Android boot waits fail fast for permission/tooling issues and do not always collapse into timeout errors.
|
|
198
|
+
- Use `agent-device boot --platform ios|android` when starting a new session only if `open` cannot find/connect to an available target.
|
|
199
|
+
- Set `AGENT_DEVICE_RETRY_LOGS=1` to print structured retry telemetry (attempt, phase, delay, elapsed/remaining deadline, reason).
|
|
191
200
|
|
|
192
201
|
## App resolution
|
|
193
202
|
- Bundle/package identifiers are accepted directly (e.g., `com.apple.Preferences`).
|
package/dist/src/274.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e,{promises as t}from"node:fs";import n from"node:path";import{fileURLToPath as o,pathToFileURL as r}from"node:url";import{spawn as i}from"node:child_process";function d(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}class
|
|
1
|
+
import e,{promises as t}from"node:fs";import n from"node:path";import{fileURLToPath as o,pathToFileURL as r}from"node:url";import{spawn as i}from"node:child_process";function d(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}class u extends Error{constructor(e,t,n,o){super(t),d(this,"code",void 0),d(this,"details",void 0),d(this,"cause",void 0),this.code=e,this.details=n,this.cause=o}}function s(e){return e instanceof u?e:e instanceof Error?new u("UNKNOWN",e.message,void 0,e):new u("UNKNOWN","Unknown error",{err:e})}function a(){try{let t=c();return JSON.parse(e.readFileSync(n.join(t,"package.json"),"utf8")).version??"0.0.0"}catch{return"0.0.0"}}function c(){let t=n.dirname(o(import.meta.url)),r=t;for(let t=0;t<6;t+=1){let t=n.join(r,"package.json");if(e.existsSync(t))return r;r=n.dirname(r)}return t}async function f(e,t,n={}){return new Promise((o,r)=>{let d=i(e,t,{cwd:n.cwd,env:n.env,stdio:["pipe","pipe","pipe"]}),s="",a=n.binaryStdout?Buffer.alloc(0):void 0,c="",f=!1,l=function(e){if(!Number.isFinite(e))return;let t=Math.floor(e);if(!(t<=0))return t}(n.timeoutMs),m=l?setTimeout(()=>{f=!0,d.kill("SIGKILL")},l):null;n.binaryStdout||d.stdout.setEncoding("utf8"),d.stderr.setEncoding("utf8"),void 0!==n.stdin&&d.stdin.write(n.stdin),d.stdin.end(),d.stdout.on("data",e=>{n.binaryStdout?a=Buffer.concat([a??Buffer.alloc(0),Buffer.isBuffer(e)?e:Buffer.from(e)]):s+=e}),d.stderr.on("data",e=>{c+=e}),d.on("error",n=>{(m&&clearTimeout(m),"ENOENT"===n.code)?r(new u("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},n)):r(new u("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},n))}),d.on("close",i=>{m&&clearTimeout(m);let d=i??1;f&&l?r(new u("COMMAND_FAILED",`${e} timed out after ${l}ms`,{cmd:e,args:t,stdout:s,stderr:c,exitCode:d,timeoutMs:l})):0===d||n.allowFailure?o({stdout:s,stderr:c,exitCode:d,stdoutBuffer:a}):r(new u("COMMAND_FAILED",`${e} exited with code ${d}`,{cmd:e,args:t,stdout:s,stderr:c,exitCode:d}))})})}async function l(e){try{var t;let{shell:n,args:o}=(t=e,"win32"===process.platform?{shell:"cmd.exe",args:["/c","where",t]}:{shell:"bash",args:["-lc",`command -v ${t}`]}),r=await f(n,o,{allowFailure:!0});return 0===r.exitCode&&r.stdout.trim().length>0}catch{return!1}}function m(e,t,n={}){i(e,t,{cwd:n.cwd,env:n.env,stdio:"ignore",detached:!0}).unref()}async function p(e,t,n={}){return new Promise((o,r)=>{let d=i(e,t,{cwd:n.cwd,env:n.env,stdio:["pipe","pipe","pipe"]}),s="",a="",c=n.binaryStdout?Buffer.alloc(0):void 0;n.binaryStdout||d.stdout.setEncoding("utf8"),d.stderr.setEncoding("utf8"),void 0!==n.stdin&&d.stdin.write(n.stdin),d.stdin.end(),d.stdout.on("data",e=>{if(n.binaryStdout){c=Buffer.concat([c??Buffer.alloc(0),Buffer.isBuffer(e)?e:Buffer.from(e)]);return}let t=String(e);s+=t,n.onStdoutChunk?.(t)}),d.stderr.on("data",e=>{let t=String(e);a+=t,n.onStderrChunk?.(t)}),d.on("error",n=>{"ENOENT"===n.code?r(new u("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},n)):r(new u("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},n))}),d.on("close",i=>{let d=i??1;0===d||n.allowFailure?o({stdout:s,stderr:a,exitCode:d,stdoutBuffer:c}):r(new u("COMMAND_FAILED",`${e} exited with code ${d}`,{cmd:e,args:t,stdout:s,stderr:a,exitCode:d}))})})}function w(e,t,n={}){let o=i(e,t,{cwd:n.cwd,env:n.env,stdio:["ignore","pipe","pipe"]}),r="",d="";o.stdout.setEncoding("utf8"),o.stderr.setEncoding("utf8"),o.stdout.on("data",e=>{r+=e}),o.stderr.on("data",e=>{d+=e});let s=new Promise((i,s)=>{o.on("error",n=>{"ENOENT"===n.code?s(new u("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},n)):s(new u("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},n))}),o.on("close",o=>{let a=o??1;0===a||n.allowFailure?i({stdout:r,stderr:d,exitCode:a}):s(new u("COMMAND_FAILED",`${e} exited with code ${a}`,{cmd:e,args:t,stdout:r,stderr:d,exitCode:a}))})});return{child:o,wait:s}}export{default as node_net}from"node:net";export{default as node_os}from"node:os";export{s as asAppError,u as errors_AppError,o as fileURLToPath,c as findProjectRoot,e as node_fs,n as node_path,r as pathToFileURL,t as promises,a as readVersion,f as runCmd,w as runCmdBackground,m as runCmdDetached,p as runCmdStreaming,l as whichCmd};
|
package/dist/src/bin.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import{node_path as e,asAppError as t,pathToFileURL as r,runCmdDetached as
|
|
1
|
+
import{node_path as e,asAppError as t,pathToFileURL as r,runCmdDetached as n,node_fs as a,node_os as i,node_net as s,errors_AppError as o,readVersion as l,findProjectRoot as c}from"./274.js";function d(e){process.stdout.write(`${JSON.stringify(e,null,2)}
|
|
2
2
|
`)}function u(e){let t=e.details?`
|
|
3
3
|
${JSON.stringify(e.details,null,2)}`:"";process.stderr.write(`Error (${e.code}): ${e.message}${t}
|
|
4
|
-
`)}function p(e,t,r){let
|
|
5
|
-
`)}),
|
|
6
|
-
`),process.exit(0)),(
|
|
4
|
+
`)}function p(e,t,r){let n=f(e.type??"Element"),a=function(e,t){var r,n;let a=e.label?.trim(),i=e.value?.trim();if("text-field"===(r=t)||"text-view"===r||"search"===r){if(i)return i;if(a)return a}else if(a)return a;if(i)return i;let s=e.identifier?.trim();return!s||(n=s,/^[\w.]+:id\/[\w.-]+$/i.test(n)&&("group"===t||"image"===t||"list"===t||"collection"===t))?"":s}(e,n),i=" ".repeat(t),s=e.ref?`@${e.ref}`:"",o=[!1===e.enabled?"disabled":null].filter(Boolean).join(", "),l=o?` [${o}]`:"",c=a?` "${a}"`:"";return r?`${i}${s} [${n}]${l}`.trimEnd():`${i}${s} [${n}]${c}${l}`.trimEnd()}function f(e){let t=e.replace(/XCUIElementType/gi,"").toLowerCase(),r=e.includes(".")&&(e.startsWith("android.")||e.startsWith("androidx.")||e.startsWith("com."));switch(t.startsWith("ax")&&(t=t.replace(/^ax/,"")),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"}}let m=e.join(i.homedir(),".agent-device"),h=e.join(m,"daemon.json"),w=function(){let e=process.env.AGENT_DEVICE_DAEMON_TIMEOUT_MS;if(!e)return 6e4;let t=Number(e);return Number.isFinite(t)?Math.max(1e3,Math.floor(t)):6e4}();async function y(e){let t=await v(),r={...e,token:t.token};return await $(t,r)}async function v(){let e=g(),t=l();if(e&&e.version===t&&await b(e))return e;e&&(e.version!==t||!await b(e))&&a.existsSync(h)&&a.unlinkSync(h),await x();let r=Date.now();for(;Date.now()-r<5e3;){let e=g();if(e&&await b(e))return e;await new Promise(e=>setTimeout(e,100))}throw new o("COMMAND_FAILED","Failed to start daemon",{infoPath:h,hint:"Run pnpm build, or delete ~/.agent-device/daemon.json if stale."})}function g(){if(!a.existsSync(h))return null;try{let e=JSON.parse(a.readFileSync(h,"utf8"));if(!e.port||!e.token)return null;return e}catch{return null}}async function b(e){return new Promise(t=>{let r=s.createConnection({host:"127.0.0.1",port:e.port},()=>{r.destroy(),t(!0)});r.on("error",()=>{t(!1)})})}async function x(){let t=c(),r=e.join(t,"dist","src","daemon.js"),i=e.join(t,"src","daemon.ts"),s=a.existsSync(r);if(!s&&!a.existsSync(i))throw new o("COMMAND_FAILED","Daemon entry not found",{distPath:r,srcPath:i});let l=s?[r]:["--experimental-strip-types",i];n(process.execPath,l)}async function $(e,t){return new Promise((r,n)=>{let a=s.createConnection({host:"127.0.0.1",port:e.port},()=>{a.write(`${JSON.stringify(t)}
|
|
5
|
+
`)}),i=setTimeout(()=>{a.destroy(),n(new o("COMMAND_FAILED","Daemon request timed out",{timeoutMs:w}))},w),l="";a.setEncoding("utf8"),a.on("data",e=>{let t=(l+=e).indexOf("\n");if(-1===t)return;let s=l.slice(0,t).trim();if(s)try{let e=JSON.parse(s);a.end(),clearTimeout(i),r(e)}catch(e){clearTimeout(i),n(e)}}),a.on("error",e=>{clearTimeout(i),n(e)})})}async function S(r){let n=function(e){let t={json:!1,help:!1,version:!1},r=[];for(let n=0;n<e.length;n+=1){let a=e[n];if("--json"===a){t.json=!0;continue}if("--help"===a||"-h"===a){t.help=!0;continue}if("--version"===a||"-V"===a){t.version=!0;continue}if("--verbose"===a||"-v"===a){t.verbose=!0;continue}if("-i"===a){t.snapshotInteractiveOnly=!0;continue}if("-c"===a){t.snapshotCompact=!0;continue}if("--raw"===a){t.snapshotRaw=!0;continue}if("--no-record"===a){t.noRecord=!0;continue}if("--save-script"===a){t.saveScript=!0;continue}if("--update"===a||"-u"===a){t.replayUpdate=!0;continue}if("--user-installed"===a){t.appsFilter="user-installed";continue}if("--all"===a){t.appsFilter="all";continue}if("--metadata"===a){t.appsMetadata=!0;continue}if(a.startsWith("--backend")){let r=a.includes("=")?a.split("=")[1]:e[n+1];if(a.includes("=")||(n+=1),"ax"!==r&&"xctest"!==r)throw new o("INVALID_ARGS",`Invalid backend: ${r}`);t.snapshotBackend=r;continue}if(a.startsWith("--")){let[r,i]=a.split("="),s=i??e[n+1];switch(!i&&(n+=1),r){case"--platform":if("ios"!==s&&"android"!==s)throw new o("INVALID_ARGS",`Invalid platform: ${s}`);t.platform=s;break;case"--depth":{let e=Number(s);if(!Number.isFinite(e)||e<0)throw new o("INVALID_ARGS",`Invalid depth: ${s}`);t.snapshotDepth=Math.floor(e);break}case"--scope":t.snapshotScope=s;break;case"--device":t.device=s;break;case"--udid":t.udid=s;break;case"--serial":t.serial=s;break;case"--out":t.out=s;break;case"--session":t.session=s;break;case"--activity":t.activity=s;break;default:throw new o("INVALID_ARGS",`Unknown flag: ${r}`)}continue}if("-d"===a){let r=e[n+1];n+=1;let a=Number(r);if(!Number.isFinite(a)||a<0)throw new o("INVALID_ARGS",`Invalid depth: ${r}`);t.snapshotDepth=Math.floor(a);continue}if("-s"===a){let r=e[n+1];n+=1,t.snapshotScope=r;continue}r.push(a)}return{command:r.shift()??null,positionals:r,flags:t}}(r);n.flags.version&&(process.stdout.write(`${l()}
|
|
6
|
+
`),process.exit(0)),(n.flags.help||!n.command)&&(process.stdout.write(`agent-device <command> [args] [--json]
|
|
7
7
|
|
|
8
8
|
CLI to control iOS and Android devices for AI agents.
|
|
9
9
|
|
|
10
10
|
Commands:
|
|
11
|
+
boot Ensure target device/simulator is booted and ready
|
|
11
12
|
open [app] Boot device/simulator; optionally launch app
|
|
12
13
|
close [app] Close app or just end session
|
|
14
|
+
reinstall <app> <path> Uninstall + install app from binary path
|
|
13
15
|
snapshot [-i] [-c] [-d <depth>] [-s <scope>] [--raw] [--backend ax|xctest]
|
|
14
16
|
Capture accessibility tree
|
|
15
17
|
-i Interactive elements only
|
|
@@ -71,30 +73,31 @@ Flags:
|
|
|
71
73
|
--all Apps: list all packages (Android only)
|
|
72
74
|
--version, -V Print version and exit
|
|
73
75
|
|
|
74
|
-
`),process.exit(+!
|
|
75
|
-
`),w&&w();return}let e=await y({session:h,command:
|
|
76
|
-
`:"";if(0===
|
|
77
|
-
`;if(t.raw){let e=
|
|
76
|
+
`),process.exit(+!n.flags.help));let{command:s,positionals:c,flags:m}=n,h=m.session??process.env.AGENT_DEVICE_SESSION??"default",w=m.verbose&&!m.json?function(){try{let t=e.join(i.homedir(),".agent-device","daemon.log"),r=0,n=!1,s=setInterval(()=>{if(n||!a.existsSync(t))return;let e=a.statSync(t);if(e.size<=r)return;let i=a.openSync(t,"r"),s=Buffer.alloc(e.size-r);a.readSync(i,s,0,s.length,r),a.closeSync(i),r=e.size,s.length>0&&process.stdout.write(s.toString("utf8"))},200);return()=>{n=!0,clearInterval(s)}}catch{return null}}():null;try{if("session"===s){let e=c[0]??"list";if("list"!==e)throw new o("INVALID_ARGS","session only supports list");let t=await y({session:h,command:"session_list",positionals:[],flags:{}});if(!t.ok)throw new o(t.error.code,t.error.message);m.json?d({success:!0,data:t.data??{}}):process.stdout.write(`${JSON.stringify(t.data??{},null,2)}
|
|
77
|
+
`),w&&w();return}let e=await y({session:h,command:s,positionals:c,flags:m});if(e.ok){if(m.json){d({success:!0,data:e.data??{}}),w&&w();return}if("snapshot"===s){process.stdout.write(function(e,t={}){let r=e.nodes,n=Array.isArray(r)?r:[],a=!!e.truncated,i="string"==typeof e.appName?e.appName:void 0,s="string"==typeof e.appBundleId?e.appBundleId:void 0,o=[];i&&o.push(`Page: ${i}`),s&&o.push(`App: ${s}`);let l=`Snapshot: ${n.length} nodes${a?" (truncated)":""}`,c=o.length>0?`${o.join("\n")}
|
|
78
|
+
`:"";if(0===n.length)return`${c}${l}
|
|
79
|
+
`;if(t.raw){let e=n.map(e=>JSON.stringify(e));return`${c}${l}
|
|
78
80
|
${e.join("\n")}
|
|
79
|
-
`}if(t.flatten){let e=
|
|
81
|
+
`}if(t.flatten){let e=n.map(e=>p(e,0,!1));return`${c}${l}
|
|
80
82
|
${e.join("\n")}
|
|
81
|
-
`}let d=[],u=[];for(let e of
|
|
83
|
+
`}let d=[],u=[];for(let e of n){let t=e.depth??0;for(;d.length>0&&t<=d[d.length-1];)d.pop();let r=e.label?.trim()||e.value?.trim()||e.identifier?.trim()||"",n="group"===f(e.type??"Element")&&!r;n&&d.push(t);let a=n?t:Math.max(0,t-d.length);u.push(p(e,a,n))}return`${c}${l}
|
|
82
84
|
${u.join("\n")}
|
|
83
|
-
`}(e.data??{},{raw:m.snapshotRaw,flatten:m.snapshotInteractiveOnly})),w&&w();return}if("get"===
|
|
85
|
+
`}(e.data??{},{raw:m.snapshotRaw,flatten:m.snapshotInteractiveOnly})),w&&w();return}if("get"===s){let t=c[0];if("text"===t){let t=e.data?.text??"";process.stdout.write(`${t}
|
|
84
86
|
`),w&&w();return}if("attrs"===t){let t=e.data?.node??{};process.stdout.write(`${JSON.stringify(t,null,2)}
|
|
85
|
-
`),w&&w();return}}if("find"===
|
|
87
|
+
`),w&&w();return}}if("find"===s){let t=e.data;if("string"==typeof t?.text){process.stdout.write(`${t.text}
|
|
86
88
|
`),w&&w();return}if("boolean"==typeof t?.found){process.stdout.write(`Found: ${t.found}
|
|
87
89
|
`),w&&w();return}if(t?.node){process.stdout.write(`${JSON.stringify(t.node,null,2)}
|
|
88
|
-
`),w&&w();return}}if("is"===
|
|
89
|
-
`),w&&w();return}if("
|
|
90
|
-
`),w&&w();return}if(
|
|
91
|
-
`),w&&w();return}if("
|
|
92
|
-
`),w&&w();return}if("
|
|
90
|
+
`),w&&w();return}}if("is"===s){let t=e.data?.predicate??"assertion";process.stdout.write(`Passed: is ${t}
|
|
91
|
+
`),w&&w();return}if("boot"===s){let t=e.data?.platform??"unknown",r=e.data?.device??e.data?.id??"unknown";process.stdout.write(`Boot ready: ${r} (${t})
|
|
92
|
+
`),w&&w();return}if("click"===s){let t=e.data?.ref??"",r=e.data?.x,n=e.data?.y;t&&"number"==typeof r&&"number"==typeof n&&process.stdout.write(`Clicked @${t} (${r}, ${n})
|
|
93
|
+
`),w&&w();return}if(e.data&&"object"==typeof e.data){let t=e.data;if("devices"===s){let e=(Array.isArray(t.devices)?t.devices:[]).map(e=>{let t=e?.name??e?.id??"unknown",r=e?.platform??"unknown",n=e?.kind?` ${e.kind}`:"",a="boolean"==typeof e?.booted?` booted=${e.booted}`:"";return`${t} (${r}${n})${a}`});process.stdout.write(`${e.join("\n")}
|
|
94
|
+
`),w&&w();return}if("apps"===s){let e=(Array.isArray(t.apps)?t.apps:[]).map(e=>{if("string"==typeof e)return e;if(e&&"object"==typeof e){let t=e.bundleId??e.package,r=e.name??e.label;return r&&t?`${r} (${t})`:t&&"boolean"==typeof e.launchable?`${t} (launchable=${e.launchable})`:t?String(t):JSON.stringify(e)}return String(e)});process.stdout.write(`${e.join("\n")}
|
|
95
|
+
`),w&&w();return}if("appstate"===s){let e=t?.platform,r=t?.appBundleId,n=t?.appName,a=t?.source,i=t?.package,s=t?.activity;if("ios"===e){process.stdout.write(`Foreground app: ${n??r}
|
|
93
96
|
`),r&&process.stdout.write(`Bundle: ${r}
|
|
94
|
-
`),
|
|
95
|
-
`),w&&w();return}if("android"===e){process.stdout.write(`Foreground app: ${
|
|
96
|
-
`),
|
|
97
|
-
`),w&&w();return}}}w&&w();return}throw new o(e.error.code,e.error.message,e.error.details)}catch(r){let e=t(r);if(m.json)d({success:!1,error:{code:e.code,message:e.message,details:e.details}});else if(u(e),m.verbose)try{let e=await import("node:fs"),t=await import("node:os"),r=(await import("node:path")).join(t.homedir(),".agent-device","daemon.log");if(e.existsSync(r)){let t=e.readFileSync(r,"utf8").split("\n"),
|
|
97
|
+
`),a&&process.stdout.write(`Source: ${a}
|
|
98
|
+
`),w&&w();return}if("android"===e){process.stdout.write(`Foreground app: ${i??"unknown"}
|
|
99
|
+
`),s&&process.stdout.write(`Activity: ${s}
|
|
100
|
+
`),w&&w();return}}}w&&w();return}throw new o(e.error.code,e.error.message,e.error.details)}catch(r){let e=t(r);if(m.json)d({success:!1,error:{code:e.code,message:e.message,details:e.details}});else if(u(e),m.verbose)try{let e=await import("node:fs"),t=await import("node:os"),r=(await import("node:path")).join(t.homedir(),".agent-device","daemon.log");if(e.existsSync(r)){let t=e.readFileSync(r,"utf8").split("\n"),n=t.slice(Math.max(0,t.length-200)).join("\n");n.trim().length>0&&process.stderr.write(`
|
|
98
101
|
[daemon log]
|
|
99
|
-
${
|
|
102
|
+
${n}
|
|
100
103
|
`)}}catch{}w&&w(),process.exit(1)}}r(process.argv[1]??"").href===import.meta.url&&S(process.argv.slice(2)).catch(e=>{u(t(e)),process.exit(1)}),S(process.argv.slice(2));
|