agent-device 0.14.9 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -4
- package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.14.9.apk → agent-device-android-snapshot-helper-0.15.0.apk} +0 -0
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.15.0.apk.sha256 +1 -0
- package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.14.9.manifest.json → agent-device-android-snapshot-helper-0.15.0.manifest.json} +6 -6
- package/dist/src/1769.js +7 -0
- package/dist/src/2151.js +429 -0
- package/dist/src/221.js +4 -4
- package/dist/src/2842.js +1 -0
- package/dist/src/3572.js +1 -0
- package/dist/src/4057.js +1 -1
- package/dist/src/840.js +2 -0
- package/dist/src/9542.js +2 -2
- package/dist/src/9639.js +2 -2
- package/dist/src/android-adb.d.ts +38 -9
- package/dist/src/android-adb.js +1 -1
- package/dist/src/android-snapshot-helper.d.ts +23 -0
- package/dist/src/cli.js +60 -57
- package/dist/src/contracts.d.ts +1 -0
- package/dist/src/finders.d.ts +1 -0
- package/dist/src/index.d.ts +19 -22
- package/dist/src/internal/companion-tunnel.js +1 -1
- package/dist/src/internal/daemon.js +51 -23
- package/dist/src/remote-config.d.ts +17 -14
- package/dist/src/selectors.d.ts +2 -0
- package/dist/src/server.js +2 -20
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/xcshareddata/xcschemes/AgentDeviceRunner.xcscheme +7 -1
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +128 -47
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +734 -10
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +93 -7
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +5 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +9 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SystemModal.swift +1 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +1 -2
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests.xctestplan +26 -0
- package/package.json +25 -11
- package/server.json +3 -3
- package/skills/agent-device/SKILL.md +2 -7
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.14.9.apk.sha256 +0 -1
- package/dist/src/180.js +0 -1
- package/dist/src/6108.js +0 -26
- package/dist/src/6642.js +0 -1
- package/dist/src/7462.js +0 -1
- package/dist/src/8809.js +0 -8
- package/dist/src/command-schema.js +0 -382
package/dist/src/6642.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{AppError as t}from"./9152.js";async function i(o,e={}){let d=["logcat","-d","-v","time"];void 0!==e.lines&&d.push("-t",String(Math.max(1,Math.floor(e.lines))));let a=await o(d,{allowFailure:!0,timeoutMs:e.timeoutMs,signal:e.signal});if(0!==a.exitCode)throw new t("COMMAND_FAILED","Failed to capture Android logcat",{stdout:a.stdout,stderr:a.stderr,exitCode:a.exitCode});return a.stdout}function o(i,e={}){if(!i.spawn)throw new t("UNSUPPORTED_OPERATION","Android ADB provider does not support streams",{capability:"adb.spawn"});let d=["logcat","-v","time"];e.pid&&d.push("--pid",e.pid);let a=i.spawn(d,{stdio:["ignore","pipe","pipe"],signal:e.signal});return e.output&&a.stdout&&a.stdout.pipe(e.output,{end:!1}),a}export{i as captureAndroidLogcatWithAdb,o as streamAndroidLogcatWithAdb};
|
package/dist/src/7462.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{AppError as t}from"./9152.js";let n="<wifi|airplane|location> <on|off>",e="location set <lat> <lon>",i="animations <on|off>",o="appearance <light|dark|toggle>",s="faceid <match|nonmatch|enroll|unenroll>",a="touchid <match|nonmatch|enroll|unenroll>",r="fingerprint <match|nonmatch>",c="permission <grant|deny|reset> <camera|microphone|photos|contacts|contacts-limited|notifications|calendar|location|location-always|media-library|motion|reminders|siri> [full|limited]",p="permission <grant|reset> <accessibility|screen-recording|input-monitoring>",l=`macOS supports only settings ${o} and settings ${p}. wifi|airplane|location|animations remain unsupported on macOS.`,m=`settings ${n} | settings ${e} | settings ${i} | settings ${o} | settings ${s} | settings ${a} | settings ${r} | settings ${c} | settings ${p}`,g=`settings requires ${n}, ${e}, ${i}, ${o}, ${s}, ${a}, ${r}, ${c}, or ${p}`;function S(t){let n=t.trim().toLowerCase();return"appearance"===n||"permission"===n}function $(t){return`Unsupported macOS setting: ${t}. ${l}`}let u=["app","frontmost-app","desktop","menubar"];function f(n){let e=n?.trim().toLowerCase();if("app"===e||"frontmost-app"===e||"desktop"===e||"menubar"===e)return e;throw new t("INVALID_ARGS",`Invalid surface: ${n}. Use ${u.join("|")}.`)}export{u as SESSION_SURFACES,g as SETTINGS_INVALID_ARGS_MESSAGE,m as SETTINGS_USAGE_OVERRIDE,$ as getUnsupportedMacOsSettingMessage,S as isMacOsSettingSupported,f as parseSessionSurface};
|
package/dist/src/8809.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import{promises as e}from"node:fs";import t from"node:os";import a from"node:path";import{asAppError as n,AppError as i}from"./9152.js";import{emitDiagnostic as r}from"./7599.js";import{runCmd as o,resolveFileOverridePath as s,whichCmd as l,runCmdDetached as d}from"./9818.js";import{ensureAndroidSdkPathConfigured as u,resolveAndroidArchivePackageName as c}from"./7651.js";import{sleep as p}from"./4829.js";import{resolveAndroidAdbExecutor as f,installAndroidAdbPackage as m}from"./9639.js";import{materializeInstallablePath as w,isTrustedInstallSourceUrl as A}from"./989.js";function h(e){let t=new Set;for(let a of e.split("\n")){let e=a.trim();if(!e)continue;let n=e.split(/\s+/)[0],i=n.includes("/")?n.split("/")[0]:n;i&&t.add(i)}return Array.from(t)}function y(e){return e.split("\n").map(e=>{let t=e.trim();return t.startsWith("package:")?t.slice(8):t}).filter(Boolean)}function b(e){let t=e.split("\n");for(let e of["mCurrentFocus=Window{","mFocusedApp=AppWindowToken{","mResumedActivity:","ResumedActivity:"])for(let a of t){let t=a.indexOf(e);if(-1===t)continue;let n=function(e){for(let t of e.trim().split(/\s+/)){let e=t.indexOf("/");if(e<=0)continue;let a=_(t.slice(0,e),!1),n=_(t.slice(e+1),!0);if(a&&n&&a.length===e)return{package:a,activity:n}}return null}(a.slice(t+e.length));if(n)return n}return null}function _(e,t){let a=0;for(;a<e.length&&function(e,t){if(!e)return!1;let a=e.charCodeAt(0);return a>=48&&a<=57||a>=65&&a<=90||a>=97&&a<=122||"_"===e||"."===e||t&&"$"===e}(e[a],t);)a+=1;return e.slice(0,a)}function v(e){let t=e.trim();if(!t||/\s/.test(t))return!1;let a=/^([A-Za-z][A-Za-z0-9+.-]*):(.+)$/.exec(t);if(!a)return!1;let n=a[1]?.toLowerCase(),i=a[2]??"";return"http"!==n&&"https"!==n&&"ws"!==n&&"wss"!==n&&"ftp"!==n&&"ftps"!==n||i.startsWith("//")}function M(e,t){let a,n=e?.trim();return n?n:"http"===(a=t.trim().split(":")[0]?.toLowerCase())||"https"===a?"com.apple.mobilesafari":void 0}function g(e={}){let t=e.ttlMs??3e4,a=e.nowMs??Date.now,n=new Map,i=e=>{var t;let a=[(t=e).platform,t.deviceId,""].join("\0");for(let e of n.keys())e.startsWith(a)&&n.delete(e)};return{get(e,t){let i=I(e,t),r=n.get(i);if(r)return r.expiresAtMs<=a()?void n.delete(i):r.value},set:(e,i,r)=>(n.set(I(e,i),{value:r,expiresAtMs:a()+t}),r),clear(e){i(e)},async invalidateWhile(e,t){i(e);try{return await t()}finally{i(e)}}}}function I(e,t){return[e.platform,e.deviceId,e.variant??"",t.trim().toLowerCase()].join("\0")}let O=N(process.env.AGENT_DEVICE_RETRY_LOGS);function N(e){return["1","true","yes","on"].includes((e??"").trim().toLowerCase())}let E={ios_boot:{startupMs:12e4,operationMs:2e4,totalMs:12e4},ios_runner_connect:{startupMs:12e4,operationMs:15e3,totalMs:12e4},android_boot:{startupMs:6e4,operationMs:1e4,totalMs:6e4}};class D{startedAtMs;expiresAtMs;constructor(e,t){this.startedAtMs=e,this.expiresAtMs=e+Math.max(0,t)}static fromTimeoutMs(e,t=Date.now()){return new D(t,e)}remainingMs(e=Date.now()){return Math.max(0,this.expiresAtMs-e)}elapsedMs(e=Date.now()){return Math.max(0,e-this.startedAtMs)}isExpired(e=Date.now()){return 0>=this.remainingMs(e)}}async function T(e,t={},a={}){let n,r={maxAttempts:t.maxAttempts??3,baseDelayMs:t.baseDelayMs??200,maxDelayMs:t.maxDelayMs??2e3,jitter:t.jitter??.2,shouldRetry:t.shouldRetry};for(let t=1;t<=r.maxAttempts;t+=1){if(a.signal?.aborted)throw new i("COMMAND_FAILED","request canceled",{reason:"request_canceled"});if(a.deadline?.isExpired()&&t>1)break;try{let n=await e({attempt:t,maxAttempts:r.maxAttempts,deadline:a.deadline});return a.onEvent?.({phase:a.phase,event:"succeeded",attempt:t,maxAttempts:r.maxAttempts,elapsedMs:a.deadline?.elapsedMs(),remainingMs:a.deadline?.remainingMs()}),L({phase:a.phase,event:"succeeded",attempt:t,maxAttempts:r.maxAttempts,elapsedMs:a.deadline?.elapsedMs(),remainingMs:a.deadline?.remainingMs()}),n}catch(d){n=d;let e=a.classifyReason?.(d),i={phase:a.phase,event:"attempt_failed",attempt:t,maxAttempts:r.maxAttempts,elapsedMs:a.deadline?.elapsedMs(),remainingMs:a.deadline?.remainingMs(),reason:e};if(a.onEvent?.(i),L(i),t>=r.maxAttempts||r.shouldRetry&&!r.shouldRetry(d,t))break;let o=function(e,t,a,n){let i=Math.min(t,e*2**(n-1));return Math.max(0,i+i*a*(2*Math.random()-1))}(r.baseDelayMs,r.maxDelayMs,r.jitter,t),s=a.deadline?Math.min(o,a.deadline.remainingMs()):o;if(s<=0)break;let l={phase:a.phase,event:"retry_scheduled",attempt:t,maxAttempts:r.maxAttempts,delayMs:s,elapsedMs:a.deadline?.elapsedMs(),remainingMs:a.deadline?.remainingMs(),reason:e};a.onEvent?.(l),L(l),await function(e,t){return new Promise(a=>{if(t?.aborted)return void a();let n=!1,i=()=>{n||(n=!0,t&&t.removeEventListener("abort",o),a())},r=setTimeout(i,e);function o(){clearTimeout(r),i()}t&&t.addEventListener("abort",o,{once:!0})})}(s,a.signal)}}let o={phase:a.phase,event:"exhausted",attempt:r.maxAttempts,maxAttempts:r.maxAttempts,elapsedMs:a.deadline?.elapsedMs(),remainingMs:a.deadline?.remainingMs(),reason:a.classifyReason?.(n)};if(a.onEvent?.(o),L(o),n)throw n;throw new i("COMMAND_FAILED","retry failed")}async function C(e,t={}){return T(()=>e(),{maxAttempts:t.attempts,baseDelayMs:t.baseDelayMs,maxDelayMs:t.maxDelayMs,jitter:t.jitter,shouldRetry:t.shouldRetry})}function L(e){r({level:"attempt_failed"===e.event||"exhausted"===e.event?"warn":"debug",phase:"retry",data:{...e}}),O&&process.stderr.write(`[agent-device][retry] ${JSON.stringify(e)}
|
|
2
|
-
`)}let S=["AGENT_DEVICE_IOS_SIMULATOR_DEVICE_SET","IOS_SIMULATOR_DEVICE_SET"],k=["AGENT_DEVICE_ANDROID_DEVICE_ALLOWLIST","ANDROID_DEVICE_ALLOWLIST"];function x(e){return e?.trim()||void 0}function R(e,t){for(let a of e){let e=x(t[a]);if(e)return e}}function P(e,t=process.env){return x(e)??R(S,t)}function U(e){return new Set(e.split(/[\s,]+/).map(e=>e.trim()).filter(Boolean))}function F(e,t=process.env){let a=x(e)??R(k,t);if(a)return U(a)}function B(e){let t=e.error?n(e.error):null,a=e.context?.platform,i=e.context?.phase;if(t?.code==="TOOL_MISSING")return"android"===a?"ADB_TRANSPORT_UNAVAILABLE":"IOS_TOOL_MISSING";let r=t?.details??{},o="string"==typeof r.message?r.message:void 0,s="string"==typeof r.stdout?r.stdout:void 0,l="string"==typeof r.stderr?r.stderr:void 0,d=r.boot&&"object"==typeof r.boot?r.boot:null,u=r.bootstatus&&"object"==typeof r.bootstatus?r.bootstatus:null,c=[e.message,t?.message,e.stdout,e.stderr,o,s,l,"string"==typeof d?.stdout?d.stdout:void 0,"string"==typeof d?.stderr?d.stderr:void 0,"string"==typeof u?.stdout?u.stdout:void 0,"string"==typeof u?.stderr?u.stderr:void 0].filter(Boolean).join("\n").toLowerCase();return"ios"===a&&(c.includes("runner did not accept connection")||"connect"===i&&(c.includes("timed out")||c.includes("timeout")||c.includes("econnrefused")||c.includes("connection refused")||c.includes("fetch failed")||c.includes("socket hang up")))?"IOS_RUNNER_CONNECT_TIMEOUT":"ios"===a&&"boot"===i&&(c.includes("timed out")||c.includes("timeout"))?"IOS_BOOT_TIMEOUT":"android"===a&&"boot"===i&&(c.includes("timed out")||c.includes("timeout"))?"ANDROID_BOOT_TIMEOUT":c.includes("resource temporarily unavailable")||c.includes("killed: 9")||c.includes("cannot allocate memory")||c.includes("system is low on memory")?"CI_RESOURCE_STARVATION_SUSPECTED":"android"===a&&(c.includes("device not found")||c.includes("no devices")||c.includes("device offline")||c.includes("offline")||c.includes("unauthorized")||c.includes("not authorized")||c.includes("unable to locate device")||c.includes("invalid device"))?"ADB_TRANSPORT_UNAVAILABLE":t?.code==="COMMAND_FAILED"||c.length>0?"BOOT_COMMAND_FAILED":"UNKNOWN"}function V(e){switch(e){case"IOS_BOOT_TIMEOUT":return"Retry simulator boot and inspect simctl bootstatus logs; in CI consider increasing AGENT_DEVICE_IOS_BOOT_TIMEOUT_MS.";case"IOS_RUNNER_CONNECT_TIMEOUT":return"Retry runner startup, inspect xcodebuild logs, and verify simulator responsiveness before command execution.";case"ANDROID_BOOT_TIMEOUT":return"Retry emulator startup and verify sys.boot_completed reaches 1; consider increasing startup budget in CI.";case"ADB_TRANSPORT_UNAVAILABLE":return"Check adb server/device transport (adb devices -l), restart adb, and ensure the target device is online and authorized.";case"CI_RESOURCE_STARVATION_SUSPECTED":return"CI machine may be resource constrained; reduce parallel jobs or use a larger runner.";case"IOS_TOOL_MISSING":return"Xcode command-line tools are missing or not in PATH; run xcode-select --install and verify xcrun works.";case"BOOT_COMMAND_FAILED":return"Inspect command stderr/stdout for the failing boot phase and retry after environment validation.";default:return"Retry once and inspect verbose logs for the failing phase."}}let $=["android.software.leanback","android.software.leanback_only","android.hardware.type.television"];async function W(e,t,a){let n=Array(e.length),i=0,r=Math.min(t,e.length);return await Promise.all(Array.from({length:r},async()=>{for(;i<e.length;){let t=i;i+=1,n[t]=await a(e[t])}})),n}function G(e){return`${e.stdout}
|
|
3
|
-
${e.stderr}`}function K(e,t){return["-s",e,...t]}function j(e){return e.startsWith("emulator-")}function H(e){return e.toLowerCase().replace(/_/g," ").replace(/\s+/g," ").trim()}async function q(e,t=E.android_boot.operationMs){return o("adb",K(e,["shell","getprop","sys.boot_completed"]),{allowFailure:!0,timeoutMs:t})}async function z(e,t){let a=t.replace(/_/g," ").trim();if(!j(e))return a||e;let n=await Z(e);return n?n.replace(/_/g," "):a||e}async function J(e,t,a){try{return await a("adb",K(e,t),{allowFailure:!0,timeoutMs:1e4})}catch(e){var i;if("COMMAND_FAILED"===(i=n(e)).code&&"number"==typeof i.details?.timeoutMs)return;throw e}}async function Z(e,t=o){for(let a of["ro.boot.qemu.avd_name","persist.sys.avd_name"]){let n=await J(e,["shell","getprop",a],t);if(!n)continue;let i=n.stdout.trim();if(0===n.exitCode&&i.length>0)return i}let a=await J(e,["emu","avd","name"],t);if(!a)return;let n=function(e){let t=e.split("\n").map(e=>e.trim()).filter(e=>e.length>0);if(0!==t.length)return"OK"===t.at(-1)&&t.pop(),t.join("\n").trim()||void 0}(a.stdout);if(0===a.exitCode&&n)return n}async function X(e,t){let a=G(await o("adb",K(e,["shell","cmd","package","has-feature",t]),{allowFailure:!0,timeoutMs:E.android_boot.operationMs})).toLowerCase();return!!a.includes("true")||!a.includes("false")&&null}async function Y(e){return(await W($,2,async t=>await X(e,t))).some(e=>!0===e)}async function Q(e){var t;let a;return"tv"===((a=G(await o("adb",K(e,["shell","getprop","ro.build.characteristics"]),{allowFailure:!0,timeoutMs:E.android_boot.operationMs})).toLowerCase()).includes("tv")||a.includes("leanback")?"tv":null)||await Y(e)?"tv":(t=G(await o("adb",K(e,["shell","pm","list","features"]),{allowFailure:!0,timeoutMs:E.android_boot.operationMs})),/feature:android\.(software\.leanback(_only)?|hardware\.type\.television)\b/i.test(t))?"tv":"mobile"}async function ee(e={}){if(await u(),!await l("adb"))throw new i("TOOL_MISSING","adb not found in PATH");let t=e.serialAllowlist??F(void 0),a=(await et()).filter(e=>!t||t.has(e.serial));return await W(a,3,async({serial:e,rawModel:t})=>{let[a,n,i]=await Promise.all([z(e,t),er(e),Q(e)]);return{platform:"android",id:e,name:a,kind:j(e)?"emulator":"device",target:i,booted:n}})}async function et(){return(await o("adb",["devices","-l"],{timeoutMs:E.android_boot.operationMs})).stdout.split("\n").map(e=>e.trim()).filter(e=>e.length>0&&!e.startsWith("List of devices")).map(e=>e.split(/\s+/)).filter(e=>"device"===e[1]).map(e=>({serial:e[0],rawModel:(e.find(e=>e.startsWith("model:"))??"").replace("model:","")}))}async function ea(){let e=await o("emulator",["-list-avds"],{allowFailure:!0,timeoutMs:E.android_boot.operationMs});if(0!==e.exitCode)throw new i("COMMAND_FAILED","Failed to list Android emulator AVDs",{stdout:e.stdout,stderr:e.stderr,exitCode:e.exitCode,hint:"Verify Android emulator tooling is installed and available in PATH."});return e.stdout.split("\n").map(e=>e.trim()).filter(e=>e.length>0)}async function en(e){let t=Date.now();for(;Date.now()-t<e.timeoutMs;){try{let t=await ei(e.avdName,e.serial);if(t)return{platform:"android",id:t,name:e.avdName,kind:"emulator",target:"mobile",booted:!1}}catch{}await p(1e3)}throw new i("COMMAND_FAILED","Android emulator did not appear in time",{avdName:e.avdName,serial:e.serial,timeoutMs:e.timeoutMs,hint:"Check emulator logs and verify the AVD can start from command line."})}async function ei(e,t){let a=H(e);for(let e of(await et()).filter(e=>(!t||e.serial===t)&&j(e.serial)))if(H(e.rawModel)===a||H(await z(e.serial,e.rawModel))===a)return e.serial}async function er(e){try{let t=await q(e);return"1"===t.stdout.trim()}catch{return!1}}async function eo(e){var t,a;let n;await u();let r=e.avdName.trim();if(!r)throw new i("INVALID_ARGS","Android emulator boot requires a non-empty AVD name.");let o=e.timeoutMs??12e4;if(!await l("adb"))throw new i("TOOL_MISSING","adb not found in PATH");if(!await l("emulator"))throw new i("TOOL_MISSING","emulator not found in PATH");let s=await ea(),c=function(e,t){let a=e.find(e=>e===t);if(a)return a;let n=H(t);return e.find(e=>H(e)===n)}(s,r);if(!c)throw new i("DEVICE_NOT_FOUND",`No Android emulator AVD named ${e.avdName}`,{requestedAvdName:r,availableAvds:s,hint:"Run `emulator -list-avds` and pass an existing AVD name to --device."});let p=Date.now(),f=(t=await ee(),a=e.serial,n=H(c),t.find(e=>"android"===e.platform&&"emulator"===e.kind&&(!a||e.id===a)&&H(e.name)===n));if(!f){let t=["-avd",c];e.headless&&t.push("-no-window","-no-audio"),d("emulator",t)}let m=f??await en({avdName:c,serial:e.serial,timeoutMs:o}),w=Math.max(1e3,o-(Date.now()-p));await es(m.id,w);let A=(await ee()).find(e=>e.id===m.id);return A?{...A,name:c,booted:!0}:{...m,name:c,booted:!0}}async function es(e,t=6e4){let a,r=D.fromTimeoutMs(t),o=Math.max(1,Math.ceil(t/1e3)),s=!1;try{await T(async({deadline:n})=>{if(n?.isExpired())throw s=!0,new i("COMMAND_FAILED","Android boot deadline exceeded",{serial:e,timeoutMs:t,elapsedMs:r.elapsedMs(),message:"timeout"});let o=Math.max(1e3,n?.remainingMs()??t),l=await q(e,Math.min(o,E.android_boot.operationMs));if(a=l,"1"!==l.stdout.trim())throw new i("COMMAND_FAILED","Android device is still booting",{serial:e,stdout:l.stdout,stderr:l.stderr,exitCode:l.exitCode})},{maxAttempts:o,baseDelayMs:1e3,maxDelayMs:1e3,jitter:0,shouldRetry:e=>{let t=B({error:e,stdout:a?.stdout,stderr:a?.stderr,context:{platform:"android",phase:"boot"}});return"ADB_TRANSPORT_UNAVAILABLE"!==t&&"ANDROID_BOOT_TIMEOUT"!==t}},{deadline:r,phase:"boot",classifyReason:e=>B({error:e,stdout:a?.stdout,stderr:a?.stderr,context:{platform:"android",phase:"boot"}})})}catch(f){let o=n(f),l=a?.stdout,d=a?.stderr,u=a?.exitCode,c=B({error:f,stdout:l,stderr:d,context:{platform:"android",phase:"boot"}});"BOOT_COMMAND_FAILED"===c&&"Android device is still booting"===o.message&&(c="ANDROID_BOOT_TIMEOUT");let p={serial:e,timeoutMs:t,elapsedMs:r.elapsedMs(),reason:c,hint:V(c),stdout:l,stderr:d,exitCode:u};if(s||"ANDROID_BOOT_TIMEOUT"===c)throw new i("COMMAND_FAILED","Android device did not finish booting in time",p);if("TOOL_MISSING"===o.code)throw new i("TOOL_MISSING",o.message,{...p,...o.details??{}});if("ADB_TRANSPORT_UNAVAILABLE"===c)throw new i("COMMAND_FAILED",o.message,{...p,...o.details??{}});throw new i(o.code,o.message,{...p,...o.details??{}},o.cause)}}async function el(e,t,a){return await f(e)(t,a)}function ed(e){return{platform:"android",id:e,name:e,kind:e.startsWith("emulator-")?"emulator":"device",booted:!0}}async function eu(){if(await u(),!await l("adb"))throw new i("TOOL_MISSING","adb not found in PATH")}function ec(e,t){let a=`${e}
|
|
4
|
-
${t}`.toLowerCase();return a.includes("no shell command implementation")||a.includes("unknown command")}let ep=/\.(?:apk|aab)$/i,ef=/^[A-Za-z_][\w]*(\.[A-Za-z_][\w]*)+$/;function em(e){var t,a;let n=e.trim();return 0===n.length?"other":ep.test(n)?n.includes("/")||n.includes("\\")||n.startsWith(".")||n.startsWith("~")||(t=n,!ef.test(t))?"binary":"package":(a=n,ef.test(a))?"package":"other"}function ew(e){return`Android runtime hints require an installed package name, not "${e}". Install or reinstall the app first, then relaunch by package.`}async function eA(e,t){let n="url"===e.kind&&A(e.url),i=await w({source:e,isInstallablePath:(e,t)=>{var n;let i;return t.isFile()&&(n=e,".apk"===(i=a.extname(n).toLowerCase())||".aab"===i)},installableLabel:"Android installable (.apk or .aab)",allowArchiveExtraction:"url"!==e.kind||n,signal:t?.signal}),r=t?.resolveIdentity===!1?{}:await eh(i.installablePath);return{archivePath:i.archivePath,installablePath:i.installablePath,packageName:r.packageName,cleanup:i.cleanup}}async function eh(e){let t=a.extname(e).toLowerCase();return".apk"!==t&&".aab"!==t?{}:{packageName:await c(e)}}let ey={settings:{type:"intent",value:"android.settings.SETTINGS"}},eb="android.intent.category.LAUNCHER",e_="android.intent.category.LEANBACK_LAUNCHER",ev="android.intent.category.DEFAULT",eM="Run agent-device apps --platform android to discover the installed package name, then retry open with that exact package.",eg=g();function eI(e){return{platform:"android",deviceId:e.id,variant:e.target??""}}async function eO(e,t){let a=t.trim();if("package"===em(a))return{type:"package",value:a};let n=ey[a.toLowerCase()];if(n)return n;let r=eI(e),o=eg.get(r,a);if(o)return o;let s=(await el(e,["shell","pm","list","packages"])).stdout.split("\n").map(e=>e.replace("package:","").trim()).filter(Boolean).filter(e=>e.toLowerCase().includes(a.toLowerCase()));if(1===s.length)return eg.set(r,a,{type:"package",value:s[0]});if(s.length>1)throw new i("INVALID_ARGS",`Multiple packages matched "${t}"`,{matches:s,hint:"Run agent-device apps --platform android to see the exact installed package names before retrying open."});throw new i("APP_NOT_INSTALLED",`No package found matching "${t}"`,{hint:eM})}async function eN(e,t="all"){let a=await eE(e);return("user-installed"===t?(await eT(e)).filter(e=>a.has(e)):Array.from(a)).sort((e,t)=>e.localeCompare(t)).map(e=>({package:e,name:eC(e)}))}async function eE(e){let t=new Set;for(let a of eD(e,{includeFallbackWhenUnknown:!0})){let n=await el(e,["shell","cmd","package","query-activities","--brief","-a","android.intent.action.MAIN","-c",a],{allowFailure:!0});if(0===n.exitCode&&0!==n.stdout.trim().length)for(let e of h(n.stdout))t.add(e)}return t}function eD(e,t={}){return"tv"===e.target?[e_]:"mobile"===e.target?[eb]:t.includeFallbackWhenUnknown?[eb,e_]:[eb]}async function eT(e){return y((await el(e,["shell","pm","list","packages","-3"])).stdout)}function eC(e){let t=new Set(["com","android","google","app","apps","service","services","mobile","client"]),a=e.split(".").flatMap(e=>e.split(/[_-]+/)).map(e=>e.trim().toLowerCase()).filter(e=>e.length>0),n=a[a.length-1]??e;for(let e=a.length-1;e>=0;e-=1){let i=a[e];if(!t.has(i)){n=i;break}}return n.split(/[^a-z0-9]+/i).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}async function eL(e){let t=await eS(e,[["shell","dumpsys","window","windows"],["shell","dumpsys","window"]]);if(t)return t;let a=await eS(e,[["shell","dumpsys","activity","activities"],["shell","dumpsys","activity"]]);return a||{}}async function eS(e,t){for(let a of t){let t=b((await el(e,a,{allowFailure:!0})).stdout??"");if(t)return t}return null}async function ek(e,t,a){e.booted||await es(e.id);let n=t.trim();if(v(n)){if(a)throw new i("INVALID_ARGS","Activity override is not supported when opening a deep link URL");await el(e,["shell","am","start","-W","-a","android.intent.action.VIEW","-d",n]);return}let r=await eO(e,t),o=eD(e)[0]??eb;if("intent"===r.type){if(a)throw new i("INVALID_ARGS","Activity override requires a package name, not an intent");await el(e,["shell","am","start","-W","-a",r.value]);return}if(a){let t=a.includes("/")?a:`${r.value}/${a.startsWith(".")?a:`.${a}`}`;try{await el(e,["shell","am","start","-W","-a","android.intent.action.MAIN","-c",ev,"-c",o,"-n",t])}catch(t){throw await eP(e,r.value,t),t}return}let s=await el(e,["shell","am","start","-W","-a","android.intent.action.MAIN","-c",ev,"-c",o,"-p",r.value],{allowFailure:!0});if(0===s.exitCode&&!eB(s.stdout,s.stderr))return;let l=await eF(e,r.value);if(!l){if(!await eR(e,r.value))throw ex(r.value);throw new i("COMMAND_FAILED",`Failed to launch ${r.value}`,{stdout:s.stdout,stderr:s.stderr})}await el(e,["shell","am","start","-W","-a","android.intent.action.MAIN","-c",ev,"-c",o,"-n",l])}function ex(e){return new i("APP_NOT_INSTALLED",`No package found matching "${e}"`,{package:e,hint:eM})}async function eR(e,t){let a=await el(e,["shell","pm","path",t],{allowFailure:!0}),n=`${a.stdout}
|
|
5
|
-
${a.stderr}`;return!!(0===a.exitCode&&/\bpackage:/i.test(n))||(eU(n),!1)}async function eP(e,t,a){if(eU(a instanceof i?`${String(a.details?.stdout??"")}
|
|
6
|
-
${String(a.details?.stderr??"")}`:"")||!await eR(e,t))throw ex(t)}function eU(e){return/\bunknown package\b/i.test(e)||/\bpackage .* (?:was|is) not found\b/i.test(e)||/\bpackage .* does not exist\b/i.test(e)||/\bcould not find package\b/i.test(e)}async function eF(e,t){for(let a of Array.from(new Set(eD(e,{includeFallbackWhenUnknown:!0})))){let n=await el(e,["shell","cmd","package","resolve-activity","--brief","-a","android.intent.action.MAIN","-c",a,t],{allowFailure:!0});if(0!==n.exitCode)continue;let i=eV(n.stdout);if(i)return i}return null}function eB(e,t){let a=`${e}
|
|
7
|
-
${t}`;return/Error:.*(?:Activity not started|unable to resolve Intent)/i.test(a)}function eV(e){let t=e.split("\n").map(e=>e.trim()).filter(Boolean);for(let e=t.length-1;e>=0;e-=1){let a=t[e];if(a.includes("/"))return a.split(/\s+/)[0]}return null}async function e$(e){e.booted||await es(e.id)}async function eW(e,t){if("settings"===t.trim().toLowerCase())return void await el(e,["shell","am","force-stop","com.android.settings"]);let a=await eO(e,t);if("intent"===a.type)throw new i("INVALID_ARGS","Close requires a package name, not an intent");await el(e,["shell","am","force-stop",a.value])}async function eG(e,t){let a=await eO(e,t);if("intent"===a.type)throw new i("INVALID_ARGS","App uninstall requires a package name, not an intent");let n=await el(e,["uninstall",a.value],{allowFailure:!0});if(0!==n.exitCode){let e=`${n.stdout}
|
|
8
|
-
${n.stderr}`.toLowerCase();if(!e.includes("unknown package")&&!e.includes("not installed"))throw new i("COMMAND_FAILED",`adb uninstall failed for ${a.value}`,{stdout:n.stdout,stderr:n.stderr,exitCode:n.exitCode})}return{package:a.value}}let eK=null;async function ej(){let e=`${process.env.PATH??""}::${process.env.AGENT_DEVICE_BUNDLETOOL_JAR??""}`;if(eK?.key===e)return eK.invocation;if(await l("bundletool")){let t={cmd:"bundletool",prefixArgs:[]};return eK={key:e,invocation:t},t}let t=await s(process.env.AGENT_DEVICE_BUNDLETOOL_JAR,"AGENT_DEVICE_BUNDLETOOL_JAR");if(!t)throw new i("TOOL_MISSING","bundletool not found in PATH. Install bundletool or set AGENT_DEVICE_BUNDLETOOL_JAR to a bundletool-all.jar path.");let a={cmd:"java",prefixArgs:["-jar",t]};return eK={key:e,invocation:a},a}async function eH(e){let t=await ej();await o(t.cmd,[...t.prefixArgs,...e])}async function eq(n,i){let r,o=await e.mkdtemp(a.join(t.tmpdir(),"agent-device-aab-")),s=a.join(o,"bundle.apks"),l=(r=process.env.AGENT_DEVICE_ANDROID_BUNDLETOOL_MODE?.trim())&&r.length>0?r:"universal";try{await eH(["build-apks","--bundle",i,"--output",s,"--mode",l]),await eH(["install-apks","--apks",s,"--device-id",n.id])}finally{await e.rm(o,{recursive:!0,force:!0})}}async function ez(e,t){".aab"===a.extname(t).toLowerCase()?await eq(e,t):await m(t,{device:e,replace:!0})}async function eJ(e){return new Set((await el(e,["shell","pm","list","packages"])).stdout.split("\n").map(e=>e.replace("package:","").trim()).filter(Boolean))}async function eZ(e,t){let a=Array.from(await eJ(e)).filter(e=>!t.has(e));if(1===a.length)return a[0]}async function eX(e,t){await eg.invalidateWhile(eI(e),async()=>{e.booted||await es(e.id),await ez(e,t)})}async function eY(e,t,a){let n=a?void 0:await eJ(e);return await eX(e,t),a??(n?await eZ(e,n):void 0)}async function eQ(e,t){e.booted||await es(e.id);let a=await eA({kind:"path",path:t});try{let t=await eY(e,a.installablePath,a.packageName),n=t?eC(t):void 0;return{archivePath:a.archivePath,installablePath:a.installablePath,packageName:t,appName:n,launchTarget:t}}finally{await a.cleanup()}}async function e0(e,t,a){return await eg.invalidateWhile(eI(e),async()=>{e.booted||await es(e.id);let{package:n}=await eG(e,t),i=await eA({kind:"path",path:a},{resolveIdentity:!1});try{await eX(e,i.installablePath)}finally{await i.cleanup()}return{package:n}})}async function e1(e){return await e2(f(e))}async function e2(e){let t=await e(["shell","dumpsys","input_method"],{allowFailure:!0});if(0!==t.exitCode)throw new i("COMMAND_FAILED","Failed to query Android keyboard state",{stdout:t.stdout,stderr:t.stderr,exitCode:t.exitCode});return function(e){let t=function(e){let t=new Map;for(let a of e.matchAll(/\b(mInputShown|mIsInputViewShown|isInputViewShown)=([a-zA-Z]+)\b/g)){let e=a[1],n=a[2]?.toLowerCase();e&&("true"===n||"false"===n)&&t.set(e,"true"===n)}if(0===t.size)return null;for(let e of t.values())if(e)return!0;return!1}(e),a=t??!1;if(null===t){let t=e.match(/\bmImeWindowVis=0x([0-9a-fA-F]+)\b/);if(t?.[1]){let e=Number.parseInt(t[1],16);Number.isNaN(e)||(a=(1&e)!=0)}}let n=Array.from(e.matchAll(/\binputType=0x([0-9a-fA-F]+)\b/gi)),i=n.length>0?n[n.length-1]?.[1]:void 0,r=i?`0x${i.toLowerCase()}`:void 0;return{visible:a,inputType:r,type:r?function(e){let t=Number.parseInt(e.replace(/^0x/i,""),16);if(Number.isNaN(t))return"unknown";let a=15&t;if(2===a)return"number";if(3===a)return"phone";if(4===a)return"datetime";if(1!==a)return"unknown";let n=4080&t;return 32===n||208===n?"email":128===n||224===n||144===n?"password":"text"}(r):void 0}}(t.stdout)}async function e4(e){return await e9(f(e))}async function e9(e){let t=await e2(e),a=t,n=0;for(;a.visible&&n<2;)await e(["shell","input","keyevent","111"]),n+=1,await p(120),a=await e2(e);if(t.visible&&a.visible)throw new i("UNSUPPORTED_OPERATION","Android keyboard dismiss is unavailable for the current IME without back navigation.",{attempts:n,inputType:a.inputType,type:a.type});return{attempts:n,wasVisible:t.visible,dismissed:t.visible&&!a.visible,visible:a.visible,inputType:a.inputType,type:a.type}}async function e3(e){return await e8(f(e))}async function e8(e){let t,a;return(a=(t=(await e7(e,["shell","cmd","clipboard","get","text"],"read")).replace(/\r\n/g,"\n").replace(/\n$/,"")).match(/^clipboard text:\s*(.*)$/i))?a[1]??"":"null"===t.trim().toLowerCase()?"":t}async function e6(e,t){await e5(f(e),t)}async function e5(e,t){await e7(e,["shell","cmd","clipboard","set","text",t],"write")}async function e7(e,t,a){let n=await e(t,{allowFailure:!0});if(ec(n.stdout,n.stderr))throw new i("UNSUPPORTED_OPERATION",`Android shell clipboard ${a} is not supported on this device.`);if(0!==n.exitCode)throw new i("COMMAND_FAILED",`Failed to ${a} Android clipboard text`,{stdout:n.stdout,stderr:n.stderr,exitCode:n.exitCode});return n.stdout}export{D as Deadline,E as TIMEOUT_PROFILES,ed as androidDeviceForSerial,V as bootFailureHint,em as classifyAndroidAppTarget,B as classifyBootFailure,eW as closeAndroidApp,g as createAppResolutionCache,e4 as dismissAndroidKeyboard,e9 as dismissAndroidKeyboardWithAdb,eu as ensureAdb,eo as ensureAndroidEmulatorBooted,ew as formatAndroidInstalledPackageRequiredMessage,eL as getAndroidAppState,e1 as getAndroidKeyboardState,e2 as getAndroidKeyboardStatusWithAdb,eC as inferAndroidAppName,eQ as installAndroidApp,eY as installAndroidInstallablePathAndResolvePackageName,eB as isAmStartError,ec as isClipboardShellUnsupported,v as isDeepLinkTarget,N as isEnvTruthy,eN as listAndroidApps,ee as listAndroidDevices,ek as openAndroidApp,e$ as openAndroidDevice,b as parseAndroidForegroundApp,eV as parseAndroidLaunchComponent,h as parseAndroidLaunchablePackages,y as parseAndroidUserInstalledPackages,U as parseSerialAllowlist,eA as prepareAndroidInstallArtifact,e3 as readAndroidClipboardText,e8 as readAndroidClipboardWithAdb,e0 as reinstallAndroidApp,eO as resolveAndroidApp,F as resolveAndroidSerialAllowlist,M as resolveIosDeviceDeepLinkBundleId,P as resolveIosSimulatorDeviceSetPath,T as retryWithPolicy,el as runAndroidAdb,es as waitForAndroidBoot,C as withRetry,e6 as writeAndroidClipboardText,e5 as writeAndroidClipboardWithAdb};
|
|
@@ -1,382 +0,0 @@
|
|
|
1
|
-
let e,t,s,a,o,i,r,n;import{SESSION_SURFACES as l,SETTINGS_USAGE_OVERRIDE as p}from"./7462.js";let c=["snapshotInteractiveOnly","snapshotCompact","snapshotDepth","snapshotScope","snapshotRaw"],d=["snapshotDepth","snapshotScope","snapshotRaw"],u={workflow:{summary:"Normal agent-device bootstrap, exploration, and validation loop",body:`agent-device help workflow
|
|
2
|
-
|
|
3
|
-
Version-matched operating guide for normal agent-device work.
|
|
4
|
-
|
|
5
|
-
Core loop:
|
|
6
|
-
devices/apps -> open -> snapshot or snapshot -i -> get/is/find/wait or press/fill/scroll/back -> verify -> close
|
|
7
|
-
|
|
8
|
-
Command shape:
|
|
9
|
-
Plans should use agent-device commands, not raw platform tools, pseudo commands, package-manager aliases, or helper prose.
|
|
10
|
-
Put subcommand first, then positionals, then flags:
|
|
11
|
-
agent-device open com.example.app --session checkout --platform android --relaunch
|
|
12
|
-
agent-device record start ./checkout.mp4 --session checkout
|
|
13
|
-
Snapshot refs look like @e12. After snapshot -i, use the exact @eN ref from that output.
|
|
14
|
-
If the exact ref is not known yet, first output snapshot -i, then use a concrete example shape like press @e12 in the next command; do not write @<ref>, @ref, @Label_Name, or @eN placeholders.
|
|
15
|
-
Close means agent-device close. App-owned back means back; system back means back --system.
|
|
16
|
-
Taps are press or click. Gestures are direct commands: swipe, longpress, pinch.
|
|
17
|
-
|
|
18
|
-
Bootstrap:
|
|
19
|
-
agent-device devices --platform ios
|
|
20
|
-
agent-device apps --platform android
|
|
21
|
-
agent-device open MyApp --platform ios --device "iPhone 17 Pro"
|
|
22
|
-
agent-device open <discovered-app-id> --session checkout --platform android
|
|
23
|
-
agent-device install com.example.app ./dist/app.apk --platform android
|
|
24
|
-
agent-device reinstall com.example.app ./build/MyApp.app --platform ios
|
|
25
|
-
agent-device install-from-source --github-actions-artifact org/repo:app-debug --platform android
|
|
26
|
-
agent-device open com.example.app --platform android --relaunch
|
|
27
|
-
If app id is unknown, plan devices, apps, then open <discovered-app-id>. Discovery is not enough when the task asks to open/start the app.
|
|
28
|
-
Install arguments are app/package id then artifact path. If the task says install, use install; use reinstall only when explicitly requested. Fresh runtime state is open --relaunch after install.
|
|
29
|
-
Do not open artifact paths or invent package ids. If apps lookup misses the target and no URL/artifact is provided, ask or stop.
|
|
30
|
-
|
|
31
|
-
Snapshots and refs:
|
|
32
|
-
snapshot reads visible state. snapshot -i gets current interactive refs.
|
|
33
|
-
Snapshot legend:
|
|
34
|
-
@e12 [button] label="Add to cart" id="add-cart" enabled hittable -> press @e12 or press 'id="add-cart"'.
|
|
35
|
-
@e13 [textinput] label="Notes" preview="Leave at side..." truncated -> snapshot -s @e13 before reading.
|
|
36
|
-
@e14 [cell] label="Profiles" focused -> tvOS focus is currently on this row.
|
|
37
|
-
[off-screen below] 4 items: "Privacy", "About" -> scroll down, then snapshot -i; those are hints, not refs.
|
|
38
|
-
Re-snapshot after navigation, submit, modal/list/reload/dynamic changes.
|
|
39
|
-
Off-screen summaries are scroll hints; use scroll, not swipe, then snapshot -i.
|
|
40
|
-
Missing target in a long list: use a short manual scroll + snapshot loop with a max attempt count; do not rely on unbounded scrollintoview.
|
|
41
|
-
Truncated text/input previews: do not use get text first; expand with snapshot -s @ref (for example snapshot -s @e7), then read the scoped output.
|
|
42
|
-
Rare iOS accessibility gaps: if a row ref is shown disabled/hittable:false and press @ref reports success but no UI change, or a horizontal tab/filter bar is collapsed into one composite/seekbar with no child refs, run agent-device snapshot -i -c --json to read rects, compute the target center, press x y, then diff snapshot -i. Coordinates are fallback-only; document why you used them.
|
|
43
|
-
|
|
44
|
-
Selectors:
|
|
45
|
-
Use selectors as positional targets: id="field-email" or label="Allow".
|
|
46
|
-
Do not use CSS selectors, pseudo refs, --selector, --text, or raw x/y when refs/selectors exist.
|
|
47
|
-
agent-device fill 'id="catalog-search"' "tart" --delay-ms 80
|
|
48
|
-
agent-device press 'id="submit-order"'
|
|
49
|
-
agent-device is visible 'label="Online"'
|
|
50
|
-
agent-device get text 'id="quantity-value"'
|
|
51
|
-
|
|
52
|
-
Text entry:
|
|
53
|
-
fill replaces; type appends to focused field.
|
|
54
|
-
agent-device fill @e5 "qa@example.com"
|
|
55
|
-
agent-device fill 'id="field-email"' "qa@example.com"
|
|
56
|
-
agent-device press 'id="product-note"'
|
|
57
|
-
agent-device type "Handle with care" --delay-ms 80
|
|
58
|
-
Empty replacement is not a supported clear-field command: do not plan fill <target> "" or fill <target> ''. Prefer a visible clear/reset control; if the app exposes none, report the tool gap instead of inventing a clear command.
|
|
59
|
-
Debounced field with no result selector: agent-device wait 1000. Keyboard read-only: keyboard status/get. Blocked control: try keyboard dismiss when supported.
|
|
60
|
-
On iOS, prefer keyboard dismiss before manually pressing visible Done; the runner can use safe native keyboard controls and still reports unsupported layouts explicitly. If it returns UNSUPPORTED_OPERATION, prefer a visible app dismiss control, or use back --system only when system navigation is an acceptable side effect.
|
|
61
|
-
Search-as-you-type fields on iOS can drop characters when driven too fast; use --delay-ms on fill/type before trying clipboard paste.
|
|
62
|
-
iOS Allow Paste prompt cannot be exercised under XCUITest. To test paste-driven app behavior, prefill first with agent-device clipboard write "some text"; test the system prompt manually.
|
|
63
|
-
Android Gboard handwriting/stylus UI can capture text in an IME-owned input instead of the app field. If fill reports that input was captured by the keyboard/IME, use the diagnostic targetInput/actualInput details, inspect with keyboard status/get if needed, and switch or disable handwriting/trusted ADB keyboard outside the command plan before retrying. Do not keep retrying fill/type against the same field while the IME owns focus.
|
|
64
|
-
Android non-ASCII can fail on some system images. Try fill/type normally; agent-device uses safer fallbacks. If the shell reports unsupported non-ASCII input, configure a trusted ADB keyboard IME outside the command plan and restore the previous IME afterward.
|
|
65
|
-
|
|
66
|
-
Session ordering:
|
|
67
|
-
Stateful commands against one --session must run serially. Do not run open/press/fill/type/scroll/back/alert/replay/batch/close commands in parallel against the same session.
|
|
68
|
-
It is fine to parallelize independent read-only collection or commands that use different sessions/devices.
|
|
69
|
-
|
|
70
|
-
Read-only and waits:
|
|
71
|
-
Read-only visible/state question: use snapshot/get/is/find.
|
|
72
|
-
agent-device snapshot
|
|
73
|
-
agent-device get text 'id="product-title"'
|
|
74
|
-
agent-device get attrs @e4
|
|
75
|
-
agent-device is visible 'label="Online"'
|
|
76
|
-
agent-device wait text "Refreshing metrics..." 3000
|
|
77
|
-
agent-device wait 'label="Ready"' 3000
|
|
78
|
-
agent-device find "Increment" press --json
|
|
79
|
-
For async/list text presence, prefer wait text over is visible when no interaction is needed.
|
|
80
|
-
Use snapshot -i only when refs are needed for an action or targeted query.
|
|
81
|
-
Ambiguous find: add --first or --last. If info is not visible/exposed, report that gap instead of typing/searching/navigating to reveal it.
|
|
82
|
-
|
|
83
|
-
Navigation and gestures:
|
|
84
|
-
Use scroll for lists; swipe for coordinate gestures/carousels.
|
|
85
|
-
If app-owned back is ambiguous or has just misrouted, prefer a visible nav/back button ref, tab-bar ref, or deep link over repeated back/system back.
|
|
86
|
-
Keep count/pause/pattern on one swipe; flags are --count, --pause-ms, --pattern ping-pong.
|
|
87
|
-
longpress duration and pinch scale/center are positional:
|
|
88
|
-
agent-device longpress 300 500 800
|
|
89
|
-
agent-device swipe 320 500 40 500 --count 8 --pause-ms 30 --pattern ping-pong
|
|
90
|
-
agent-device pinch 0.5 200 400
|
|
91
|
-
|
|
92
|
-
Validation and evidence:
|
|
93
|
-
Nearby mutation diff: agent-device diff snapshot -i.
|
|
94
|
-
Expected text/selector verification must include the exact text or selector via wait, is, get, or find; bare screenshots/snapshots are insufficient for named expectations.
|
|
95
|
-
Prefer provided testIDs/ids/selectors for verification; use visible text when no durable selector is provided.
|
|
96
|
-
If task says snapshot, use snapshot. If it asks visual evidence, use screenshot.
|
|
97
|
-
Icon/tappable visual proof: screenshot --overlay-refs. Flag is --overlay-refs.
|
|
98
|
-
Startup/frame health/CPU/memory: perf --json or metrics. Replay maintenance: replay -u ./flow.ad.
|
|
99
|
-
Recording: record start/stop. Tracing: trace start ./trace.log, trace stop ./trace.log. Paths are positional.
|
|
100
|
-
Stable known flow: batch ./steps.json, not workflow batch.
|
|
101
|
-
Inline batch JSON example:
|
|
102
|
-
agent-device batch --steps '[{"command":"open","positionals":["settings"],"flags":{}},{"command":"wait","positionals":["100"],"flags":{}}]'
|
|
103
|
-
Batch step keys are command, positionals, flags, and optional runtime. Never use args, step, text, or target as batch step fields.
|
|
104
|
-
Android animations: settings animations off/on, not animations disable/restore.
|
|
105
|
-
Debug logs: logs clear --restart, logs mark, reproduce, then logs path; do not split clear/restart into separate stop/start commands.
|
|
106
|
-
Network headers: network dump --include headers; do not write network log headers.
|
|
107
|
-
Remote/cloud: connect to discover a cloud profile, or connect --remote-config ./remote-config.json for a local profile; then open, snapshot, disconnect.
|
|
108
|
-
macOS menu bar: open ... --platform macos --surface menubar; snapshot -i --platform macos --surface menubar.
|
|
109
|
-
|
|
110
|
-
React Native dev loop:
|
|
111
|
-
JS-only change with Metro connected:
|
|
112
|
-
agent-device metro reload
|
|
113
|
-
agent-device find "Home"
|
|
114
|
-
Do not use agent-device reload. Use open --relaunch for native startup reset.
|
|
115
|
-
Warning/error overlays can obscure UI and intercept taps. If snapshot -i shows one, dismiss/close its visible control (for example Dismiss or Close) if it is not the task target, then diff snapshot -i or snapshot -i before tapping the real UI.
|
|
116
|
-
Expo Go is a host shell. Use a provided project URL instead of inventing a bundle id; if no URL is provided but a target/app name is provided, open that target and do not inspect project files to find one. On iOS, prefer host + URL when the host shell is known because direct URL open can report success while leaving the runner/shell focused; verify with snapshot -i after opening:
|
|
117
|
-
agent-device open "Expo Go" exp://127.0.0.1:8081 --platform ios
|
|
118
|
-
agent-device snapshot -i --platform ios
|
|
119
|
-
Direct iOS URL open remains valid when no host shell is known, but verify that the app UI loaded:
|
|
120
|
-
agent-device open exp://127.0.0.1:8081 --platform ios
|
|
121
|
-
Android uses the URL target directly; do not write open <app> <url> there:
|
|
122
|
-
agent-device open exp://127.0.0.1:8081 --platform android
|
|
123
|
-
Android URL/deep-link opens infer the foreground package after launch when possible, so logs/perf can remain package-bound. If perf still says no package is associated, open the host package/app id first, then open the URL in the same session.
|
|
124
|
-
If apps lookup misses the project but shows Expo Go/dev-client and a project URL is available, open the URL/host shell; if no URL is available, ask instead of inventing an app id.
|
|
125
|
-
Expo Dev Client/development builds: open the installed dev-client app id/name; if a dev-client URL is provided, open that URL next. For Metro setup use metro prepare --kind expo.
|
|
126
|
-
|
|
127
|
-
React DevTools minimum loop:
|
|
128
|
-
Keep the agent-device react-devtools prefix on every React DevTools command:
|
|
129
|
-
agent-device react-devtools status
|
|
130
|
-
agent-device react-devtools wait --connected
|
|
131
|
-
agent-device react-devtools profile start
|
|
132
|
-
interact with normal agent-device commands
|
|
133
|
-
agent-device react-devtools profile stop
|
|
134
|
-
agent-device react-devtools profile slow --limit 5
|
|
135
|
-
agent-device react-devtools profile rerenders --limit 5
|
|
136
|
-
|
|
137
|
-
Escalate:
|
|
138
|
-
help debugging logs, network, alerts, traces, flaky runtime failures
|
|
139
|
-
help react-devtools React Native performance, profiling, props/state/hooks, slow renders, rerenders
|
|
140
|
-
help remote remote/cloud config, tenant, lease, local service tunnels
|
|
141
|
-
help macos desktop, frontmost-app, menu bar surfaces
|
|
142
|
-
help dogfood exploratory QA report workflow`},debugging:{summary:"Targeted failure evidence without dumping stale context",body:`agent-device help debugging
|
|
143
|
-
|
|
144
|
-
Use this when behavior fails, hangs, times out, throws alerts, or needs runtime evidence.
|
|
145
|
-
|
|
146
|
-
Logs:
|
|
147
|
-
Keep log windows small. Prefer clear, mark, reproduce, then path.
|
|
148
|
-
agent-device logs clear --restart
|
|
149
|
-
agent-device logs mark "before diagnostics retry"
|
|
150
|
-
agent-device press 'id="load-diagnostics"'
|
|
151
|
-
agent-device logs path
|
|
152
|
-
Do not cat a full stale log into agent context. Open or grep only the relevant window when needed.
|
|
153
|
-
logs clear --restart is the compact command to clear old logs and start a fresh capture; do not split it into logs stop, logs clear, logs start.
|
|
154
|
-
|
|
155
|
-
Network:
|
|
156
|
-
Use network dump for recent session HTTP traffic parsed from app logs.
|
|
157
|
-
agent-device network dump --include headers
|
|
158
|
-
agent-device network dump 20 --include all
|
|
159
|
-
Use this instead of logs path when the question is request/response metadata.
|
|
160
|
-
network log is a supported alias, but network dump --include headers is the clearest plan form. Do not write network log headers.
|
|
161
|
-
|
|
162
|
-
Alerts:
|
|
163
|
-
Native alerts:
|
|
164
|
-
agent-device alert wait 3000
|
|
165
|
-
agent-device alert accept
|
|
166
|
-
agent-device alert dismiss
|
|
167
|
-
If alert accept says no alert but a permission sheet is visibly on screen, treat it as normal UI:
|
|
168
|
-
agent-device snapshot -i
|
|
169
|
-
agent-device press 'label="Allow"'
|
|
170
|
-
|
|
171
|
-
Diagnostics and traces:
|
|
172
|
-
Use --debug for CLI/daemon diagnostic ids and log paths.
|
|
173
|
-
Use trace for low-level session diagnostics around one repro:
|
|
174
|
-
agent-device trace start ./traces/diagnostics.trace
|
|
175
|
-
agent-device press 'id="load-diagnostics"'
|
|
176
|
-
agent-device trace stop ./traces/diagnostics.trace
|
|
177
|
-
The trace path is positional. Do not use --path for trace start or trace stop.
|
|
178
|
-
|
|
179
|
-
Stabilizers:
|
|
180
|
-
Android animation-sensitive flows:
|
|
181
|
-
agent-device settings animations off
|
|
182
|
-
agent-device snapshot
|
|
183
|
-
agent-device settings animations on
|
|
184
|
-
Re-enable settings you changed before finishing.
|
|
185
|
-
|
|
186
|
-
React Native internals:
|
|
187
|
-
If the question is about React Native performance, profiling, props, state, hooks, render causes, slow components, or rerenders, use help react-devtools instead of inferring from screenshots or logs.`},"react-devtools":{summary:"React Native performance, profiling, and component internals",body:`agent-device help react-devtools
|
|
188
|
-
|
|
189
|
-
Use this for React Native performance/profiling and internals that the accessibility tree cannot expose: components, props, state, hooks, ownership, slow renders, and rerenders.
|
|
190
|
-
|
|
191
|
-
Core commands:
|
|
192
|
-
agent-device react-devtools start
|
|
193
|
-
agent-device react-devtools stop
|
|
194
|
-
agent-device react-devtools status
|
|
195
|
-
agent-device react-devtools wait --connected
|
|
196
|
-
agent-device react-devtools wait --component <ComponentName>
|
|
197
|
-
agent-device react-devtools count
|
|
198
|
-
agent-device react-devtools get tree --depth 3
|
|
199
|
-
agent-device react-devtools find <ComponentName>
|
|
200
|
-
agent-device react-devtools find <ComponentName> --exact
|
|
201
|
-
agent-device react-devtools get component @c5
|
|
202
|
-
agent-device react-devtools errors
|
|
203
|
-
agent-device react-devtools profile start
|
|
204
|
-
agent-device react-devtools profile stop
|
|
205
|
-
agent-device react-devtools profile slow --limit 5
|
|
206
|
-
agent-device react-devtools profile rerenders --limit 5
|
|
207
|
-
agent-device react-devtools profile report @c5
|
|
208
|
-
agent-device react-devtools profile timeline --limit 20
|
|
209
|
-
agent-device react-devtools profile export profile.json
|
|
210
|
-
agent-device react-devtools profile diff before.json after.json --limit 10
|
|
211
|
-
|
|
212
|
-
Profiling loop:
|
|
213
|
-
1. Verify the app is connected: react-devtools status, then wait --connected if needed.
|
|
214
|
-
2. Start profiling immediately before the interaction.
|
|
215
|
-
3. Drive the interaction with normal agent-device commands.
|
|
216
|
-
4. Stop profiling.
|
|
217
|
-
5. Inspect slow components and rerenders.
|
|
218
|
-
6. Use profile report @cN for render causes and changed props/state/hooks; use get component @cN for current props/state/hooks.
|
|
219
|
-
|
|
220
|
-
Rules:
|
|
221
|
-
Start with get tree --depth 3 or find <name>; use find --exact when fuzzy results are noisy.
|
|
222
|
-
@c refs reset after reload/remount. After reload, wait --connected and inspect again.
|
|
223
|
-
Keep the profile window narrow; unrelated navigation makes render data noisy.
|
|
224
|
-
For cross-platform validation with explicit device selectors, prefer isolated --state-dir and restart react-devtools between platforms.
|
|
225
|
-
Remote Android and iOS bridge runs normally through agent-device react-devtools; the CLI keeps the needed local service tunnel alive until agent-device react-devtools stop or disconnect. Expo support depends on the SDK's bundled React Native runtime.
|
|
226
|
-
Remote iOS apps attempt the legacy React DevTools websocket during JavaScript startup. If the app was already open before react-devtools start, run open <bundle-id> --platform ios --relaunch, then wait --connected.
|
|
227
|
-
|
|
228
|
-
Example:
|
|
229
|
-
agent-device react-devtools status
|
|
230
|
-
agent-device react-devtools wait --connected
|
|
231
|
-
agent-device react-devtools profile start
|
|
232
|
-
agent-device fill 'id="catalog-search"' "tart" --delay-ms 80
|
|
233
|
-
agent-device react-devtools profile stop
|
|
234
|
-
agent-device react-devtools profile slow --limit 5
|
|
235
|
-
agent-device react-devtools profile rerenders --limit 5
|
|
236
|
-
agent-device react-devtools profile report @c5
|
|
237
|
-
|
|
238
|
-
Use snapshot, screenshot, logs, network, and perf for device/app runtime evidence. Use react-devtools only when component internals or React rendering behavior matters.`},remote:{summary:"Remote config, tenant, lease, and remote host flow",body:`agent-device help remote
|
|
239
|
-
|
|
240
|
-
Use remote config or the cloud connection profile when a profile owns daemon URL, auth, tenant, run, lease, device scope, and Metro hints. Do not restate those as individual flags unless overriding intentionally.
|
|
241
|
-
|
|
242
|
-
Cloud profile flow:
|
|
243
|
-
agent-device connect
|
|
244
|
-
agent-device open com.example.app
|
|
245
|
-
agent-device snapshot
|
|
246
|
-
agent-device disconnect
|
|
247
|
-
|
|
248
|
-
Local profile flow:
|
|
249
|
-
agent-device connect --remote-config ./remote-config.json
|
|
250
|
-
agent-device open com.example.app
|
|
251
|
-
agent-device snapshot
|
|
252
|
-
agent-device disconnect
|
|
253
|
-
|
|
254
|
-
Script flow, per-command config:
|
|
255
|
-
agent-device open com.example.app --remote-config ./remote-config.json
|
|
256
|
-
agent-device snapshot --remote-config ./remote-config.json
|
|
257
|
-
agent-device disconnect --remote-config ./remote-config.json
|
|
258
|
-
|
|
259
|
-
Rules:
|
|
260
|
-
connect and disconnect are top-level commands. Do not write agent-device remote connect or agent-device remote disconnect.
|
|
261
|
-
Use connect without --remote-config when the cloud control plane owns the connection profile.
|
|
262
|
-
Prefer --remote-config over --daemon-base-url, --tenant, --run-id, and --lease-id when using a local profile.
|
|
263
|
-
For self-contained scripts, pass the same --remote-config to every operational command, including disconnect; a preceding connect is optional but not required.
|
|
264
|
-
For remote artifact installs, use install-from-source <url> or install-from-source --github-actions-artifact org/repo:artifact; do not download CI artifacts locally first.
|
|
265
|
-
After connect, let the active remote connection supply runtime hints.
|
|
266
|
-
For remote Android and iOS bridge React DevTools, run agent-device react-devtools normally. The CLI opens the needed local service tunnel for the DevTools daemon and keeps it alive until agent-device react-devtools stop or disconnect.
|
|
267
|
-
Use --debug when remote connection or transport errors need diagnostic ids and remote log hints.`},macos:{summary:"macOS desktop, frontmost-app, and menu bar surfaces",body:`agent-device help macos
|
|
268
|
-
|
|
269
|
-
Use macOS only when the task targets desktop apps, desktop surfaces, or menu bar extras.
|
|
270
|
-
|
|
271
|
-
Open and inspect:
|
|
272
|
-
agent-device open TextEdit --platform macos
|
|
273
|
-
agent-device snapshot -i --platform macos
|
|
274
|
-
|
|
275
|
-
Surfaces:
|
|
276
|
-
--surface app normal app session
|
|
277
|
-
--surface frontmost-app inspect whichever app is frontmost
|
|
278
|
-
--surface desktop desktop-wide surface
|
|
279
|
-
--surface menubar menu bar extras and menu bar-only apps
|
|
280
|
-
|
|
281
|
-
Menu bar app example:
|
|
282
|
-
agent-device open "Agent Device Tester Menu" --platform macos --surface menubar
|
|
283
|
-
agent-device snapshot -i --platform macos --surface menubar
|
|
284
|
-
|
|
285
|
-
Context menu example:
|
|
286
|
-
agent-device click @e66 --button secondary --platform macos
|
|
287
|
-
agent-device snapshot -i --platform macos
|
|
288
|
-
|
|
289
|
-
Rules:
|
|
290
|
-
Use open and snapshot -i for menu bar inspection. Do not output inspect as a command.
|
|
291
|
-
Context menus are not ambient UI: secondary-click a visible target, then re-snapshot and use the new menu-item refs.
|
|
292
|
-
Do not let iOS simulator-set scoping hide macOS desktop targets.
|
|
293
|
-
Prefer refs/selectors over raw coordinates.
|
|
294
|
-
macOS snapshot rects are window-space; use current refs or overlay refs instead of guessing coordinates.`},dogfood:{summary:"Exploratory QA workflow with reproducible evidence",body:`agent-device help dogfood
|
|
295
|
-
|
|
296
|
-
Use this when asked to dogfood, exploratory test, bug hunt, QA, or find issues in an app.
|
|
297
|
-
|
|
298
|
-
Goal:
|
|
299
|
-
Find user-visible issues from runtime behavior. Do not read app source or invent findings from code.
|
|
300
|
-
Produce a concise report with severity, repro commands, expected/actual behavior, and evidence paths.
|
|
301
|
-
|
|
302
|
-
Loop:
|
|
303
|
-
1. Identify target app/platform; ask only if missing.
|
|
304
|
-
2. Create output dirs and open a named session. If auth or OTP is required, sign in or ask the user for the code.
|
|
305
|
-
3. Capture baseline snapshot -i and screenshot.
|
|
306
|
-
4. Map top-level navigation, then exercise primary flows and edge states.
|
|
307
|
-
5. For each issue, capture evidence and write the finding immediately, then continue.
|
|
308
|
-
6. Close the session and reconcile the report summary.
|
|
309
|
-
Keep stateful commands serial within the same session. Parallel runs can pollute text fields, focus, alerts, and navigation state.
|
|
310
|
-
|
|
311
|
-
Coverage:
|
|
312
|
-
Navigation, forms, empty/error/loading states, offline or retry behavior, permissions, settings, accessibility labels, orientation/keyboard, and obvious performance stalls.
|
|
313
|
-
React Native warning/error overlays can be real findings or test blockers. Capture them, dismiss if unrelated, re-snapshot, and report them.
|
|
314
|
-
Expo Go/dev-client shells: use the provided exp:// or dev-client URL and record whether the shell, project load, or app UI is being tested. On iOS dogfood, prefer agent-device open "Expo Go" <url> when Expo Go is the known shell, then snapshot -i to confirm the project UI rather than the runner splash.
|
|
315
|
-
Categories: visual, functional, UX, content, performance, diagnostics, permissions, accessibility.
|
|
316
|
-
Severity: critical blocks a core flow/data/crashes; high breaks a major feature; medium has friction or workaround; low is polish.
|
|
317
|
-
|
|
318
|
-
Evidence commands:
|
|
319
|
-
mkdir -p ./dogfood-output/screenshots ./dogfood-output/videos ./dogfood-output/traces
|
|
320
|
-
agent-device --session qa open <app> --platform ios
|
|
321
|
-
agent-device --session qa snapshot -i
|
|
322
|
-
agent-device --session qa screenshot ./dogfood-output/screenshots/initial.png
|
|
323
|
-
agent-device --session qa screenshot ./dogfood-output/screenshots/issue-001.png --overlay-refs
|
|
324
|
-
agent-device --session qa logs clear --restart
|
|
325
|
-
agent-device --session qa logs mark "issue-001 repro"
|
|
326
|
-
agent-device --session qa logs path
|
|
327
|
-
agent-device --session qa record start ./dogfood-output/videos/issue-001.mp4
|
|
328
|
-
agent-device --session qa record stop
|
|
329
|
-
agent-device --session qa close
|
|
330
|
-
|
|
331
|
-
Evidence rules:
|
|
332
|
-
Interactive/behavioral issues need step screenshots and usually a repro video.
|
|
333
|
-
Static/on-load issues can use one screenshot; set repro video to N/A.
|
|
334
|
-
Use screenshot --overlay-refs when showing the tappable target or broken state helps repro.
|
|
335
|
-
|
|
336
|
-
Report shape:
|
|
337
|
-
./dogfood-output/report.md
|
|
338
|
-
Include date, platform, target app, session, scope, severity counts, and issues.
|
|
339
|
-
For each finding: ID, severity, category, title, affected flow/screen, repro commands, expected, actual, evidence files, notes.
|
|
340
|
-
Target 5-10 well-evidenced issues when available. If no issues are found, report coverage completed and residual risk instead of claiming the app is bug-free.
|
|
341
|
-
|
|
342
|
-
Rules:
|
|
343
|
-
Findings must come from observed runtime behavior, not source reads.
|
|
344
|
-
Re-snapshot after each mutation.
|
|
345
|
-
Keep commands in the report reproducible; use selectors or refs from fresh snapshots, not guessed coordinates.
|
|
346
|
-
Prefer refs for exploration and selectors for deterministic replay.
|
|
347
|
-
Use logs, network, screenshot --overlay-refs, trace, perf, or react-devtools only when they add evidence to a specific issue.
|
|
348
|
-
Never delete screenshots, videos, traces, or report artifacts during a session.
|
|
349
|
-
Escalate to help debugging or help react-devtools when runtime symptoms require those tools.`}},m=[{key:"config",names:["--config"],type:"string",usageLabel:"--config <path>",usageDescription:"Load CLI defaults from a specific config file"},{key:"remoteConfig",names:["--remote-config"],type:"string",usageLabel:"--remote-config <path>",usageDescription:"Load remote host + Metro workflow settings from a specific profile file"},{key:"stateDir",names:["--state-dir"],type:"string",usageLabel:"--state-dir <path>",usageDescription:"Daemon state directory (defaults to ~/.agent-device)"},{key:"daemonBaseUrl",names:["--daemon-base-url"],type:"string",usageLabel:"--daemon-base-url <url>",usageDescription:"Explicit remote HTTP daemon base URL (skip local daemon discovery/startup)"},{key:"daemonAuthToken",names:["--daemon-auth-token"],type:"string",usageLabel:"--daemon-auth-token <token>",usageDescription:"Remote HTTP daemon auth token (sent as request token and bearer header)"},{key:"daemonTransport",names:["--daemon-transport"],type:"enum",enumValues:["auto","socket","http"],usageLabel:"--daemon-transport auto|socket|http",usageDescription:"Daemon client transport preference"},{key:"daemonServerMode",names:["--daemon-server-mode"],type:"enum",enumValues:["socket","http","dual"],usageLabel:"--daemon-server-mode socket|http|dual",usageDescription:"Daemon server mode used when spawning daemon"},{key:"tenant",names:["--tenant"],type:"string",usageLabel:"--tenant <id>",usageDescription:"Tenant scope identifier for isolated daemon sessions"},{key:"sessionIsolation",names:["--session-isolation"],type:"enum",enumValues:["none","tenant"],usageLabel:"--session-isolation none|tenant",usageDescription:"Session isolation strategy (tenant prefixes session namespace)"},{key:"runId",names:["--run-id"],type:"string",usageLabel:"--run-id <id>",usageDescription:"Run identifier used for tenant lease admission checks"},{key:"leaseId",names:["--lease-id"],type:"string",usageLabel:"--lease-id <id>",usageDescription:"Lease identifier bound to tenant/run admission scope"},{key:"leaseBackend",names:["--lease-backend"],type:"enum",enumValues:["ios-simulator","ios-instance","android-instance"],usageLabel:"--lease-backend ios-simulator|ios-instance|android-instance",usageDescription:"Lease backend for remote tenant connection admission"},{key:"force",names:["--force"],type:"boolean",usageLabel:"--force",usageDescription:"Force connection state replacement when reconnecting"},{key:"noLogin",names:["--no-login"],type:"boolean",usageLabel:"--no-login",usageDescription:"Connect: fail instead of starting implicit cloud login"},{key:"sessionLock",names:["--session-lock"],type:"enum",enumValues:["reject","strip"],usageLabel:"--session-lock reject|strip",usageDescription:"Lock bound-session device routing for this CLI invocation and nested batch steps"},{key:"sessionLocked",names:["--session-locked"],type:"boolean",usageLabel:"--session-locked",usageDescription:"Deprecated alias for --session-lock reject"},{key:"sessionLockConflicts",names:["--session-lock-conflicts"],type:"enum",enumValues:["reject","strip"],usageLabel:"--session-lock-conflicts reject|strip",usageDescription:"Deprecated alias for --session-lock"},{key:"platform",names:["--platform"],type:"enum",enumValues:["ios","macos","android","linux","apple"],usageLabel:"--platform ios|macos|android|linux|apple",usageDescription:"Platform to target (`apple` aliases the Apple automation backend)"},{key:"target",names:["--target"],type:"enum",enumValues:["mobile","tv","desktop"],usageLabel:"--target mobile|tv|desktop",usageDescription:"Device target class to match"},{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:"surface",names:["--surface"],type:"enum",enumValues:l,usageLabel:"--surface app|frontmost-app|desktop|menubar",usageDescription:"macOS session surface for open (defaults to app)"},{key:"headless",names:["--headless"],type:"boolean",usageLabel:"--headless",usageDescription:"Boot: launch Android emulator without a GUI window"},{key:"runtime",names:["--runtime"],type:"string",usageLabel:"--runtime <id>",usageDescription:"ensure-simulator: CoreSimulator runtime identifier (e.g. com.apple.CoreSimulator.SimRuntime.iOS-18-0)"},{key:"metroHost",names:["--metro-host"],type:"string",usageLabel:"--metro-host <host>",usageDescription:"Session-scoped Metro/debug host hint"},{key:"metroPort",names:["--metro-port"],type:"int",min:1,max:65535,usageLabel:"--metro-port <port>",usageDescription:"Session-scoped Metro/debug port hint"},{key:"metroProjectRoot",names:["--project-root"],type:"string",usageLabel:"--project-root <path>",usageDescription:"metro prepare: React Native project root (default: cwd)"},{key:"metroKind",names:["--kind"],type:"enum",enumValues:["auto","react-native","expo"],usageLabel:"--kind auto|react-native|expo",usageDescription:"metro prepare: detect or force the Metro launcher kind"},{key:"metroPublicBaseUrl",names:["--public-base-url"],type:"string",usageLabel:"--public-base-url <url>",usageDescription:"metro prepare: public base URL used for direct bundle hints"},{key:"metroProxyBaseUrl",names:["--proxy-base-url"],type:"string",usageLabel:"--proxy-base-url <url>",usageDescription:"metro prepare: optional bridge origin for remote Metro access"},{key:"metroBearerToken",names:["--bearer-token"],type:"string",usageLabel:"--bearer-token <token>",usageDescription:"metro prepare: host bridge bearer token (prefer AGENT_DEVICE_PROXY_TOKEN or AGENT_DEVICE_METRO_BEARER_TOKEN)"},{key:"metroPreparePort",names:["--port"],type:"int",min:1,max:65535,usageLabel:"--port <port>",usageDescription:"metro prepare: local Metro port (default: 8081)"},{key:"metroListenHost",names:["--listen-host"],type:"string",usageLabel:"--listen-host <host>",usageDescription:"metro prepare: host Metro listens on (default: 0.0.0.0)"},{key:"metroStatusHost",names:["--status-host"],type:"string",usageLabel:"--status-host <host>",usageDescription:"metro prepare: host used for local /status polling (default: 127.0.0.1)"},{key:"metroStartupTimeoutMs",names:["--startup-timeout-ms"],type:"int",min:1,usageLabel:"--startup-timeout-ms <ms>",usageDescription:"metro prepare: timeout while waiting for Metro to become ready"},{key:"metroProbeTimeoutMs",names:["--probe-timeout-ms"],type:"int",min:1,usageLabel:"--probe-timeout-ms <ms>",usageDescription:"metro prepare: timeout for /status and proxy bridge calls"},{key:"metroRuntimeFile",names:["--runtime-file"],type:"string",usageLabel:"--runtime-file <path>",usageDescription:"metro prepare: optional file path to persist the JSON result"},{key:"metroNoReuseExisting",names:["--no-reuse-existing"],type:"boolean",usageLabel:"--no-reuse-existing",usageDescription:"metro prepare: always start a fresh Metro process"},{key:"metroNoInstallDeps",names:["--no-install-deps"],type:"boolean",usageLabel:"--no-install-deps",usageDescription:"metro prepare: skip package-manager install when node_modules is missing"},{key:"bundleUrl",names:["--bundle-url"],type:"string",usageLabel:"--bundle-url <url>",usageDescription:"Session-scoped bundle URL hint"},{key:"launchUrl",names:["--launch-url"],type:"string",usageLabel:"--launch-url <url>",usageDescription:"Session-scoped deep link / launch URL hint"},{key:"boot",names:["--boot"],type:"boolean",usageLabel:"--boot",usageDescription:"ensure-simulator: boot the simulator after ensuring it exists"},{key:"reuseExisting",names:["--reuse-existing"],type:"boolean",usageLabel:"--reuse-existing",usageDescription:"ensure-simulator: reuse an existing simulator (default: true)"},{key:"iosSimulatorDeviceSet",names:["--ios-simulator-device-set"],type:"string",usageLabel:"--ios-simulator-device-set <path>",usageDescription:"Scope iOS simulator discovery/commands to this simulator device set"},{key:"androidDeviceAllowlist",names:["--android-device-allowlist"],type:"string",usageLabel:"--android-device-allowlist <serials>",usageDescription:"Comma/space separated Android serial allowlist for discovery/selection"},{key:"activity",names:["--activity"],type:"string",usageLabel:"--activity <component>",usageDescription:"Android app launch activity (package/Activity); not for URL opens"},{key:"header",names:["--header"],type:"string",multiple:!0,usageLabel:"--header <name:value>",usageDescription:"install-from-source: repeatable HTTP header for URL downloads"},{key:"githubActionsArtifact",names:["--github-actions-artifact"],type:"string",usageLabel:"--github-actions-artifact <owner/repo:artifact>",usageDescription:"install-from-source: GitHub Actions artifact resolved by a remote daemon"},{key:"installSource",names:[],type:"string"},{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:"fps",names:["--fps"],type:"int",min:1,max:120,usageLabel:"--fps <n>",usageDescription:"Record: target frames per second (iOS physical device runner)"},{key:"quality",names:["--quality"],type:"int",min:5,max:10,usageLabel:"--quality <5-10>",usageDescription:"Record: scale recording resolution from 5 (50%) through 10 (native resolution)"},{key:"hideTouches",names:["--hide-touches"],type:"boolean",usageLabel:"--hide-touches",usageDescription:"Record: disable touch overlays in the final video"},{key:"intervalMs",names:["--interval-ms"],type:"int",min:0,max:1e4,usageLabel:"--interval-ms <ms>",usageDescription:"Delay between press iterations"},{key:"delayMs",names:["--delay-ms"],type:"int",min:0,max:1e4,usageLabel:"--delay-ms <ms>",usageDescription:"Delay between typed characters"},{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:"pixels",names:["--pixels"],type:"int",min:1,max:1e5,usageLabel:"--pixels <n>",usageDescription:"Scroll: explicit gesture distance in pixels"},{key:"doubleTap",names:["--double-tap"],type:"boolean",usageLabel:"--double-tap",usageDescription:"Use double-tap gesture per press iteration"},{key:"clickButton",names:["--button"],type:"enum",enumValues:["primary","secondary","middle"],usageLabel:"--button primary|secondary|middle",usageDescription:"Click: choose mouse button (middle reserved for future macOS support)"},{key:"backMode",names:["--in-app"],type:"enum",enumValues:["in-app","system"],setValue:"in-app",usageLabel:"--in-app",usageDescription:"Back: use app-provided back UI when available"},{key:"backMode",names:["--system"],type:"enum",enumValues:["in-app","system"],setValue:"system",usageLabel:"--system",usageDescription:"Back: use system back input or gesture when available"},{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:"snapshotDiff",names:["--diff"],type:"boolean",usageLabel:"--diff",usageDescription:"Snapshot: show structural diff against the previous session baseline"},{key:"saveScript",names:["--save-script"],type:"booleanOrString",usageLabel:"--save-script [path]",usageDescription:"Save session script (.ad) on close; optional custom output path"},{key:"networkInclude",names:["--include"],type:"enum",enumValues:["summary","headers","body","all"],usageLabel:"--include summary|headers|body|all",usageDescription:"Network: include headers, bodies, or both in output"},{key:"shutdown",names:["--shutdown"],type:"boolean",usageLabel:"--shutdown",usageDescription:"close: shutdown associated iOS simulator after ending session"},{key:"relaunch",names:["--relaunch"],type:"boolean",usageLabel:"--relaunch",usageDescription:"open: terminate app process before launching it"},{key:"restart",names:["--restart"],type:"boolean",usageLabel:"--restart",usageDescription:"logs clear: stop active stream, clear logs, then start streaming again"},{key:"retainPaths",names:["--retain-paths"],type:"boolean",usageLabel:"--retain-paths",usageDescription:"install-from-source: keep materialized artifact paths after install"},{key:"retentionMs",names:["--retention-ms"],type:"int",min:1,usageLabel:"--retention-ms <ms>",usageDescription:"install-from-source: retention TTL for materialized artifact paths"},{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:"replayEnv",names:["-e","--env"],type:"string",multiple:!0,usageLabel:"-e KEY=VALUE, --env KEY=VALUE",usageDescription:"Replay/Test: inject or override a ${KEY} variable for the script (repeatable)"},{key:"failFast",names:["--fail-fast"],type:"boolean",usageLabel:"--fail-fast",usageDescription:"Test: stop the suite after the first failing script"},{key:"timeoutMs",names:["--timeout"],type:"int",min:1,usageLabel:"--timeout <ms>",usageDescription:"Test: maximum wall-clock time per script attempt"},{key:"retries",names:["--retries"],type:"int",min:0,max:3,usageLabel:"--retries <n>",usageDescription:"Test: retry each failed script up to n additional times"},{key:"artifactsDir",names:["--artifacts-dir"],type:"string",usageLabel:"--artifacts-dir <path>",usageDescription:"Test: root directory for suite artifacts"},{key:"reportJunit",names:["--report-junit"],type:"string",usageLabel:"--report-junit <path>",usageDescription:"Test: write a JUnit XML report for the replay suite"},{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:"findFirst",names:["--first"],type:"boolean",usageLabel:"--first",usageDescription:"Find: pick the first match when ambiguous"},{key:"findLast",names:["--last"],type:"boolean",usageLabel:"--last",usageDescription:"Find: pick the last match when ambiguous"},{key:"out",names:["--out"],type:"string",usageLabel:"--out <path>",usageDescription:"Output path"},{key:"overlayRefs",names:["--overlay-refs"],type:"boolean",usageLabel:"--overlay-refs",usageDescription:"Screenshot: draw current snapshot refs and target rectangles onto the saved PNG; diff screenshot: also write a separate current-screen overlay guide"},{key:"screenshotFullscreen",names:["--fullscreen"],type:"boolean",usageLabel:"--fullscreen",usageDescription:"Screenshot: capture the full screen instead of the app window"},{key:"screenshotMaxSize",names:["--max-size"],type:"int",min:1,usageLabel:"--max-size <px>",usageDescription:"Screenshot: downscale so the longest edge is at most <px>"},{key:"baseline",names:["--baseline","-b"],type:"string",usageLabel:"--baseline, -b <path>",usageDescription:"Diff screenshot: path to baseline image file"},{key:"threshold",names:["--threshold"],type:"string",usageLabel:"--threshold <0-1>",usageDescription:"Diff screenshot: color distance threshold (default 0.1)"}],g=new Set(["json","config","remoteConfig","stateDir","daemonBaseUrl","daemonAuthToken","daemonTransport","daemonServerMode","tenant","sessionIsolation","runId","leaseId","leaseBackend","sessionLock","sessionLocked","sessionLockConflicts","help","version","verbose","platform","target","device","udid","serial","iosSimulatorDeviceSet","androidDeviceAllowlist","session","noRecord"]),h={boot:{helpDescription:"Ensure target device/simulator is booted and ready",summary:"Boot target device/simulator",positionalArgs:[],allowedFlags:["headless"]},open:{helpDescription:"Boot device/simulator; optionally launch app or deep link URL (macOS also supports --surface app|frontmost-app|desktop|menubar)",summary:"Open an app, deep link or URL, save replays",positionalArgs:["appOrUrl?","url?"],allowedFlags:["activity","saveScript","relaunch","surface"]},connect:{usageOverride:"connect [--remote-config <path>] [--tenant <id>] [--run-id <id>] [--lease-backend <backend>] [--force] [--no-login]",helpDescription:"Connect to a remote daemon, authenticate when needed, and save remote session state. AGENT_DEVICE_CLOUD_BASE_URL is the bridge/control-plane API origin; use AGENT_DEVICE_DAEMON_AUTH_TOKEN=adc_live_... for CI/service-token automation.",summary:"Connect to remote daemon",positionalArgs:[],allowedFlags:["force","noLogin","metroProjectRoot","metroKind","metroPublicBaseUrl","metroProxyBaseUrl","metroBearerToken","metroPreparePort","metroListenHost","metroStatusHost","metroStartupTimeoutMs","metroProbeTimeoutMs","metroRuntimeFile","metroNoReuseExisting","metroNoInstallDeps","launchUrl"],skipCapabilityCheck:!0},mcp:{helpDescription:"Start the official stdio MCP router for status, install guidance, version-matched CLI help, workflow prompts, and help resources. The MCP router does not expose device automation or shell execution tools.",summary:"Start MCP discovery router",positionalArgs:[],allowedFlags:[],skipCapabilityCheck:!0},disconnect:{helpDescription:"Disconnect remote daemon state, stop owned Metro companion, and release lease",summary:"Disconnect remote daemon",positionalArgs:[],allowedFlags:["shutdown"],skipCapabilityCheck:!0},connection:{usageOverride:"connection status",listUsageOverride:"connection status",helpDescription:"Inspect active remote connection state",summary:"Inspect remote connection",positionalArgs:["status"],allowedFlags:[],skipCapabilityCheck:!0},auth:{usageOverride:"auth status|login|logout",listUsageOverride:"auth status|login|logout",helpDescription:"Manage cloud CLI authentication",summary:"Manage cloud authentication",positionalArgs:["status|login|logout"],allowedFlags:[],skipCapabilityCheck:!0},close:{helpDescription:"Close app or just end session",summary:"Close app or end session",positionalArgs:["app?"],allowedFlags:["saveScript","shutdown"]},reinstall:{helpDescription:"Uninstall + install app from binary path",summary:"Reinstall app from binary path",positionalArgs:["app","path"],allowedFlags:[]},install:{helpDescription:"Install app from binary path without uninstalling first",summary:"Install app from binary path",positionalArgs:["app","path"],allowedFlags:[]},"install-from-source":{usageOverride:"install-from-source <url> | install-from-source --github-actions-artifact <owner/repo:artifact>",listUsageOverride:"install-from-source <url> | install-from-source --github-actions-artifact",helpDescription:"Install app from a URL or remote-resolved source",summary:"Install app from a source",positionalArgs:["url?"],allowedFlags:["header","githubActionsArtifact","installSource","retainPaths","retentionMs"]},push:{helpDescription:"Simulate push notification payload delivery",summary:"Deliver push payload",positionalArgs:["bundleOrPackage","payloadOrJson"],allowedFlags:[]},snapshot:{usageOverride:"snapshot [--diff] [-i] [-c] [-d <depth>] [-s <scope>] [--raw]",helpDescription:"Capture accessibility tree or diff against the previous session baseline",positionalArgs:[],allowedFlags:["snapshotDiff",...c]},diff:{usageOverride:"diff snapshot | diff screenshot --baseline <path> [current.png] [--out <diff.png>] [--threshold <0-1>] [--overlay-refs]",helpDescription:"Diff accessibility snapshot or compare screenshots pixel-by-pixel",summary:"Diff snapshot or screenshot",positionalArgs:["kind","current?"],allowedFlags:[...c,"baseline","threshold","out","overlayRefs"]},"ensure-simulator":{helpDescription:"Ensure an iOS simulator exists in a device set (create if missing)",summary:"Ensure iOS simulator exists",positionalArgs:[],allowedFlags:["runtime","boot","reuseExisting"],skipCapabilityCheck:!0},devices:{helpDescription:"List available devices",positionalArgs:[],allowedFlags:[],skipCapabilityCheck:!0},apps:{helpDescription:"List installed apps (includes default/system apps by default)",summary:"List installed apps",positionalArgs:[],allowedFlags:["appsFilter"],defaults:{appsFilter:"all"}},appstate:{helpDescription:"Show foreground app/activity",positionalArgs:[],allowedFlags:[],skipCapabilityCheck:!0},metro:{usageOverride:"metro prepare (--public-base-url <url> | --proxy-base-url <url>) [--project-root <path>] [--port <port>] [--kind auto|react-native|expo]\n agent-device metro reload [--metro-host <host>] [--metro-port <port>] [--bundle-url <url>]",listUsageOverride:"metro prepare --public-base-url <url> | --proxy-base-url <url>; metro reload",helpDescription:"Prepare a local Metro runtime or ask Metro to reload connected React Native apps",summary:"Prepare Metro or reload apps",positionalArgs:["prepare|reload"],allowedFlags:["metroHost","metroPort","metroProjectRoot","metroKind","metroPublicBaseUrl","metroProxyBaseUrl","metroBearerToken","metroPreparePort","metroListenHost","metroStatusHost","metroStartupTimeoutMs","metroProbeTimeoutMs","metroRuntimeFile","metroNoReuseExisting","metroNoInstallDeps","bundleUrl"],skipCapabilityCheck:!0},clipboard:{usageOverride:"clipboard read | clipboard write <text>",listUsageOverride:"clipboard read | clipboard write <text>",helpDescription:"Read or write device clipboard text",positionalArgs:["read|write","text?"],allowsExtraPositionals:!0,allowedFlags:[]},keyboard:{usageOverride:"keyboard [status|get|dismiss]",helpDescription:"Inspect Android keyboard visibility/type or dismiss the device keyboard",summary:"Inspect or dismiss the device keyboard",positionalArgs:["action?"],allowedFlags:[]},perf:{helpDescription:"Show session performance metrics, including frame health on Android and iOS devices",summary:"Show performance metrics",positionalArgs:[],allowedFlags:[]},"react-devtools":{usageOverride:"react-devtools [...args]",listUsageOverride:"react-devtools [...args]",helpDescription:"Run pinned agent-react-devtools commands for React Native performance profiling, component trees, props/state/hooks, and render analysis",summary:"Profile React Native performance and component renders",positionalArgs:["args?"],allowsExtraPositionals:!0,allowedFlags:[],skipCapabilityCheck:!0},back:{usageOverride:"back [--in-app|--system]",helpDescription:"Navigate back with explicit app or system semantics",summary:"Go back",positionalArgs:[],allowedFlags:["backMode"]},home:{helpDescription:"Go to home screen (where supported)",summary:"Go home",positionalArgs:[],allowedFlags:[]},rotate:{usageOverride:"rotate <portrait|portrait-upside-down|landscape-left|landscape-right>",helpDescription:"Rotate device orientation on iOS and Android",summary:"Rotate device orientation",positionalArgs:["orientation"],allowedFlags:[]},"app-switcher":{helpDescription:"Open app switcher (where supported)",summary:"Open app switcher",positionalArgs:[],allowedFlags:[]},wait:{usageOverride:"wait <ms>|text <text>|@ref|<selector> [timeoutMs]",helpDescription:"Wait for duration, text, ref, or selector to appear",summary:"Wait for time, text, ref, or selector",positionalArgs:["durationOrSelector","timeoutMs?"],allowsExtraPositionals:!0,allowedFlags:[...d]},alert:{usageOverride:"alert [get|accept|dismiss|wait] [timeout]",helpDescription:"Inspect or handle alert (iOS simulator and macOS desktop)",summary:"Inspect or handle iOS/macOS alerts",positionalArgs:["action?","timeout?"],allowedFlags:[]},click:{usageOverride:"click <x y|@ref|selector>",helpDescription:"Tap/click by coordinates, snapshot ref, or selector",summary:"Tap by coordinates, ref, or selector",positionalArgs:["target"],allowsExtraPositionals:!0,allowedFlags:["count","intervalMs","holdMs","jitterPx","doubleTap","clickButton",...d]},get:{usageOverride:"get text|attrs <@ref|selector>",helpDescription:"Return exposed element text/attributes by ref or selector; use snapshot -s @ref for truncated previews",summary:"Get exposed text or attrs by ref or selector",positionalArgs:["subcommand","target"],allowedFlags:[...d]},replay:{helpDescription:"Replay a recorded session",positionalArgs:["path"],allowedFlags:["replayUpdate","replayEnv"],skipCapabilityCheck:!0},test:{usageOverride:"test <path-or-glob>...",listUsageOverride:"test <path-or-glob>...",helpDescription:"Run one or more .ad scripts as a serial test suite",summary:"Run .ad test suites",positionalArgs:["pathOrGlob"],allowsExtraPositionals:!0,allowedFlags:["replayUpdate","replayEnv","failFast","timeoutMs","retries","artifactsDir","reportJunit"],skipCapabilityCheck:!0},batch:{usageOverride:"batch [--steps <json> | --steps-file <path>]",listUsageOverride:"batch --steps <json> | --steps-file <path>",helpDescription:"Execute multiple commands in one daemon request",summary:"Run multiple commands",positionalArgs:[],allowedFlags:["steps","stepsFile","batchOnError","batchMaxSteps","out"],skipCapabilityCheck:!0},press:{usageOverride:"press <x y|@ref|selector>",helpDescription:"Tap/press by coordinates, snapshot ref, or selector (supports repeated series)",summary:"Press by coordinates, ref, or selector",positionalArgs:["targetOrX","y?"],allowsExtraPositionals:!0,allowedFlags:["count","intervalMs","holdMs","jitterPx","doubleTap",...d]},longpress:{helpDescription:"Long press by coordinates (iOS and Android)",summary:"Long press by coordinates",positionalArgs:["x","y","durationMs?"],allowedFlags:[]},swipe:{helpDescription:"Swipe coordinates with optional repeat pattern",summary:"Swipe coordinates",positionalArgs:["x1","y1","x2","y2","durationMs?"],allowedFlags:["count","pauseMs","pattern"]},focus:{helpDescription:"Focus input at coordinates",positionalArgs:["x","y"],allowedFlags:[]},type:{helpDescription:"Type text in focused field",positionalArgs:["text"],allowsExtraPositionals:!0,allowedFlags:["delayMs"]},fill:{usageOverride:"fill <x> <y> <text> | fill <@ref|selector> <text>",helpDescription:"Tap then type",positionalArgs:["targetOrX","yOrText","text?"],allowsExtraPositionals:!0,allowedFlags:[...d,"delayMs"]},scroll:{usageOverride:"scroll <direction> [amount] [--pixels <n>]",helpDescription:"Scroll in direction (relative amount or explicit pixels)",summary:"Scroll in a direction",positionalArgs:["direction","amount?"],allowedFlags:["pixels"]},pinch:{helpDescription:"Pinch/zoom gesture (Apple simulator or macOS app session)",positionalArgs:["scale","x?","y?"],allowedFlags:[]},screenshot:{helpDescription:"Capture screenshot (macOS app sessions default to the app window; use --fullscreen for full desktop, --max-size to downscale, or --overlay-refs to annotate the image with current refs)",positionalArgs:["path?"],allowedFlags:["out","overlayRefs","screenshotFullscreen","screenshotMaxSize"]},"trigger-app-event":{usageOverride:"trigger-app-event <event> [payloadJson]",helpDescription:"Trigger app-defined event hook via deep link template",summary:"Trigger app event hook",positionalArgs:["event","payloadJson?"],allowedFlags:[]},record:{usageOverride:"record start [path] [--fps <n>] [--quality <5-10>] [--hide-touches] | record stop",listUsageOverride:"record start [path] | record stop",helpDescription:"Start/stop screen recording",summary:"Start or stop screen recording",positionalArgs:["start|stop","path?"],allowedFlags:["fps","quality","hideTouches"]},trace:{usageOverride:"trace start <path> | trace stop <path>",listUsageOverride:"trace start <path> | trace stop <path>",helpDescription:"Start/stop trace log capture; when an artifact path is requested, pass the same positional path to start and stop",summary:"Start or stop trace capture",positionalArgs:["start|stop","path?"],allowedFlags:[],skipCapabilityCheck:!0},logs:{usageOverride:"logs path | logs start | logs stop | logs clear [--restart] | logs doctor | logs mark [message...]",helpDescription:"Session app log info, start/stop streaming, diagnostics, and markers",summary:"Manage session app logs",positionalArgs:["path|start|stop|clear|doctor|mark","message?"],allowsExtraPositionals:!0,allowedFlags:["restart"]},network:{usageOverride:"network dump [limit] [summary|headers|body|all] [--include summary|headers|body|all] | network log [limit] [summary|headers|body|all] [--include summary|headers|body|all]",helpDescription:"Dump recent HTTP(s) traffic parsed from the session app log",summary:"Show recent HTTP traffic",positionalArgs:["dump|log","limit?","include?"],allowedFlags:["networkInclude"]},find:{usageOverride:"find <locator|text> <action> [value] [--first|--last]",helpDescription:"Find by text/label/value/role/id and run action",summary:"Find an element and act",positionalArgs:["query","action","value?"],allowsExtraPositionals:!0,allowedFlags:["snapshotDepth","snapshotRaw","findFirst","findLast"]},is:{helpDescription:"Assert UI state (visible|hidden|exists|editable|selected|text)",summary:"Assert UI state",positionalArgs:["predicate","selector","value?"],allowsExtraPositionals:!0,allowedFlags:[...d]},settings:{usageOverride:p,listUsageOverride:"settings [area] [options]",helpDescription:"Toggle OS settings, animation scales, appearance, and app permissions (macOS supports only settings appearance <light|dark|toggle> and settings permission <grant|reset> <accessibility|screen-recording|input-monitoring>; wifi|airplane|location|animations remain unsupported on macOS; mobile permission actions use the active session app)",summary:"Change OS settings and app permissions",positionalArgs:["setting","state","target?","mode?"],allowedFlags:[]},session:{usageOverride:"session list",helpDescription:"List active sessions",positionalArgs:["list?"],allowedFlags:[],skipCapabilityCheck:!0}},f=new Map,y=new Map;for(let e of m){for(let t of e.names)f.set(t,e);let t=y.get(e.key);t?t.push(e):y.set(e.key,[e])}function v(e){return f.get(e)}function b(){return m}function k(e){if(e)return h[e]}function w(){return Object.keys(h)}function D(e){if(!e)return!1;let t=e.trim().toLowerCase();return"1"===t||"true"===t||"yes"===t||"on"===t}function x(e){let t=e.endsWith("?"),s=t?e.slice(0,-1):e;return t?`[${s}]`:`<${s}>`}let L=(e=`agent-device <command> [args] [--json]
|
|
350
|
-
|
|
351
|
-
CLI to control iOS and Android devices for AI agents.
|
|
352
|
-
`,t=R("Commands:",w().map(e=>{let t=h[e];if(!t)throw Error(`Missing command schema for ${e}`);return{name:e,schema:t,usage:function(e,t){if(t.listUsageOverride)return t.listUsageOverride;let s=t.positionalArgs.map(s=>{var a,o,i;let r,n,l,p;return a=e,o=t,n=(r=(i=s).endsWith("?"))?i.slice(0,-1):i,p=(l=/^[a-z-]+(?:\|[a-z-]+)+$/i.test(n))||void 0!==o.usageOverride&&o.usageOverride.startsWith(`${a} ${n}`),r?l?`[${n}]`:p?n:`[${n}]`:p?n:`<${n}>`});return[e,...s].join(" ")}(e,t)}}).map(e=>({label:e.usage,description:e.schema.summary??e.schema.helpDescription}))),s=O("Flags:",A(g)),a=E("Agent Quickstart:",["Default loop: devices/apps -> open -> snapshot -i -> press/fill/get/is/wait/find -> verify -> close.",'Use selectors or refs as positional targets: id="submit", label="Allow", or @e12 from snapshot -i.',"Plain snapshot reads state; snapshot -i is required to refresh interactive refs.","Read-only visible/state question: use snapshot/get/is/find; use snapshot -i only when refs are needed.","Truncated text/input preview: expand first with snapshot -s @e12, not get text.","RN warning/error overlays can block taps: snapshot -i, dismiss/close, then diff snapshot -i.",'Expo Go/dev clients: use the provided URL when given; on iOS prefer open "Expo Go" <url>; Android URL opens infer the foreground package for logs/perf when possible.',"Install flows: install/install-from-source first, then open the installed id with --relaunch.",'Text: fill \'id="field-email"\' "qa@example.com" replaces; type appends after press.','Clearing text: do not use fill <target> ""; use a visible clear/reset control or report that clearing is unsupported.',"Android IME capture: if fill says input was captured by the keyboard/IME, inspect keyboard state and switch/disable handwriting before retrying; do not loop fill/type.","Run mutating commands serially against one session; parallelize only read-only commands or separate sessions.","Clipboard limits: iOS Allow Paste cannot be automated through XCUITest; prefill with clipboard write. Android non-ASCII should use fill/type, not raw adb input.","After mutation: diff snapshot -i. Off-screen hints: scroll, then snapshot -i.","Raw coordinates are fallback-only: use snapshot -i -c --json rects when iOS refs no-op or child refs are missing.",'Batch JSON steps use "command", "positionals", "flags"; never "args" or "step".',"Navigation: app-owned back uses back; system back uses back --system.","Verification commands must name the expected text/selector; bare screenshots/snapshots are not enough.","Debug evidence: logs clear --restart/mark/path; trace start ./path; trace stop ./path; network dump --include headers.","Use agent-device commands in final plans; raw platform tools, pseudo commands, and helper prose are wrong.","Full operating guide: agent-device help workflow. Exploratory QA: agent-device help dogfood."]),o=R("Agent Workflows:",[{label:"help workflow",description:"Normal bootstrap, exploration, and validation loop"},{label:"help debugging",description:"Logs, network, alerts, diagnostics, and traces"},{label:"help react-devtools",description:"React Native performance, profiling, component tree, and renders"},{label:"help remote",description:"Remote/cloud config, tenants, leases, and local service tunnels"},{label:"help macos",description:"Desktop, frontmost-app, and menu bar surfaces"},{label:"help dogfood",description:"Exploratory QA report workflow"}]),i=E("Configuration:",["Default config files: ~/.agent-device/config.json, ./agent-device.json","Use --config <path> or AGENT_DEVICE_CONFIG to load one explicit config file."]),r=R("Environment:",[{label:"AGENT_DEVICE_SESSION",description:"Default session name"},{label:"AGENT_DEVICE_PLATFORM",description:"Default platform binding"},{label:"AGENT_DEVICE_SESSION_LOCK",description:"Bound-session conflict mode"},{label:"AGENT_DEVICE_DAEMON_BASE_URL",description:"Connect to remote daemon"},{label:"AGENT_DEVICE_DAEMON_AUTH_TOKEN",description:"Remote daemon service/API token"},{label:"AGENT_DEVICE_CLOUD_BASE_URL",description:"Bridge/control-plane API origin for cloud auth and /api-keys"}]),n=E("Examples:",["agent-device open Settings --platform ios","agent-device open TextEdit --platform macos","agent-device snapshot -i","agent-device react-devtools get tree --depth 3",'agent-device fill @e3 "test@example.com"',"agent-device replay ./session.ad","agent-device test ./suite --platform android"]),`${e}
|
|
353
|
-
${t}
|
|
354
|
-
|
|
355
|
-
${s}
|
|
356
|
-
|
|
357
|
-
${a}
|
|
358
|
-
|
|
359
|
-
${o}
|
|
360
|
-
|
|
361
|
-
${i}
|
|
362
|
-
|
|
363
|
-
${r}
|
|
364
|
-
|
|
365
|
-
${n}
|
|
366
|
-
`);function S(){return L}function A(e){return m.filter(t=>e.has(t.key)&&void 0!==t.usageLabel&&void 0!==t.usageDescription)}function O(e,t){return R(e,t.map(e=>({label:e.usageLabel??"",description:e.usageDescription??""})))}function R(e,t){if(0===t.length)return`${e}
|
|
367
|
-
(none)`;let s=Math.max(...t.map(e=>e.label.length))+2,a=[e];for(let e of t)a.push(` ${e.label.padEnd(s)}${e.description}`);return a.join("\n")}function E(e,t){return 0===t.length?`${e}
|
|
368
|
-
(none)`:[e,...t.map(e=>` ${e}`)].join("\n")}function U(e){var t,s;let a,o=(a=u[e])?`${a.body}
|
|
369
|
-
|
|
370
|
-
Related:
|
|
371
|
-
agent-device help command list and global flags
|
|
372
|
-
agent-device help <command> command-specific flags
|
|
373
|
-
agent-device help workflow normal app automation loop
|
|
374
|
-
`:null;if(o)return o;let i=k(e);if(!i)return null;let r=(t=e,(s=i).usageOverride?s.usageOverride:[t,...s.positionalArgs.map(x),...s.allowedFlags.flatMap(e=>(y.get(e)??[]).map(e=>e.usageLabel??e.names[0])).map(e=>`[${e}]`)].join(" ")),n=A(new Set(i.allowedFlags)),l=A(g),p=[];return n.length>0&&p.push(O("Command flags:",n)),p.push(O("Global flags:",l)),`agent-device ${r}
|
|
375
|
-
|
|
376
|
-
${i.helpDescription}
|
|
377
|
-
|
|
378
|
-
Usage:
|
|
379
|
-
agent-device ${r}
|
|
380
|
-
|
|
381
|
-
${p.join("\n\n")}
|
|
382
|
-
`}export{g as GLOBAL_FLAG_KEYS,U as buildCommandUsageText,S as buildUsageText,w as getCliCommandNames,k as getCommandSchema,v as getFlagDefinition,b as getFlagDefinitions,D as isStrictFlagModeEnabled};
|