agent-device 0.21.19 → 0.21.20

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.
Files changed (114) hide show
  1. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.19.apk → agent-device-android-ime-helper-0.21.20.apk} +0 -0
  2. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.20.apk.sha256 +1 -0
  3. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.19.manifest.json → agent-device-android-ime-helper-0.21.20.manifest.json} +4 -4
  4. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.21.19.apk → agent-device-android-snapshot-helper-0.21.20.apk} +0 -0
  5. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.20.apk.sha256 +1 -0
  6. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.21.19.manifest.json → agent-device-android-snapshot-helper-0.21.20.manifest.json} +6 -6
  7. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +12 -0
  8. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +1 -14
  9. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +14 -16
  10. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +40 -14
  11. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotModels.swift +20 -5
  12. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentationProjection.swift +2 -15
  13. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotVisibilityFold.swift +10 -25
  14. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotVisibilityFoldProjection.swift +1 -18
  15. package/dist/src/adapter.js +1 -1
  16. package/dist/src/adb-failure.js +1 -1
  17. package/dist/src/agent-browser-process-record.js +1 -1
  18. package/dist/src/agent-browser-provider.js +1 -1
  19. package/dist/src/agent-browser-tool.js +1 -1
  20. package/dist/src/agent-device-client.js +2 -2
  21. package/dist/src/alert-runtime.js +1 -1
  22. package/dist/src/alert.js +1 -0
  23. package/dist/src/android-input-ownership.js +1 -1
  24. package/dist/src/app-launch.js +2 -2
  25. package/dist/src/app-lifecycle.js +1 -1
  26. package/dist/src/app-resolution.js +2 -2
  27. package/dist/src/apple-runner-platform.js +1 -1
  28. package/dist/src/archive.js +2 -2
  29. package/dist/src/browser-launch.js +1 -1
  30. package/dist/src/cache.js +1 -1
  31. package/dist/src/cli-config.js +1 -1
  32. package/dist/src/cli-help.js +287 -301
  33. package/dist/src/cli-runner.js +1 -1
  34. package/dist/src/cli.js +1 -1
  35. package/dist/src/client-metro-companion.js +1 -1
  36. package/dist/src/client-metro.js +1 -1
  37. package/dist/src/client-types.d.ts +14 -2
  38. package/dist/src/{scroll-command.d.ts → cloud-artifacts.d.ts} +242 -238
  39. package/dist/src/command-schema.js +3 -3
  40. package/dist/src/command-tools.js +2 -2
  41. package/dist/src/command.js +1 -1
  42. package/dist/src/config.js +1 -1
  43. package/dist/src/connection-runtime.js +1 -1
  44. package/dist/src/daemon-client-lifecycle.js +1 -1
  45. package/dist/src/daemon-client-transport.js +1 -1
  46. package/dist/src/daemon-registration.js +1 -1
  47. package/dist/src/debug-symbols-facade.js +3 -3
  48. package/dist/src/device-rotation.js +1 -1
  49. package/dist/src/devicectl.js +1 -1
  50. package/dist/src/display-inventory.js +1 -1
  51. package/dist/src/exec.js +1 -1
  52. package/dist/src/hdc.js +1 -1
  53. package/dist/src/helper.js +2 -2
  54. package/dist/src/host.js +1 -1
  55. package/dist/src/index.d.ts +1 -1
  56. package/dist/src/interactor.js +5 -5
  57. package/dist/src/interactor2.js +1 -1
  58. package/dist/src/internal/bin.js +1 -1
  59. package/dist/src/internal/daemon.js +2 -2
  60. package/dist/src/launch-confirmation.js +1 -0
  61. package/dist/src/limrun-runtime-dependencies.js +1 -1
  62. package/dist/src/limrun.d.ts +50 -42
  63. package/dist/src/mechanics.js +16 -16
  64. package/dist/src/owner-identity.js +1 -1
  65. package/dist/src/perf-facade.js +1 -1
  66. package/dist/src/perf-target.js +1 -1
  67. package/dist/src/physical-device-control.js +1 -1
  68. package/dist/src/platform-runtime-android-adb-host.js +1 -1
  69. package/dist/src/platform-runtime-app-log-process.js +1 -1
  70. package/dist/src/platform-runtime-host.js +1 -1
  71. package/dist/src/platform-runtime-operation-host.js +2 -2
  72. package/dist/src/platform-runtime-runtime-hints.js +1 -1
  73. package/dist/src/platform-runtime-screen-recording-apple-simulator-host.js +1 -1
  74. package/dist/src/platform-runtime-screen-recording-finalizer-host.js +1 -1
  75. package/dist/src/provider-session-ownership.js +1 -1
  76. package/dist/src/provider-webdriver.js +1 -1
  77. package/dist/src/registry.js +20 -4
  78. package/dist/src/result-serialization.js +1 -1
  79. package/dist/src/runner-cache-metadata.js +3 -3
  80. package/dist/src/runner-client.js +1 -1
  81. package/dist/src/runner-dev-tools-security.js +1 -1
  82. package/dist/src/runner-device-readiness.js +1 -1
  83. package/dist/src/runner-disposal.js +1 -1
  84. package/dist/src/runner-error-classification.js +1 -1
  85. package/dist/src/runner-exchange.js +1 -1
  86. package/dist/src/runner-operations-facade.js +1 -1
  87. package/dist/src/runner-session-types.js +1 -1
  88. package/dist/src/runner-start-budget.js +1 -1
  89. package/dist/src/runner-startup-transport.js +1 -1
  90. package/dist/src/runner-xctestrun.js +1 -1
  91. package/dist/src/runtime.js +1 -1
  92. package/dist/src/runtime10.js +1 -1
  93. package/dist/src/runtime2.js +1 -1
  94. package/dist/src/runtime4.js +5 -5
  95. package/dist/src/screenshot-result.js +1 -1
  96. package/dist/src/screenshot-runtime.js +1 -1
  97. package/dist/src/screenshot3.js +1 -1
  98. package/dist/src/sdk-contracts.d.ts +2 -0
  99. package/dist/src/sdk-contracts.js +2 -2
  100. package/dist/src/sdk-selectors.d.ts +4 -4
  101. package/dist/src/session2.js +1 -1
  102. package/dist/src/simulator-boot.js +1 -1
  103. package/dist/src/simulator.js +1 -1
  104. package/dist/src/snapshot-runtime.js +1 -1
  105. package/dist/src/tool-provider.js +3 -3
  106. package/dist/src/tool-provider2.js +1 -1
  107. package/dist/src/tool-provider3.js +1 -1
  108. package/dist/src/toolchain-probe.js +1 -1
  109. package/dist/src/update-check.js +1 -1
  110. package/dist/src/video.js +1 -1
  111. package/package.json +1 -1
  112. package/server.json +2 -2
  113. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.19.apk.sha256 +0 -1
  114. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.19.apk.sha256 +0 -1
@@ -1,6 +1,6 @@
1
- import{D as e,M as t,N as n,O as r,T as i,g as a,p as o,t as s,x as c}from"./sdk-contracts.js";import{a as ee,d as te,f as ne,m as re,n as ie,p as ae,t as oe,u as se}from"./capture.js";import{_ as l,c as ce,d as le,f as ue,k as de,l as fe,m as pe,p as me,s as he,u as ge}from"./sdk-batch-runner.js";import{t as _e}from"./viewport.js";import{a as ve,i as ye,n as be,t as xe}from"./sdk-device.js";import{a as Se,c as Ce}from"./lease-scope.js";import{An as we,Cr as Te,In as Ee,Mn as De,Nr as Oe,Rr as ke,_r as Ae,d as je,dr as Me,ei as Ne,fr as Pe,h as Fe,hr as Ie,i as Le,ln as Re,m as ze,s as Be,sn as Ve,ur as He}from"./sdk-batch.js";import{n as Ue}from"./recording-export-quality.js";import{t as We}from"./recording-scope.js";import{a as Ge,n as Ke,o as qe,r as Je,s as Ye}from"./scroll-gesture.js";import{E as Xe,T as Ze,b as Qe,v as $e,w as et}from"./script.js";import{a as tt,i as nt,r as rt}from"./vars.js";import{n as it,r as at,t as ot}from"./format.js";import{n as u,t as st}from"./catalog.js";import{i as ct,r as lt,t as ut}from"./back-mode.js";import{a as dt}from"./sdk-remote-config.js";import{t as ft}from"./session.js";import{a as pt,c as mt,d as ht,f as gt,g as _t,h as vt,i as yt,l as bt,n as xt,o as St,p as Ct,s as wt,t as Tt,u as Et}from"./observability.js";import{n as Dt,t as Ot}from"./success-text.js";import{a as d,i as kt,n as At,r as f}from"./output-common.js";import{t as jt}from"./alert-contract.js";import{a as Mt,i as Nt,l as Pt,o as Ft,s as It,t as Lt,u as Rt,x as zt}from"./settings.js";import{t as Bt}from"./location-coordinates.js";import{t as Vt}from"./snapshot-quality-backend-capabilities.js";import{f as Ht,m as Ut,p as Wt,r as Gt,s as Kt}from"./overlay.js";import{t as qt}from"./mobile-snapshot-semantics.js";import{t as Jt}from"./click-button.js";import{n as Yt}from"./scroll-command.js";import{n as Xt,t as Zt}from"./interaction-positionals.js";import{t as Qt}from"./app-inventory.js";import"./device.js";import{a as $t,d as en,f as tn,i as nn,o as rn,r as an,t as on,u as sn}from"./device-rotation.js";import"./app-inventory-contract.js";import{t as cn}from"./prepare-kind.js";import{n as ln,t as un}from"./path-resolution.js";import"./file.js";import{n as dn,r as fn,s as pn,t as mn}from"./tv-remote.js";import p from"node:path";import hn from"node:fs";const gn=[`path`,`start`,`stop`,`doctor`,`mark`,`clear`],_n=new Set(lt().map(e=>e.key));function vn(t){let n=Se(t);return de({stateDir:t.stateDir,daemonBaseUrl:t.daemonBaseUrl,daemonTransport:t.daemonTransport,daemonServerMode:t.daemonServerMode,...Ce(n),provider:t.provider,providerSessionId:t.providerSessionId,providerApp:t.providerApp,providerOsVersion:t.providerOsVersion,providerProject:t.providerProject,providerBuild:t.providerBuild,providerSessionName:t.providerSessionName,providerDeviceOrientation:t.providerDeviceOrientation,providerGeoLocation:t.providerGeoLocation,providerTimezone:t.providerTimezone,providerAppiumVersion:t.providerAppiumVersion,providerLanguage:t.providerLanguage,providerLocale:t.providerLocale,providerNetworkProfile:t.providerNetworkProfile,providerCustomNetwork:t.providerCustomNetwork,providerNoResignApp:t.providerNoResignApp,awsProjectArn:t.awsProjectArn,awsDeviceArn:t.awsDeviceArn,awsAppArn:t.awsAppArn,awsRegion:t.awsRegion,awsInteractionMode:t.awsInteractionMode,sessionIsolation:t.sessionIsolation,platform:t.platform,target:t.target,device:t.device,udid:t.udid,serial:t.serial,iosSimulatorDeviceSet:t.iosSimulatorDeviceSet,iosXctestrunFile:t.iosXctestrunFile,iosXctestDerivedDataPath:t.iosXctestDerivedDataPath,iosXctestEnvDir:t.iosXctestEnvDir,androidDeviceAllowlist:t.androidDeviceAllowlist,surface:t.surface,activity:t.activity,launchConsole:t.launchConsole,launchArgs:t.launchArgs,relaunch:t.relaunch,shutdown:t.shutdown,saveScript:t.saveScript,testIme:t.testIme,noRecord:t.noRecord,recordAs:t.recordAs,record:t.record,backMode:t.backMode,metroHost:t.metroHost,metroPort:t.metroPort,bundleUrl:t.bundleUrl,launchUrl:t.launchUrl,...e(t,a),...se(t),appsFilter:t.appsFilter,kind:t.kind,out:t.out,count:t.count,fps:t.fps,quality:t.quality,hideTouches:t.hideTouches,recordingScope:t.recordingScope,intervalMs:t.intervalMs,delayMs:t.delayMs,durationMs:t.durationMs,holdMs:t.holdMs,jitterPx:t.jitterPx,readinessTimeoutMs:t.readinessTimeoutMs,pixels:t.pixels,until:t.until,doubleTap:t.doubleTap,verify:t.verify,settle:t.settle,settleQuietMs:t.settleQuietMs,clickButton:t.clickButton,pauseMs:t.pauseMs,pattern:t.pattern,headless:t.headless,restart:t.restart,replayUpdate:t.replayUpdate,replayBackend:t.replayBackend,replayEnv:t.replayEnv,replayShellEnv:t.replayShellEnv,replayScriptSource:t.replayScriptSource,replayScriptSources:t.replayScriptSources,replayFrom:t.replayFrom,replayPlanDigest:t.replayPlanDigest,replayKeepSession:t.replayKeepSession,failFast:t.failFast,timeoutMs:t.timeoutMs,retries:t.retries,recordVideo:t.recordVideo,artifactsDir:t.artifactsDir,shardAll:t.shardAll,shardSplit:t.shardSplit,findFirst:t.findFirst,findLast:t.findLast,networkInclude:t.networkInclude,batchOnError:t.batchOnError,batchMaxSteps:t.batchMaxSteps,batchSteps:t.batchSteps,verbose:t.debug})}function yn(e,t){return{...vn(e),...t}}function bn(e,t){let n=e.inputSchema.properties;if(!n)return{};let r={},i=t;for(let e of Object.keys(n)){if(!_n.has(e))continue;let t=i[e];xn(t)&&(r[e]=t)}return r}function xn(e){return typeof e==`boolean`||typeof e==`number`||typeof e==`string`}function Sn(e){return{kind:`operator`,source:e}}function Cn(e,t){return`${e} is not accepted as a tool argument. ${wn(e,t)}`}function wn(e,t){if(`operatorPath`in t)return t.operatorPath;let n=`for the process serving these tools.`,r=(t.envFlagKeys??[e]).map(dt).join(` or `);return r===``?`Set ${e} in ~/.agent-device/config.json ${n}`:`Set the ${r} environment variable${t.operatorConfig?` (or ${e} in ~/.agent-device/config.json)`:``} ${n}`}function Tn(e){if(e==null)return{};if(!e||typeof e!=`object`||Array.isArray(e))throw new n(`INVALID_ARGS`,`Expected object arguments.`);return e}function En(e,t){let r=e[t];if(!r||typeof r!=`object`||Array.isArray(r))throw new n(`INVALID_ARGS`,`Expected ${t} to be an object.`);return r}function Dn(e,t){let r=e[t];if(typeof r!=`string`||r.length===0)throw new n(`INVALID_ARGS`,`Expected ${t} to be a non-empty string.`);return r}function On(e,t){let r=e[t];if(r!==void 0){if(typeof r!=`string`)throw new n(`INVALID_ARGS`,`Expected ${t} to be a string.`);return r}}function m(e,t){let r=e[t];if(r!==void 0){if(typeof r!=`string`||r.length===0)throw new n(`INVALID_ARGS`,`Expected ${t} to be a non-empty string.`);return r}}function kn(e,t){let r=e[t];if(typeof r!=`number`||!Number.isFinite(r))throw new n(`INVALID_ARGS`,`Expected ${t} to be a finite number.`);return r}function An(e,t){let r=e[t];if(r!==void 0){if(typeof r!=`boolean`)throw new n(`INVALID_ARGS`,`Expected ${t} to be a boolean.`);return r}}function jn(e,t,r){let i=e[t];if(typeof i!=`string`||!r.includes(i))throw new n(`INVALID_ARGS`,`Expected ${t} to be one of: ${r.join(`, `)}.`);return i}function Mn(e,t,r){let i=e[t];if(i!==void 0){if(typeof i!=`string`||!r.includes(i))throw new n(`INVALID_ARGS`,`Expected ${t} to be one of: ${r.join(`, `)}.`);return i}}function Nn(e,t){let r=e[t];if(r!==void 0){if(!r||typeof r!=`object`||Array.isArray(r))throw new n(`INVALID_ARGS`,`Expected ${t} to be an object.`);return r}}function Pn(e,t){let r=e[t];if(r!==void 0){if(!Array.isArray(r)||r.some(e=>typeof e!=`string`))throw new n(`INVALID_ARGS`,`Expected ${t} to be an array of strings.`);return r}}function Fn(e,t,r,i){let a=new Set(t),o=Object.keys(e).filter(e=>!a.has(e));if(o.length>0)throw new n(`INVALID_ARGS`,`${r} has unknown field(s): ${o.join(`, `)}.`,i===void 0?void 0:{hint:i})}function h(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>e!==void 0))}const In=Sn({operatorConfig:!0}),Ln={session:{schema:{type:`string`,description:`Agent-device session name.`},read:e=>m(e,`session`),flagKey:`session`,flagIn:[`input`]},platform:{schema:{type:`string`,enum:ye,description:`Platform selector used to resolve a device.`},read:e=>Mn(e,`platform`,ye),flagKey:`platform`,flagIn:[`input`,`selection`]},deviceTarget:{schema:{type:`string`,enum:be,description:`Device target form. Maps to the CLI --target flag.`},read:Un,clientKey:`target`,flagKey:`target`,flagIn:[`input`,`selection`]},target:{schema:{type:`string`,enum:be,description:`Alias for deviceTarget on commands without a UI target field. Interaction commands reserve target for the UI element.`}},device:{schema:{type:`string`,description:`Device name selector (a UDID belongs in udid, a serial in serial).`},read:e=>m(e,`device`),flagKey:`device`,flagIn:[`input`,`selection`]},udid:{schema:{type:`string`,description:`Apple device or simulator UDID; the selector that pins one device when several share a name.`},read:e=>m(e,`udid`),flagKey:`udid`,flagIn:[`input`,`selection`]},serial:{schema:{type:`string`,description:`Android, HarmonyOS, or Vega VVD serial selector.`},read:e=>m(e,`serial`),flagKey:`serial`,flagIn:[`input`,`selection`]},iosSimulatorDeviceSet:{schema:{type:`string`,description:`iOS simulator device-set path used for device resolution.`},read:e=>m(e,`iosSimulatorDeviceSet`),audience:Sn({envFlagKeys:[],operatorConfig:!0}),flagKey:`iosSimulatorDeviceSet`,flagIn:[`input`,`selection`]},iosXctestrunFile:{schema:{type:`string`,description:`Externally built iOS XCTest runner .xctestrun artifact path.`},read:e=>m(e,`iosXctestrunFile`),audience:In,flagKey:`iosXctestrunFile`,flagIn:[`input`]},iosXctestDerivedDataPath:{schema:{type:`string`,description:`Derived data path for external iOS XCTest runner execution.`},read:e=>m(e,`iosXctestDerivedDataPath`),audience:In,flagKey:`iosXctestDerivedDataPath`,flagIn:[`input`]},iosXctestEnvDir:{schema:{type:`string`,description:`Writable directory for iOS XCTest runner env overlays.`},read:e=>m(e,`iosXctestEnvDir`),audience:In,flagKey:`iosXctestEnvDir`,flagIn:[`input`]},androidDeviceAllowlist:{schema:{type:`string`,description:`Android serial allowlist used for device resolution.`},read:e=>m(e,`androidDeviceAllowlist`),flagKey:`androidDeviceAllowlist`,flagIn:[`input`,`selection`]},noRecord:{read:e=>An(e,`noRecord`),flagKey:`noRecord`,flagIn:[`input`,`selection`]},readinessTimeoutMs:{read:Wn},daemonBaseUrl:{schema:{type:`string`,description:`Remote daemon base URL.`},read:e=>m(e,`daemonBaseUrl`),audience:In},daemonAuthToken:{schema:{type:`string`,description:`Remote daemon auth token.`},read:e=>m(e,`daemonAuthToken`),audience:In},tenant:{schema:{type:`string`,description:`Remote tenant identifier.`},read:e=>m(e,`tenant`)},runId:{schema:{type:`string`,description:`Lease run identifier.`},read:e=>m(e,`runId`)},leaseId:{schema:{type:`string`,description:`Existing lease identifier.`},read:e=>m(e,`leaseId`)},cwd:{schema:{type:`string`,description:`Working directory for command execution.`},read:e=>m(e,`cwd`),audience:Sn({operatorPath:`Start the process serving these tools in the desired working directory, or pass absolute paths.`})},debug:{schema:{type:`boolean`,description:`Enable debug diagnostics.`},read:e=>An(e,`debug`)}},Rn=Object.entries(Ln),zn=Object.fromEntries(Rn.flatMap(([e,t])=>t.audience?[[e,t.audience]]:[]));function Bn(){return Object.fromEntries(Rn.flatMap(([e,t])=>t.schema?[[e,t.schema]]:[]))}function Vn(e,t={}){let n={};for(let[r,i]of Rn)i.read&&(n[r]=i.read(e,t));return n}function g(e){let t={};for(let[n,r]of Rn)r.read&&(t[r.clientKey??n]=e[n]);return h(t)}function Hn(e,t){let n={};for(let[r,i]of Rn){if(i.flagIn===void 0||!i.flagIn.includes(t))continue;let a=t===`selection`?i.clientKey??r:r;n[a]=e[i.flagKey]}return n}function Un(e,t){let r=Mn(e,`deviceTarget`,be);if(t.readTargetAlias===!1||e.target===void 0)return r;let i=Mn(e,`target`,be);if(r!==void 0&&i!==r)throw new n(`INVALID_ARGS`,`Expected target alias to match deviceTarget when both are set.`);return r??i}function Wn(e,t){if(!(t.readinessBudgetDeclared===!0||!Object.hasOwn(e,`readinessTimeoutMs`)))throw new n(`INVALID_ARGS`,`readinessTimeoutMs applies only to commands that wait for their target: press, click, and longpress.`)}function _(e,t){return n=>v(e,t?t(n):[],n)}function v(e,t,n,r){return{command:e,positionals:t,...r?{input:r}:{},options:Gn(n)}}function Gn(e){let t=Kn(e.deviceTarget??e.target);if(t===void 0&&e.target===void 0)return e;let{target:n,...r}=e;return t===void 0?r:{...r,target:t}}function Kn(e){return e===`mobile`||e===`tv`||e===`desktop`?e:void 0}function y(e){return h(Hn(e,`input`))}function qn(e){return h({record:e.record})}function Jn(e){return Hn(e,`selection`)}function Yn(e){return h({depth:e.snapshotDepth,scope:e.snapshotScope,raw:e.snapshotRaw})}function Xn(e){return{depth:e.snapshotDepth,scope:e.snapshotScope,raw:e.snapshotRaw}}function Zn(e){return h({settle:e.settle,settleQuietMs:e.settleQuietMs,timeoutMs:e.timeoutMs})}function Qn(e){return h({count:e.count,intervalMs:e.intervalMs,holdMs:e.holdMs,jitterPx:e.jitterPx,doubleTap:e.doubleTap})}function $n(e){if(`ref`in e&&e.ref!==void 0)return h({kind:`ref`,ref:e.ref,label:e.label});if(`selector`in e&&e.selector!==void 0)return{kind:`selector`,selector:e.selector};let t=e;return{kind:`point`,x:t.x,y:t.y}}function er(e){let t=nr(e);if(typeof t.ref==`string`)return[t.ref,...ir(t.label)];if(typeof t.selector==`string`)return[t.selector];if(t.kind===`point`||t.x!==void 0||t.y!==void 0)return[String(rr(t.x,`x`)),String(rr(t.y,`y`))];throw new n(`INVALID_ARGS`,`interaction requires @ref, selector, or point target`)}function tr(e){let t=nr(e);if(typeof t.ref==`string`)return[t.ref,...ir(t.label)];if(typeof t.selector==`string`)return[t.selector];throw new n(`INVALID_ARGS`,`element command requires @ref or selector target`)}function nr(e){if(!e||typeof e!=`object`||Array.isArray(e))throw new n(`INVALID_ARGS`,`Expected target object.`);let t=e,r=t.target;return r&&typeof r==`object`&&!Array.isArray(r)?r:t}function rr(e,t){if(typeof e!=`number`||!Number.isFinite(e))throw new n(`INVALID_ARGS`,`point target requires numeric ${t}.`);return e}function ir(e){return typeof e==`string`&&e.length>0?[e]:[]}function ar(e){let t=Me(e);if(!t.ok)throw new n(t.code,t.message);return`ref`in t?{ref:t.ref,label:lr(e.slice(1))}:{selector:t.selector}}function or(e){let t=Pe(e);if(!t.ok)throw new n(t.code,t.message);return t.format}function sr(e,t={}){let r=Te(e,t);if(!r)throw new n(`INVALID_ARGS`,He);return r}function cr(e,t){try{let t=JSON.parse(e);if(t&&typeof t==`object`&&!Array.isArray(t))return t}catch{}throw new n(`INVALID_ARGS`,`${t} must be a JSON object`)}function lr(e){return e.join(` `).trim()||void 0}function b(...e){return new Set(e)}function x(e,t){return e!==void 0&&t.has(e)}function ur(e){return e===void 0||e.trim()===``?!1:Number.isFinite(Number(e))}function dr(e,t){if(e===void 0)return;let r=Number(e);if(Number.isFinite(r))return r;throw new n(`INVALID_ARGS`,`${t} must be a finite number.`)}function fr(e){return e===void 0?void 0:Number(e)}function S(e){return e===void 0?[]:[e]}function C(e){return e===void 0?[]:[String(e)]}function w(e,t){if(e===void 0||e===``)throw new n(`INVALID_ARGS`,t);return e}function T(e,t){if(typeof e!=`string`||e.length===0)throw new n(`INVALID_ARGS`,t);return e}function pr(e){return gr(e.description,e.cliDetail)}function mr(e){return gr(e.description,e.mcpDetail)}function hr(e,t){return{...e,description:t}}function gr(e,t){return t?`${e} ${t}`:e}function _r(e){return e}const vr=[`ref`,`selector`,`point`];function yr(e,t=[]){return{type:`object`,properties:{...Bn(),...e},...t.length>0?{required:t}:{},additionalProperties:!1}}function E(e,t=[],n){return{type:`object`,...n?{description:n}:{},properties:e,...t.length>0?{required:t}:{}}}function br(e){return{type:`object`,description:e,properties:{x:{type:`number`},y:{type:`number`}},required:[`x`,`y`],additionalProperties:!1}}function D(e,t){return{type:`string`,enum:e,...t?{description:t}:{}}}function O(e){return{type:`string`,const:e}}function k(e){return{type:`string`,...e?{description:e}:{}}}function xr(e){return{type:[`string`,`null`],...e?{description:e}:{}}}function A(e,t={}){return{type:`number`,...e?{description:e}:{},...t.min===void 0?{}:{minimum:t.min},...t.max===void 0?{}:{maximum:t.max}}}function Sr(e){return{type:`integer`,...e?{description:e}:{}}}function j(e){return{type:`boolean`,...e?{description:e}:{}}}function Cr(e){return{type:`array`,items:{type:`string`},...e?{description:e}:{}}}function M(e){return{type:`object`,additionalProperties:!0,...e?{description:e}:{}}}function N(e){return{...e,required:!0}}function P(e,t={}){return B(k(e),t.allowEmpty===!0?On:m)}function wr(e){return{schema:{type:`null`},required:!1,audience:{kind:`retired`,message:e},read:(t,r)=>{if(Object.hasOwn(t,r))throw new n(`INVALID_ARGS`,e)}}}function Tr(e,t){return{...e,audience:{kind:`operator`,source:t}}}function F(e,t={}){return B(A(e,t),(e,n)=>pe(e,n,t))}function I(e,t={}){return B(qr(e,t),(e,n)=>me(e,n,t))}function L(e){return B(j(e),An)}function Er(e){return Or(Dr(e))}function Dr(e){let[t,...n]=ct(e).filter(e=>e.inputDescription!==void 0);if(!t)throw Error(`Flag ${e} declares no inputDescription; optionField cannot derive a field`);if(n.length>0)throw Error(`Flag ${e} declares inputDescription more than once`);return t}function Or(e){let t=e.inputDescription,n={min:e.min,max:e.max};switch(e.type){case`boolean`:return L(t);case`int`:return I(t,n);case`number`:return F(t,n);case`string`:return P(t);case`enum`:return R(kr(e),t);case`booleanOrString`:throw Error(`Flag ${e.key} is booleanOrString; declare its field shape explicitly with jsonSchemaField`)}}function kr(e){let t=e.enumValues;if(!t)throw Error(`Flag ${e.key} is an enum with no enumValues`);return t}function R(e,t){return B(D(e,t),(t,n)=>Mn(t,n,e))}function Ar(e){return B(M(e),Nn)}function jr(e){return B(Cr(e),Pn)}function z(e){return B(e,(e,t)=>e[t])}function Mr(e,t){return B(e,t)}function Nr(){return B(Zr(),(e,t)=>e[t]===void 0?void 0:Br(e,t))}function Pr(){return B(Qr(),(e,t)=>e[t]===void 0?void 0:Vr(e,t))}function Fr(e){return B(br(e),(e,t)=>e[t]===void 0?void 0:Hr(e,t))}function Ir(){return{depth:I(`Snapshot traversal depth.`,{min:0}),scope:P(`Snapshot scope selector used before resolution.`),raw:L(`Use raw snapshot data during selector resolution.`)}}function Lr(){return{count:I(`Number of press/click repetitions.`,{min:1}),intervalMs:I(`Delay between repeated press/click actions.`,{min:0}),holdMs:I(`Hold duration for each action.`,{min:0}),jitterPx:I(`Randomization radius in pixels.`,{min:0}),doubleTap:L(`Request a double-tap action.`)}}function Rr(e){return yr(Jr(e),Yr(e))}function zr(e,t){let r=Tn(e),i=Object.fromEntries(Object.entries(t).flatMap(([e,t])=>{let i=t.read(r,e);if(t.required&&i===void 0)throw new n(`INVALID_ARGS`,`Expected ${e} to be set.`);return i===void 0?[]:[[e,i]]})),a=Vn(r,{readTargetAlias:!Object.hasOwn(t,`target`),readinessBudgetDeclared:Object.hasOwn(t,`readinessTimeoutMs`)});return h({...a,...g(a),...i})}function Br(e,t){let n=En(e,t),r=jn(n,`kind`,vr);switch(r){case`ref`:return{kind:r,ref:Dn(n,`ref`),label:m(n,`label`)};case`selector`:return{kind:r,selector:Dn(n,`selector`)};case`point`:return{kind:r,x:kn(n,`x`),y:kn(n,`y`)}}}function Vr(e,t){let n=En(e,t),r=jn(n,`kind`,[`ref`,`selector`]);return r===`ref`?{kind:r,ref:Dn(n,`ref`),label:m(n,`label`)}:{kind:r,selector:Dn(n,`selector`)}}function Hr(e,t){let n=En(e,t);return{x:kn(n,`x`),y:kn(n,`y`)}}function Ur(e){switch(e.kind){case`ref`:return{ref:e.ref,label:e.label};case`selector`:return{selector:e.selector};case`point`:return{x:e.x,y:e.y}}}function Wr(e){switch(e.kind){case`ref`:return{ref:e.ref,label:e.label};case`selector`:return{selector:e.selector}}}function Gr(e){return{count:e.count,intervalMs:e.intervalMs,holdMs:e.holdMs,jitterPx:e.jitterPx,doubleTap:e.doubleTap}}function Kr(e){return{depth:e.depth,scope:e.scope,raw:e.raw}}function B(e,t){return{schema:e,required:!1,read:t}}function qr(e,t){return{...Sr(e),...t.min===void 0?{}:{minimum:t.min},...t.max===void 0?{}:{maximum:t.max}}}function Jr(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>e.audience?.kind!==`retired`).map(([e,t])=>[e,t.schema]))}function Yr(e){return Object.entries(e).flatMap(([e,t])=>t.required?[e]:[])}function Xr(e){return Object.fromEntries(Object.entries(e).flatMap(([e,t])=>t.audience?[[e,t.audience]]:[]))}function Zr(){return{oneOf:[...$r(),{type:`object`,properties:{kind:{type:`string`,const:`point`},x:{type:`number`},y:{type:`number`}},required:[`kind`,`x`,`y`],additionalProperties:!1}],description:`UI target. This is separate from deviceTarget, which selects the device form.`}}function Qr(){return{oneOf:$r(),description:`UI element target by snapshot ref or selector expression.`}}function $r(){return[{type:`object`,properties:{kind:{type:`string`,const:`ref`},ref:{type:`string`,description:`Snapshot element ref such as @e12.`},label:{type:`string`,description:`Optional human label for the ref.`}},required:[`kind`,`ref`],additionalProperties:!1},{type:`object`,properties:{kind:{type:`string`,const:`selector`},selector:{type:`string`,description:`Agent-device selector expression.`}},required:[`kind`,`selector`],additionalProperties:!1}]}function V(e,t,n,r){return _r({name:e,description:t,inputSchema:Rr(n),readInput:r?.readInput??(e=>zr(e,n)),inputAudience:Xr(n),...r?.parseableOutput?{parseableOutput:!0}:{}})}function H(e){let t=hr(e.text,e.metadata.description),n=t.mcpDetail?{mcpDetail:t.mcpDetail}:{},r={...e.metadata,...n},i={name:r.name,description:r.description,inputSchema:r.inputSchema,...n,invoke:async(t,n)=>await e.run(t,r.readInput(n))};return{name:e.name,metadata:r,definition:i,cliSchema:{...e.cliSchema,text:t},cliReader:e.cliReader,daemonWriter:e.daemonWriter,cliOutputFormatter:e.cliOutputFormatter,text:t}}function ei(e){return H({name:e.name,text:e.text,metadata:V(e.name,e.description,{}),run:e.run,cliSchema:{},cliReader:(e,t)=>y(t),daemonWriter:_(e.name),cliOutputFormatter:e.cliOutputFormatter})}function U(e){let t={},n={},r={},i={};for(let a of e.commands)ti(t,`CLI schema`,a.name,a.cliSchema),ti(n,`CLI reader`,a.name,a.cliReader),a.daemonWriter&&ti(r,`daemon writer`,a.name,a.daemonWriter),a.cliOutputFormatter&&ti(i,`CLI output formatter`,a.name,a.cliOutputFormatter);return{name:e.name,clientSurface:e.clientSurface,metadata:e.commands.map(e=>e.metadata),definitions:e.commands.map(e=>e.definition),cliSchemas:t,cliReaders:n,daemonWriters:r,cliOutputFormatters:i}}function ti(e,t,n,r){if(Object.hasOwn(e,n))throw Error(`Duplicate command family ${t}: ${n}`);e[n]=r}function ni(e){let t=[`Batch completed: ${e.executed}/${e.total} steps in ${e.totalDurationMs}ms`];for(let n of e.results)t.push(ii(n));return{data:e,text:t.join(`
1
+ import{D as e,M as t,N as n,O as r,T as i,g as a,p as o,t as s,x as c}from"./sdk-contracts.js";import{a as ee,d as te,f as ne,m as re,n as ie,p as ae,t as oe,u as se}from"./capture.js";import{_ as l,c as ce,d as le,f as ue,k as de,l as fe,m as pe,p as me,s as he,u as ge}from"./sdk-batch-runner.js";import{t as _e}from"./viewport.js";import{a as ve,i as ye,n as be,t as xe}from"./sdk-device.js";import{a as Se,c as Ce}from"./lease-scope.js";import{An as we,Cr as Te,In as Ee,Mn as De,Nr as Oe,Rr as ke,_r as Ae,d as je,dr as Me,ei as Ne,fr as Pe,h as Fe,hr as Ie,i as Le,ln as Re,m as ze,s as Be,sn as Ve,ur as He}from"./sdk-batch.js";import{n as Ue}from"./recording-export-quality.js";import{t as We}from"./recording-scope.js";import{a as Ge,n as Ke,o as qe,r as Je,s as Ye}from"./scroll-gesture.js";import{E as Xe,T as Ze,b as Qe,v as $e,w as et}from"./script.js";import{a as tt,i as nt,r as rt}from"./vars.js";import{n as it,r as at,t as ot}from"./format.js";import{n as u,t as st}from"./catalog.js";import{i as ct,r as lt,t as ut}from"./back-mode.js";import{a as dt}from"./sdk-remote-config.js";import{t as ft}from"./session.js";import{a as pt,c as mt,d as ht,f as gt,g as _t,h as vt,i as yt,l as bt,n as xt,o as St,p as Ct,s as wt,t as Tt,u as Et}from"./observability.js";import{n as Dt,t as Ot}from"./success-text.js";import{a as d,i as kt,n as At,r as f}from"./output-common.js";import{t as jt}from"./alert-contract.js";import{a as Mt,i as Nt,l as Pt,o as Ft,s as It,t as Lt,u as Rt,x as zt}from"./settings.js";import{t as Bt}from"./location-coordinates.js";import{t as Vt}from"./snapshot-quality-backend-capabilities.js";import{f as Ht,m as Ut,p as Wt,r as Gt,s as Kt}from"./overlay.js";import{t as qt}from"./mobile-snapshot-semantics.js";import{t as Jt}from"./click-button.js";import{n as Yt}from"./scroll-command.js";import{n as Xt,t as Zt}from"./interaction-positionals.js";import{t as Qt}from"./app-inventory.js";import"./device.js";import{a as $t,f as en,i as tn,m as nn,o as rn,p as an,r as on,t as sn}from"./device-rotation.js";import"./app-inventory-contract.js";import{t as cn}from"./prepare-kind.js";import{n as ln,t as un}from"./path-resolution.js";import"./file.js";import{n as dn,r as fn,s as pn,t as mn}from"./tv-remote.js";import p from"node:path";import hn from"node:fs";const gn=[`path`,`start`,`stop`,`doctor`,`mark`,`clear`],_n=new Set(lt().map(e=>e.key));function vn(t){let n=Se(t);return de({stateDir:t.stateDir,daemonBaseUrl:t.daemonBaseUrl,daemonTransport:t.daemonTransport,daemonServerMode:t.daemonServerMode,...Ce(n),provider:t.provider,providerSessionId:t.providerSessionId,providerApp:t.providerApp,providerOsVersion:t.providerOsVersion,providerProject:t.providerProject,providerBuild:t.providerBuild,providerSessionName:t.providerSessionName,providerDeviceOrientation:t.providerDeviceOrientation,providerGeoLocation:t.providerGeoLocation,providerTimezone:t.providerTimezone,providerAppiumVersion:t.providerAppiumVersion,providerLanguage:t.providerLanguage,providerLocale:t.providerLocale,providerNetworkProfile:t.providerNetworkProfile,providerCustomNetwork:t.providerCustomNetwork,providerNoResignApp:t.providerNoResignApp,awsProjectArn:t.awsProjectArn,awsDeviceArn:t.awsDeviceArn,awsAppArn:t.awsAppArn,awsRegion:t.awsRegion,awsInteractionMode:t.awsInteractionMode,sessionIsolation:t.sessionIsolation,platform:t.platform,target:t.target,device:t.device,udid:t.udid,serial:t.serial,iosSimulatorDeviceSet:t.iosSimulatorDeviceSet,iosXctestrunFile:t.iosXctestrunFile,iosXctestDerivedDataPath:t.iosXctestDerivedDataPath,iosXctestEnvDir:t.iosXctestEnvDir,androidDeviceAllowlist:t.androidDeviceAllowlist,surface:t.surface,activity:t.activity,launchConsole:t.launchConsole,launchArgs:t.launchArgs,relaunch:t.relaunch,shutdown:t.shutdown,saveScript:t.saveScript,testIme:t.testIme,noRecord:t.noRecord,recordAs:t.recordAs,record:t.record,backMode:t.backMode,metroHost:t.metroHost,metroPort:t.metroPort,bundleUrl:t.bundleUrl,launchUrl:t.launchUrl,...e(t,a),...se(t),appsFilter:t.appsFilter,kind:t.kind,out:t.out,count:t.count,fps:t.fps,quality:t.quality,hideTouches:t.hideTouches,recordingScope:t.recordingScope,intervalMs:t.intervalMs,delayMs:t.delayMs,durationMs:t.durationMs,holdMs:t.holdMs,jitterPx:t.jitterPx,readinessTimeoutMs:t.readinessTimeoutMs,pixels:t.pixels,until:t.until,doubleTap:t.doubleTap,verify:t.verify,settle:t.settle,settleQuietMs:t.settleQuietMs,clickButton:t.clickButton,pauseMs:t.pauseMs,pattern:t.pattern,headless:t.headless,restart:t.restart,replayUpdate:t.replayUpdate,replayBackend:t.replayBackend,replayEnv:t.replayEnv,replayShellEnv:t.replayShellEnv,replayScriptSource:t.replayScriptSource,replayScriptSources:t.replayScriptSources,replayFrom:t.replayFrom,replayPlanDigest:t.replayPlanDigest,replayKeepSession:t.replayKeepSession,failFast:t.failFast,timeoutMs:t.timeoutMs,retries:t.retries,recordVideo:t.recordVideo,artifactsDir:t.artifactsDir,shardAll:t.shardAll,shardSplit:t.shardSplit,findFirst:t.findFirst,findLast:t.findLast,networkInclude:t.networkInclude,batchOnError:t.batchOnError,batchMaxSteps:t.batchMaxSteps,batchSteps:t.batchSteps,verbose:t.debug})}function yn(e,t){return{...vn(e),...t}}function bn(e,t){let n=e.inputSchema.properties;if(!n)return{};let r={},i=t;for(let e of Object.keys(n)){if(!_n.has(e))continue;let t=i[e];xn(t)&&(r[e]=t)}return r}function xn(e){return typeof e==`boolean`||typeof e==`number`||typeof e==`string`}function Sn(e){return{kind:`operator`,source:e}}function Cn(e,t){return`${e} is not accepted as a tool argument. ${wn(e,t)}`}function wn(e,t){if(`operatorPath`in t)return t.operatorPath;let n=`for the process serving these tools.`,r=(t.envFlagKeys??[e]).map(dt).join(` or `);return r===``?`Set ${e} in ~/.agent-device/config.json ${n}`:`Set the ${r} environment variable${t.operatorConfig?` (or ${e} in ~/.agent-device/config.json)`:``} ${n}`}function Tn(e){if(e==null)return{};if(!e||typeof e!=`object`||Array.isArray(e))throw new n(`INVALID_ARGS`,`Expected object arguments.`);return e}function En(e,t){let r=e[t];if(!r||typeof r!=`object`||Array.isArray(r))throw new n(`INVALID_ARGS`,`Expected ${t} to be an object.`);return r}function Dn(e,t){let r=e[t];if(typeof r!=`string`||r.length===0)throw new n(`INVALID_ARGS`,`Expected ${t} to be a non-empty string.`);return r}function On(e,t){let r=e[t];if(r!==void 0){if(typeof r!=`string`)throw new n(`INVALID_ARGS`,`Expected ${t} to be a string.`);return r}}function m(e,t){let r=e[t];if(r!==void 0){if(typeof r!=`string`||r.length===0)throw new n(`INVALID_ARGS`,`Expected ${t} to be a non-empty string.`);return r}}function kn(e,t){let r=e[t];if(typeof r!=`number`||!Number.isFinite(r))throw new n(`INVALID_ARGS`,`Expected ${t} to be a finite number.`);return r}function An(e,t){let r=e[t];if(r!==void 0){if(typeof r!=`boolean`)throw new n(`INVALID_ARGS`,`Expected ${t} to be a boolean.`);return r}}function jn(e,t,r){let i=e[t];if(typeof i!=`string`||!r.includes(i))throw new n(`INVALID_ARGS`,`Expected ${t} to be one of: ${r.join(`, `)}.`);return i}function Mn(e,t,r){let i=e[t];if(i!==void 0){if(typeof i!=`string`||!r.includes(i))throw new n(`INVALID_ARGS`,`Expected ${t} to be one of: ${r.join(`, `)}.`);return i}}function Nn(e,t){let r=e[t];if(r!==void 0){if(!r||typeof r!=`object`||Array.isArray(r))throw new n(`INVALID_ARGS`,`Expected ${t} to be an object.`);return r}}function Pn(e,t){let r=e[t];if(r!==void 0){if(!Array.isArray(r)||r.some(e=>typeof e!=`string`))throw new n(`INVALID_ARGS`,`Expected ${t} to be an array of strings.`);return r}}function Fn(e,t,r,i){let a=new Set(t),o=Object.keys(e).filter(e=>!a.has(e));if(o.length>0)throw new n(`INVALID_ARGS`,`${r} has unknown field(s): ${o.join(`, `)}.`,i===void 0?void 0:{hint:i})}function h(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>e!==void 0))}const In=Sn({operatorConfig:!0}),Ln={session:{schema:{type:`string`,description:`Agent-device session name.`},read:e=>m(e,`session`),flagKey:`session`,flagIn:[`input`]},platform:{schema:{type:`string`,enum:ye,description:`Platform selector used to resolve a device.`},read:e=>Mn(e,`platform`,ye),flagKey:`platform`,flagIn:[`input`,`selection`]},deviceTarget:{schema:{type:`string`,enum:be,description:`Device target form. Maps to the CLI --target flag.`},read:Un,clientKey:`target`,flagKey:`target`,flagIn:[`input`,`selection`]},target:{schema:{type:`string`,enum:be,description:`Alias for deviceTarget on commands without a UI target field. Interaction commands reserve target for the UI element.`}},device:{schema:{type:`string`,description:`Device name selector (a UDID belongs in udid, a serial in serial).`},read:e=>m(e,`device`),flagKey:`device`,flagIn:[`input`,`selection`]},udid:{schema:{type:`string`,description:`Apple device or simulator UDID; the selector that pins one device when several share a name.`},read:e=>m(e,`udid`),flagKey:`udid`,flagIn:[`input`,`selection`]},serial:{schema:{type:`string`,description:`Android, HarmonyOS, or Vega VVD serial selector.`},read:e=>m(e,`serial`),flagKey:`serial`,flagIn:[`input`,`selection`]},iosSimulatorDeviceSet:{schema:{type:`string`,description:`iOS simulator device-set path used for device resolution.`},read:e=>m(e,`iosSimulatorDeviceSet`),audience:Sn({envFlagKeys:[],operatorConfig:!0}),flagKey:`iosSimulatorDeviceSet`,flagIn:[`input`,`selection`]},iosXctestrunFile:{schema:{type:`string`,description:`Externally built iOS XCTest runner .xctestrun artifact path.`},read:e=>m(e,`iosXctestrunFile`),audience:In,flagKey:`iosXctestrunFile`,flagIn:[`input`]},iosXctestDerivedDataPath:{schema:{type:`string`,description:`Derived data path for external iOS XCTest runner execution.`},read:e=>m(e,`iosXctestDerivedDataPath`),audience:In,flagKey:`iosXctestDerivedDataPath`,flagIn:[`input`]},iosXctestEnvDir:{schema:{type:`string`,description:`Writable directory for iOS XCTest runner env overlays.`},read:e=>m(e,`iosXctestEnvDir`),audience:In,flagKey:`iosXctestEnvDir`,flagIn:[`input`]},androidDeviceAllowlist:{schema:{type:`string`,description:`Android serial allowlist used for device resolution.`},read:e=>m(e,`androidDeviceAllowlist`),flagKey:`androidDeviceAllowlist`,flagIn:[`input`,`selection`]},noRecord:{read:e=>An(e,`noRecord`),flagKey:`noRecord`,flagIn:[`input`,`selection`]},readinessTimeoutMs:{read:Wn},daemonBaseUrl:{schema:{type:`string`,description:`Remote daemon base URL.`},read:e=>m(e,`daemonBaseUrl`),audience:In},daemonAuthToken:{schema:{type:`string`,description:`Remote daemon auth token.`},read:e=>m(e,`daemonAuthToken`),audience:In},tenant:{schema:{type:`string`,description:`Remote tenant identifier.`},read:e=>m(e,`tenant`)},runId:{schema:{type:`string`,description:`Lease run identifier.`},read:e=>m(e,`runId`)},leaseId:{schema:{type:`string`,description:`Existing lease identifier.`},read:e=>m(e,`leaseId`)},cwd:{schema:{type:`string`,description:`Working directory for command execution.`},read:e=>m(e,`cwd`),audience:Sn({operatorPath:`Start the process serving these tools in the desired working directory, or pass absolute paths.`})},debug:{schema:{type:`boolean`,description:`Enable debug diagnostics.`},read:e=>An(e,`debug`)}},Rn=Object.entries(Ln),zn=Object.fromEntries(Rn.flatMap(([e,t])=>t.audience?[[e,t.audience]]:[]));function Bn(){return Object.fromEntries(Rn.flatMap(([e,t])=>t.schema?[[e,t.schema]]:[]))}function Vn(e,t={}){let n={};for(let[r,i]of Rn)i.read&&(n[r]=i.read(e,t));return n}function g(e){let t={};for(let[n,r]of Rn)r.read&&(t[r.clientKey??n]=e[n]);return h(t)}function Hn(e,t){let n={};for(let[r,i]of Rn){if(i.flagIn===void 0||!i.flagIn.includes(t))continue;let a=t===`selection`?i.clientKey??r:r;n[a]=e[i.flagKey]}return n}function Un(e,t){let r=Mn(e,`deviceTarget`,be);if(t.readTargetAlias===!1||e.target===void 0)return r;let i=Mn(e,`target`,be);if(r!==void 0&&i!==r)throw new n(`INVALID_ARGS`,`Expected target alias to match deviceTarget when both are set.`);return r??i}function Wn(e,t){if(!(t.readinessBudgetDeclared===!0||!Object.hasOwn(e,`readinessTimeoutMs`)))throw new n(`INVALID_ARGS`,`readinessTimeoutMs applies only to commands that wait for their target: press, click, and longpress.`)}function _(e,t){return n=>v(e,t?t(n):[],n)}function v(e,t,n,r){return{command:e,positionals:t,...r?{input:r}:{},options:Gn(n)}}function Gn(e){let t=Kn(e.deviceTarget??e.target);if(t===void 0&&e.target===void 0)return e;let{target:n,...r}=e;return t===void 0?r:{...r,target:t}}function Kn(e){return e===`mobile`||e===`tv`||e===`desktop`?e:void 0}function y(e){return h(Hn(e,`input`))}function qn(e){return h({record:e.record})}function Jn(e){return Hn(e,`selection`)}function Yn(e){return h({depth:e.snapshotDepth,scope:e.snapshotScope,raw:e.snapshotRaw})}function Xn(e){return{depth:e.snapshotDepth,scope:e.snapshotScope,raw:e.snapshotRaw}}function Zn(e){return h({settle:e.settle,settleQuietMs:e.settleQuietMs,timeoutMs:e.timeoutMs})}function Qn(e){return h({count:e.count,intervalMs:e.intervalMs,holdMs:e.holdMs,jitterPx:e.jitterPx,doubleTap:e.doubleTap})}function $n(e){if(`ref`in e&&e.ref!==void 0)return h({kind:`ref`,ref:e.ref,label:e.label});if(`selector`in e&&e.selector!==void 0)return{kind:`selector`,selector:e.selector};let t=e;return{kind:`point`,x:t.x,y:t.y}}function er(e){let t=nr(e);if(typeof t.ref==`string`)return[t.ref,...ir(t.label)];if(typeof t.selector==`string`)return[t.selector];if(t.kind===`point`||t.x!==void 0||t.y!==void 0)return[String(rr(t.x,`x`)),String(rr(t.y,`y`))];throw new n(`INVALID_ARGS`,`interaction requires @ref, selector, or point target`)}function tr(e){let t=nr(e);if(typeof t.ref==`string`)return[t.ref,...ir(t.label)];if(typeof t.selector==`string`)return[t.selector];throw new n(`INVALID_ARGS`,`element command requires @ref or selector target`)}function nr(e){if(!e||typeof e!=`object`||Array.isArray(e))throw new n(`INVALID_ARGS`,`Expected target object.`);let t=e,r=t.target;return r&&typeof r==`object`&&!Array.isArray(r)?r:t}function rr(e,t){if(typeof e!=`number`||!Number.isFinite(e))throw new n(`INVALID_ARGS`,`point target requires numeric ${t}.`);return e}function ir(e){return typeof e==`string`&&e.length>0?[e]:[]}function ar(e){let t=Me(e);if(!t.ok)throw new n(t.code,t.message);return`ref`in t?{ref:t.ref,label:lr(e.slice(1))}:{selector:t.selector}}function or(e){let t=Pe(e);if(!t.ok)throw new n(t.code,t.message);return t.format}function sr(e,t={}){let r=Te(e,t);if(!r)throw new n(`INVALID_ARGS`,He);return r}function cr(e,t){try{let t=JSON.parse(e);if(t&&typeof t==`object`&&!Array.isArray(t))return t}catch{}throw new n(`INVALID_ARGS`,`${t} must be a JSON object`)}function lr(e){return e.join(` `).trim()||void 0}function b(...e){return new Set(e)}function x(e,t){return e!==void 0&&t.has(e)}function ur(e){return e===void 0||e.trim()===``?!1:Number.isFinite(Number(e))}function dr(e,t){if(e===void 0)return;let r=Number(e);if(Number.isFinite(r))return r;throw new n(`INVALID_ARGS`,`${t} must be a finite number.`)}function fr(e){return e===void 0?void 0:Number(e)}function S(e){return e===void 0?[]:[e]}function C(e){return e===void 0?[]:[String(e)]}function w(e,t){if(e===void 0||e===``)throw new n(`INVALID_ARGS`,t);return e}function T(e,t){if(typeof e!=`string`||e.length===0)throw new n(`INVALID_ARGS`,t);return e}function pr(e){return gr(e.description,e.cliDetail)}function mr(e){return gr(e.description,e.mcpDetail)}function hr(e,t){return{...e,description:t}}function gr(e,t){return t?`${e} ${t}`:e}function _r(e){return e}const vr=[`ref`,`selector`,`point`];function yr(e,t=[]){return{type:`object`,properties:{...Bn(),...e},...t.length>0?{required:t}:{},additionalProperties:!1}}function E(e,t=[],n){return{type:`object`,...n?{description:n}:{},properties:e,...t.length>0?{required:t}:{}}}function br(e){return{type:`object`,description:e,properties:{x:{type:`number`},y:{type:`number`}},required:[`x`,`y`],additionalProperties:!1}}function D(e,t){return{type:`string`,enum:e,...t?{description:t}:{}}}function O(e){return{type:`string`,const:e}}function k(e){return{type:`string`,...e?{description:e}:{}}}function xr(e){return{type:[`string`,`null`],...e?{description:e}:{}}}function A(e,t={}){return{type:`number`,...e?{description:e}:{},...t.min===void 0?{}:{minimum:t.min},...t.max===void 0?{}:{maximum:t.max}}}function Sr(e){return{type:`integer`,...e?{description:e}:{}}}function j(e){return{type:`boolean`,...e?{description:e}:{}}}function Cr(e){return{type:`array`,items:{type:`string`},...e?{description:e}:{}}}function M(e){return{type:`object`,additionalProperties:!0,...e?{description:e}:{}}}function N(e){return{...e,required:!0}}function P(e,t={}){return B(k(e),t.allowEmpty===!0?On:m)}function wr(e){return{schema:{type:`null`},required:!1,audience:{kind:`retired`,message:e},read:(t,r)=>{if(Object.hasOwn(t,r))throw new n(`INVALID_ARGS`,e)}}}function Tr(e,t){return{...e,audience:{kind:`operator`,source:t}}}function F(e,t={}){return B(A(e,t),(e,n)=>pe(e,n,t))}function I(e,t={}){return B(qr(e,t),(e,n)=>me(e,n,t))}function L(e){return B(j(e),An)}function Er(e){return Or(Dr(e))}function Dr(e){let[t,...n]=ct(e).filter(e=>e.inputDescription!==void 0);if(!t)throw Error(`Flag ${e} declares no inputDescription; optionField cannot derive a field`);if(n.length>0)throw Error(`Flag ${e} declares inputDescription more than once`);return t}function Or(e){let t=e.inputDescription,n={min:e.min,max:e.max};switch(e.type){case`boolean`:return L(t);case`int`:return I(t,n);case`number`:return F(t,n);case`string`:return P(t);case`enum`:return R(kr(e),t);case`booleanOrString`:throw Error(`Flag ${e.key} is booleanOrString; declare its field shape explicitly with jsonSchemaField`)}}function kr(e){let t=e.enumValues;if(!t)throw Error(`Flag ${e.key} is an enum with no enumValues`);return t}function R(e,t){return B(D(e,t),(t,n)=>Mn(t,n,e))}function Ar(e){return B(M(e),Nn)}function jr(e){return B(Cr(e),Pn)}function z(e){return B(e,(e,t)=>e[t])}function Mr(e,t){return B(e,t)}function Nr(){return B(Zr(),(e,t)=>e[t]===void 0?void 0:Br(e,t))}function Pr(){return B(Qr(),(e,t)=>e[t]===void 0?void 0:Vr(e,t))}function Fr(e){return B(br(e),(e,t)=>e[t]===void 0?void 0:Hr(e,t))}function Ir(){return{depth:I(`Snapshot traversal depth.`,{min:0}),scope:P(`Snapshot scope selector used before resolution.`),raw:L(`Use raw snapshot data during selector resolution.`)}}function Lr(){return{count:I(`Number of press/click repetitions.`,{min:1}),intervalMs:I(`Delay between repeated press/click actions.`,{min:0}),holdMs:I(`Hold duration for each action.`,{min:0}),jitterPx:I(`Randomization radius in pixels.`,{min:0}),doubleTap:L(`Request a double-tap action.`)}}function Rr(e){return yr(Jr(e),Yr(e))}function zr(e,t){let r=Tn(e),i=Object.fromEntries(Object.entries(t).flatMap(([e,t])=>{let i=t.read(r,e);if(t.required&&i===void 0)throw new n(`INVALID_ARGS`,`Expected ${e} to be set.`);return i===void 0?[]:[[e,i]]})),a=Vn(r,{readTargetAlias:!Object.hasOwn(t,`target`),readinessBudgetDeclared:Object.hasOwn(t,`readinessTimeoutMs`)});return h({...a,...g(a),...i})}function Br(e,t){let n=En(e,t),r=jn(n,`kind`,vr);switch(r){case`ref`:return{kind:r,ref:Dn(n,`ref`),label:m(n,`label`)};case`selector`:return{kind:r,selector:Dn(n,`selector`)};case`point`:return{kind:r,x:kn(n,`x`),y:kn(n,`y`)}}}function Vr(e,t){let n=En(e,t),r=jn(n,`kind`,[`ref`,`selector`]);return r===`ref`?{kind:r,ref:Dn(n,`ref`),label:m(n,`label`)}:{kind:r,selector:Dn(n,`selector`)}}function Hr(e,t){let n=En(e,t);return{x:kn(n,`x`),y:kn(n,`y`)}}function Ur(e){switch(e.kind){case`ref`:return{ref:e.ref,label:e.label};case`selector`:return{selector:e.selector};case`point`:return{x:e.x,y:e.y}}}function Wr(e){switch(e.kind){case`ref`:return{ref:e.ref,label:e.label};case`selector`:return{selector:e.selector}}}function Gr(e){return{count:e.count,intervalMs:e.intervalMs,holdMs:e.holdMs,jitterPx:e.jitterPx,doubleTap:e.doubleTap}}function Kr(e){return{depth:e.depth,scope:e.scope,raw:e.raw}}function B(e,t){return{schema:e,required:!1,read:t}}function qr(e,t){return{...Sr(e),...t.min===void 0?{}:{minimum:t.min},...t.max===void 0?{}:{maximum:t.max}}}function Jr(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>e.audience?.kind!==`retired`).map(([e,t])=>[e,t.schema]))}function Yr(e){return Object.entries(e).flatMap(([e,t])=>t.required?[e]:[])}function Xr(e){return Object.fromEntries(Object.entries(e).flatMap(([e,t])=>t.audience?[[e,t.audience]]:[]))}function Zr(){return{oneOf:[...$r(),{type:`object`,properties:{kind:{type:`string`,const:`point`},x:{type:`number`},y:{type:`number`}},required:[`kind`,`x`,`y`],additionalProperties:!1}],description:`UI target. This is separate from deviceTarget, which selects the device form.`}}function Qr(){return{oneOf:$r(),description:`UI element target by snapshot ref or selector expression.`}}function $r(){return[{type:`object`,properties:{kind:{type:`string`,const:`ref`},ref:{type:`string`,description:`Snapshot element ref such as @e12.`},label:{type:`string`,description:`Optional human label for the ref.`}},required:[`kind`,`ref`],additionalProperties:!1},{type:`object`,properties:{kind:{type:`string`,const:`selector`},selector:{type:`string`,description:`Agent-device selector expression.`}},required:[`kind`,`selector`],additionalProperties:!1}]}function V(e,t,n,r){return _r({name:e,description:t,inputSchema:Rr(n),readInput:r?.readInput??(e=>zr(e,n)),inputAudience:Xr(n),...r?.parseableOutput?{parseableOutput:!0}:{}})}function H(e){let t=hr(e.text,e.metadata.description),n=t.mcpDetail?{mcpDetail:t.mcpDetail}:{},r={...e.metadata,...n},i={name:r.name,description:r.description,inputSchema:r.inputSchema,...n,invoke:async(t,n)=>await e.run(t,r.readInput(n))};return{name:e.name,metadata:r,definition:i,cliSchema:{...e.cliSchema,text:t},cliReader:e.cliReader,daemonWriter:e.daemonWriter,cliOutputFormatter:e.cliOutputFormatter,text:t}}function ei(e){return H({name:e.name,text:e.text,metadata:V(e.name,e.description,{}),run:e.run,cliSchema:{},cliReader:(e,t)=>y(t),daemonWriter:_(e.name),cliOutputFormatter:e.cliOutputFormatter})}function U(e){let t={},n={},r={},i={};for(let a of e.commands)ti(t,`CLI schema`,a.name,a.cliSchema),ti(n,`CLI reader`,a.name,a.cliReader),a.daemonWriter&&ti(r,`daemon writer`,a.name,a.daemonWriter),a.cliOutputFormatter&&ti(i,`CLI output formatter`,a.name,a.cliOutputFormatter);return{name:e.name,clientSurface:e.clientSurface,metadata:e.commands.map(e=>e.metadata),definitions:e.commands.map(e=>e.definition),cliSchemas:t,cliReaders:n,daemonWriters:r,cliOutputFormatters:i}}function ti(e,t,n,r){if(Object.hasOwn(e,n))throw Error(`Duplicate command family ${t}: ${n}`);e[n]=r}function ni(e){let t=[`Batch completed: ${e.executed}/${e.total} steps in ${e.totalDurationMs}ms`];for(let n of e.results)t.push(ii(n));return{data:e,text:t.join(`
2
2
  `)}}const ri={batch:d(ni)};function ii(e){let t=Ot(e.data)??e.command;return`${e.step}. OK ${t} (${e.durationMs}ms)`}function ai(e=Le){let t=oi(e);return V(`batch`,`Execute multiple commands in one daemon request.`,t,{readInput:e=>ci(e,t)})}function oi(e){return{steps:N(Mr({type:`array`,description:`Structured batch steps. Each step uses a command name and the same input object as that command tool.`,items:si(e)},(t,n)=>li(t[n],e))),onError:R([`stop`],`Batch failure policy.`),maxSteps:I(`Maximum number of steps accepted for this batch.`,{min:1,max:1e3}),out:P(`Optional output path for command artifacts.`)}}function si(e){return{type:`object`,properties:{command:{type:`string`,enum:e,description:`Command name to run with structured input.`},input:{type:`object`,additionalProperties:!0,commandInputFor:`command`,description:`Structured command input for the nested command. Use the matching MCP tool schema for this object.`},runtime:{type:`object`,additionalProperties:!0,description:`Optional per-step runtime payload.`}},required:[`command`,`input`],additionalProperties:!1}}function ci(e,t){let r=zr(e,t),i=r.maxSteps??100;if(!fe(i))throw new n(`INVALID_ARGS`,`Invalid batch maxSteps: ${String(r.maxSteps)}`);return ce(r.steps.length,i),{...r}}function li(e,t){if(!Array.isArray(e))throw new n(`INVALID_ARGS`,`Expected steps to be an array.`);return e.map((e,n)=>ui(e,n+1,t))}function ui(e,t,n){let r=ue(e,t);return Fn(r,[`command`,`input`,`runtime`],`Batch step ${t}`,he),{command:di(r,t,n),input:le(r,t),...fi(r,t)}}function di(e,t,r){if(r===Le)return Be(e.command,t);let i=e.command;if(typeof i!=`string`||!r.includes(i))throw new n(`INVALID_ARGS`,`Expected command to be one of: ${r.join(`, `)}.`);return i}function fi(e,t){let n=ge(e.runtime,t);return n===void 0?{}:{runtime:n}}function pi(e){return async t=>v(u.batch,[],{...t,batchSteps:await mi(t.steps,e),batchOnError:t.onError,batchMaxSteps:t.maxSteps})}async function mi(e,t){if(!Array.isArray(e)||e.length===0)throw new n(`INVALID_ARGS`,`batch requires a non-empty steps array.`);let r=[];for(let[n,i]of e.entries())r.push(await hi(i,n+1,t));return r}async function hi(e,t,n){let r=ue(e,t),i=gi(r,t),a=le(r,t),o=ge(r.runtime,t),s=await n(i,a,t);return{command:s.command,positionals:s.positionals,...s.input?{input:s.input}:{},flags:yn(s.options,s.metadataFlags),runtime:o??s.options.runtime}}function gi(e,t){return Be(e.command,t)}const _i=ai(),vi={usageOverride:`batch [--steps <json> | --steps-file <path>]`,usageFlags:[],listUsageOverride:`batch --steps <json> | --steps-file <path>`,allowedFlags:[`steps`,`stepsFile`,`batchOnError`,`batchMaxSteps`,`out`]},yi=(e,t)=>({...y(t),steps:t.batchSteps??[],onError:t.batchOnError,maxSteps:t.batchMaxSteps,out:t.out}),bi=[{command:`snapshot`,input:{interactiveOnly:!0}},{command:`press`,input:{target:{kind:`ref`,ref:`@e12`},settle:!0}},{command:`fill`,input:{target:{kind:`selector`,selector:`id="field-email"`},text:`qa@example.com`}}];function xi(){return`${[`Each step is {"command":"<name>","input":{...}}, and input is that command's structured input object rather than its terminal spelling: a CLI positional becomes a named field (target, text, direction) and a CLI flag becomes a camelCase key (--settle -> "settle":true, -i -> "interactiveOnly":true). There is no positional step form; args, argv, positionals, and flags are not step fields. agent-device help <command> lists arguments in CLI spelling only. The examples below carry the structured spelling; over MCP, the command tool schema states it in full.`,`Steps run serially in one daemon request against the same session, in order. Mutating UI verbs are included (press, click, fill, longpress, scroll, back), which is where the round-trip saving is; --on-error stop halts at the first failing step.`,`Available through batch: ${[...Le].sort().join(`, `)}.`,`Every other command is excluded: batch and replay never nest, and session, daemon, connection, and host tooling commands own lifecycle a batch request cannot carry. Run those on their own.`].join(` `)}\n\nExamples:\n${[` agent-device batch --steps '${JSON.stringify(bi)}'`,` agent-device batch --steps-file ./steps.json --json`].join(`
3
- `)}`}const Si=U({name:`batch`,clientSurface:!1,commands:[H({name:`batch`,text:{summary:`Run multiple commands`,cliDetail:xi()},metadata:_i,run:(e,t)=>e.batch.run(Ci(t)),cliSchema:vi,cliReader:yi,cliOutputFormatter:ri.batch})]});function Ci(e){return{...g(e),steps:e.steps,onError:e.onError,maxSteps:e.maxSteps,out:e.out}}const wi=`alert`,Ti=H({name:wi,text:{summary:`Inspect, accept, or dismiss a platform alert`},metadata:V(wi,`Inspect, wait for, accept, or dismiss a platform alert. Use get before acting when the alert content matters; accept and dismiss change the active alert state. Each iOS XCTest execution activates once, then only observes. Inspect again after an unconfirmed action; never assume a timeout means nothing happened.`,{action:R(jt),timeoutMs:I()}),run:(e,t)=>e.command.alert(t),cliSchema:{usageOverride:`alert [get|accept|dismiss|wait] [timeout]`,positionalArgs:[`action?`,`timeout?`]},cliReader:(e,t)=>({...y(t),...Di(e)}),daemonWriter:_(u.alert,e=>Ei(e)),cliOutputFormatter:f});function Ei(e){return[e.action??`get`,...C(e.timeoutMs)]}function Di(e){if(e.length>2)throw new n(`INVALID_ARGS`,`alert accepts at most action and timeout arguments.`);return h({action:Oi(e[0]),timeoutMs:dr(e[1],`alert timeout`)})}function Oi(e){let t=e?.toLowerCase();if(t===void 0||t===`get`||t===`accept`||t===`dismiss`||t===`wait`)return t;throw new n(`INVALID_ARGS`,`alert action must be get, accept, dismiss, or wait.`)}function W(...e){return e}const ki=W(`keyframes`),Ai=W(`snapshotInteractiveOnly`,`snapshotDepth`,`snapshotScope`,`snapshotRaw`),ji=W(`snapshotDepth`,`snapshotScope`,`snapshotRaw`),Mi=W(`metroProjectRoot`,`kind`,`metroKind`,`metroPublicBaseUrl`,`metroProxyBaseUrl`,`metroBearerToken`,`metroPreparePort`,`metroListenHost`,`metroStatusHost`,`metroStartupTimeoutMs`,`metroProbeTimeoutMs`,`metroRuntimeFile`,`metroNoReuseExisting`,`metroNoInstallDeps`),Ni=W(`metroHost`,`metroPort`,`bundleUrl`),Pi=W(`count`,`intervalMs`,`holdMs`,`jitterPx`,`doubleTap`),Fi=W(`settle`,`settleQuietMs`,`timeoutMs`),Ii=W(`replayUpdate`,`replayEnv`),Li=W(`remoteConfig`,`stateDir`,`daemonBaseUrl`,`daemonAuthToken`,`daemonTransport`,`daemonServerMode`,`tenant`,`sessionIsolation`,`runId`,`leaseId`,`leaseBackend`,`sessionLock`,`platform`,`target`,`device`,`providerApp`,`providerOsVersion`,`providerProject`,`providerBuild`,`providerSessionName`,`providerDeviceOrientation`,`providerGeoLocation`,`providerTimezone`,`providerAppiumVersion`,`providerLanguage`,`providerLocale`,`providerNetworkProfile`,`providerCustomNetwork`,`providerNoResignApp`,`awsProjectArn`,`awsDeviceArn`,`awsAppArn`,`awsRegion`,`awsInteractionMode`,`udid`,`serial`,`iosSimulatorDeviceSet`,`iosXctestrunFile`,`iosXctestDerivedDataPath`,`iosXctestEnvDir`,`androidDeviceAllowlist`,`session`,`noRecord`),Ri=new Set([`platform`,`device`,`udid`,`serial`,`session`]),zi=new Set([`json`,`config`,`help`,`version`,`verbose`,`cost`,`responseLevel`]),Bi=`diff`,Vi={diff:E({mode:O(`snapshot`),baselineInitialized:j(),summary:E({additions:A(),removals:A(),unchanged:A()},[`additions`,`removals`,`unchanged`]),lines:{type:`array`,items:E({kind:D([`added`,`removed`,`unchanged`]),text:k(),ref:k()},[`kind`,`text`])},warnings:Cr()},[`mode`,`baselineInitialized`,`summary`,`lines`])},Hi=H({name:Bi,text:{summary:`Diff snapshot or screenshot`,cliDetail:`Screenshot --threshold is a per-pixel RGB tolerance: 0 requires exact colors and 1 ignores color differences; image dimensions must still match. Both screenshot inputs are decoded from their bytes, so a baseline or current image may be PNG or JPEG, and the diff image is always PNG. JPEG is lossy, so keep --threshold above 0 whenever either input is JPEG. Live iOS simulator screenshot diffs normalize status-bar chrome by default; use screenshot --normalize-status-bar when capturing reusable baselines.`},metadata:V(Bi,`Compare accessibility snapshots or screenshots to identify UI changes. Use snapshot comparisons for semantic tree changes and screenshot comparisons for pixel differences.`,{kind:N(z({type:`string`,const:`snapshot`})),out:P(),interactiveOnly:L(),depth:I(),scope:P(),raw:L()}),run:(e,t)=>e.capture.diff(t),cliSchema:{usageOverride:`diff snapshot | diff screenshot --baseline <path> [current.png] [--out <diff.png>] [--threshold <0-1>] [--overlay-refs]`,usageFlags:[],positionalArgs:[`kind`,`current?`],allowedFlags:[...Ai,`baseline`,`threshold`,`out`,`overlayRefs`]},cliReader:(e,t)=>{if(e[0]!==`snapshot`)throw new n(`INVALID_ARGS`,`Only diff snapshot is available through this parser.`);return{...y(t),kind:`snapshot`,out:t.out,interactiveOnly:t.snapshotInteractiveOnly,depth:t.snapshotDepth,scope:t.snapshotScope,raw:t.snapshotRaw}},daemonWriter:_(u.diff,e=>[T(e.kind,`diff requires kind`)])}),Ui=`screenshot`,Wi=H({name:Ui,text:{summary:`Capture a screenshot`,cliDetail:`Web defaults to the viewport; use --fullscreen, --full, or -f for the entire page. iOS simulators default to 1x logical-point output; use --pixel-density to request a different screenshot density. macOS app sessions default to the app window; use --fullscreen for full desktop, --scale to downscale, --crop-on <selector> to crop the capture to the frame the selector resolves on the same screen (currently iOS simulators and Android emulators), --overlay-refs to annotate current refs, --normalize-status-bar for deterministic iOS simulator chrome, or --no-stabilize for low-latency Android capture loops. On macOS, any --surface other than app (desktop, menubar, frontmost-app) always captures the main display and refuses an explicit --fullscreen.`},metadata:V(Ui,`Capture a screenshot of the active app or web session. Choose the capture scope, density, size, or annotations through the corresponding input fields when needed.`,{path:P(`Output path.`),cropOn:P(`Selector expression; the capture is cropped to the frame the selector resolves on the same screen.`),overlayRefs:L(),pixelDensity:I(`Output screenshot pixel density in pixels per logical point.`,{min:1}),fullscreen:L(),scale:F(`Screenshot scale factor.`,ee),maxSize:wr(oe.migration.screenshot),stabilize:L(),normalizeStatusBar:L(),surface:R(ft)}),run:(e,t)=>e.capture.screenshot(t),cliSchema:{positionalArgs:[`path?`],allowedFlags:ie},cliReader:(e,t)=>({...y(t),path:e[0]??t.out,...ne(t)}),daemonWriter:e=>(ae(`screenshot`,e),re(e),v(u.screenshot,S(e.path),{...e,...te(e)}))}),Gi=`settings`,Ki=V(Gi,`Read or change supported operating-system settings, animation scales, appearance, or app permissions on the selected target. Platform support varies by setting and action.`,{setting:N(P()),state:P(),app:P(),latitude:F(),longitude:F(),permission:P(),mode:R([...It])}),qi={usageOverride:Rt,listUsageOverride:`settings [area] [options]`,positionalArgs:[`setting`,`state?`,`target?`,`mode?`]},Ji=(e,t)=>Zi(e,t),Yi=_(u.settings,e=>Qi(e)),Xi=H({name:Gi,text:{summary:`Change OS settings and app permissions`,cliDetail:`macOS supports only settings appearance <light|dark|toggle> and settings ${Pt}; wifi|airplane|location|animations|text-size remain unsupported on macOS. Mobile permission actions use the active session app. On Android, deny|reset of a permission the app currently holds kills a running app; the response reports priorGrantState (granted|not_granted|unknown) and warns for granted and unknown, with open <app> --relaunch to restore it. Permission changes require a resolvable foreground user and fail without mutating if adb cannot report one. Android settings airplane on|off is applied by the connectivity service (Android 11+) and reports the airplaneMode that service holds; older builds fail without changing device state. settings reset-keychain clear is iOS-simulator-only and resets the whole simulator keychain, not just the selected app: simctl exposes no per-app keychain reset, so every app on that simulator loses its keychain-backed credentials (e.g. Firebase auth). clear-app-state does not touch the keychain, so a full fresh-install reset needs both; relaunch the app afterward to observe the signed-out state. settings text-size reads the preferred text size the target holds and settings text-size <category> applies one, on iPhone and iPad simulators (simctl content size) and on Android targets (system font_scale); tvOS and visionOS simulators, physical Apple devices, and the macOS host refuse it. Android has no category ladder of its own, so the read names the nearest rung and reports the exact multiplier as platformValue; an already-running app adopts a changed size at its next configuration change, so relaunch the app under test to observe it.`},metadata:Ki,run:(e,t)=>e.settings.update(t),cliSchema:qi,cliReader:Ji,daemonWriter:Yi,cliOutputFormatter:f});function Zi(e,t){let r=Jn(t),i=e[0],a=e[1];if(x(i,ta)&&x(a,na))return{...r,setting:i,state:a};if(i===`location`&&a===`set`)return{...r,setting:i,state:a,latitude:Bt(e[2],`latitude`),longitude:Bt(e[3],`longitude`)};if(i===`appearance`&&x(a,ra))return{...r,setting:i,state:a};if(i===`text-size`)return a===void 0?{...r,setting:i}:{...r,setting:i,state:zt(a)};if(x(i,ia)&&x(a,aa)||i===`fingerprint`&&x(a,oa))return{...r,setting:i,state:a};if(i===`permission`&&x(a,ca))return{...r,setting:i,state:a,permission:$i(e[2]),mode:ea(e[3])};if(i===`clear-app-state`){let t=a===`clear`?e[2]:a;return{...r,setting:i,state:`clear`,app:t}}if(i===`reset-keychain`&&a===`clear`&&e.length===2)return{...r,setting:i,state:a};throw new n(`INVALID_ARGS`,`Invalid settings arguments.`)}function Qi(e){return e.setting===`clear-app-state`?[e.setting,...S(e.app)]:e.setting===`text-size`?[e.setting,...S(e.state)]:e.setting===`location`&&e.state===`set`?[e.setting,e.state,String(e.latitude),String(e.longitude)]:e.setting===`permission`?[e.setting,e.state,e.permission,...S(e.mode)]:[e.setting,e.state]}function $i(e){if(x(e,la))return e;throw new n(`INVALID_ARGS`,`settings permission requires a permission target.`)}function ea(e){if(e===void 0||x(e,sa))return e;throw new n(`INVALID_ARGS`,`settings permission mode must be full or limited.`)}const ta=b(`wifi`,`airplane`,`location`,`animations`),na=b(`on`,`off`),ra=b(...Lt),ia=b(`faceid`,`touchid`),aa=b(`match`,`nonmatch`,`enroll`,`unenroll`),oa=b(`match`,`nonmatch`),sa=b(...It),ca=b(...Ft),la=b(...Mt,...Nt);function ua(e){if(!Array.isArray(e)||e.length===0)return e;let t=ke(e);return e.map(e=>{let n=da(e,t,e=>e.label),r=da(e,t,e=>e.identifier),i=typeof e.label==`string`&&e.label.length>=24&&e.label===n,a=typeof e.identifier==`string`&&e.identifier.length>=24&&e.identifier===r;if(!i&&!a)return e;let o={...e};return i&&(delete o.label,o.inheritsLabel=!0),a&&(delete o.identifier,o.inheritsIdentifier=!0),o})}function da(e,t,n){let r=typeof e.parentIndex==`number`?t.get(e.parentIndex):void 0;for(;r;){let e=n(r);if(typeof e==`string`&&e.length>0)return e;r=typeof r.parentIndex==`number`?t.get(r.parentIndex):void 0}}function fa(e){return e.width>0&&e.height>0}function pa(e,t){return!e||!t?!1:e.x>=t.x-2&&e.y>=t.y-2&&e.x+e.width<=t.x+t.width+2&&e.y+e.height<=t.y+t.height+2}function ma(e,t){if(!e||!t)return!0;let n=e.y+e.height/2,r=t.y+t.height/2;return Math.abs(n-r)<=Math.max(e.height,t.height,1)}function ha(e){return typeof e.parentIndex!=`number`}function ga(e){let t=(e.type??``).toLowerCase(),n=(e.identifier??``).trim().toLowerCase();return t.includes(`edittext`)||t.includes(`textfield`)||n===`composer`}function _a(e){let t=(e.type??``).toLowerCase();return t.includes(`scroll`)||t.includes(`list`)||t.includes(`recyclerview`)}function va(e){let t=Oe(e);if(!t||t!==e.identifier?.trim()||!ba(t))return t;let n=(e.type??``).toLowerCase();return n.includes(`view`)||n.includes(`layout`)||n.includes(`image`)||n.includes(`list`)||n.includes(`recyclerview`)||n.includes(`collection`)?``:t}function ya(e){let t=e.trim().replaceAll(/\s+/g,` `).toLowerCase();return!t||/^(true|false|\d+)$/.test(t)?null:t}function ba(e){return/^[\w.]+:id\/[\w.-]+$/i.test(e)}function xa(e,t,n){let r=e;for(;typeof r.parentIndex==`number`;){let e=t.get(r.parentIndex);if(!e)return null;if(n(e))return e;r=e}return null}function Sa(e,t){let n=[],r=[t];for(;r.length>0;){let t=r.pop();for(let i of e)i.parentIndex===t&&(n.push(i),r.push(i.index))}return n}function Ca(e,t,n){let r=[t];for(;r.length>0;){let t=r.pop();if(!n.has(t)){n.add(t);for(let i of e)i.parentIndex!==t||n.has(i.index)||r.push(i.index)}}}const wa=[`button`,`switch`,`checkbox`,`radio`],Ta=[`button`,`image`,`textview`,`view`];function Ea(e,t,n){for(let r of e){if(t.has(r.index)||!ka(r))continue;let i=Aa(Sa(e,r.index),r,t);if(i.label){n.set(r.index,{...r,...n.get(r.index),label:i.label});for(let n of i.removableIndexes)Ca(e,n,t)}}}function Da(e,t,n){let r=new Map;for(let i of e){if(t.has(i.index)||!Ra(i))continue;let a=ya(va(i));if(!a)continue;let o=r.get(a);if(o&&Fa(o,i,t)){let s=Ia(e,o,i,t,n);r.set(a,s);continue}r.set(a,i)}}function Oa(e,t){for(let n of e){if(t.has(n.index)||!Na(n))continue;let r=ya(va(n));if(!r)continue;let i=Sa(e,n.index).filter(e=>!t.has(e.index)&&Pa(n,e,r));for(let n of i.filter(Ma))Ca(e,n.index,t);let a=i.filter(e=>!Ma(e)),o=new Set(a.map(e=>ya(va(e))).filter(e=>!!e));if(!(a.length<2||o.size<2))for(let n of a)Ca(e,n.index,t)}}function ka(e){return e.hittable===!0&&!ga(e)&&!!(e.rect&&fa(e.rect))&&va(e).trim().length===0}function Aa(e,t,n){let r=[],i=[],a=new Set;for(let o of e){if(n.has(o.index)||!ja(o,e)||!pa(o.rect,t.rect))continue;let s=va(o).trim().replaceAll(/\s+/g,` `),c=ya(s);i.push(o.index),!(!s||!c||a.has(c))&&(a.add(c),r.push(s))}return{label:r.join(`, `),removableIndexes:i}}function ja(e,t){if(Ma(e))return!0;let n=(e.type??``).toLowerCase();return e.hittable!==!0&&!ga(e)&&n.includes(`view`)&&va(e).trim().length>0&&!t.some(t=>t.parentIndex===e.index)}function Ma(e){if(e.hittable===!0||ga(e))return!1;let t=(e.type??``).toLowerCase();return t.includes(`text`)||t.includes(`image`)||t.includes(`icon`)}function Na(e){return e.hittable===!0&&!ga(e)&&!!(e.rect&&fa(e.rect))&&!!ya(va(e))}function Pa(e,t,n){if(!Ra(t)||!pa(t.rect,e.rect))return!1;let r=ya(va(t));return!!(r&&n!==r&&n.includes(r))}function Fa(e,t,n){return!n.has(e.index)&&ma(e.rect,t.rect)&&(Ua(e,t)||Ha(e,t))}function Ia(e,t,n,r,i){let a=za(t,n),o=a.index===t.index?n:t,s=(o.type??``).toLowerCase().includes(`image`)?`has image`:null,c=i.get(a.index)??a,ee=i.get(o.index)?.presentationHints??o.presentationHints;return i.set(a.index,{...c,presentationHints:La(c.presentationHints,ee,s)}),Ca(e,o.index,r),i.get(a.index)??a}function La(e,t,n){return[...new Set([...Array.isArray(e)?e:[],...Array.isArray(t)?t:[],...n?[n]:[]])]}function Ra(e){if(!e.rect||!fa(e.rect)||ha(e)||ga(e))return!1;let t=(e.type??``).toLowerCase();return t===`text`||Va(t,Ta)}function za(e,t){let n=Ba(e);return Ba(t)>n?t:e}function Ba(e){let t=(e.type??``).toLowerCase(),n=0;return Va(t,wa)?n+=100:t.includes(`image`)?n+=30:t.includes(`textview`)||t===`text`?n+=20:t.includes(`view`)&&(n+=10),e.hittable===!0&&(n+=20),e.enabled!==!1&&(n+=5),n}function Va(e,t){return t.some(t=>e.includes(t))}function Ha(e,t){let n=e.parentIndex===t.index?t:t.parentIndex===e.index?e:null,r=n?.index===e.index?t:n?.index===t.index?e:null;return!n||!r?!1:za(n,r).index===n.index}function Ua(e,t){return e.parentIndex===t.parentIndex?Math.abs(e.index-t.index)<=3:e.parentIndex===t.index||t.parentIndex===e.index?!1:Math.abs((e.depth??0)-(t.depth??0))<=1&&Math.abs(e.index-t.index)<=2}function Wa(e,t,n){if(n.raw||!Ga(e))return{nodes:t,filteredCount:0};let r=Ka(t);return{nodes:r,filteredCount:t.length-r.length}}function Ga(e){let t=e.androidSnapshot;return!t||typeof t!=`object`?!1:t.backend===`android-helper`}function Ka(e){if(e.length===0)return e;let t=new Set,n=new Map,r=new Map(e.map(e=>[e.index,e]));return qa(e,t),Ja(e,r,t,n),Ea(e,t,n),Oa(e,t),Da(e,t,n),e.filter(e=>!t.has(e.index)).map(e=>n.get(e.index)??e)}function qa(e,t){for(let n of e)!n.rect||fa(n.rect)||ha(n)||Ca(e,n.index,t)}function Ja(e,t,n,r){for(let i of e){if(n.has(i.index)||i.rect||ha(i))continue;let a=xa(i,t,_a);a&&Xa(r,a,Ya(i,e)),Ca(e,i.index,n)}}function Ya(e,t){let n=t.filter(t=>t.parentIndex===e.parentIndex&&t.rect&&fa(t.rect)).map(e=>e.index);return n.length===0?null:e.index<Math.min(...n)?`above`:e.index>Math.max(...n)?`below`:null}function Xa(e,t,n){if(!n)return;let r=e.get(t.index)??t;e.set(t.index,{...r,hiddenContentAbove:r.hiddenContentAbove===!0||n===`above`||void 0,hiddenContentBelow:r.hiddenContentBelow===!0||n===`below`||void 0})}function Za(e){if(e.length<20)return!1;let t=new Map;for(let n of e){let e=(n.type??``).toLowerCase(),r=Qa(Oe(n));if(!r)continue;let i=`${e}|${r}`,a=t.get(i)??[];a.push(n),t.set(i,a)}let n=0;for(let e of t.values())e.length<=1||!eo(e)||(n+=e.length);return n>=8}function Qa(e){let t=e.trim().replaceAll(/\s+/g,` `).toLowerCase();return!t||$a(t)?null:t}function $a(e){return/\S+@\S+\.\S+/.test(e)}function eo(e){for(let t=0;t<e.length;t+=1)for(let n=t+1;n<e.length;n+=1)if(to(e[t]?.rect,e[n]?.rect))return!0;return!1}function to(e,t){if(!e||!t)return!0;let n=.5;return!(e.x+e.width<=t.x+n||t.x+t.width<=e.x+n||e.y+e.height<=t.y+n||t.y+t.height<=e.y+n)}function no(e,n={}){let r=e.nodes,a=Array.isArray(r)?r:[],o=i(e.backend)?e.backend:void 0,s=t(o),c=Wa(e,a,n),ee=oo(e),te=go(e,a,n,c),ne=te.length>0?`${te.join(`
3
+ `)}`}const Si=U({name:`batch`,clientSurface:!1,commands:[H({name:`batch`,text:{summary:`Run multiple commands`,cliDetail:xi()},metadata:_i,run:(e,t)=>e.batch.run(Ci(t)),cliSchema:vi,cliReader:yi,cliOutputFormatter:ri.batch})]});function Ci(e){return{...g(e),steps:e.steps,onError:e.onError,maxSteps:e.maxSteps,out:e.out}}const wi=`alert`,Ti=H({name:wi,text:{summary:`Inspect, accept, or dismiss a platform alert`},metadata:V(wi,`Inspect, wait for, accept, or dismiss a platform alert. Use get before acting when the alert content matters; accept and dismiss change the active alert state. Each iOS XCTest execution activates once, then only observes. Inspect again after an unconfirmed action; never assume a timeout means nothing happened.`,{action:R(jt),timeoutMs:I()}),run:(e,t)=>e.command.alert(t),cliSchema:{usageOverride:`alert [get|accept|dismiss|wait] [timeout]`,positionalArgs:[`action?`,`timeout?`]},cliReader:(e,t)=>({...y(t),...Di(e)}),daemonWriter:_(u.alert,e=>Ei(e)),cliOutputFormatter:f});function Ei(e){return[e.action??`get`,...C(e.timeoutMs)]}function Di(e){if(e.length>2)throw new n(`INVALID_ARGS`,`alert accepts at most action and timeout arguments.`);return h({action:Oi(e[0]),timeoutMs:dr(e[1],`alert timeout`)})}function Oi(e){let t=e?.toLowerCase();if(t===void 0||t===`get`||t===`accept`||t===`dismiss`||t===`wait`)return t;throw new n(`INVALID_ARGS`,`alert action must be get, accept, dismiss, or wait.`)}function W(...e){return e}const ki=W(`keyframes`),Ai=W(`snapshotInteractiveOnly`,`snapshotDepth`,`snapshotScope`,`snapshotRaw`),ji=W(`snapshotDepth`,`snapshotScope`,`snapshotRaw`),Mi=W(`metroProjectRoot`,`kind`,`metroKind`,`metroPublicBaseUrl`,`metroProxyBaseUrl`,`metroBearerToken`,`metroPreparePort`,`metroListenHost`,`metroStatusHost`,`metroStartupTimeoutMs`,`metroProbeTimeoutMs`,`metroRuntimeFile`,`metroNoReuseExisting`,`metroNoInstallDeps`),Ni=W(`metroHost`,`metroPort`,`bundleUrl`),Pi=W(`count`,`intervalMs`,`holdMs`,`jitterPx`,`doubleTap`),Fi=W(`settle`,`settleQuietMs`,`timeoutMs`),Ii=W(`replayUpdate`,`replayEnv`),Li=W(`remoteConfig`,`stateDir`,`daemonBaseUrl`,`daemonAuthToken`,`daemonTransport`,`daemonServerMode`,`tenant`,`sessionIsolation`,`runId`,`leaseId`,`leaseBackend`,`sessionLock`,`platform`,`target`,`device`,`providerApp`,`providerOsVersion`,`providerProject`,`providerBuild`,`providerSessionName`,`providerDeviceOrientation`,`providerGeoLocation`,`providerTimezone`,`providerAppiumVersion`,`providerLanguage`,`providerLocale`,`providerNetworkProfile`,`providerCustomNetwork`,`providerNoResignApp`,`awsProjectArn`,`awsDeviceArn`,`awsAppArn`,`awsRegion`,`awsInteractionMode`,`udid`,`serial`,`iosSimulatorDeviceSet`,`iosXctestrunFile`,`iosXctestDerivedDataPath`,`iosXctestEnvDir`,`androidDeviceAllowlist`,`session`,`noRecord`),Ri=new Set([`platform`,`device`,`udid`,`serial`,`session`]),zi=new Set([`json`,`config`,`help`,`version`,`verbose`,`cost`,`responseLevel`]),Bi=`diff`,Vi={diff:E({mode:O(`snapshot`),baselineInitialized:j(),summary:E({additions:A(),removals:A(),unchanged:A()},[`additions`,`removals`,`unchanged`]),lines:{type:`array`,items:E({kind:D([`added`,`removed`,`unchanged`]),text:k(),ref:k()},[`kind`,`text`])},warnings:Cr()},[`mode`,`baselineInitialized`,`summary`,`lines`])},Hi=H({name:Bi,text:{summary:`Diff snapshot or screenshot`,cliDetail:`Screenshot --threshold is a per-pixel RGB tolerance: 0 requires exact colors and 1 ignores color differences; image dimensions must still match. Both screenshot inputs are decoded from their bytes, so a baseline or current image may be PNG or JPEG, and the diff image is always PNG. JPEG is lossy, so keep --threshold above 0 whenever either input is JPEG. Live iOS simulator screenshot diffs normalize status-bar chrome by default; use screenshot --normalize-status-bar when capturing reusable baselines.`},metadata:V(Bi,`Compare accessibility snapshots or screenshots to identify UI changes. Use snapshot comparisons for semantic tree changes and screenshot comparisons for pixel differences.`,{kind:N(z({type:`string`,const:`snapshot`})),out:P(),interactiveOnly:L(),depth:I(),scope:P(),raw:L()}),run:(e,t)=>e.capture.diff(t),cliSchema:{usageOverride:`diff snapshot | diff screenshot --baseline <path> [current.png] [--out <diff.png>] [--threshold <0-1>] [--overlay-refs]`,usageFlags:[],positionalArgs:[`kind`,`current?`],allowedFlags:[...Ai,`baseline`,`threshold`,`out`,`overlayRefs`]},cliReader:(e,t)=>{if(e[0]!==`snapshot`)throw new n(`INVALID_ARGS`,`Only diff snapshot is available through this parser.`);return{...y(t),kind:`snapshot`,out:t.out,interactiveOnly:t.snapshotInteractiveOnly,depth:t.snapshotDepth,scope:t.snapshotScope,raw:t.snapshotRaw}},daemonWriter:_(u.diff,e=>[T(e.kind,`diff requires kind`)])}),Ui=`screenshot`,Wi=H({name:Ui,text:{summary:`Capture a screenshot`,cliDetail:`Web defaults to the viewport; use --fullscreen, --full, or -f for the entire page. iOS simulators default to 1x logical-point output; use --pixel-density to request a different screenshot density. macOS app sessions default to the app window; use --fullscreen for full desktop, --scale to downscale, --crop-on <selector> to crop the capture to the frame the selector resolves on the same screen (currently iOS simulators and Android emulators), --overlay-refs to annotate current refs, --normalize-status-bar for deterministic iOS simulator chrome, or --no-stabilize for low-latency Android capture loops. On macOS, any --surface other than app (desktop, menubar, frontmost-app) always captures the main display and refuses an explicit --fullscreen. On single-display iOS simulators and Android devices, the result reports displayRotation, the rotation the display rendered the capture in, when the device returns a reading in time; otherwise the field is absent.`},metadata:V(Ui,`Capture a screenshot of the active app or web session. Choose the capture scope, density, size, or annotations through the corresponding input fields when needed.`,{path:P(`Output path.`),cropOn:P(`Selector expression; the capture is cropped to the frame the selector resolves on the same screen.`),overlayRefs:L(),pixelDensity:I(`Output screenshot pixel density in pixels per logical point.`,{min:1}),fullscreen:L(),scale:F(`Screenshot scale factor.`,ee),maxSize:wr(oe.migration.screenshot),stabilize:L(),normalizeStatusBar:L(),surface:R(ft)}),run:(e,t)=>e.capture.screenshot(t),cliSchema:{positionalArgs:[`path?`],allowedFlags:ie},cliReader:(e,t)=>({...y(t),path:e[0]??t.out,...ne(t)}),daemonWriter:e=>(ae(`screenshot`,e),re(e),v(u.screenshot,S(e.path),{...e,...te(e)}))}),Gi=`settings`,Ki=V(Gi,`Read or change supported operating-system settings, animation scales, appearance, or app permissions on the selected target. Platform support varies by setting and action.`,{setting:N(P()),state:P(),app:P(),latitude:F(),longitude:F(),permission:P(),mode:R([...It])}),qi={usageOverride:Rt,listUsageOverride:`settings [area] [options]`,positionalArgs:[`setting`,`state?`,`target?`,`mode?`]},Ji=(e,t)=>Zi(e,t),Yi=_(u.settings,e=>Qi(e)),Xi=H({name:Gi,text:{summary:`Change OS settings and app permissions`,cliDetail:`macOS supports only settings appearance <light|dark|toggle> and settings ${Pt}; wifi|airplane|location|animations|text-size remain unsupported on macOS. Mobile permission actions use the active session app. On Android, deny|reset of a permission the app currently holds kills a running app; the response reports priorGrantState (granted|not_granted|unknown) and warns for granted and unknown, with open <app> --relaunch to restore it. Permission changes require a resolvable foreground user and fail without mutating if adb cannot report one. Android settings airplane on|off is applied by the connectivity service (Android 11+) and reports the airplaneMode that service holds; older builds fail without changing device state. settings reset-keychain clear is iOS-simulator-only and resets the whole simulator keychain, not just the selected app: simctl exposes no per-app keychain reset, so every app on that simulator loses its keychain-backed credentials (e.g. Firebase auth). clear-app-state does not touch the keychain, so a full fresh-install reset needs both; relaunch the app afterward to observe the signed-out state. settings text-size reads the preferred text size the target holds and settings text-size <category> applies one, on iPhone and iPad simulators (simctl content size) and on Android targets (system font_scale); tvOS and visionOS simulators, physical Apple devices, and the macOS host refuse it. Android has no category ladder of its own, so the read names the nearest rung and reports the exact multiplier as platformValue; an already-running app adopts a changed size at its next configuration change, so relaunch the app under test to observe it.`},metadata:Ki,run:(e,t)=>e.settings.update(t),cliSchema:qi,cliReader:Ji,daemonWriter:Yi,cliOutputFormatter:f});function Zi(e,t){let r=Jn(t),i=e[0],a=e[1];if(x(i,ta)&&x(a,na))return{...r,setting:i,state:a};if(i===`location`&&a===`set`)return{...r,setting:i,state:a,latitude:Bt(e[2],`latitude`),longitude:Bt(e[3],`longitude`)};if(i===`appearance`&&x(a,ra))return{...r,setting:i,state:a};if(i===`text-size`)return a===void 0?{...r,setting:i}:{...r,setting:i,state:zt(a)};if(x(i,ia)&&x(a,aa)||i===`fingerprint`&&x(a,oa))return{...r,setting:i,state:a};if(i===`permission`&&x(a,ca))return{...r,setting:i,state:a,permission:$i(e[2]),mode:ea(e[3])};if(i===`clear-app-state`){let t=a===`clear`?e[2]:a;return{...r,setting:i,state:`clear`,app:t}}if(i===`reset-keychain`&&a===`clear`&&e.length===2)return{...r,setting:i,state:a};throw new n(`INVALID_ARGS`,`Invalid settings arguments.`)}function Qi(e){return e.setting===`clear-app-state`?[e.setting,...S(e.app)]:e.setting===`text-size`?[e.setting,...S(e.state)]:e.setting===`location`&&e.state===`set`?[e.setting,e.state,String(e.latitude),String(e.longitude)]:e.setting===`permission`?[e.setting,e.state,e.permission,...S(e.mode)]:[e.setting,e.state]}function $i(e){if(x(e,la))return e;throw new n(`INVALID_ARGS`,`settings permission requires a permission target.`)}function ea(e){if(e===void 0||x(e,sa))return e;throw new n(`INVALID_ARGS`,`settings permission mode must be full or limited.`)}const ta=b(`wifi`,`airplane`,`location`,`animations`),na=b(`on`,`off`),ra=b(...Lt),ia=b(`faceid`,`touchid`),aa=b(`match`,`nonmatch`,`enroll`,`unenroll`),oa=b(`match`,`nonmatch`),sa=b(...It),ca=b(...Ft),la=b(...Mt,...Nt);function ua(e){if(!Array.isArray(e)||e.length===0)return e;let t=ke(e);return e.map(e=>{let n=da(e,t,e=>e.label),r=da(e,t,e=>e.identifier),i=typeof e.label==`string`&&e.label.length>=24&&e.label===n,a=typeof e.identifier==`string`&&e.identifier.length>=24&&e.identifier===r;if(!i&&!a)return e;let o={...e};return i&&(delete o.label,o.inheritsLabel=!0),a&&(delete o.identifier,o.inheritsIdentifier=!0),o})}function da(e,t,n){let r=typeof e.parentIndex==`number`?t.get(e.parentIndex):void 0;for(;r;){let e=n(r);if(typeof e==`string`&&e.length>0)return e;r=typeof r.parentIndex==`number`?t.get(r.parentIndex):void 0}}function fa(e){return e.width>0&&e.height>0}function pa(e,t){return!e||!t?!1:e.x>=t.x-2&&e.y>=t.y-2&&e.x+e.width<=t.x+t.width+2&&e.y+e.height<=t.y+t.height+2}function ma(e,t){if(!e||!t)return!0;let n=e.y+e.height/2,r=t.y+t.height/2;return Math.abs(n-r)<=Math.max(e.height,t.height,1)}function ha(e){return typeof e.parentIndex!=`number`}function ga(e){let t=(e.type??``).toLowerCase(),n=(e.identifier??``).trim().toLowerCase();return t.includes(`edittext`)||t.includes(`textfield`)||n===`composer`}function _a(e){let t=(e.type??``).toLowerCase();return t.includes(`scroll`)||t.includes(`list`)||t.includes(`recyclerview`)}function va(e){let t=Oe(e);if(!t||t!==e.identifier?.trim()||!ba(t))return t;let n=(e.type??``).toLowerCase();return n.includes(`view`)||n.includes(`layout`)||n.includes(`image`)||n.includes(`list`)||n.includes(`recyclerview`)||n.includes(`collection`)?``:t}function ya(e){let t=e.trim().replaceAll(/\s+/g,` `).toLowerCase();return!t||/^(true|false|\d+)$/.test(t)?null:t}function ba(e){return/^[\w.]+:id\/[\w.-]+$/i.test(e)}function xa(e,t,n){let r=e;for(;typeof r.parentIndex==`number`;){let e=t.get(r.parentIndex);if(!e)return null;if(n(e))return e;r=e}return null}function Sa(e,t){let n=[],r=[t];for(;r.length>0;){let t=r.pop();for(let i of e)i.parentIndex===t&&(n.push(i),r.push(i.index))}return n}function Ca(e,t,n){let r=[t];for(;r.length>0;){let t=r.pop();if(!n.has(t)){n.add(t);for(let i of e)i.parentIndex!==t||n.has(i.index)||r.push(i.index)}}}const wa=[`button`,`switch`,`checkbox`,`radio`],Ta=[`button`,`image`,`textview`,`view`];function Ea(e,t,n){for(let r of e){if(t.has(r.index)||!ka(r))continue;let i=Aa(Sa(e,r.index),r,t);if(i.label){n.set(r.index,{...r,...n.get(r.index),label:i.label});for(let n of i.removableIndexes)Ca(e,n,t)}}}function Da(e,t,n){let r=new Map;for(let i of e){if(t.has(i.index)||!Ra(i))continue;let a=ya(va(i));if(!a)continue;let o=r.get(a);if(o&&Fa(o,i,t)){let s=Ia(e,o,i,t,n);r.set(a,s);continue}r.set(a,i)}}function Oa(e,t){for(let n of e){if(t.has(n.index)||!Na(n))continue;let r=ya(va(n));if(!r)continue;let i=Sa(e,n.index).filter(e=>!t.has(e.index)&&Pa(n,e,r));for(let n of i.filter(Ma))Ca(e,n.index,t);let a=i.filter(e=>!Ma(e)),o=new Set(a.map(e=>ya(va(e))).filter(e=>!!e));if(!(a.length<2||o.size<2))for(let n of a)Ca(e,n.index,t)}}function ka(e){return e.hittable===!0&&!ga(e)&&!!(e.rect&&fa(e.rect))&&va(e).trim().length===0}function Aa(e,t,n){let r=[],i=[],a=new Set;for(let o of e){if(n.has(o.index)||!ja(o,e)||!pa(o.rect,t.rect))continue;let s=va(o).trim().replaceAll(/\s+/g,` `),c=ya(s);i.push(o.index),!(!s||!c||a.has(c))&&(a.add(c),r.push(s))}return{label:r.join(`, `),removableIndexes:i}}function ja(e,t){if(Ma(e))return!0;let n=(e.type??``).toLowerCase();return e.hittable!==!0&&!ga(e)&&n.includes(`view`)&&va(e).trim().length>0&&!t.some(t=>t.parentIndex===e.index)}function Ma(e){if(e.hittable===!0||ga(e))return!1;let t=(e.type??``).toLowerCase();return t.includes(`text`)||t.includes(`image`)||t.includes(`icon`)}function Na(e){return e.hittable===!0&&!ga(e)&&!!(e.rect&&fa(e.rect))&&!!ya(va(e))}function Pa(e,t,n){if(!Ra(t)||!pa(t.rect,e.rect))return!1;let r=ya(va(t));return!!(r&&n!==r&&n.includes(r))}function Fa(e,t,n){return!n.has(e.index)&&ma(e.rect,t.rect)&&(Ua(e,t)||Ha(e,t))}function Ia(e,t,n,r,i){let a=za(t,n),o=a.index===t.index?n:t,s=(o.type??``).toLowerCase().includes(`image`)?`has image`:null,c=i.get(a.index)??a,ee=i.get(o.index)?.presentationHints??o.presentationHints;return i.set(a.index,{...c,presentationHints:La(c.presentationHints,ee,s)}),Ca(e,o.index,r),i.get(a.index)??a}function La(e,t,n){return[...new Set([...Array.isArray(e)?e:[],...Array.isArray(t)?t:[],...n?[n]:[]])]}function Ra(e){if(!e.rect||!fa(e.rect)||ha(e)||ga(e))return!1;let t=(e.type??``).toLowerCase();return t===`text`||Va(t,Ta)}function za(e,t){let n=Ba(e);return Ba(t)>n?t:e}function Ba(e){let t=(e.type??``).toLowerCase(),n=0;return Va(t,wa)?n+=100:t.includes(`image`)?n+=30:t.includes(`textview`)||t===`text`?n+=20:t.includes(`view`)&&(n+=10),e.hittable===!0&&(n+=20),e.enabled!==!1&&(n+=5),n}function Va(e,t){return t.some(t=>e.includes(t))}function Ha(e,t){let n=e.parentIndex===t.index?t:t.parentIndex===e.index?e:null,r=n?.index===e.index?t:n?.index===t.index?e:null;return!n||!r?!1:za(n,r).index===n.index}function Ua(e,t){return e.parentIndex===t.parentIndex?Math.abs(e.index-t.index)<=3:e.parentIndex===t.index||t.parentIndex===e.index?!1:Math.abs((e.depth??0)-(t.depth??0))<=1&&Math.abs(e.index-t.index)<=2}function Wa(e,t,n){if(n.raw||!Ga(e))return{nodes:t,filteredCount:0};let r=Ka(t);return{nodes:r,filteredCount:t.length-r.length}}function Ga(e){let t=e.androidSnapshot;return!t||typeof t!=`object`?!1:t.backend===`android-helper`}function Ka(e){if(e.length===0)return e;let t=new Set,n=new Map,r=new Map(e.map(e=>[e.index,e]));return qa(e,t),Ja(e,r,t,n),Ea(e,t,n),Oa(e,t),Da(e,t,n),e.filter(e=>!t.has(e.index)).map(e=>n.get(e.index)??e)}function qa(e,t){for(let n of e)!n.rect||fa(n.rect)||ha(n)||Ca(e,n.index,t)}function Ja(e,t,n,r){for(let i of e){if(n.has(i.index)||i.rect||ha(i))continue;let a=xa(i,t,_a);a&&Xa(r,a,Ya(i,e)),Ca(e,i.index,n)}}function Ya(e,t){let n=t.filter(t=>t.parentIndex===e.parentIndex&&t.rect&&fa(t.rect)).map(e=>e.index);return n.length===0?null:e.index<Math.min(...n)?`above`:e.index>Math.max(...n)?`below`:null}function Xa(e,t,n){if(!n)return;let r=e.get(t.index)??t;e.set(t.index,{...r,hiddenContentAbove:r.hiddenContentAbove===!0||n===`above`||void 0,hiddenContentBelow:r.hiddenContentBelow===!0||n===`below`||void 0})}function Za(e){if(e.length<20)return!1;let t=new Map;for(let n of e){let e=(n.type??``).toLowerCase(),r=Qa(Oe(n));if(!r)continue;let i=`${e}|${r}`,a=t.get(i)??[];a.push(n),t.set(i,a)}let n=0;for(let e of t.values())e.length<=1||!eo(e)||(n+=e.length);return n>=8}function Qa(e){let t=e.trim().replaceAll(/\s+/g,` `).toLowerCase();return!t||$a(t)?null:t}function $a(e){return/\S+@\S+\.\S+/.test(e)}function eo(e){for(let t=0;t<e.length;t+=1)for(let n=t+1;n<e.length;n+=1)if(to(e[t]?.rect,e[n]?.rect))return!0;return!1}function to(e,t){if(!e||!t)return!0;let n=.5;return!(e.x+e.width<=t.x+n||t.x+t.width<=e.x+n||e.y+e.height<=t.y+n||t.y+t.height<=e.y+n)}function no(e,n={}){let r=e.nodes,a=Array.isArray(r)?r:[],o=i(e.backend)?e.backend:void 0,s=t(o),c=Wa(e,a,n),ee=oo(e),te=go(e,a,n,c),ne=te.length>0?`${te.join(`
4
4
  `)}\n`:``,re=n.raw?null:ro(e);if(re)return`${ee}${ne}${io(re)}\n`;let ie=n.raw||!s?null:qt(c.nodes),ae=!!e.truncated,oe=ie?.nodes??a,se=n.raw||!s?null:po(e,ie,oe.length,a.length,c.filteredCount),l=so(a.length,se,ae);return a.length===0?`${ee}${l}\n${ne}`:n.raw?`${ee}${l}\n${ne}${co(a)}\n`:n.flatten?`${ee}${l}\n${ne}${lo(oe)}${fo(ie)}\n`:`${ee}${l}\n${ne}${uo(oe)}${fo(ie)}\n`}function ro(e){let t=e.unchanged;if(!t||typeof t!=`object`)return null;let n=t;return typeof n.ageMs!=`number`||typeof n.nodeCount!=`number`?null:{ageMs:n.ageMs,nodeCount:n.nodeCount,interactiveOnly:n.interactiveOnly===!0||void 0,scope:typeof n.scope==`string`?n.scope:void 0}}function io(e){let t=ao(e.ageMs);return e.scope?[`Scoped snapshot unchanged for scope "${e.scope}" since previous read ${t} ago.`,`Previous refs in this scope remain valid. Use find/get/is for a targeted query, or --force-full to re-emit.`].join(`
5
5
  `):e.interactiveOnly?[`Interactive snapshot unchanged since previous read ${t} ago.`,`${e.nodeCount} visible nodes are unchanged. Previous @e refs are still valid. Use find/get/is for a targeted query, or --force-full to re-emit.`].join(`
6
6
  `):[`Snapshot unchanged since previous read ${t} ago.`,`Refs from the previous snapshot are still valid. Use --force-full to re-emit the tree, or use find/get/is for a targeted query.`].join(`
@@ -22,7 +22,7 @@ import{D as e,M as t,N as n,O as r,T as i,g as a,p as o,t as s,x as c}from"./sdk
22
22
  `):e.message??`No daemon artifacts available.`};let t=[e.message??`No cloud artifacts available for ${e.provider}.`],n=Xc(e);return n&&t.push(`Retry: ${n}`),{data:e,text:e.cloudArtifacts.length>0?e.cloudArtifacts.map(Jc).join(`
23
23
  `):t.join(`
24
24
  `)}}function Vc(e){return`source`in e&&e.source===`daemon`}async function Hc(e){let{serializeDeployResult:t}=await import(`./result-serialization.js`).then(e=>e.t);return At(t(e))}async function Uc(e){let{serializeInstallFromSourceResult:t}=await import(`./result-serialization.js`).then(e=>e.t);return At(t(e))}function Wc(e){let t=e,n=t.platform??`unknown`;return{data:t,text:`Boot ready: ${t.device??t.id??`unknown`} (${n})`}}function Gc(e){let t=e,n=t.platform??`unknown`,r=t.device??t.id??`unknown`,i=t.shutdown;return{data:t,text:`${i&&typeof i==`object`&&`success`in i&&i.success===!0?`Shutdown`:`Shutdown failed`}: ${r} (${n})`}}async function Kc(e,t){let{formatDoctorCheckDetailLines:n,formatDoctorCheckSummaryLine:r}=await import(`./doctor-output.js`).then(e=>e.t),i=e,a=[`Doctor: ${typeof i.status==`string`?i.status:`unknown`}`],o=Zc(i.checks);if(t?.renderedToStderr){let e=typeof i.summary==`string`?i.summary:void 0;e&&a.push(e)}else if(o.length===0){let e=typeof i.summary==`string`?i.summary:`No blockers found.`;a.push(e)}else for(let e of o)a.push(r(e)),a.push(...n(e));return{data:i,text:a.join(`
25
- `)}}const Z={boot:d(Wc),shutdown:d(Gc),devices:d(Nc),capabilities:d(Pc),doctor:({result:e,progress:t})=>Kc(e,t),apps:({input:e,result:t})=>Fc({result:t,appsFilter:e.appsFilter}),session:d(Ic),artifacts:d(Bc),open:d(Lc),close:d(zc),install:d(Hc),reinstall:d(Hc),"install-from-source":d(Uc),prepare:f,viewport:f};function qc(e){let t=e.kind?` ${e.kind}`:``,n=e.target?` target=${e.target}`:``,r=typeof e.booted==`boolean`?` booted=${e.booted}`:``,i=e.claimedBy?` claimed by session "${e.claimedBy.session}" in ${e.claimedBy.workspace}`:``;return`${e.name} (${e.platform}${t}${n})${r}${i}`}function Jc(e){let t=e.url?` ${e.url}`:``,n=e.availability?` ${e.availability}`:``;return`${e.kind}: ${e.name}${n}${t}`}function Yc(e){let t=e.artifactType?` (${e.artifactType})`:``;return`${e.filename}${t}: ${e.mimeType} ${e.sizeBytes} bytes id=${e.id}`}function Xc(e){if(e.providerSessionId)return`agent-device artifacts ${e.providerSessionId} --provider ${e.provider} --json`}function Zc(e){return Array.isArray(e)?e.filter(e=>!!e&&typeof e==`object`&&!Array.isArray(e)):[]}const Qc=H({name:`artifacts`,text:{summary:`List daemon or cloud provider session artifacts`},metadata:V(`artifacts`,`List daemon or cloud provider artifacts for an active or completed session.`,{provider:P(`Cloud provider name, for example browserstack or aws-device-farm.`),providerSessionId:P(`Cloud provider session id or ARN.`)}),run:(e,t)=>e.sessions.artifacts(t),cliSchema:{usageOverride:`artifacts [provider-session-id]`,usageFlags:[`provider`],positionalArgs:[`provider-session-id?`],allowedFlags:[`provider`,`providerSessionId`]},cliReader:(e,t)=>({...y(t),provider:t.provider,providerSessionId:e[0]??t.providerSessionId}),daemonWriter:_(u.artifacts),cliOutputFormatter:Z.artifacts});function $c(e){let{metroHost:t,metroPort:n,bundleUrl:r,launchUrl:i}=e;if(t!==void 0||n!==void 0||r!==void 0||i!==void 0)return{metroHost:t,metroPort:n,bundleUrl:r,launchUrl:i}}function el(e){let{metroHost:t,metroPort:n,bundleUrl:r,launchUrl:i,...a}=e,o=$c({metroHost:t,metroPort:n,bundleUrl:r,launchUrl:i});return o?{...a,runtime:o}:a}const tl=V(`apps`,`List the apps installed on the selected device. Include system or OEM apps only when they are needed as automation targets.`,{appsFilter:R([`user-installed`,`all`],`Restrict the listing to user-installed apps, or include system and OEM apps.`)}),nl=V(`open`,`Boot the selected device when needed, then open an app, deep link, or URL in a session. Use the app or URL inputs to choose what becomes the foreground automation target.`,{app:P(`App name, bundle id, package, or URL.`),url:P(`Optional URL passed with an app shell.`),surface:R(ft,`macOS presentation surface to open: the app itself, the frontmost app, the desktop, or the menu bar.`),activity:P(`Android activity name.`),launchConsole:P(`Launch console mode.`),launchArgs:jr(`Launch arguments forwarded verbatim to the platform launch command.`),relaunch:L(`Force relaunch.`),timeoutMs:I(`Startup budget in milliseconds. Bounds the Simulator boot wait, so a never-booted Simulator can finish its first-boot migration; omit for the default startup behavior.`,{min:1}),waitMs:Er(`waitMs`),foreground:Er(`foreground`),saveScript:z({oneOf:[j(),k()]}),force:L(`Overwrite an existing --save-script target instead of refusing (alias: --overwrite).`),testIme:L(`Activate the headless Android test IME for deterministic Unicode text entry (default on for emulators; opt-in on real devices).`),noRecord:L(`Do not record this action.`),metroHost:P(`Session-scoped Metro/debug host hint applied to the opened app.`),metroPort:I(`Session-scoped Metro/debug port hint applied to the opened app. On an emulator/simulator the host defaults to the loopback alias (Android 10.0.2.2, iOS 127.0.0.1) when --metro-host is omitted; physical devices still require an explicit --metro-host.`,{min:1,max:65535}),bundleUrl:P(`Session-scoped bundle URL hint applied to the opened app.`),launchUrl:P(`Session-scoped launch URL hint applied to the opened app.`)}),rl=V(`close`,`Close the named app, or close the active session app when app is omitted. Use shutdown only when the selected simulator or emulator should also stop.`,{app:P(`Optional app to close.`),shutdown:L(`Shutdown the session/device where supported.`),saveScript:z({oneOf:[j(),k()]}),force:L(`Overwrite an existing --save-script target instead of refusing (alias: --overwrite).`)});function il(e){return el(e)}const al={allowedFlags:[`appsFilter`]},ol={positionalArgs:[`appOrUrl?`,`url?`],allowedFlags:[`activity`,`launchConsole`,`launchArgs`,`testIme`,`saveScript`,`force`,`noRecord`,`relaunch`,`foreground`,`timeoutMs`,`waitMs`,`surface`,...Ni,`launchUrl`]},sl={positionalArgs:[`app?`],allowedFlags:[`saveScript`,`force`,`shutdown`]},cl=(e,t)=>({...y(t),appsFilter:Qt(t.appsFilter)}),ll=(e,t)=>({...y(t),app:e[0],url:e[1],surface:t.surface,activity:t.activity,launchConsole:t.launchConsole,launchArgs:t.launchArgs,relaunch:t.relaunch,foreground:t.foreground,timeoutMs:t.timeoutMs,waitMs:t.waitMs,saveScript:t.saveScript,force:t.force,testIme:t.testIme,noRecord:t.noRecord,metroHost:t.metroHost,metroPort:t.metroPort,bundleUrl:t.bundleUrl,launchUrl:t.launchUrl}),ul=(e,t)=>({...y(t),app:e[0],shutdown:t.shutdown,saveScript:t.saveScript,force:t.force}),dl=_(u.apps),fl=_(u.open,vl),pl=_(u.close,e=>S(e.app)),ml=H({name:`apps`,text:{summary:`List installed apps or deferred provider app assets`,cliDetail:`Before provider allocation, lists uploaded app assets when the selected provider exposes a catalog. On a live device, defaults to user-installed apps; use --all to include system/OEM apps.`},metadata:tl,run:(e,t)=>e.apps.list(t),cliSchema:al,cliReader:cl,daemonWriter:dl,cliOutputFormatter:Z.apps}),hl=H({name:`open`,text:{summary:`Open an app, deep link or URL, save replays`,cliDetail:`Use --platform to bind URL/deep-link opens to the target platform. For iOS simulator initial stdout/stderr, put --launch-console <path> on this open command, for example agent-device open "Agent Device Tester" --platform ios --launch-console artifacts/launch-console.log. Expo Go/dev-client shells accept host + URL, for example agent-device open "Expo Go" exp://127.0.0.1:8081 --platform ios. macOS also supports --surface app|frontmost-app|desktop|menubar. --metro-host/--metro-port/--bundle-url/--launch-url set this session's Metro/debug runtime hints as part of open itself (applied to the app's dev-server prefs and recorded as the session's dev-server binding), so a fresh session has them before its first reload instead of needing a throwaway reload-first call just to seed hints; a later plain metro reload in the same session reuses whichever of these were set. A fresh open without these flags clears any leftover binding from a previous same-name session; close also clears it. A device another session is holding refuses at once; add --wait <ms> to block up to that budget for it and only then fail with DEVICE_IN_USE naming the owning session.`,mcpDetail:`Metro and debug runtime hints given here are recorded as the session's dev-server binding, so a later reload reuses them; a fresh open without them clears any binding left by a previous same-name session.`},metadata:nl,run:(e,t)=>e.apps.open(il(t)),cliSchema:ol,cliReader:ll,daemonWriter:fl,cliOutputFormatter:Z.open}),gl=H({name:`close`,text:{summary:`Close an app or end the session`},metadata:rl,run:(e,t)=>t.app?e.apps.close(t):e.sessions.close(_l(t)),cliSchema:sl,cliReader:ul,daemonWriter:pl,cliOutputFormatter:Z.close});function _l(e){let{app:t,...n}=e;return n}function vl(e){return e.app?e.url?[e.app,e.url]:[e.app]:[]}const yl={platform:D(ve),target:D(be),device:k(`Human-readable device name.`),id:k(`Stable device id.`),kind:D(xe)},bl=[`platform`,`target`,`device`,`id`,`kind`],xl=E({success:j(),exitCode:A(),stdout:k(),stderr:k(),error:M(`Normalized error detail when shutdown failed.`)},[`success`,`exitCode`,`stdout`,`stderr`]),Sl={boot:E({...yl,booted:{type:`boolean`,const:!0}},[...bl,`booted`]),shutdown:E({...yl,shutdown:xl},[...bl,`shutdown`])},Cl=V(`devices`,`List available devices and simulators that can be selected for automation. Use platform, device, udid, or serial inputs on later commands to target one result.`,{}),wl=V(`capabilities`,`List the commands supported by the selected device or active session. Use device-selection inputs when checking support before a session is open.`,{}),Tl=V(`boot`,`Boot or prepare the selected device or simulator so later commands can target it. The device is chosen through the device-selection inputs, not by naming it here.`,{headless:L(`Boot without showing simulator UI when supported.`),timeoutMs:I(`Startup budget in milliseconds. Bounds the boot wait, so a never-booted Simulator can finish its first-boot migration or a cold emulator its boot; omit for the default startup behavior.`,{min:1})}),El=V(`shutdown`,`Shutdown a selected simulator or emulator.`,{}),Dl={allowedFlags:[`headless`,`timeoutMs`]},Ol={},kl={},Al={},jl=(e,t)=>y(t),Ml=(e,t)=>({...y(t),headless:t.headless,timeoutMs:t.timeoutMs}),Nl=_(u.devices),Pl=_(u.capabilities),Fl=_(u.boot),Il=_(u.shutdown),Ll=[H({name:`devices`,text:{summary:`List available devices and simulators`},metadata:Cl,run:(e,t)=>e.devices.list(t),cliSchema:Ol,cliReader:jl,daemonWriter:Nl,cliOutputFormatter:Z.devices}),H({name:`capabilities`,text:{summary:`List supported commands for the selected device`,cliDetail:`Select an explicit target with --platform/--device/--udid/--serial.`},metadata:wl,run:(e,t)=>e.devices.capabilities(t),cliSchema:kl,cliReader:jl,daemonWriter:Pl,cliOutputFormatter:Z.capabilities}),H({name:`boot`,text:{summary:`Boot target device/simulator`},metadata:Tl,run:(e,t)=>e.devices.boot(t),cliSchema:Dl,cliReader:Ml,daemonWriter:Fl,cliOutputFormatter:Z.boot}),H({name:`shutdown`,text:{summary:`Shutdown target simulator/emulator`},metadata:El,run:(e,t)=>e.devices.shutdown(t),cliSchema:Al,cliReader:jl,daemonWriter:Il,cliOutputFormatter:Z.shutdown})],Rl={doctor:E({status:D([`pass`,`warn`,`fail`,`info`]),summary:k(),kind:D([`auto`,`react-native`,`expo`,`repack`]),platform:k(),target:D(be),targetApp:k(),metro:E({host:k(),port:A()},[`host`,`port`]),checks:{type:`array`,items:E({id:k(),status:D([`pass`,`warn`,`fail`,`info`]),summary:k(),hint:k(),command:k(),evidence:M()},[`id`,`status`,`summary`])}},[`status`,`summary`,`kind`,`checks`])},zl=H({name:`doctor`,text:{summary:`Diagnose device, app, dev-server, and RN/Expo readiness`,cliDetail:`Metro reachability is inferred from cwd/runtime. On iOS simulators it also warms the XCTest runner build cache in the background when missing. Pass --app to verify a target app on the one matching booted device without opening a session. Use --remote to check remote connection setup without probing local devices. Default output is compact; use --json for full checks and evidence.`,mcpDetail:`On iOS simulators it also warms the XCTest runner build cache in the background when missing, so run it before the first Apple snapshot or interaction of a session.`},metadata:V(`doctor`,`Diagnose device, app, development-server, and React Native or Expo readiness issues. Returns compact evidence for local inventory, sessions, optional app discovery, toolchains, and server reachability.`,{targetApp:P(`Installed app package/bundle id or app name to verify without opening a session.`),remote:L(`Check remote connection setup instead of local device inventory.`)}),run:(e,t)=>e.command.doctor(t),cliSchema:{usageOverride:`doctor [--platform ios|android|harmonyos|vega|macos|linux|web|apple]`,allowedFlags:[`targetApp`,`remote`]},cliReader:(e,t)=>({...y(t),targetApp:t.targetApp,remote:t.remote}),daemonWriter:_(u.doctor),cliOutputFormatter:Z.doctor});function Bl(e,t=`--github-actions-artifact`){let r=e.indexOf(`:`);if(r<=0||r===e.length-1)throw new n(`INVALID_ARGS`,`${t} must use owner/repo:artifact, for example thymikee/RNCLI83:6635342232`);let{owner:i,repo:a}=Ul(e.slice(0,r),t);return Hl(i,a,e.slice(r+1),`${t} artifact`)}function Vl(e,t){if(!e||typeof e!=`object`||Array.isArray(e))throw new n(`INVALID_ARGS`,`${t} installSource must be an object.`);let r=e;if(Gl(r.type,`${t} installSource.type`)!==`github-actions-artifact`)throw new n(`INVALID_ARGS`,`${t} installSource.type must be "github-actions-artifact".`);let{owner:i,repo:a}=Ul(Gl(r.repo,`${t} installSource.repo`),`${t} installSource.repo`);return Hl(i,a,r.artifact,`${t} installSource.artifact`)}function Hl(e,t,n,r){return typeof n==`number`||ql(n)?{kind:`github-actions-artifact`,owner:e,repo:t,artifactId:Kl(n,r)}:{kind:`github-actions-artifact`,owner:e,repo:t,artifactName:Gl(n,r)}}function Ul(e,t){let r=e.indexOf(`/`);if(r<=0||r===e.length-1||e.indexOf(`/`,r+1)!==-1)throw new n(`INVALID_ARGS`,`${t} must use owner/repo.`);let i={owner:e.slice(0,r).trim(),repo:e.slice(r+1).trim()};if(!i.owner||!i.repo)throw new n(`INVALID_ARGS`,`${t} must use owner/repo.`);return i}function Wl(e){return typeof e==`string`&&e.trim().length>0?e.trim():void 0}function Gl(e,t){let r=Wl(e);if(!r)throw new n(`INVALID_ARGS`,`${t} must be a non-empty string.`);return r}function Kl(e,t){let r=typeof e==`number`?e:typeof e==`string`?Number(e):NaN;if(!Number.isInteger(r))throw new n(`INVALID_ARGS`,`${t} must be an integer.`);return r}function ql(e){return typeof e==`string`&&/^\d+$/.test(e.trim())}const Jl=V(`install`,`Install an app binary from a local path. Provide an app identifier with the path when the target needs explicit app selection; use reinstall to replace an already installed app.`,{app:P(`Optional app identifier hint.`),appPath:N(P(`Path to app binary.`))}),Yl=V(`reinstall`,`Replace an installed app with a binary from a local path. Use this when preserving the same app identity while installing a new build on the selected device.`,{app:N(P()),appPath:N(P(`Path to app binary.`))}),Xl=V(`install-from-source`,`Install app builds from URLs, remote source specs, or CI artifacts resolved by a remote daemon.`,{source:N(z(M(`Install source object.`))),retainPaths:L(),retentionMs:I()}),Zl={usageOverride:`install <path> | install <app> <path>`,listUsageOverride:`install <path>`,positionalArgs:[`appOrPath`,`path?`]},Ql={positionalArgs:[`app`,`path`]},$l={usageOverride:`install-from-source <url> | install-from-source --github-actions-artifact <owner/repo:artifact>`,usageFlags:[],listUsageOverride:`install-from-source`,positionalArgs:[`url?`],allowedFlags:[`header`,`githubActionsArtifact`,`installSource`,`retainPaths`,`retentionMs`]},eu=(e,t)=>ou(e,t),tu=(e,t)=>su(e,t),nu=(e,t)=>({...y(t),source:uu(e,t),retainPaths:t.retainPaths,retentionMs:t.retentionMs}),ru=_(u.install,e=>cu(e.app,e.appPath)),iu=_(u.reinstall,e=>lu(e.app,e.appPath)),au=[H({name:`install`,text:{summary:`Install an app binary from a path`},metadata:Jl,run:(e,t)=>e.apps.install(t),cliSchema:Zl,cliReader:eu,daemonWriter:ru,cliOutputFormatter:Z.install}),H({name:`reinstall`,text:{summary:`Replace an installed app with a new build`},metadata:Yl,run:(e,t)=>e.apps.reinstall(t),cliSchema:Ql,cliReader:tu,daemonWriter:iu,cliOutputFormatter:Z.reinstall}),H({name:`install-from-source`,text:{summary:`Install app builds from URLs or CI artifacts`},metadata:Xl,run:(e,t)=>e.apps.installFromSource(t),cliSchema:$l,cliReader:nu,daemonWriter:e=>v(st.installSource,[],{...e,installSource:e.source,retainMaterializedPaths:e.retainPaths,materializedPathRetentionMs:e.retentionMs}),cliOutputFormatter:Z[`install-from-source`]})];function ou(e,t){let[n,r]=e,i=r!==void 0;return{...y(t),...i?{app:w(n,`install requires app`)}:{},appPath:w(i?r:n,`install requires path`)}}function su(e,t){return{...y(t),app:w(e[0],`reinstall requires app`),appPath:w(e[1],`reinstall requires path`)}}function cu(e,t){let n=T(t,`missing app path`);return typeof e==`string`&&e.length>0?[e,n]:[n]}function lu(e,t){return[T(e,`missing first positional`),T(t,`missing second positional`)]}function uu(e,t){let r=e[0]?.trim();if(e.length>1)throw new n(`INVALID_ARGS`,`install-from-source accepts either one <url> positional or --github-actions-artifact`);let i=t.githubActionsArtifact?Bl(t.githubActionsArtifact):void 0,a=t.installSource;if(+!!r+ +!!i+ +!!a!=1)throw new n(`INVALID_ARGS`,`install-from-source requires exactly one source: <url>, --github-actions-artifact, or config installSource`);if(!r&&t.header&&t.header.length>0)throw new n(`INVALID_ARGS`,`install-from-source --header is only supported for URL sources`);return i||a||{kind:`url`,url:r,headers:du(t.header)}}function du(e){if(!e||e.length===0)return;let t={};for(let r of e){let e=r.indexOf(`:`);if(e<=0)throw new n(`INVALID_ARGS`,`Invalid --header value "${r}". Expected "name:value".`);let i=r.slice(0,e).trim(),a=r.slice(e+1).trim();if(!i)throw new n(`INVALID_ARGS`,`Invalid --header value "${r}". Header name cannot be empty.`);t[i]=a}return t}const fu=[`ios-runner`],pu={prepare:E({action:O(`ios-runner`),platform:D(ve),deviceId:k(),deviceName:k(),kind:D(xe),durationMs:A(),runner:E({},[]),cache:D([`exact`,`miss`,`external`]),artifact:D([`valid`,`rebuilt`]),buildMs:A(),connectMs:A(),healthCheckMs:A(),xctestrunPath:k(),recoveryReason:k(),failureReason:k(),timing:E({totalMs:A(),additiveParts:E({buildMs:A(),connectAfterBuildMs:A(),healthCheckMs:A()},[`connectAfterBuildMs`,`healthCheckMs`]),containment:E({connectMs:{type:`array`,items:O(`buildMs`)},healthCheckMs:{type:`array`,items:k()}},[`healthCheckMs`]),note:k()},[`totalMs`,`additiveParts`,`containment`,`note`]),message:k()},[`action`,`platform`,`deviceId`,`deviceName`,`kind`,`durationMs`,`runner`,`connectMs`,`healthCheckMs`,`timing`,`message`])},mu=H({name:`prepare`,text:{summary:`Pre-warm platform helpers before automation`},metadata:V(`prepare`,`Prepare platform helper infrastructure. ios-runner builds/reuses, starts, and health-checks the XCTest runner so later Apple snapshots and interactions do not pay first-use startup cost. In JSON output, top-level buildMs/connectMs/healthCheckMs are diagnostic fields and may overlap; use timing.additiveParts for additive wall-clock phase totals. In CI, run it after boot/install and before replay/test; if replay/test starts a separate daemon, stop the prepare daemon before replay/test so it does not keep the prepared runner lease. It is not a recovery step for "runner already owned by another agent-device daemon"; stop the owning daemon on the Mac with simulator access instead. Runner build/start output is written to the session runner.log; daemon.log is for daemon lifecycle/startup issues.`,{action:N(R(fu)),timeoutMs:I(`Maximum wall-clock time for the prepare command.`)}),run:(e,t)=>e.command.prepare(t),cliSchema:{usageOverride:`prepare ios-runner --platform ios|macos`,listUsageOverride:`prepare`,positionalArgs:[`ios-runner`],allowedFlags:[`timeoutMs`]},cliReader:(e,t)=>({...y(t),action:w(e[0],`prepare requires subcommand`),timeoutMs:t.timeoutMs}),daemonWriter:_(u.prepare,e=>[T(e.action,`prepare requires subcommand`)]),cliOutputFormatter:Z.prepare}),hu={push:{type:`object`,oneOf:[E({platform:O(`ios`),bundleId:k(),message:k()},[`platform`,`bundleId`,`message`]),E({platform:O(`android`),package:k(),action:k(),extrasCount:A(),message:k()},[`platform`,`package`,`action`,`extrasCount`,`message`])]},"trigger-app-event":E({event:k(),eventUrl:k(),transport:O(`deep-link`),message:k()},[`event`,`eventUrl`,`transport`,`message`])},gu=V(`push`,`Deliver push notification payloads to an installed app.`,{app:N(P()),payload:N(z({oneOf:[k(),M()]}))}),_u=V(`trigger-app-event`,`Ask the app to handle an app-defined automation or test event, with an optional structured payload. Call this only for event names and payload shapes the app documents.`,{event:N(P(`Name of an app-defined automation or test event the app documents.`)),payload:Du(`Structured payload passed to the event, in the shape the app documents for it.`)}),vu={listUsageOverride:`push`,positionalArgs:[`bundleOrPackage`,`payloadOrJson`]},yu={usageOverride:`trigger-app-event <event> [payloadJson]`,listUsageOverride:`trigger-app-event`,positionalArgs:[`event`,`payloadJson?`]},bu=(e,t)=>({...y(t),app:w(e[0],`push requires bundleOrPackage`),payload:w(e[1],`push requires payloadOrJson`)}),xu=(e,t)=>({...y(t),event:w(e[0],`trigger-app-event requires event`),payload:e[1]?cr(e[1],`trigger-app-event payload`):void 0}),Su=_(u.push,e=>Tu(e)),Cu=_(u.triggerAppEvent,e=>Eu(e)),wu=[H({name:`push`,text:{summary:`Deliver a push notification payload`},metadata:gu,run:(e,t)=>e.apps.push(t),cliSchema:vu,cliReader:bu,daemonWriter:Su}),H({name:`trigger-app-event`,text:{summary:`Invoke an app-defined automation event`},metadata:_u,run:(e,t)=>e.apps.triggerEvent(t),cliSchema:yu,cliReader:xu,daemonWriter:Cu})];function Tu(e){return[e.app,typeof e.payload==`string`?e.payload:JSON.stringify(e.payload)]}function Eu(e){return[e.event,...e.payload?[JSON.stringify(e.payload)]:[]]}function Du(e){return Ar(e)}const Ou=H({name:`session`,text:{summary:`List sessions, show the state dir, or publish a script`},metadata:V(`session`,`List active sessions, print the effective daemon state directory, or publish an armed open-to-destination script without closing its session`,{action:R([`list`,`state-dir`,`save-script`],`list shows active sessions; state-dir prints the daemon state directory; save-script publishes an armed recording without teardown.`),path:P(`Optional .ad output path for save-script.`),force:L(`Atomically replace an existing save-script target.`)}),run:async(e,{action:t,path:n,force:r,...i})=>{let a=t??`list`;return Au(a,n,r),a===`state-dir`?{stateDir:await e.sessions.stateDir(i)}:a===`save-script`?await e.sessions.saveScript({...i,path:n,force:r}):{sessions:await e.sessions.list(i)}},cliSchema:{usageOverride:`session list | session state-dir | session save-script [path] [--force]`,usageFlags:[],listUsageOverride:`session`,positionalArgs:[`list|state-dir|save-script?`,`path?`],allowedFlags:[`force`]},cliReader:(e,t)=>({...y(t),action:ku(e[0]),path:e[1],force:t.force}),cliOutputFormatter:Z.session});function ku(e){let t=e??`list`;if(t===`list`||t===`state-dir`||t===`save-script`)return t;throw new n(`INVALID_ARGS`,`session only supports list, state-dir, or save-script`)}function Au(e,t,r){if(e!==`save-script`&&(t!==void 0||r===!0))throw new n(`INVALID_ARGS`,`session ${e} does not accept a path or --force; use session save-script [path] [--force] to publish a recording.`)}const ju={viewport:E({width:A(),height:A(),message:k()},[`width`,`height`,`message`])},Mu=H({name:`viewport`,text:{summary:`Resize the active web viewport for the current session`},metadata:V(`viewport`,`Resize the active web viewport before taking snapshots or screenshots. Useful for fixed-layout or 100vh apps where changing the viewport reveals different content.`,{width:N(I(`Viewport width in CSS pixels.`,{min:1})),height:N(I(`Viewport height in CSS pixels.`,{min:1}))}),run:(e,t)=>e.command.viewport(t),cliSchema:{positionalArgs:[`width`,`height`]},cliReader:(e,t)=>({...y(t),..._e(e)}),daemonWriter:_(u.viewport,e=>{let{width:t,height:n}=e;return[String(t),String(n)]}),cliOutputFormatter:Z.viewport}),Nu=U({name:`management`,commands:[...Ll,Qc,zl,mu,ml,Ou,hl,gl,Mu,...au,...wu]});function Pu(e){return{data:e.result,text:e.action===`reload`?`Reloaded React Native apps via ${e.result.reloadUrl}`:JSON.stringify(e.result,null,2)}}const Fu={metro:({input:e,result:t})=>Pu({result:t,action:e.action})},Iu=`metro`,Lu=U({name:`metro`,commands:[H({name:Iu,text:{summary:`Prepare the dev server or reload apps`,cliDetail:`reload with no --metro-host/--metro-port/--bundle-url resolves against the dev server this session last bound via metro prepare or open's metro hint flags (falling back to localhost:8081 only when the session never bound one), so it never silently reloads a different project's server on the default port; pass an explicit flag to override the session hint for one call. The reload URL keeps the bound bundle URL mount prefix instead of collapsing to the host root, and when the server has no HTTP /reload route (Expo) the reload is broadcast over its /message websocket instead of trusting the app-page fallback. The binding is cleared when the session closes, and a fresh open without hint flags also clears any leftover binding from a previous same-name session. --kind expo (detected or forced) requests the virtual-entry bundle URL (.expo/.virtual-metro-entry.bundle) instead of index.bundle, since index.bundle 404s/500s against Expo dev servers in monorepos. Dependency install auto-detects the package manager from the nearest yarn.lock/pnpm-lock.yaml/bun.lock/bun.lockb/package-lock.json walking up from --project-root (bounded at the repo root), so Yarn/pnpm workspace monorepos with the lockfile at the repo root do not wrongly fall back to npm install (which fails on workspace: dependency specifiers); if install still fails, pass --no-install-deps when dependencies are already installed (for example via a monorepo root install).`,mcpDetail:`The binding is cleared when the session closes, and a fresh open without runtime hints also clears any leftover binding from a previous same-name session.`},metadata:V(Iu,`Prepare a React Native development server or ask connected apps to reload, using the development server this session is bound to. Provide explicit runtime inputs only to override that binding for one call.`,{action:N(R([`prepare`,`reload`])),projectRoot:P(),kind:z(k()),publicBaseUrl:P(),proxyBaseUrl:Tr(P(),{operatorPath:`Configure the remote proxy in the remote-config profile or operator config for the process serving these tools.`}),bearerToken:Tr(P(),{envFlagKeys:[`metroBearerToken`,`daemonAuthToken`]}),bridgeScope:z({type:`object`,additionalProperties:!0}),launchUrl:P(),port:I(),listenHost:P(),statusHost:P(),startupTimeoutMs:I(),probeTimeoutMs:I(),reuseExisting:L(),installDependenciesIfNeeded:L(),runtimeFilePath:P(),logPath:P(),metroHost:P(),metroPort:I(),bundleUrl:P(),timeoutMs:I()}),run:async(e,t)=>t.action===`prepare`?await e.metro.prepare(Ru(t)):await e.metro.reload(Bu(t)),cliSchema:{usageOverride:`metro prepare (--public-base-url <url> | --proxy-base-url <url>) [--project-root <path>] [--port <port>] [--kind auto|react-native|expo|repack]
25
+ `)}}const Z={boot:d(Wc),shutdown:d(Gc),devices:d(Nc),capabilities:d(Pc),doctor:({result:e,progress:t})=>Kc(e,t),apps:({input:e,result:t})=>Fc({result:t,appsFilter:e.appsFilter}),session:d(Ic),artifacts:d(Bc),open:d(Lc),close:d(zc),install:d(Hc),reinstall:d(Hc),"install-from-source":d(Uc),prepare:f,viewport:f};function qc(e){let t=e.kind?` ${e.kind}`:``,n=e.target?` target=${e.target}`:``,r=typeof e.booted==`boolean`?` booted=${e.booted}`:``,i=e.claimedBy?` claimed by session "${e.claimedBy.session}" in ${e.claimedBy.workspace}`:``;return`${e.name} (${e.platform}${t}${n})${r}${i}`}function Jc(e){let t=e.url?` ${e.url}`:``,n=e.availability?` ${e.availability}`:``;return`${e.kind}: ${e.name}${n}${t}`}function Yc(e){let t=e.artifactType?` (${e.artifactType})`:``;return`${e.filename}${t}: ${e.mimeType} ${e.sizeBytes} bytes id=${e.id}`}function Xc(e){if(e.providerSessionId)return`agent-device artifacts ${e.providerSessionId} --provider ${e.provider} --json`}function Zc(e){return Array.isArray(e)?e.filter(e=>!!e&&typeof e==`object`&&!Array.isArray(e)):[]}const Qc=H({name:`artifacts`,text:{summary:`List daemon or cloud provider session artifacts`},metadata:V(`artifacts`,`List daemon or cloud provider artifacts for an active or completed session.`,{provider:P(`Cloud provider name, for example browserstack or aws-device-farm.`),providerSessionId:P(`Cloud provider session id or ARN.`)}),run:(e,t)=>e.sessions.artifacts(t),cliSchema:{usageOverride:`artifacts [provider-session-id]`,usageFlags:[`provider`],positionalArgs:[`provider-session-id?`],allowedFlags:[`provider`,`providerSessionId`]},cliReader:(e,t)=>({...y(t),provider:t.provider,providerSessionId:e[0]??t.providerSessionId}),daemonWriter:_(u.artifacts),cliOutputFormatter:Z.artifacts});function $c(e){let{metroHost:t,metroPort:n,bundleUrl:r,launchUrl:i}=e;if(t!==void 0||n!==void 0||r!==void 0||i!==void 0)return{metroHost:t,metroPort:n,bundleUrl:r,launchUrl:i}}function el(e){let{metroHost:t,metroPort:n,bundleUrl:r,launchUrl:i,...a}=e,o=$c({metroHost:t,metroPort:n,bundleUrl:r,launchUrl:i});return o?{...a,runtime:o}:a}const tl=V(`apps`,`List the apps installed on the selected device. Include system or OEM apps only when they are needed as automation targets.`,{appsFilter:R([`user-installed`,`all`],`Restrict the listing to user-installed apps, or include system and OEM apps.`)}),nl=V(`open`,`Boot the selected device when needed, then open an app, deep link, or URL in a session. Use the app or URL inputs to choose what becomes the foreground automation target.`,{app:P(`App name, bundle id, package, or URL.`),url:P(`Optional URL passed with an app shell.`),surface:R(ft,`macOS presentation surface to open: the app itself, the frontmost app, the desktop, or the menu bar.`),activity:P(`Android activity name.`),launchConsole:P(`Launch console mode.`),launchArgs:jr(`Launch arguments forwarded verbatim to the platform launch command.`),relaunch:L(`Force relaunch.`),timeoutMs:I(`Startup budget in milliseconds. Bounds the Simulator boot wait, so a never-booted Simulator can finish its first-boot migration; omit for the default startup behavior.`,{min:1}),waitMs:Er(`waitMs`),foreground:Er(`foreground`),saveScript:z({oneOf:[j(),k()]}),force:L(`Overwrite an existing --save-script target instead of refusing (alias: --overwrite).`),testIme:L(`Activate the headless Android test IME for deterministic Unicode text entry (default on for emulators; opt-in on real devices).`),noRecord:L(`Do not record this action.`),metroHost:P(`Session-scoped Metro/debug host hint applied to the opened app.`),metroPort:I(`Session-scoped Metro/debug port hint applied to the opened app. On an emulator/simulator the host defaults to the loopback alias (Android 10.0.2.2, iOS 127.0.0.1) when --metro-host is omitted; physical devices still require an explicit --metro-host.`,{min:1,max:65535}),bundleUrl:P(`Session-scoped bundle URL hint applied to the opened app.`),launchUrl:P(`Session-scoped launch URL hint applied to the opened app.`)}),rl=V(`close`,`Close the named app, or close the active session app when app is omitted. Use shutdown only when the selected simulator or emulator should also stop.`,{app:P(`Optional app to close.`),shutdown:L(`Shutdown the session/device where supported.`),saveScript:z({oneOf:[j(),k()]}),force:L(`Overwrite an existing --save-script target instead of refusing (alias: --overwrite).`)});function il(e){return el(e)}const al={allowedFlags:[`appsFilter`]},ol={positionalArgs:[`appOrUrl?`,`url?`],allowedFlags:[`activity`,`launchConsole`,`launchArgs`,`testIme`,`saveScript`,`force`,`noRecord`,`relaunch`,`foreground`,`timeoutMs`,`waitMs`,`surface`,...Ni,`launchUrl`]},sl={positionalArgs:[`app?`],allowedFlags:[`saveScript`,`force`,`shutdown`]},cl=(e,t)=>({...y(t),appsFilter:Qt(t.appsFilter)}),ll=(e,t)=>({...y(t),app:e[0],url:e[1],surface:t.surface,activity:t.activity,launchConsole:t.launchConsole,launchArgs:t.launchArgs,relaunch:t.relaunch,foreground:t.foreground,timeoutMs:t.timeoutMs,waitMs:t.waitMs,saveScript:t.saveScript,force:t.force,testIme:t.testIme,noRecord:t.noRecord,metroHost:t.metroHost,metroPort:t.metroPort,bundleUrl:t.bundleUrl,launchUrl:t.launchUrl}),ul=(e,t)=>({...y(t),app:e[0],shutdown:t.shutdown,saveScript:t.saveScript,force:t.force}),dl=_(u.apps),fl=_(u.open,vl),pl=_(u.close,e=>S(e.app)),ml=H({name:`apps`,text:{summary:`List installed apps or deferred provider app assets`,cliDetail:`Before provider allocation, lists uploaded app assets when the selected provider exposes a catalog. On a live device, defaults to user-installed apps; use --all to include system/OEM apps.`},metadata:tl,run:(e,t)=>e.apps.list(t),cliSchema:al,cliReader:cl,daemonWriter:dl,cliOutputFormatter:Z.apps}),hl=H({name:`open`,text:{summary:`Open an app, deep link or URL, save replays`,cliDetail:`Use --platform to bind URL/deep-link opens to the target platform. For iOS simulator initial stdout/stderr, put --launch-console <path> on this open command, for example agent-device open "Agent Device Tester" --platform ios --launch-console artifacts/launch-console.log. Expo Go/dev-client shells accept host + URL, for example agent-device open "Expo Go" exp://127.0.0.1:8081 --platform ios. macOS also supports --surface app|frontmost-app|desktop|menubar. --metro-host/--metro-port/--bundle-url/--launch-url set this session's Metro/debug runtime hints as part of open itself (applied to the app's dev-server prefs and recorded as the session's dev-server binding), so a fresh session has them before its first reload instead of needing a throwaway reload-first call just to seed hints; a later plain metro reload in the same session reuses whichever of these were set. A fresh open without these flags clears any leftover binding from a previous same-name session; close also clears it. On an iOS simulator, when --launch-url raises an "Open in <App>?" prompt that keeps open from seeing the app, open accepts it if the URL scheme belongs to the session app (data.launchConfirmation: "accepted"); if the scheme belongs to another installed app, the prompt stays on screen and open fails with details.reason launch_confirmation_foreign_app; if no single installed app owns the scheme, the prompt stays on screen and open returns as usual. The prompt is read whenever open cannot see the app afterwards, including on a slow host where the app's target discovery fails on its own deadline. When an accept leaves the app provably not running, open hands the same URL over once more and reads again; still not running, it fails with details.reason launch_confirmation_unanswered. A simulator openurl that CoreSimulator never answers fails within 20s with details.reason ios-simulator-openurl-timeout. Only the English prompt title is recognized, so on a Simulator set to another language the prompt stays on screen. A prompt that appears after open has already seen the app is not answered; use alert accept. A device another session is holding refuses at once; add --wait <ms> to block up to that budget for it and only then fail with DEVICE_IN_USE naming the owning session.`,mcpDetail:`Metro and debug runtime hints given here are recorded as the session's dev-server binding, so a later reload reuses them; a fresh open without them clears any binding left by a previous same-name session.`},metadata:nl,run:(e,t)=>e.apps.open(il(t)),cliSchema:ol,cliReader:ll,daemonWriter:fl,cliOutputFormatter:Z.open}),gl=H({name:`close`,text:{summary:`Close an app or end the session`},metadata:rl,run:(e,t)=>t.app?e.apps.close(t):e.sessions.close(_l(t)),cliSchema:sl,cliReader:ul,daemonWriter:pl,cliOutputFormatter:Z.close});function _l(e){let{app:t,...n}=e;return n}function vl(e){return e.app?e.url?[e.app,e.url]:[e.app]:[]}const yl={platform:D(ve),target:D(be),device:k(`Human-readable device name.`),id:k(`Stable device id.`),kind:D(xe)},bl=[`platform`,`target`,`device`,`id`,`kind`],xl=E({success:j(),exitCode:A(),stdout:k(),stderr:k(),error:M(`Normalized error detail when shutdown failed.`)},[`success`,`exitCode`,`stdout`,`stderr`]),Sl={boot:E({...yl,booted:{type:`boolean`,const:!0}},[...bl,`booted`]),shutdown:E({...yl,shutdown:xl},[...bl,`shutdown`])},Cl=V(`devices`,`List available devices and simulators that can be selected for automation. Use platform, device, udid, or serial inputs on later commands to target one result.`,{}),wl=V(`capabilities`,`List the commands supported by the selected device or active session. Use device-selection inputs when checking support before a session is open.`,{}),Tl=V(`boot`,`Boot or prepare the selected device or simulator so later commands can target it. The device is chosen through the device-selection inputs, not by naming it here.`,{headless:L(`Boot without showing simulator UI when supported.`),timeoutMs:I(`Startup budget in milliseconds. Bounds the boot wait, so a never-booted Simulator can finish its first-boot migration or a cold emulator its boot; omit for the default startup behavior.`,{min:1})}),El=V(`shutdown`,`Shutdown a selected simulator or emulator.`,{}),Dl={allowedFlags:[`headless`,`timeoutMs`]},Ol={},kl={},Al={},jl=(e,t)=>y(t),Ml=(e,t)=>({...y(t),headless:t.headless,timeoutMs:t.timeoutMs}),Nl=_(u.devices),Pl=_(u.capabilities),Fl=_(u.boot),Il=_(u.shutdown),Ll=[H({name:`devices`,text:{summary:`List available devices and simulators`},metadata:Cl,run:(e,t)=>e.devices.list(t),cliSchema:Ol,cliReader:jl,daemonWriter:Nl,cliOutputFormatter:Z.devices}),H({name:`capabilities`,text:{summary:`List supported commands for the selected device`,cliDetail:`Select an explicit target with --platform/--device/--udid/--serial.`},metadata:wl,run:(e,t)=>e.devices.capabilities(t),cliSchema:kl,cliReader:jl,daemonWriter:Pl,cliOutputFormatter:Z.capabilities}),H({name:`boot`,text:{summary:`Boot target device/simulator`},metadata:Tl,run:(e,t)=>e.devices.boot(t),cliSchema:Dl,cliReader:Ml,daemonWriter:Fl,cliOutputFormatter:Z.boot}),H({name:`shutdown`,text:{summary:`Shutdown target simulator/emulator`},metadata:El,run:(e,t)=>e.devices.shutdown(t),cliSchema:Al,cliReader:jl,daemonWriter:Il,cliOutputFormatter:Z.shutdown})],Rl={doctor:E({status:D([`pass`,`warn`,`fail`,`info`]),summary:k(),kind:D([`auto`,`react-native`,`expo`,`repack`]),platform:k(),target:D(be),targetApp:k(),metro:E({host:k(),port:A()},[`host`,`port`]),checks:{type:`array`,items:E({id:k(),status:D([`pass`,`warn`,`fail`,`info`]),summary:k(),hint:k(),command:k(),evidence:M()},[`id`,`status`,`summary`])}},[`status`,`summary`,`kind`,`checks`])},zl=H({name:`doctor`,text:{summary:`Diagnose device, app, dev-server, and RN/Expo readiness`,cliDetail:`Metro reachability is inferred from cwd/runtime. On iOS simulators it also warms the XCTest runner build cache in the background when missing. Pass --app to verify a target app on the one matching booted device without opening a session. Use --remote to check remote connection setup without probing local devices. Default output is compact; use --json for full checks and evidence.`,mcpDetail:`On iOS simulators it also warms the XCTest runner build cache in the background when missing, so run it before the first Apple snapshot or interaction of a session.`},metadata:V(`doctor`,`Diagnose device, app, development-server, and React Native or Expo readiness issues. Returns compact evidence for local inventory, sessions, optional app discovery, toolchains, and server reachability.`,{targetApp:P(`Installed app package/bundle id or app name to verify without opening a session.`),remote:L(`Check remote connection setup instead of local device inventory.`)}),run:(e,t)=>e.command.doctor(t),cliSchema:{usageOverride:`doctor [--platform ios|android|harmonyos|vega|macos|linux|web|apple]`,allowedFlags:[`targetApp`,`remote`]},cliReader:(e,t)=>({...y(t),targetApp:t.targetApp,remote:t.remote}),daemonWriter:_(u.doctor),cliOutputFormatter:Z.doctor});function Bl(e,t=`--github-actions-artifact`){let r=e.indexOf(`:`);if(r<=0||r===e.length-1)throw new n(`INVALID_ARGS`,`${t} must use owner/repo:artifact, for example thymikee/RNCLI83:6635342232`);let{owner:i,repo:a}=Ul(e.slice(0,r),t);return Hl(i,a,e.slice(r+1),`${t} artifact`)}function Vl(e,t){if(!e||typeof e!=`object`||Array.isArray(e))throw new n(`INVALID_ARGS`,`${t} installSource must be an object.`);let r=e;if(Gl(r.type,`${t} installSource.type`)!==`github-actions-artifact`)throw new n(`INVALID_ARGS`,`${t} installSource.type must be "github-actions-artifact".`);let{owner:i,repo:a}=Ul(Gl(r.repo,`${t} installSource.repo`),`${t} installSource.repo`);return Hl(i,a,r.artifact,`${t} installSource.artifact`)}function Hl(e,t,n,r){return typeof n==`number`||ql(n)?{kind:`github-actions-artifact`,owner:e,repo:t,artifactId:Kl(n,r)}:{kind:`github-actions-artifact`,owner:e,repo:t,artifactName:Gl(n,r)}}function Ul(e,t){let r=e.indexOf(`/`);if(r<=0||r===e.length-1||e.indexOf(`/`,r+1)!==-1)throw new n(`INVALID_ARGS`,`${t} must use owner/repo.`);let i={owner:e.slice(0,r).trim(),repo:e.slice(r+1).trim()};if(!i.owner||!i.repo)throw new n(`INVALID_ARGS`,`${t} must use owner/repo.`);return i}function Wl(e){return typeof e==`string`&&e.trim().length>0?e.trim():void 0}function Gl(e,t){let r=Wl(e);if(!r)throw new n(`INVALID_ARGS`,`${t} must be a non-empty string.`);return r}function Kl(e,t){let r=typeof e==`number`?e:typeof e==`string`?Number(e):NaN;if(!Number.isInteger(r))throw new n(`INVALID_ARGS`,`${t} must be an integer.`);return r}function ql(e){return typeof e==`string`&&/^\d+$/.test(e.trim())}const Jl=V(`install`,`Install an app binary from a local path. Provide an app identifier with the path when the target needs explicit app selection; use reinstall to replace an already installed app.`,{app:P(`Optional app identifier hint.`),appPath:N(P(`Path to app binary.`))}),Yl=V(`reinstall`,`Replace an installed app with a binary from a local path. Use this when preserving the same app identity while installing a new build on the selected device.`,{app:N(P()),appPath:N(P(`Path to app binary.`))}),Xl=V(`install-from-source`,`Install app builds from URLs, remote source specs, or CI artifacts resolved by a remote daemon.`,{source:N(z(M(`Install source object.`))),retainPaths:L(),retentionMs:I()}),Zl={usageOverride:`install <path> | install <app> <path>`,listUsageOverride:`install <path>`,positionalArgs:[`appOrPath`,`path?`]},Ql={positionalArgs:[`app`,`path`]},$l={usageOverride:`install-from-source <url> | install-from-source --github-actions-artifact <owner/repo:artifact>`,usageFlags:[],listUsageOverride:`install-from-source`,positionalArgs:[`url?`],allowedFlags:[`header`,`githubActionsArtifact`,`installSource`,`retainPaths`,`retentionMs`]},eu=(e,t)=>ou(e,t),tu=(e,t)=>su(e,t),nu=(e,t)=>({...y(t),source:uu(e,t),retainPaths:t.retainPaths,retentionMs:t.retentionMs}),ru=_(u.install,e=>cu(e.app,e.appPath)),iu=_(u.reinstall,e=>lu(e.app,e.appPath)),au=[H({name:`install`,text:{summary:`Install an app binary from a path`},metadata:Jl,run:(e,t)=>e.apps.install(t),cliSchema:Zl,cliReader:eu,daemonWriter:ru,cliOutputFormatter:Z.install}),H({name:`reinstall`,text:{summary:`Replace an installed app with a new build`},metadata:Yl,run:(e,t)=>e.apps.reinstall(t),cliSchema:Ql,cliReader:tu,daemonWriter:iu,cliOutputFormatter:Z.reinstall}),H({name:`install-from-source`,text:{summary:`Install app builds from URLs or CI artifacts`},metadata:Xl,run:(e,t)=>e.apps.installFromSource(t),cliSchema:$l,cliReader:nu,daemonWriter:e=>v(st.installSource,[],{...e,installSource:e.source,retainMaterializedPaths:e.retainPaths,materializedPathRetentionMs:e.retentionMs}),cliOutputFormatter:Z[`install-from-source`]})];function ou(e,t){let[n,r]=e,i=r!==void 0;return{...y(t),...i?{app:w(n,`install requires app`)}:{},appPath:w(i?r:n,`install requires path`)}}function su(e,t){return{...y(t),app:w(e[0],`reinstall requires app`),appPath:w(e[1],`reinstall requires path`)}}function cu(e,t){let n=T(t,`missing app path`);return typeof e==`string`&&e.length>0?[e,n]:[n]}function lu(e,t){return[T(e,`missing first positional`),T(t,`missing second positional`)]}function uu(e,t){let r=e[0]?.trim();if(e.length>1)throw new n(`INVALID_ARGS`,`install-from-source accepts either one <url> positional or --github-actions-artifact`);let i=t.githubActionsArtifact?Bl(t.githubActionsArtifact):void 0,a=t.installSource;if(+!!r+ +!!i+ +!!a!=1)throw new n(`INVALID_ARGS`,`install-from-source requires exactly one source: <url>, --github-actions-artifact, or config installSource`);if(!r&&t.header&&t.header.length>0)throw new n(`INVALID_ARGS`,`install-from-source --header is only supported for URL sources`);return i||a||{kind:`url`,url:r,headers:du(t.header)}}function du(e){if(!e||e.length===0)return;let t={};for(let r of e){let e=r.indexOf(`:`);if(e<=0)throw new n(`INVALID_ARGS`,`Invalid --header value "${r}". Expected "name:value".`);let i=r.slice(0,e).trim(),a=r.slice(e+1).trim();if(!i)throw new n(`INVALID_ARGS`,`Invalid --header value "${r}". Header name cannot be empty.`);t[i]=a}return t}const fu=[`ios-runner`],pu={prepare:E({action:O(`ios-runner`),platform:D(ve),deviceId:k(),deviceName:k(),kind:D(xe),durationMs:A(),runner:E({},[]),cache:D([`exact`,`miss`,`external`]),artifact:D([`valid`,`rebuilt`]),buildMs:A(),connectMs:A(),healthCheckMs:A(),xctestrunPath:k(),recoveryReason:k(),failureReason:k(),timing:E({totalMs:A(),additiveParts:E({buildMs:A(),connectAfterBuildMs:A(),healthCheckMs:A()},[`connectAfterBuildMs`,`healthCheckMs`]),containment:E({connectMs:{type:`array`,items:O(`buildMs`)},healthCheckMs:{type:`array`,items:k()}},[`healthCheckMs`]),note:k()},[`totalMs`,`additiveParts`,`containment`,`note`]),message:k()},[`action`,`platform`,`deviceId`,`deviceName`,`kind`,`durationMs`,`runner`,`connectMs`,`healthCheckMs`,`timing`,`message`])},mu=H({name:`prepare`,text:{summary:`Pre-warm platform helpers before automation`},metadata:V(`prepare`,`Prepare platform helper infrastructure. ios-runner builds/reuses, starts, and health-checks the XCTest runner so later Apple snapshots and interactions do not pay first-use startup cost. In JSON output, top-level buildMs/connectMs/healthCheckMs are diagnostic fields and may overlap; use timing.additiveParts for additive wall-clock phase totals. In CI, run it after boot/install and before replay/test; if replay/test starts a separate daemon, stop the prepare daemon before replay/test so it does not keep the prepared runner lease. It is not a recovery step for "runner already owned by another agent-device daemon"; stop the owning daemon on the Mac with simulator access instead. Runner build/start output is written to the session runner.log; daemon.log is for daemon lifecycle/startup issues.`,{action:N(R(fu)),timeoutMs:I(`Maximum wall-clock time for the prepare command.`)}),run:(e,t)=>e.command.prepare(t),cliSchema:{usageOverride:`prepare ios-runner --platform ios|macos`,listUsageOverride:`prepare`,positionalArgs:[`ios-runner`],allowedFlags:[`timeoutMs`]},cliReader:(e,t)=>({...y(t),action:w(e[0],`prepare requires subcommand`),timeoutMs:t.timeoutMs}),daemonWriter:_(u.prepare,e=>[T(e.action,`prepare requires subcommand`)]),cliOutputFormatter:Z.prepare}),hu={push:{type:`object`,oneOf:[E({platform:O(`ios`),bundleId:k(),message:k()},[`platform`,`bundleId`,`message`]),E({platform:O(`android`),package:k(),action:k(),extrasCount:A(),message:k()},[`platform`,`package`,`action`,`extrasCount`,`message`])]},"trigger-app-event":E({event:k(),eventUrl:k(),transport:O(`deep-link`),message:k()},[`event`,`eventUrl`,`transport`,`message`])},gu=V(`push`,`Deliver push notification payloads to an installed app.`,{app:N(P()),payload:N(z({oneOf:[k(),M()]}))}),_u=V(`trigger-app-event`,`Ask the app to handle an app-defined automation or test event, with an optional structured payload. Call this only for event names and payload shapes the app documents.`,{event:N(P(`Name of an app-defined automation or test event the app documents.`)),payload:Du(`Structured payload passed to the event, in the shape the app documents for it.`)}),vu={listUsageOverride:`push`,positionalArgs:[`bundleOrPackage`,`payloadOrJson`]},yu={usageOverride:`trigger-app-event <event> [payloadJson]`,listUsageOverride:`trigger-app-event`,positionalArgs:[`event`,`payloadJson?`]},bu=(e,t)=>({...y(t),app:w(e[0],`push requires bundleOrPackage`),payload:w(e[1],`push requires payloadOrJson`)}),xu=(e,t)=>({...y(t),event:w(e[0],`trigger-app-event requires event`),payload:e[1]?cr(e[1],`trigger-app-event payload`):void 0}),Su=_(u.push,e=>Tu(e)),Cu=_(u.triggerAppEvent,e=>Eu(e)),wu=[H({name:`push`,text:{summary:`Deliver a push notification payload`},metadata:gu,run:(e,t)=>e.apps.push(t),cliSchema:vu,cliReader:bu,daemonWriter:Su}),H({name:`trigger-app-event`,text:{summary:`Invoke an app-defined automation event`},metadata:_u,run:(e,t)=>e.apps.triggerEvent(t),cliSchema:yu,cliReader:xu,daemonWriter:Cu})];function Tu(e){return[e.app,typeof e.payload==`string`?e.payload:JSON.stringify(e.payload)]}function Eu(e){return[e.event,...e.payload?[JSON.stringify(e.payload)]:[]]}function Du(e){return Ar(e)}const Ou=H({name:`session`,text:{summary:`List sessions, show the state dir, or publish a script`},metadata:V(`session`,`List active sessions, print the effective daemon state directory, or publish an armed open-to-destination script without closing its session`,{action:R([`list`,`state-dir`,`save-script`],`list shows active sessions; state-dir prints the daemon state directory; save-script publishes an armed recording without teardown.`),path:P(`Optional .ad output path for save-script.`),force:L(`Atomically replace an existing save-script target.`)}),run:async(e,{action:t,path:n,force:r,...i})=>{let a=t??`list`;return Au(a,n,r),a===`state-dir`?{stateDir:await e.sessions.stateDir(i)}:a===`save-script`?await e.sessions.saveScript({...i,path:n,force:r}):{sessions:await e.sessions.list(i)}},cliSchema:{usageOverride:`session list | session state-dir | session save-script [path] [--force]`,usageFlags:[],listUsageOverride:`session`,positionalArgs:[`list|state-dir|save-script?`,`path?`],allowedFlags:[`force`]},cliReader:(e,t)=>({...y(t),action:ku(e[0]),path:e[1],force:t.force}),cliOutputFormatter:Z.session});function ku(e){let t=e??`list`;if(t===`list`||t===`state-dir`||t===`save-script`)return t;throw new n(`INVALID_ARGS`,`session only supports list, state-dir, or save-script`)}function Au(e,t,r){if(e!==`save-script`&&(t!==void 0||r===!0))throw new n(`INVALID_ARGS`,`session ${e} does not accept a path or --force; use session save-script [path] [--force] to publish a recording.`)}const ju={viewport:E({width:A(),height:A(),message:k()},[`width`,`height`,`message`])},Mu=H({name:`viewport`,text:{summary:`Resize the active web viewport for the current session`},metadata:V(`viewport`,`Resize the active web viewport before taking snapshots or screenshots. Useful for fixed-layout or 100vh apps where changing the viewport reveals different content.`,{width:N(I(`Viewport width in CSS pixels.`,{min:1})),height:N(I(`Viewport height in CSS pixels.`,{min:1}))}),run:(e,t)=>e.command.viewport(t),cliSchema:{positionalArgs:[`width`,`height`]},cliReader:(e,t)=>({...y(t),..._e(e)}),daemonWriter:_(u.viewport,e=>{let{width:t,height:n}=e;return[String(t),String(n)]}),cliOutputFormatter:Z.viewport}),Nu=U({name:`management`,commands:[...Ll,Qc,zl,mu,ml,Ou,hl,gl,Mu,...au,...wu]});function Pu(e){return{data:e.result,text:e.action===`reload`?`Reloaded React Native apps via ${e.result.reloadUrl}`:JSON.stringify(e.result,null,2)}}const Fu={metro:({input:e,result:t})=>Pu({result:t,action:e.action})},Iu=`metro`,Lu=U({name:`metro`,commands:[H({name:Iu,text:{summary:`Prepare the dev server or reload apps`,cliDetail:`reload with no --metro-host/--metro-port/--bundle-url resolves against the dev server this session last bound via metro prepare or open's metro hint flags (falling back to localhost:8081 only when the session never bound one), so it never silently reloads a different project's server on the default port; pass an explicit flag to override the session hint for one call. The reload URL keeps the bound bundle URL mount prefix instead of collapsing to the host root, and when the server has no HTTP /reload route (Expo) the reload is broadcast over its /message websocket instead of trusting the app-page fallback. The binding is cleared when the session closes, and a fresh open without hint flags also clears any leftover binding from a previous same-name session. --kind expo (detected or forced) requests the virtual-entry bundle URL (.expo/.virtual-metro-entry.bundle) instead of index.bundle, since index.bundle 404s/500s against Expo dev servers in monorepos. Dependency install auto-detects the package manager from the nearest yarn.lock/pnpm-lock.yaml/bun.lock/bun.lockb/package-lock.json walking up from --project-root (bounded at the repo root), so Yarn/pnpm workspace monorepos with the lockfile at the repo root do not wrongly fall back to npm install (which fails on workspace: dependency specifiers); if install still fails, pass --no-install-deps when dependencies are already installed (for example via a monorepo root install).`,mcpDetail:`The binding is cleared when the session closes, and a fresh open without runtime hints also clears any leftover binding from a previous same-name session.`},metadata:V(Iu,`Prepare a React Native development server or ask connected apps to reload, using the development server this session is bound to. Provide explicit runtime inputs only to override that binding for one call.`,{action:N(R([`prepare`,`reload`])),projectRoot:P(),kind:z(k()),publicBaseUrl:P(),proxyBaseUrl:Tr(P(),{operatorPath:`Configure the remote proxy in the remote-config profile or operator config for the process serving these tools.`}),bearerToken:Tr(P(),{envFlagKeys:[`metroBearerToken`,`daemonAuthToken`]}),bridgeScope:z({type:`object`,additionalProperties:!0}),launchUrl:P(),port:I(),listenHost:P(),statusHost:P(),startupTimeoutMs:I(),probeTimeoutMs:I(),reuseExisting:L(),installDependenciesIfNeeded:L(),runtimeFilePath:P(),logPath:P(),metroHost:P(),metroPort:I(),bundleUrl:P(),timeoutMs:I()}),run:async(e,t)=>t.action===`prepare`?await e.metro.prepare(Ru(t)):await e.metro.reload(Bu(t)),cliSchema:{usageOverride:`metro prepare (--public-base-url <url> | --proxy-base-url <url>) [--project-root <path>] [--port <port>] [--kind auto|react-native|expo|repack]
26
26
  agent-device metro reload [--metro-host <host>] [--metro-port <port>] [--bundle-url <url>]`,usageFlags:[],listUsageOverride:`metro`,positionalArgs:[`prepare|reload`],allowedFlags:[...Ni,...Mi]},cliReader:(e,t)=>{let r=(e[0]??``).toLowerCase();if(r!==`prepare`&&r!==`reload`)throw new n(`INVALID_ARGS`,`metro requires a subcommand: prepare or reload`);if(r===`reload`)return{action:r,metroHost:t.metroHost,metroPort:t.metroPort,bundleUrl:t.bundleUrl,timeoutMs:t.metroProbeTimeoutMs};if(!t.metroPublicBaseUrl&&!t.metroProxyBaseUrl)throw new n(`INVALID_ARGS`,`metro prepare requires --public-base-url <url> or --proxy-base-url <url>.`);return{action:r,projectRoot:t.metroProjectRoot,kind:cn(t.kind??t.metroKind),port:t.metroPreparePort,listenHost:t.metroListenHost,statusHost:t.metroStatusHost,publicBaseUrl:t.metroPublicBaseUrl,proxyBaseUrl:t.metroProxyBaseUrl,bearerToken:t.metroBearerToken,bridgeScope:t.tenant&&t.runId&&t.leaseId?{tenantId:t.tenant,runId:t.runId,leaseId:t.leaseId}:void 0,startupTimeoutMs:t.metroStartupTimeoutMs,probeTimeoutMs:t.metroProbeTimeoutMs,reuseExisting:!t.metroNoReuseExisting&&void 0,installDependenciesIfNeeded:!t.metroNoInstallDeps&&void 0,runtimeFilePath:t.metroRuntimeFile}},cliOutputFormatter:Fu.metro})]});function Ru(e){return{projectRoot:e.projectRoot,kind:e.kind,publicBaseUrl:e.publicBaseUrl,proxyBaseUrl:e.proxyBaseUrl,bearerToken:e.bearerToken,bridgeScope:e.bridgeScope??zu(e),port:e.port,listenHost:e.listenHost,statusHost:e.statusHost,startupTimeoutMs:e.startupTimeoutMs,probeTimeoutMs:e.probeTimeoutMs,reuseExisting:e.reuseExisting,installDependenciesIfNeeded:e.installDependenciesIfNeeded,runtimeFilePath:e.runtimeFilePath}}function zu(e){return e.tenant&&e.runId&&e.leaseId?{tenantId:e.tenant,runId:e.runId,leaseId:e.leaseId}:void 0}function Bu(e){return{metroHost:e.metroHost,metroPort:e.metroPort,bundleUrl:e.bundleUrl,timeoutMs:e.timeoutMs}}function Vu(e,t){return e.includes(t)}function Hu(e,t,r={}){let i=t===void 0?void 0:r.normalize?.(t)??t;if(i!==void 0&&Vu(e,i))return i;let a=typeof r.message==`function`?r.message(t):r.message;throw new n(`INVALID_ARGS`,a??`Invalid value: ${t}. Use ${e.join(`|`)}.`)}const Uu=[`started`,`stopped`,`marked`,`cleared`,`restarted`,`removedRotatedFiles`];function Wu(e){return{data:e,text:e.path,stderr:vd([gd(e,[`active`,`state`,`backend`,`sizeBytes`]),cd(e),e.hint,_d(e.notes)])}}function Gu(e){let t=e.events??[];return{data:e,text:t.length>0?Yu(t):`No session events found.`,stderr:vd([gd(e,[`path`,`cursor`,`nextCursor`,`limit`])])}}function Ku(e){let t=[],n=e.entries??[];if(e.path&&t.push(e.path),n.length===0)t.push(`No recent HTTP(s) entries found.`);else for(let e of n)t.push(...pd(e));return{data:e,text:t.join(`
27
27
  `),stderr:vd([gd(e,[`active`,`state`,`backend`,`include`,`scannedLines`,`matchedLines`]),_d(e.notes)])}}function qu(e){return{data:e,text:[`Audio probe: ${String(e.state??`stopped`)} heard=${String(e.heard===!0)}`,sd(`rmsDbfs`,e.rmsDbfs),sd(`peakDbfs`,e.peakDbfs)].filter(e=>!!e).join(`
28
28
  `),stderr:vd([gd(e,[`active`,`backend`,`source`,`durationMs`,`elapsedMs`,`bucketMs`,`sampleCount`,`sourceCount`,`mediaElementCount`]),_d(e.notes)])}}const Ju={logs:d(Wu),events:d(Gu),network:d(Ku),audio:d(qu)};function Yu(e){let t=e.map(Xu),n=Math.min(Math.max(...t.map(e=>e.label.length),5),32);return t.map(e=>Zu(e,n)).join(`
@@ -49,4 +49,20 @@ import{D as e,M as t,N as n,O as r,T as i,g as a,p as o,t as s,x as c}from"./sdk
49
49
  `)}function Lf(e){let t=e.chunks;return Array.isArray(t)?t.flatMap(e=>{if(!e||typeof e!=`object`)return[];let t=e;return typeof t.index!=`number`||typeof t.path!=`string`?[]:[{index:t.index,path:t.path}]}):[]}const Rf=`record`,zf=`trace`,Bf=[`start`,`stop`],Vf=V(Rf,`Start or stop a screen recording for the active app session or, where supported, the selected device, or build a contact-sheet PNG from a recording already exported. Long Android recordings can return multiple video artifacts; HarmonyOS supports whole-screen recording on physical devices.`,{action:N(R(Bf)),path:P(),fps:I(),maxSize:wr(oe.migration.record),quality:R(Ue),hideTouches:L(),recordingScope:R(We)},{parseableOutput:!0}),Hf=V(zf,`Start or stop trace-log capture and return the resulting artifact when capture ends. Use the same artifact path for the matching start and stop requests when an explicit path is required.`,{action:N(R(Bf)),path:P()}),Uf={start:[`recordingScope`,`fps`,`quality`,`hideTouches`],stop:[],"contact-sheet":[`out`]},Wf={usageOverride:`record start [path] [--scope <app|device|system>] [--fps <n>] [--quality <medium|high>] [--hide-touches] | record stop | record contact-sheet <video.mp4> [--out <sheet.png>]`,usageFlags:[],listUsageOverride:`record start [path] | record stop | record contact-sheet <video>`,positionalArgs:[`start|stop|contact-sheet`,`path?`],allowedFlags:[...new Set(Object.values(Uf).flat())],flagsByAction:Uf},Gf={usageOverride:`trace start <path> | trace stop <path>`,listUsageOverride:`trace start <path> | trace stop <path>`,positionalArgs:[`start|stop`,`path?`]},Kf=(e,t)=>({...y(t),action:$f(e[0],Rf),path:e[1],fps:t.fps,quality:t.quality,hideTouches:t.hideTouches,recordingScope:t.recordingScope}),qf=(e,t)=>({...y(t),action:$f(e[0],zf),path:e[1]}),Jf=_(Rf,e=>Qf(e)),Yf=e=>(ae(`record`,e),Jf(e)),Xf=_(zf,e=>Qf(e)),Zf=U({name:`recording`,commands:[H({name:Rf,text:{summary:`Start or stop screen recording`,cliDetail:`The default --scope app requires an active app session from open <app>; use --scope device/system to explicitly request whole-screen recording where the selected backend supports it. Android record start publishes a durable device manifest, recordings longer than the 180s adb screenrecord limit are returned as multiple MP4 chunks while the daemon stays alive, and daemon-restart recovery uses only manifest-owned chunks. Android screenrecord encodes a frame only when the screen changes, so a clip can end at the last frame the recorder encoded instead of at record stop; durationMs is host wall clock from record start until the export finished, and capturedDurationMs reports the video timeline when it can be measured, with a warning naming how much of the window that video covers. An Android manifest left by an unreachable recording is retired on the next start once its recorders are proven gone; one still owned refuses with non-retriable DEVICE_IN_USE and reason native_recovery_evidence_open naming the session to run record stop for, and a recorder that is still writing an artifact refuses with DEVICE_IN_USE and reason native_recording_artifact_claimed, which clears itself once that recorder ends at the 180s limit. HarmonyOS supports whole-screen recording on physical devices only: use --scope device/system; --fps, --quality, and --hide-touches are unsupported. Use --quality to choose medium or high export quality on supported backends. An iOS simulator host recording lock returns non-retriable DEVICE_IN_USE with reason apple_simulator_recording_busy. Stop the recording in its owning session; if a dead recorder left the host locked, ask the host operator to restart the CoreSimulator stream service. A record stop that cannot produce its export keeps what a retry reads back — on Android the device-side artifact and the native manifest — leaves the recording manifest of that session open, and returns its own error, so retry record stop in that session; only closing the session disposes them. Every stopped recording also reports what became of its recorder and of the file that recorder writes to: recorder is confirmed, or lost when the session holding it died, and nativePathDisposition is retirable while that file still owes a removal or retired once its removal was verified. Both are optional disclosures, not failures; ADR 0024 declares further states (unconfirmed, pending) for the steps that gain those probes, and no stop reports them yet. record contact-sheet <video.mp4> [--out <sheet.png>] is a local report over a file you already exported: it decodes frames out of that MP4 and writes one PNG holding the cells where the screen visibly changed, each labeled with its elapsed time on the clip timeline. It needs no session and no device, rebuilds any past recording, and works for every backend that exports MP4; a WebM recording is refused with reason contact_sheet_container_unsupported. The sample grid is bounded and spread across the whole clip, so it reports coverage rather than a review: a flash that opens and closes entirely between two sample times is not in the sheet, which is why the result also reports how many times it sampled and how many frames came back. Frame decoding is Apple AVFoundation tooling, so the command runs only on macOS hosts and refuses elsewhere with reason contact_sheet_unsupported_host.`},metadata:Vf,run:(e,t)=>e.recording.record(t),cliSchema:Wf,cliReader:Kf,daemonWriter:Yf,cliOutputFormatter:Ff.record}),H({name:zf,text:{summary:`Start or stop trace capture`,cliDetail:`Pass that path as the same positional argument to start and stop.`},metadata:Hf,run:(e,t)=>e.recording.trace(t),cliSchema:Gf,cliReader:qf,daemonWriter:Xf})]});function Qf(e){return[e.action,...S(e.path)]}function $f(e,t){if(e===`start`||e===`stop`)return e;throw new n(`INVALID_ARGS`,`${t} requires start|stop`)}const ep=2097152;async function tp(e){let t=ln(e.inputPath,{cwd:e.cwd}),n=rp(t,e.inputPath);if(at(t,e.replayBackend)!==`maestro`)return np(t,{[t]:n},e.inputPath);let{collectMaestroFlowSources:r}=await import(`./src8.js`).then(e=>e.t);return np(t,r({entryPath:t,entrySource:n,env:e.env,readSource:ip}),e.inputPath)}function np(e,t,n){return ap(t,n),{entry:e,files:t}}function rp(e,t){try{return hn.readFileSync(e,`utf8`)}catch(r){throw new n(`INVALID_ARGS`,`replay script not found on this machine: ${t}`,{path:e,hint:`Replay scripts are read by the client and sent to the daemon, so the path must resolve where you ran the command.`},r instanceof Error?r:void 0)}}function ip(e){try{return hn.readFileSync(e,`utf8`)}catch{return}}function ap(e,t){let r=Object.values(e).reduce((e,t)=>e+Buffer.byteLength(t,`utf8`),0);if(!(r<=2097152))throw new n(`INVALID_ARGS`,`replay script sources for ${t} total ${r} bytes, over the ${ep}-byte limit; split the flow or drop unused runFlow includes.`,{limitBytes:ep,totalBytes:r})}const op=/[*?[\]{}]/;function sp(e){let t=e.replayBackend===`maestro`?new Set([`.yaml`,`.yml`,`.ad`]):new Set([`.ad`]),n=[],r=[];for(let i of e.inputs){let a=cp(i,e.cwd,t);if(a.source===`file`){n.push(...a.paths);continue}r.push(a.source===`directory`?pp(a.paths):dp(a.paths))}return pp([...n,...r.flat()])}function cp(e,t,r){let i=ln(e,{cwd:t});if(hn.existsSync(i)){let t=hn.statSync(i);return t.isDirectory()?{paths:up(i,r),source:`directory`}:t.isFile()?(lp(e,i,r),{paths:[i],source:`file`}):{paths:[],source:`file`}}if(!mp(e))throw new n(`INVALID_ARGS`,`test input not found: ${e}`);return{source:`glob`,paths:hn.globSync(un(e),{cwd:t}).map(e=>p.isAbsolute(e)?e:p.resolve(t,e)).filter(e=>r.has(p.extname(e).toLowerCase())&&hp(e))}}function lp(e,t,r){if(!r.has(p.extname(t).toLowerCase()))throw ot(t)&&!r.has(`.yaml`)?new n(`INVALID_ARGS`,it(`test`,e)):new n(`INVALID_ARGS`,`test does not support this file type: ${e}`)}function up(e,t){let n=[],r=hn.opendirSync(e);try{let i;for(;(i=r.readSync())!==null;){let r=p.join(e,i.name);i.isDirectory()?n.push(...up(r,t)):i.isFile()&&t.has(p.extname(i.name).toLowerCase())&&n.push(r)}}finally{r.closeSync()}return n}function dp(e){return e.map(e=>p.normalize(e)).sort((e,t)=>{let n=fp(e)-fp(t);return n===0?e.localeCompare(t):n})}function fp(e){return+(p.extname(e).toLowerCase()===`.ad`)}function pp(e){return[...new Set(e.map(e=>p.normalize(e)))]}function mp(e){return op.test(e)}function hp(e){try{return hn.statSync(e).isFile()}catch{return!1}}const gp=`replay`,_p=`test`,vp=V(gp,`Run a recorded automation script, including compatible Maestro YAML flows. A script without a terminal close leaves its session active for subsequent automation.`,{path:N(P()),update:L(),backend:P(),maestro:L(),env:jr(),metroHost:P(`Metro/debug host hint inherited by replay-opened sessions.`),metroPort:I(`Metro/debug port hint inherited by replay-opened sessions.`),bundleUrl:P(`Bundle URL hint inherited by replay-opened sessions.`),resumeFrom:I(),resumePlanDigest:P(),keepSession:L(`Leave the session active by suppressing exactly an authored terminal close in native .ad.`),timeoutMs:I(`Maximum wall-clock duration for the replay request.`),saveScript:z({oneOf:[j(),k()]}),force:L()}),yp=V(_p,`Run one or more replay scripts as a serial test suite`,{paths:N(jr()),update:L(),backend:P(),maestro:L(),env:jr(),metroHost:P(`Metro/debug host hint inherited by each test session.`),metroPort:I(`Metro/debug port hint inherited by each test session.`),bundleUrl:P(`Bundle URL hint inherited by each test session.`),failFast:L(),timeoutMs:I(),retries:I(),recordVideo:L(),artifactsDir:P(),shardAll:I(),shardSplit:I()}),bp={replay:E({replayed:A(),healed:A(),session:k(),sessionActive:j(`True iff the session is still active — the script had no terminal close.`),artifactPaths:Cr(),snapshotDiagnostics:M(),message:k()},[`replayed`,`healed`,`session`,`sessionActive`,`artifactPaths`,`message`]),test:E({total:A(),executed:A(),passed:A(),failed:A(),skipped:A(),notRun:A(),durationMs:A(),failures:{type:`array`,items:M()},tests:{type:`array`,items:M()},snapshotDiagnostics:M()},[`total`,`executed`,`passed`,`failed`,`skipped`,`notRun`,`durationMs`,`failures`,`tests`])},xp={usageOverride:`replay <path> | replay export <file.ad> [--out <path>]`,usageFlags:[],positionalArgs:[`path`],allowsExtraPositionals:!0,allowedFlags:[`replayMaestro`,...Ii,...Ni,`replayFrom`,`replayPlanDigest`,`replayKeepSession`,`timeoutMs`,`out`,`saveScript`,`force`],flagDescriptionOverrides:{saveScript:`Arm a repair transaction from this replay (ADR 0012): recording starts at step 1, and the healed script commits when the repair-armed session tears down (close, close --save-script, or idle-reap). Independent of the open/close authoring arm-on-open. Optional custom output path.`}},Sp={usageOverride:`test <path-or-glob>...`,usageFlags:[],listUsageOverride:`test <path-or-glob>...`,positionalArgs:[`pathOrGlob`],allowsExtraPositionals:!0,allowedFlags:[`replayMaestro`,...Ii,...Ni,`failFast`,`timeoutMs`,`retries`,`recordVideo`,`artifactsDir`,`reporter`,`reportJunit`,`shardAll`,`shardSplit`]},Cp=U({name:`replay`,commands:[H({name:gp,text:{summary:`Replay a recorded session or Maestro flow`,cliDetail:`For Maestro YAML compatibility flows, use replay <flow.yaml> --maestro and keep the target binding such as --platform ios on the replay command. A script with no terminal close leaves its session (and daemon) running until you close it or it idle-reaps — no different from a session opened interactively. For native .ad scripts, --keep-session suppresses exactly an authored terminal close so you can continue interactively. replay export <file.ad> converts compatible actions to Maestro YAML locally, including app switches with explicit launchApp.appId targets, deep links (including tel: and mailto:) as openLink, and home as pressKey: Home.`},metadata:vp,run:(e,t)=>e.replay.run(el(t)),cliSchema:xp,cliReader:(e,t)=>({...y(t),path:w(e[0],`replay requires path`),update:t.replayUpdate,backend:t.replayMaestro?`maestro`:void 0,env:t.replayEnv,metroHost:t.metroHost,metroPort:t.metroPort,bundleUrl:t.bundleUrl,resumeFrom:t.replayFrom,resumePlanDigest:t.replayPlanDigest,keepSession:t.replayKeepSession,timeoutMs:t.timeoutMs,saveScript:t.saveScript,force:t.force}),daemonWriter:async e=>{let t=T(e.path,`replay requires path`),n=Ep(e),r=Op(process.env);return v(gp,[t],{...e,replayUpdate:e.update,replayBackend:n,replayEnv:e.env,replayShellEnv:r,replayScriptSource:await tp({inputPath:t,cwd:Tp(e),replayBackend:n,env:wp(e,r)}),replayFrom:e.resumeFrom,replayPlanDigest:e.resumePlanDigest,replayKeepSession:e.keepSession,saveScript:e.saveScript})},cliOutputFormatter:f}),H({name:_p,text:{summary:`Run replay test suites`,cliDetail:`Relative globs are expanded on the caller from its working directory, whose name is treated literally. Quote glob inputs to defer expansion to test. Copied diagnostic artifacts receive numbered filenames when needed to preserve other artifacts, replay sources, timing traces, and attempt manifests. JUnit reports (--reporter junit:<path>) replace characters forbidden by XML 1.0 with U+FFFD and preserve legal Unicode and whitespace. JSON and other reporters retain the original suite values. Custom reporter getExitCode hooks must return an integer from 0 to 255 or undefined; the highest valid code wins and cannot lower a failing suite's exit code.`},metadata:yp,run:(e,t)=>e.replay.test(el(t)),cliSchema:Sp,cliReader:(e,t)=>({...y(t),paths:e,update:t.replayUpdate,backend:t.replayMaestro?`maestro`:void 0,env:t.replayEnv,metroHost:t.metroHost,metroPort:t.metroPort,bundleUrl:t.bundleUrl,failFast:t.failFast,timeoutMs:t.timeoutMs,retries:t.retries,recordVideo:t.recordVideo,artifactsDir:t.artifactsDir,shardAll:t.shardAll,shardSplit:t.shardSplit}),daemonWriter:async e=>{let t=e.paths??[],n=Ep(e),r=Tp(e),i=Op(process.env),a=wp(e,i);return v(_p,t,{...Dp(e),replayUpdate:e.update,replayBackend:n,replayEnv:e.env,replayShellEnv:i,replayScriptSources:await Promise.all(sp({inputs:t,cwd:r,replayBackend:n}).map(async e=>await tp({inputPath:e,cwd:r,replayBackend:n,env:a})))})}})]});function wp(e,t){return{...rt(t),...nt(tt(e.env))}}function Tp(e){return typeof e.cwd==`string`&&e.cwd.length>0?e.cwd:process.cwd()}function Ep(e){return e.backend??(e.maestro===!0?`maestro`:void 0)}function Dp(e){let t={...e};return delete t.reporter,delete t.reportJunit,t}function Op(e){let t={};for(let[n,r]of Object.entries(e))typeof r==`string`&&n.startsWith(`AD_VAR_`)&&(t[n]=r);return t}function kp(e){return{data:e,text:Mp(e)}}function Ap(e){if(e.platform===`android`&&e.action===`status`){let t=[`Keyboard visible: ${e.visible===!0?`yes`:`no`}`,`Input type: ${e.type??e.inputType??`unknown`}`,`Input owner: ${e.inputOwner??`unknown`}`];return e.inputMethodPackage&&t.push(`Input method: ${e.inputMethodPackage}`),e.focusedPackage&&t.push(`Focused package: ${e.focusedPackage}`),e.focusedResourceId&&t.push(`Focused resource: ${e.focusedResourceId}`),t.push(`Next action: ${Pp(e.visible,e.inputOwner)}`),{data:e,text:t.join(`
50
50
  `)}}return At(e)}function jp(e){return e.action===`read`?{data:e,text:e.text}:At(e)}const $=Rs({appstate:d(kp),back:f,home:f,orientation:f,fold:f,"app-switcher":f,"action-button":f,keyboard:d(Ap),clipboard:d(jp),"tv-remote":f});function Mp(e){if(e.platform===`ios`)return Np(e);if(e.platform===`android`){let t=[`Foreground app: ${e.package??`unknown`}`];return e.activity&&t.push(`Activity: ${e.activity}`),t.join(`
51
51
  `)}return null}function Np(e){let t=[`Session app: ${e.appName??e.appBundleId??`unknown`}`];return e.appBundleId&&t.push(`Bundle: ${e.appBundleId}`),e.state&&t.push(`State: ${e.state}`),e.source&&t.push(`Source: ${e.source}`),t.join(`
52
- `)}function Pp(e,t){return t===`ime`?`Focused input appears to be owned by the keyboard/IME; dismiss or change the IME before retrying text entry.`:e===!0?`Keyboard is visible and focused input appears app-owned; fill/type can proceed.`:`Keyboard is hidden; focus an app field before type, or use fill with a concrete target.`}const Fp=`back`,Ip=`orientation`,Lp=`fold`,Rp=`keyboard`,zp=`clipboard`,Bp=`tv-remote`,Vp=[`read`,`write`],Hp=[`status`,`dismiss`,`enter`,`return`],Up={back:E({action:O(`back`),mode:D(ut),message:k()},[`action`,`mode`,`message`]),home:E({action:O(`home`),message:k()},[`action`,`message`]),orientation:E({action:O(`orientation`),orientation:D(on),message:k(),confirmed:j(),warning:k()},[`action`,`orientation`,`message`]),"app-switcher":E({action:O(`app-switcher`),message:k()},[`action`,`message`]),fold:E({action:O(`fold`),pose:D(an),hingeAngleDegrees:A(`Hinge angle CoreDevice read back after the pose settled.`),screen:E({display:k(`CoreDevice name of the panel the device now lights.`),coordinateSpace:O($t),widthPt:A(`Panel width in native panel points (pixels divided by point scale), NOT snapshot coordinates; take a fresh snapshot to place a tap.`),heightPt:A(`Panel height in native panel points (pixels divided by point scale), NOT snapshot coordinates; take a fresh snapshot to place a tap.`)},[`display`,`coordinateSpace`,`widthPt`,`heightPt`]),message:k()},[`action`,`pose`,`hingeAngleDegrees`,`message`]),"action-button":E({action:O(`action-button`),message:k()},[`action`,`message`]),"tv-remote":E({action:O(`tv-remote`),button:D(mn),durationMs:A(),message:k()},[`action`,`button`,`message`]),clipboard:{type:`object`,oneOf:[E({action:O(`read`),text:k()},[`action`,`text`]),E({action:O(`write`),textLength:A(),message:k()},[`action`,`textLength`,`message`])]},appstate:{type:`object`,oneOf:[E({platform:D([`ios`,`macos`]),appName:k(),appBundleId:k(),source:D([`session`,`runner`],`runner when a live runner read the session app state; session when the record alone answered.`),state:D(o,`The session app XCUIApplication state as a live runner reads it; absent with source session.`),surface:D(ft),device_udid:k(`iOS only — the session device UDID.`),ios_simulator_device_set:{type:[`string`,`null`],description:`iOS only — the simulator set path, or null when unknown.`}},[`platform`,`appName`,`source`,`surface`]),E({platform:O(`android`),package:k(),activity:k()},[`platform`,`package`,`activity`])]},keyboard:E({platform:D([`android`,`ios`]),action:D([`status`,`dismiss`,`enter`]),visible:j(),wasVisible:j(),dismissed:j(),attempts:A(),inputType:k(),type:D([`text`,`number`,`email`,`phone`,`password`,`datetime`,`unknown`]),inputMethodPackage:k(),focusedPackage:k(),focusedResourceId:k(),inputOwner:D([`app`,`ime`,`unknown`]),message:k()},[`platform`,`action`])},Wp=V(Fp,`Navigate back in the app or through system navigation. Use in-app for the app navigation stack and system when the platform back behavior is required.`,{mode:R(ut),...as(Fp)}),Gp=V(Ip,`Set device orientation on iOS and Android`,{orientation:N(R(on))}),Kp={pose:R(an,`Instant preset; mutually exclusive with keyframes.`),keyframes:z({type:`array`,minItems:2,maxItems:64,description:`Piecewise-linear hinge motion. Start at 0ms; strictly increasing timestamps, up to 60000ms. Repeated angles create holds. Mutually exclusive with pose.`,items:{type:`object`,required:[`atMs`,`angle`],additionalProperties:!1,properties:{atMs:{type:`integer`,minimum:0,maximum:rn},angle:{type:`number`,minimum:0,maximum:180}}}})},qp=V(Lp,`Fold or unfold a foldable iPhone simulator (iPhone Duo) into the closed, half-open, or open pose, or follow timestamped angle keyframes, by sending a simulator HID hinge event, then read the hinge angle back from CoreDevice to confirm it. A pose change moves the app to a different panel with a different point size, so every ref and coordinate from before it is stale: re-snapshot after this command. Taps, long presses, and scrolling target the app window on its current panel in closed, half-open, and open poses. Simulator-only; requires the iOS simulator SDK; Device Hub and host Accessibility permission are not required. A simulator scoped to a non-default simulator set is refused with UNSUPPORTED_OPERATION and reason unsupported-device-scope; run fold against a simulator in the default set.`,Kp,{readInput:e=>{let{pose:t,keyframes:n,...r}=zr(e,Kp);return{...r,...en({pose:t,keyframes:n})}}}),Jp={...qp,inputSchema:{...qp.inputSchema,oneOf:[{required:[`pose`],not:{required:[`keyframes`]}},{required:[`keyframes`],not:{required:[`pose`]}}]}},Yp=V(Rp,`Inspect Android keyboard visibility/type or press/dismiss the device keyboard. To hide the keyboard, use keyboard dismiss. It taps the keyboard dismiss/hide key when one is exposed, verifies the keyboard closed, and reports UNSUPPORTED_OPERATION when no dismiss key exists — background taps are never attempted.`,{action:R(Hp)}),Xp=V(zp,`Read the current device clipboard text, or replace its contents with the given text. Android runs both through the clipboard service shell command, and a build that implements none (Android 16 does not) refuses with UNSUPPORTED_OPERATION rather than reporting an empty clipboard.`,{action:N(R(Vp)),text:P()},{parseableOutput:!0}),Zp=V(Bp,`Press or long-press a TV remote or D-pad button on Android TV, tvOS, or Vega OS. Choose the button and optional hold duration through the input fields. The aliases ok, center, and enter all map to select.`,{button:N(R(mn)),durationMs:I(`Press duration in milliseconds. tvOS and Vega OS use the exact hold duration; Android TV maps any positive value to an ADB longpress (${pn(`android`)}).`,{min:0})}),Qp={usageOverride:`back [--in-app|--system] [--settle]`,usageFlags:[],allowedFlags:[`backMode`,...os(Fp)]},$p={usageOverride:`orientation <portrait|portrait-upside-down|landscape-left|landscape-right>`,positionalArgs:[`orientation`]},em={usageOverride:`fold [${nn}]`,positionalArgs:[`pose?`],allowedFlags:ki},tm={usageOverride:`keyboard [status|get|dismiss|enter|return]`,positionalArgs:[`action?`]},nm={usageOverride:`clipboard read | clipboard write <text>`,listUsageOverride:`clipboard read | clipboard write <text>`,positionalArgs:[`read|write`,`text?`],allowsExtraPositionals:!0},rm={usageOverride:`tv-remote [press|longpress] ${dn}`,listUsageOverride:`tv-remote press|longpress <button> [--duration-ms <ms>]`,positionalArgs:[`press|longpress?`,`button`],allowedFlags:[`durationMs`]},im=(e,t)=>({...y(t),mode:t.backMode}),am=(e,t)=>({...y(t),orientation:sn(e[0])}),om=(e,t)=>({...y(t),...en({pose:e[0],keyframes:t.keyframes===void 0?void 0:tn(t.keyframes)})}),sm=(e,t)=>({...y(t),...Dm(e)}),cm=(e,t)=>({...y(t),...Om(e)}),lm=(e,t)=>({...y(t),...km(e,t.durationMs)}),um=e=>v(Fp,[],{...e,backMode:Tm(e.mode)}),dm=_(Ip,e=>[T(e.orientation,`orientation requires orientation`)]),fm=e=>{let t=en(e);return v(Lp,t.pose?[t.pose]:[],{...e,keyframes:t.keyframes?JSON.stringify(t.keyframes):void 0})},pm=_(Rp,e=>S(e.action)),mm=_(zp,e=>Em(e)),hm=_(Bp,e=>[T(e.button,`tv-remote requires button`)]),gm=ei({name:`appstate`,description:`Show foreground app/activity (Android; iOS answers per command)`,text:{summary:`Show the foreground app and activity`},run:(e,t)=>e.command.appState(t),cliOutputFormatter:$.appstate}),_m=H({name:Fp,text:{summary:`Navigate back in the app or system`},metadata:Wp,run:(e,t)=>e.command.back(t),cliSchema:Qp,cliReader:im,daemonWriter:um,cliOutputFormatter:$.back}),vm=ei({name:`home`,description:`Send the selected device to its home screen. This leaves the app session open but moves the foreground away from the app.`,text:{summary:`Go to the device home screen`},run:(e,t)=>e.command.home(t),cliOutputFormatter:$.home}),ym=H({name:Ip,text:{summary:`Set device orientation`},metadata:Gp,run:(e,t)=>e.command.orientation(t),cliSchema:$p,cliReader:am,daemonWriter:dm,cliOutputFormatter:$.orientation}),bm=H({name:Lp,text:{summary:`Fold or unfold a foldable iPhone simulator`,cliDetail:`iPhone Duo simulators only. Sends a simulator HID hinge event and confirms the hinge angle through CoreDevice; refs and coordinates do not survive a pose change.`},metadata:Jp,run:(e,t)=>e.command.fold(t),cliSchema:em,cliReader:om,daemonWriter:fm,cliOutputFormatter:$.fold}),xm=ei({name:`app-switcher`,description:`Open the device app switcher to inspect or change foreground apps. This changes the visible system UI and may move focus away from the current app.`,text:{summary:`Open the device app switcher`},run:(e,t)=>e.command.appSwitcher(t),cliOutputFormatter:$[`app-switcher`]}),Sm=H({name:Rp,text:{summary:`Inspect, press, or dismiss the device keyboard`},metadata:Yp,run:(e,t)=>e.command.keyboard(t),cliSchema:tm,cliReader:sm,daemonWriter:pm,cliOutputFormatter:$.keyboard}),Cm=H({name:zp,text:{summary:`Read or write device clipboard text`},metadata:Xp,run:(e,t)=>e.command.clipboard(t),cliSchema:nm,cliReader:cm,daemonWriter:mm,cliOutputFormatter:$.clipboard}),wm=U({name:`system`,commands:[gm,_m,vm,ym,bm,xm,ei({name:`action-button`,description:`Press the iPhone or iPad Action Button once. The press is dispatched without activating the session app and nothing is re-observed afterwards, so the app keeps the state the press found. What the system does with the press is not observed by this command: Simulators run no Shortcuts or App Intents, so delivery to an assigned Shortcut is verifiable only on a physical iPhone.`,text:{summary:`Press the iPhone or iPad Action Button`,cliDetail:`iPhone and iPad only. The runner asks the device for the button and reports unsupported when that model has none.`},run:(e,t)=>e.command.actionButton(t),cliOutputFormatter:$[`action-button`]}),Sm,Cm,H({name:Bp,text:{summary:`Press a TV remote/D-pad button`,cliDetail:`longpress holds for 500ms by default; --duration-ms overrides the preset.`},metadata:Zp,run:(e,t)=>e.command.tvRemote(t),cliSchema:rm,cliReader:lm,daemonWriter:hm,cliOutputFormatter:$[`tv-remote`]})]});function Tm(e){return e===`in-app`||e===`system`?e:void 0}function Em(e){return e.action===`read`?[`read`]:[`write`,e.text]}function Dm(e){if(e.length>1)throw new n(`INVALID_ARGS`,`keyboard accepts at most one action argument.`);return h({action:Am(e[0])})}function Om(e){let t=e[0]?.toLowerCase();if(t!==`read`&&t!==`write`)throw new n(`INVALID_ARGS`,`clipboard requires a subcommand: read or write.`);if(t===`read`){if(e.length!==1)throw new n(`INVALID_ARGS`,`clipboard read does not accept additional arguments.`);return{action:t}}if(e.length<2)throw new n(`INVALID_ARGS`,`clipboard write requires text.`);return{action:t,text:e.slice(1).join(` `)}}function km(e,t){let r=e[0]?.toLowerCase(),i=r===`press`||r===`longpress`?e.slice(1):e;if(i.length!==1)throw new n(`INVALID_ARGS`,`tv-remote requires exactly one button: ${mn.join(`, `)}.`);let a=t??(r===`longpress`?500:void 0);return h({button:fn(i[0]),durationMs:a})}function Am(e){let t=e?.toLowerCase();if(t===`get`)return`status`;if(t===void 0||t===`status`||t===`dismiss`||t===`enter`||t===`return`)return t;throw new n(`INVALID_ARGS`,`keyboard action must be status, get, dismiss, enter, or return.`)}const jm=[vc,Nu,Xo,wm,Mf,Cp,Ad,xf,ts,Zf,Lu,Si];function Mm(){return jm.flatMap(e=>[...e.metadata])}function Nm(){return jm.flatMap(e=>[...e.definitions])}function Pm(){return Rm(`cliSchemas`)}function Fm(){return Rm(`cliReaders`)}function Im(){return Rm(`daemonWriters`)}function Lm(){return Rm(`cliOutputFormatters`)}function Rm(e){let t={};for(let n of jm){let r=n[e];if(r)for(let[n,i]of Object.entries(r)){if(Object.hasOwn(t,n))throw Error(`Duplicate ${String(e)} command family entry: ${n}`);t[n]=i}}return t}export{A,bn as B,zi as C,O as D,j as E,zn as F,Fn as I,Sn as L,k as M,pr as N,D as O,mr as P,Cn as R,Ri as S,pi as T,ss as _,Nm as a,Vi as b,bp as c,pu as d,Vl as f,cc as g,_c as h,Im as i,E as j,M as k,ju as l,Sl as m,Fm as n,Mm as o,Rl as p,Pm as r,Up as s,Lm as t,hu as u,Bo as v,Mi as w,Li as x,xo as y,yn as z};
52
+ `)}function Pp(e,t){return t===`ime`?`Focused input appears to be owned by the keyboard/IME; dismiss or change the IME before retrying text entry.`:e===!0?`Keyboard is visible and focused input appears app-owned; fill/type can proceed.`:`Keyboard is hidden; focus an app field before type, or use fill with a concrete target.`}const Fp=`back`,Ip=`orientation`,Lp=`fold`,Rp=`keyboard`,zp=`clipboard`,Bp=`tv-remote`,Vp=[`read`,`write`],Hp=[`status`,`dismiss`,`enter`,`return`],Up={back:E({action:O(`back`),mode:D(ut),message:k()},[`action`,`mode`,`message`]),home:E({action:O(`home`),message:k()},[`action`,`message`]),orientation:E({action:O(`orientation`),orientation:D(sn),message:k(),confirmed:j(),warning:k()},[`action`,`orientation`,`message`]),"app-switcher":E({action:O(`app-switcher`),message:k()},[`action`,`message`]),fold:E({action:O(`fold`),pose:D(on),hingeAngleDegrees:A(`Hinge angle CoreDevice read back after the pose settled.`),screen:E({display:k(`CoreDevice name of the panel the device now lights.`),coordinateSpace:O($t),widthPt:A(`Panel width in native panel points (pixels divided by point scale), NOT snapshot coordinates; take a fresh snapshot to place a tap.`),heightPt:A(`Panel height in native panel points (pixels divided by point scale), NOT snapshot coordinates; take a fresh snapshot to place a tap.`)},[`display`,`coordinateSpace`,`widthPt`,`heightPt`]),message:k()},[`action`,`pose`,`hingeAngleDegrees`,`message`]),"action-button":E({action:O(`action-button`),message:k()},[`action`,`message`]),"tv-remote":E({action:O(`tv-remote`),button:D(mn),durationMs:A(),message:k()},[`action`,`button`,`message`]),clipboard:{type:`object`,oneOf:[E({action:O(`read`),text:k()},[`action`,`text`]),E({action:O(`write`),textLength:A(),message:k()},[`action`,`textLength`,`message`])]},appstate:{type:`object`,oneOf:[E({platform:D([`ios`,`macos`]),appName:k(),appBundleId:k(),source:D([`session`,`runner`],`runner when a live runner read the session app state; session when the record alone answered.`),state:D(o,`The session app XCUIApplication state as a live runner reads it; absent with source session.`),surface:D(ft),device_udid:k(`iOS only — the session device UDID.`),ios_simulator_device_set:{type:[`string`,`null`],description:`iOS only — the simulator set path, or null when unknown.`}},[`platform`,`appName`,`source`,`surface`]),E({platform:O(`android`),package:k(),activity:k()},[`platform`,`package`,`activity`])]},keyboard:E({platform:D([`android`,`ios`]),action:D([`status`,`dismiss`,`enter`]),visible:j(),wasVisible:j(),dismissed:j(),attempts:A(),inputType:k(),type:D([`text`,`number`,`email`,`phone`,`password`,`datetime`,`unknown`]),inputMethodPackage:k(),focusedPackage:k(),focusedResourceId:k(),inputOwner:D([`app`,`ime`,`unknown`]),message:k()},[`platform`,`action`])},Wp={hingeEvent:`simulator HID hinge event`,permissions:`Device Hub and host Accessibility permission are not required.`,unsupportedScope:`UNSUPPORTED_OPERATION and reason unsupported-device-scope`},Gp=`Fold or unfold a foldable iPhone simulator (iPhone Duo) into the closed, half-open, or open pose, or follow timestamped angle keyframes, by sending a ${Wp.hingeEvent}, then read the hinge angle back from CoreDevice to confirm it. A pose change moves the app to a different panel with a different point size, so every ref and coordinate from before it is stale: re-snapshot after this command. Taps, long presses, and scrolling target the app window on its current panel in closed, half-open, and open poses. Simulator-only; requires the iOS simulator SDK; ${Wp.permissions} A simulator scoped to a non-default simulator set is refused with ${Wp.unsupportedScope}; run fold against a simulator in the default set.`,Kp={summary:`Foldable Apple devices: panels, pose, and which screen you are on`,body:`agent-device help foldable
53
+
54
+ A foldable Apple device (iPhone Duo) carries two integrated panels, which Apple calls the outer display and the inner display. Only one is lit at a time, and which one is lit is the device pose.
55
+
56
+ Screens are handled for you:
57
+ Each iOS simulator capture resolves the CoreDevice display table, captures the lit panel explicitly, and normalizes density with that panel's own point scale. Do not add a screen flag to the normal loop; there is none, because the lit panel is always the only capturable one: the dark panel yields an all-black PNG.
58
+ The two panels are different sizes (iPhone Duo: 466x678 points closed on the outer panel, 669x951 open on the inner). A pose change therefore invalidates every ref and coordinate. Re-snapshot after any pose change and never carry coordinates or refs across one.
59
+ Check which panel is lit before trusting a geometry claim: agent-device screenshot reports its point size, and 466x678 versus 669x951 says which panel you captured.
60
+
61
+ Changing the pose:
62
+ agent-device fold closed | half-open | open
63
+ fold sends a private HID hinge event inside the selected simulator and then reads the hinge angle back from CoreDevice until it agrees: closed is 0 degrees, open is 180, and half-open is any angle between them (requested at 130 degrees). An angle in that interval only proves the category, so half-open is reported once two consecutive readings both fall inside it and agree within 0.5 degrees. The response reports the verified pose, the hinge angle, and the panel the device now lights with its native panel point size, marked coordinateSpace "native-panel". That point size is the panel's own geometry, not the next snapshot's viewport, so it cannot place a tap: the active app window can differ (a 669x951 inner panel hosts a 951x669 window). A hinge whose last reading is some other pose fails with COMMAND_FAILED and reason fold-pose-unverified, naming the angle CoreDevice still reports; a hinge seen half-open but never at rest fails with reason fold-pose-unsettled, naming the observed and previous angles. A single-panel simulator fails with UNSUPPORTED_OPERATION. A simulator scoped to a non-default set with --ios-simulator-device-set is refused before any hinge is touched, with ${Wp.unsupportedScope}: the HID send honors the set, but CoreDevice's display inventory and hinge-angle readback resolve a scoped simulator as not found, so the pose could not be verified. Run fold without --ios-simulator-device-set (in the default set).
64
+ Expect a fold to take 10-16 seconds: each hinge read is a five-second devicectl stream, and half-open waits for the hinge to stop moving. Re-snapshot after every fold; refs and coordinates from before it are stale, and the command's message says so.
65
+ Timed motion: fold --keyframes '[{"atMs":0,"angle":0},{"atMs":5000,"angle":180}]'. Use 2–64 frames starting at 0ms, increasing integer timestamps up to 60000ms, and angles from 0 to 180. The last timestamp sets motion duration, excluding setup and verification. Equal angles hold; cancellation stops motion. See the fold examples in the command and Node API documentation for trajectories.
66
+
67
+ Requirements: an iOS simulator session on a foldable device and an Xcode toolchain with the iOS simulator SDK and foldable HID support (verified on Xcode 27.1). ${Wp.permissions} The command runs a small helper through simctl spawn for the session UDID; the helper is built once per Fold.m source hash and Xcode toolchain, cached under ~/.agent-device/fold-helper, and rebuilt only when the source or the toolchain changes. Build or dispatch failures are reported without a UI fallback. The app under test reads the resulting pose as UIHinge.status.
68
+ If a task asserts behavior for more than one pose, fold to each pose and re-snapshot, and report which poses the run covered.`},qp=V(Fp,`Navigate back in the app or through system navigation. Use in-app for the app navigation stack and system when the platform back behavior is required.`,{mode:R(ut),...as(Fp)}),Jp=V(Ip,`Set device orientation on iOS and Android`,{orientation:N(R(sn))}),Yp={pose:R(on,`Instant preset; mutually exclusive with keyframes.`),keyframes:z({type:`array`,minItems:2,maxItems:64,description:`Piecewise-linear hinge motion. Start at 0ms; strictly increasing timestamps, up to 60000ms. Repeated angles create holds. Mutually exclusive with pose.`,items:{type:`object`,required:[`atMs`,`angle`],additionalProperties:!1,properties:{atMs:{type:`integer`,minimum:0,maximum:rn},angle:{type:`number`,minimum:0,maximum:180}}}})},Xp=V(Lp,Gp,Yp,{readInput:e=>{let{pose:t,keyframes:n,...r}=zr(e,Yp);return{...r,...an({pose:t,keyframes:n})}}}),Zp={...Xp,inputSchema:{...Xp.inputSchema,oneOf:[{required:[`pose`],not:{required:[`keyframes`]}},{required:[`keyframes`],not:{required:[`pose`]}}]}},Qp=V(Rp,`Inspect Android keyboard visibility/type or press/dismiss the device keyboard. To hide the keyboard, use keyboard dismiss. It taps the keyboard dismiss/hide key when one is exposed, verifies the keyboard closed, and reports UNSUPPORTED_OPERATION when no dismiss key exists — background taps are never attempted.`,{action:R(Hp)}),$p=V(zp,`Read the current device clipboard text, or replace its contents with the given text. Android runs both through the clipboard service shell command, and a build that implements none (Android 16 does not) refuses with UNSUPPORTED_OPERATION rather than reporting an empty clipboard.`,{action:N(R(Vp)),text:P()},{parseableOutput:!0}),em=V(Bp,`Press or long-press a TV remote or D-pad button on Android TV, tvOS, or Vega OS. Choose the button and optional hold duration through the input fields. The aliases ok, center, and enter all map to select.`,{button:N(R(mn)),durationMs:I(`Press duration in milliseconds. tvOS and Vega OS use the exact hold duration; Android TV maps any positive value to an ADB longpress (${pn(`android`)}).`,{min:0})}),tm={usageOverride:`back [--in-app|--system] [--settle]`,usageFlags:[],allowedFlags:[`backMode`,...os(Fp)]},nm={usageOverride:`orientation <portrait|portrait-upside-down|landscape-left|landscape-right>`,positionalArgs:[`orientation`]},rm={usageOverride:`fold [${tn}]`,positionalArgs:[`pose?`],allowedFlags:ki},im={usageOverride:`keyboard [status|get|dismiss|enter|return]`,positionalArgs:[`action?`]},am={usageOverride:`clipboard read | clipboard write <text>`,listUsageOverride:`clipboard read | clipboard write <text>`,positionalArgs:[`read|write`,`text?`],allowsExtraPositionals:!0},om={usageOverride:`tv-remote [press|longpress] ${dn}`,listUsageOverride:`tv-remote press|longpress <button> [--duration-ms <ms>]`,positionalArgs:[`press|longpress?`,`button`],allowedFlags:[`durationMs`]},sm=(e,t)=>({...y(t),mode:t.backMode}),cm=(e,t)=>({...y(t),orientation:en(e[0])}),lm=(e,t)=>({...y(t),...an({pose:e[0],keyframes:t.keyframes===void 0?void 0:nn(t.keyframes)})}),um=(e,t)=>({...y(t),...Am(e)}),dm=(e,t)=>({...y(t),...jm(e)}),fm=(e,t)=>({...y(t),...Mm(e,t.durationMs)}),pm=e=>v(Fp,[],{...e,backMode:Om(e.mode)}),mm=_(Ip,e=>[T(e.orientation,`orientation requires orientation`)]),hm=e=>{let t=an(e);return v(Lp,t.pose?[t.pose]:[],{...e,keyframes:t.keyframes?JSON.stringify(t.keyframes):void 0})},gm=_(Rp,e=>S(e.action)),_m=_(zp,e=>km(e)),vm=_(Bp,e=>[T(e.button,`tv-remote requires button`)]),ym=ei({name:`appstate`,description:`Show foreground app/activity (Android; iOS answers per command)`,text:{summary:`Show the foreground app and activity`},run:(e,t)=>e.command.appState(t),cliOutputFormatter:$.appstate}),bm=H({name:Fp,text:{summary:`Navigate back in the app or system`},metadata:qp,run:(e,t)=>e.command.back(t),cliSchema:tm,cliReader:sm,daemonWriter:pm,cliOutputFormatter:$.back}),xm=ei({name:`home`,description:`Send the selected device to its home screen. This leaves the app session open but moves the foreground away from the app.`,text:{summary:`Go to the device home screen`},run:(e,t)=>e.command.home(t),cliOutputFormatter:$.home}),Sm=H({name:Ip,text:{summary:`Set device orientation`},metadata:Jp,run:(e,t)=>e.command.orientation(t),cliSchema:nm,cliReader:cm,daemonWriter:mm,cliOutputFormatter:$.orientation}),Cm=H({name:Lp,text:{summary:`Fold or unfold a foldable iPhone simulator`,cliDetail:`iPhone Duo simulators only. Sends a ${Wp.hingeEvent} and confirms the hinge angle through CoreDevice; refs and coordinates do not survive a pose change.`},metadata:Zp,run:(e,t)=>e.command.fold(t),cliSchema:rm,cliReader:lm,daemonWriter:hm,cliOutputFormatter:$.fold}),wm=ei({name:`app-switcher`,description:`Open the device app switcher to inspect or change foreground apps. This changes the visible system UI and may move focus away from the current app.`,text:{summary:`Open the device app switcher`},run:(e,t)=>e.command.appSwitcher(t),cliOutputFormatter:$[`app-switcher`]}),Tm=H({name:Rp,text:{summary:`Inspect, press, or dismiss the device keyboard`},metadata:Qp,run:(e,t)=>e.command.keyboard(t),cliSchema:im,cliReader:um,daemonWriter:gm,cliOutputFormatter:$.keyboard}),Em=H({name:zp,text:{summary:`Read or write device clipboard text`},metadata:$p,run:(e,t)=>e.command.clipboard(t),cliSchema:am,cliReader:dm,daemonWriter:_m,cliOutputFormatter:$.clipboard}),Dm=U({name:`system`,commands:[ym,bm,xm,Sm,Cm,wm,ei({name:`action-button`,description:`Press the iPhone or iPad Action Button once. The press is dispatched without activating the session app and nothing is re-observed afterwards, so the app keeps the state the press found. What the system does with the press is not observed by this command: Simulators run no Shortcuts or App Intents, so delivery to an assigned Shortcut is verifiable only on a physical iPhone.`,text:{summary:`Press the iPhone or iPad Action Button`,cliDetail:`iPhone and iPad only. The runner asks the device for the button and reports unsupported when that model has none.`},run:(e,t)=>e.command.actionButton(t),cliOutputFormatter:$[`action-button`]}),Tm,Em,H({name:Bp,text:{summary:`Press a TV remote/D-pad button`,cliDetail:`longpress holds for 500ms by default; --duration-ms overrides the preset.`},metadata:em,run:(e,t)=>e.command.tvRemote(t),cliSchema:om,cliReader:fm,daemonWriter:vm,cliOutputFormatter:$[`tv-remote`]})]});function Om(e){return e===`in-app`||e===`system`?e:void 0}function km(e){return e.action===`read`?[`read`]:[`write`,e.text]}function Am(e){if(e.length>1)throw new n(`INVALID_ARGS`,`keyboard accepts at most one action argument.`);return h({action:Nm(e[0])})}function jm(e){let t=e[0]?.toLowerCase();if(t!==`read`&&t!==`write`)throw new n(`INVALID_ARGS`,`clipboard requires a subcommand: read or write.`);if(t===`read`){if(e.length!==1)throw new n(`INVALID_ARGS`,`clipboard read does not accept additional arguments.`);return{action:t}}if(e.length<2)throw new n(`INVALID_ARGS`,`clipboard write requires text.`);return{action:t,text:e.slice(1).join(` `)}}function Mm(e,t){let r=e[0]?.toLowerCase(),i=r===`press`||r===`longpress`?e.slice(1):e;if(i.length!==1)throw new n(`INVALID_ARGS`,`tv-remote requires exactly one button: ${mn.join(`, `)}.`);let a=t??(r===`longpress`?500:void 0);return h({button:fn(i[0]),durationMs:a})}function Nm(e){let t=e?.toLowerCase();if(t===`get`)return`status`;if(t===void 0||t===`status`||t===`dismiss`||t===`enter`||t===`return`)return t;throw new n(`INVALID_ARGS`,`keyboard action must be status, get, dismiss, enter, or return.`)}const Pm=[vc,Nu,Xo,Dm,Mf,Cp,Ad,xf,ts,Zf,Lu,Si];function Fm(){return Pm.flatMap(e=>[...e.metadata])}function Im(){return Pm.flatMap(e=>[...e.definitions])}function Lm(){return Vm(`cliSchemas`)}function Rm(){return Vm(`cliReaders`)}function zm(){return Vm(`daemonWriters`)}function Bm(){return Vm(`cliOutputFormatters`)}function Vm(e){let t={};for(let n of Pm){let r=n[e];if(r)for(let[n,i]of Object.entries(r)){if(Object.hasOwn(t,n))throw Error(`Duplicate ${String(e)} command family entry: ${n}`);t[n]=i}}return t}export{M as A,yn as B,Ri as C,j as D,pi as E,mr as F,zn as I,Fn as L,E as M,k as N,O,pr as P,Sn as R,Li as S,Mi as T,bn as V,cc as _,Im as a,xo as b,Kp as c,hu as d,pu as f,_c as g,Sl as h,zm as i,A as j,D as k,bp as l,Rl as m,Rm as n,Fm as o,Vl as p,Lm as r,Up as s,Bm as t,ju as u,ss as v,zi as w,Vi as x,Bo as y,Cn as z};
@@ -1 +1 @@
1
- import{r as e}from"./rolldown-runtime.js";import"./capture.js";import{t}from"./snapshot-capture-annotations.js";import{v as n}from"./sdk-device.js";import{i as r,r as i}from"./success-text.js";import{n as a,t as o}from"./deploy-result-target.js";var s=e({serializeCloseResult:()=>g,serializeDeployResult:()=>p,serializeDevice:()=>d,serializeInstallFromSourceResult:()=>m,serializeOpenResult:()=>h,serializeSessionListEntry:()=>u,serializeSnapshotResult:()=>f});function c(e,t={}){let n=t.includeSerial??!0,r=l(e);return{platform:e.platform,target:e.target,device:e.name,id:e.id,...e.platform===`ios`?{device_udid:e.ios?.udid??e.id,ios_simulator_device_set:e.ios?.simulatorSetPath??null}:{},...n&&r?{serial:r}:{}}}function l(e){return n(e.platform)?e.identifiers.serial??e.id:void 0}function u(e){return{name:e.name,...e.address?{address:e.address}:{},...e.sessionStateDir?{sessionStateDir:e.sessionStateDir}:{},...e.runnerLogPath?{runnerLogPath:e.runnerLogPath}:{},...c(e.device,{includeSerial:!1}),createdAt:e.createdAt}}function d(e){return{platform:e.platform,...e.appleOs?{appleOs:e.appleOs}:{},id:e.id,name:e.name,kind:e.kind,target:e.target,...typeof e.booted==`boolean`?{booted:e.booted}:{},...e.claimedBy?{claimedBy:e.claimedBy}:{}}}function f(e){return{nodes:e.nodes,...e.truncated===void 0?{}:{truncated:e.truncated},...e.appName?{appName:e.appName}:{},...e.appBundleId?{appBundleId:e.appBundleId}:{},...e.visibility?{visibility:e.visibility}:{},...e.keyboard?{keyboard:e.keyboard}:{},...t({...e,...e.snapshotQuality?{quality:e.snapshotQuality}:{}}),...e.unchanged?{unchanged:e.unchanged}:{},...e.snapshotDiagnostics?{snapshotDiagnostics:e.snapshotDiagnostics}:{},...e.fallbackScreenshotPath?{fallbackScreenshotPath:e.fallbackScreenshotPath}:{},...e.refsGeneration===void 0?{}:{refsGeneration:e.refsGeneration}}}function p(e){return r({app:e.app,appPath:e.appPath,platform:e.platform,...e.appId?{appId:e.appId}:{},...e.bundleId?{bundleId:e.bundleId}:{},...e.package?{package:e.package}:{}},`Installed: ${o(e)}`)}function m(e){return r({launchTarget:e.launchTarget,...e.appName?{appName:e.appName}:{},...e.appId?{appId:e.appId}:{},...e.bundleId?{bundleId:e.bundleId}:{},...e.packageName?{package:e.packageName}:{},...e.installablePath?{installablePath:e.installablePath}:{},...e.archivePath?{archivePath:e.archivePath}:{},...e.materializationId?{materializationId:e.materializationId}:{},...e.materializationExpiresAt?{materializationExpiresAt:e.materializationExpiresAt}:{}},`Installed: ${a(e)}`)}function h(e){let t=e.appName??e.appBundleId??e.session;return r({session:e.session,...e.warnings?.length?{warnings:e.warnings}:{},...e.sessionStateDir?{sessionStateDir:e.sessionStateDir}:{},...e.runnerLogPath?{runnerLogPath:e.runnerLogPath}:{},...e.requestLogPath?{requestLogPath:e.requestLogPath}:{},...e.eventLogPath?{eventLogPath:e.eventLogPath}:{},...e.appName?{appName:e.appName}:{},...e.appBundleId?{appBundleId:e.appBundleId}:{},...e.selection?{selection:e.selection}:{},...e.startup?{startup:e.startup}:{},...e.runtime?{runtime:e.runtime}:{},...e.device?c(e.device):{},...e.snapshot?{snapshot:e.snapshot}:{},...e.initialSnapshotError?{initialSnapshotError:e.initialSnapshotError}:{}},t?`Opened: ${t}`:`Opened`)}function g(e){return{session:e.session,...e.shutdown?{shutdown:e.shutdown}:{},...`provider`in e&&e.provider?{provider:e.provider}:{},...i(e.session?`Closed: ${e.session}`:`Closed`)}}export{d as n,s as t};
1
+ import{r as e}from"./rolldown-runtime.js";import"./capture.js";import{t}from"./snapshot-capture-annotations.js";import{v as n}from"./sdk-device.js";import{i as r,r as i}from"./success-text.js";import{n as a,t as o}from"./deploy-result-target.js";var s=e({serializeCloseResult:()=>_,serializeDeployResult:()=>p,serializeDevice:()=>d,serializeInstallFromSourceResult:()=>m,serializeOpenResult:()=>h,serializeSessionListEntry:()=>u,serializeSnapshotResult:()=>f});function c(e,t={}){let n=t.includeSerial??!0,r=l(e);return{platform:e.platform,target:e.target,device:e.name,id:e.id,...e.platform===`ios`?{device_udid:e.ios?.udid??e.id,ios_simulator_device_set:e.ios?.simulatorSetPath??null}:{},...n&&r?{serial:r}:{}}}function l(e){return n(e.platform)?e.identifiers.serial??e.id:void 0}function u(e){return{name:e.name,...e.address?{address:e.address}:{},...e.sessionStateDir?{sessionStateDir:e.sessionStateDir}:{},...e.runnerLogPath?{runnerLogPath:e.runnerLogPath}:{},...c(e.device,{includeSerial:!1}),createdAt:e.createdAt}}function d(e){return{platform:e.platform,...e.appleOs?{appleOs:e.appleOs}:{},id:e.id,name:e.name,kind:e.kind,target:e.target,...typeof e.booted==`boolean`?{booted:e.booted}:{},...e.claimedBy?{claimedBy:e.claimedBy}:{}}}function f(e){return{nodes:e.nodes,...e.truncated===void 0?{}:{truncated:e.truncated},...e.appName?{appName:e.appName}:{},...e.appBundleId?{appBundleId:e.appBundleId}:{},...e.visibility?{visibility:e.visibility}:{},...e.keyboard?{keyboard:e.keyboard}:{},...t({...e,...e.snapshotQuality?{quality:e.snapshotQuality}:{}}),...e.unchanged?{unchanged:e.unchanged}:{},...e.snapshotDiagnostics?{snapshotDiagnostics:e.snapshotDiagnostics}:{},...e.fallbackScreenshotPath?{fallbackScreenshotPath:e.fallbackScreenshotPath}:{},...e.refsGeneration===void 0?{}:{refsGeneration:e.refsGeneration}}}function p(e){return r({app:e.app,appPath:e.appPath,platform:e.platform,...e.appId?{appId:e.appId}:{},...e.bundleId?{bundleId:e.bundleId}:{},...e.package?{package:e.package}:{}},`Installed: ${o(e)}`)}function m(e){return r({launchTarget:e.launchTarget,...e.appName?{appName:e.appName}:{},...e.appId?{appId:e.appId}:{},...e.bundleId?{bundleId:e.bundleId}:{},...e.packageName?{package:e.packageName}:{},...e.installablePath?{installablePath:e.installablePath}:{},...e.archivePath?{archivePath:e.archivePath}:{},...e.materializationId?{materializationId:e.materializationId}:{},...e.materializationExpiresAt?{materializationExpiresAt:e.materializationExpiresAt}:{}},`Installed: ${a(e)}`)}function h(e){let t=e.appName??e.appBundleId??e.session;return r({session:e.session,...e.warnings?.length?{warnings:e.warnings}:{},...g(e),...e.appName?{appName:e.appName}:{},...e.appBundleId?{appBundleId:e.appBundleId}:{},...e.selection?{selection:e.selection}:{},...e.startup?{startup:e.startup}:{},...e.runtime?{runtime:e.runtime}:{},...e.device?c(e.device):{},...e.launchConfirmation?{launchConfirmation:e.launchConfirmation}:{},...e.snapshot?{snapshot:e.snapshot}:{},...e.initialSnapshotError?{initialSnapshotError:e.initialSnapshotError}:{}},t?`Opened: ${t}`:`Opened`)}function g(e){return{...e.sessionStateDir?{sessionStateDir:e.sessionStateDir}:{},...e.runnerLogPath?{runnerLogPath:e.runnerLogPath}:{},...e.requestLogPath?{requestLogPath:e.requestLogPath}:{},...e.eventLogPath?{eventLogPath:e.eventLogPath}:{}}}function _(e){return{session:e.session,...e.shutdown?{shutdown:e.shutdown}:{},...`provider`in e&&e.provider?{provider:e.provider}:{},...i(e.session?`Closed: ${e.session}`:`Closed`)}}export{d as n,s as t};
@@ -1,3 +1,3 @@
1
- import{F as e,G as t,N as n}from"./sdk-contracts.js";import{m as r}from"./sdk-device.js";import{D as i,P as a,c as o,d as s,h as ee,m as te,t as c}from"./host.js";import{a as l,c as u,i as d,t as ne,u as f}from"./apple-runner-platform.js";import p from"node:path";import m from"node:fs";import h from"node:crypto";import re from"node:os";const ie=p.join(`apple`,`runner`,`AgentDeviceRunner`),ae=p.join(`dist`,`apple`,`runner`,`AgentDeviceRunner`),oe=p.join(`apple`,`snapshot-presentation`),se=p.join(`dist`,`apple`,`snapshot-presentation`);function g(e){let t=p.join(e,ie);return m.existsSync(t)?t:p.join(e,ae)}function _(e){return p.join(g(e),`AgentDeviceRunner.xcodeproj`)}function v(e){let t=p.join(e,oe);return m.existsSync(t)?t:p.join(e,se)}const y=new Set([`.build`,`.swiftpm`,`xcuserdata`]),ce=new Set([`.DS_Store`]),le=new Set([`.xcuserstate`]),ue=new Set([`.build`,`.swiftpm`,`SnapshotPresentationConformance`,`Tests`,`xcuserdata`]),b=new Map;function de(e){let t=[{path:g(e),ignoredDirectoryNames:y},{path:v(e),ignoredDirectoryNames:ue}],n=pe(t),r=fe(e,n),i=JSON.stringify(t.map(({path:e})=>e)),a=b.get(i);if(a?.fileStatsFingerprint===r)return a.sourceFingerprint;let o=h.createHash(`sha256`);for(let t of n){let n=p.relative(e,t);o.update(n),o.update(`\0`),o.update(m.readFileSync(t)),o.update(`\0`)}let s=o.digest(`hex`);return b.set(i,{fileStatsFingerprint:r,sourceFingerprint:s}),s}function fe(e,t){let n=h.createHash(`sha256`);for(let r of t){let t=p.relative(e,r),i=m.statSync(r);n.update(t),n.update(`\0`),n.update(String(i.size)),n.update(`\0`),n.update(String(Math.trunc(i.mtimeMs))),n.update(`\0`)}return n.digest(`hex`)}function pe(e){return[...new Set(e.flatMap(({path:e,ignoredDirectoryNames:t})=>me(e,t)))].sort((e,t)=>e.localeCompare(t))}function me(e,t){return m.existsSync(e)?x(e,t,!1):[]}function x(e,t,n){let r=[];for(let i of m.readdirSync(e,{withFileTypes:!0})){let a=p.join(e,i.name);if(i.isDirectory()){if(t.has(i.name))continue;let e=n||ge(i.name);r.push(...x(a,t,e))}else if(i.isFile()){if(n?he(i.name):!ve(i.name))continue;r.push(a)}}return r}function he(e){return ce.has(e)||le.has(p.extname(e))}function ge(e){return e.endsWith(`.xcodeproj`)||e.endsWith(`.xcworkspace`)}const _e=new Set([`.jpg`,`.json`,`.png`,`.swift`,`.m`,`.h`,`.plist`,`.entitlements`,`.xctestplan`,`.xcconfig`,`.storyboard`,`.xib`,`.xcscheme`,`.xcworkspacedata`]);function ve(e){return _e.has(p.extname(e))}const S=p.join(re.homedir(),`.agent-device`,`apple-runner`),C=`.agent-device-runner-cache.json`,w=[`-IDEPackageSupportDisableManifestSandbox=1`,`-IDEPackageSupportDisablePluginExecutionSandbox=1`,`ENABLE_USER_SCRIPT_SANDBOXING=NO`],T=`$(inherited) -disable-sandbox -D AGENT_DEVICE_RUNNER_ISOLATION_CANARY`,E=`${T} -D AGENT_DEVICE_RUNNER_UNIT_TESTS`;function D(e,t){return{deadline:e!==void 0&&Number.isFinite(e)?c.fromTimeoutMs(Math.max(0,e)):void 0,signal:t}}function O(e,t){let n=e?.deadline;if(!n)return;let r=Math.floor(n.remainingMs());if(r<=0)throw k(t);return r}function k(e){return new n(`COMMAND_FAILED`,`The Apple runner budget ran out before this step began`,{phase:e,reason:`runner_phase_budget_exhausted`,retriable:!0})}function A(t){let n=t?.deadline,r=c.fromTimeoutMs(Math.min(45e3,n?n.remainingMs():1/0));return{attemptTimeoutMs:()=>Math.min(ne,Math.floor(r.remainingMs())),throwIfCanceled:()=>{if(t?.signal?.aborted)throw e({phase:`apple_toolchain_probe`})}}}function j(e){return e?.trim()??``}function M(e=process.env){return j(e.AGENT_DEVICE_IOS_BUNDLE_ID)||j(e.AGENT_DEVICE_IOS_RUNNER_APP_BUNDLE_ID)||`com.callstack.agentdevice.runner`}function N(e=process.env){return j(e.AGENT_DEVICE_IOS_RUNNER_TEST_BUNDLE_ID)||`${M(e)}.uitests`}function P(e=process.env){let t=M(e),n=N(e);return Array.from(new Set([j(e.AGENT_DEVICE_IOS_RUNNER_CONTAINER_BUNDLE_ID),`${n}.xctrunner`,t].filter(e=>e.length>0)))}const F=P(process.env);function ye(e,t=s(),n){let r=u(e);return{schemaVersion:3,packageVersion:i(t),runnerSourceFingerprint:de(t),...be(f(r,e.kind),n),platformName:r,deviceKind:e.kind,target:e.target??`mobile`,buildDestinationFamily:d(e),runnerBundleBuildSettings:J(process.env),runnerSigningBuildSettings:q(process.env,e.kind===`device`,e),runnerPerformanceBuildSettings:Y(),runnerArchBuildSettings:X(process.env),runnerSandboxBuildArgs:Z()}}let I;function L(){return I??=o(),I}function be(e,t){let n=A(t);n.throwIfCanceled();let r=L().get(e);if(r)return r;let i=Se(e,n);if(!i.ok)throw Ce(i.failures);return L().set(e,i.value),i.value}function xe(e){return L().get(f(u(e),e.kind))?.xcodeVersion}function Se(e,t){let n=Te(R(`xcodebuild`,[`-version`],t)),r=R(`xcrun`,[`--sdk`,e,`--show-sdk-version`],t),i=R(`xcrun`,[`--sdk`,e,`--show-sdk-build-version`],t);return!n.ok||!r.ok||!i.ok?{ok:!1,failures:[n,r,i].flatMap(e=>e.ok?[]:[e.failure])}:{ok:!0,value:{xcodeVersion:n.value.version,xcodeBuildVersion:n.value.buildVersion,sdkName:e,sdkVersion:r.value,sdkBuildVersion:i.value}}}function Ce(e){return new n(`COMMAND_FAILED`,`Could not read the Xcode toolchain versions the Apple runner cache is keyed on (${e.map(e=>`${e.probe}: ${e.detail}`).join(`; `)})`,{reason:`apple_toolchain_probe_unavailable`,retriable:!0,probes:e,hint:"The Apple runner cache is keyed on the toolchain version, so a cache decision cannot be made without it. Retry once the host is less loaded, or check `xcode-select -p` and `xcodebuild -version`."})}function R(e,n,r){let i=[e,...n].join(` `),a;try{a=z(e,n,r)}catch(e){if(r.throwIfCanceled(),t(e)||we(e))throw e;return V(i,`probe_error`,e instanceof Error?e.message:`${e}`)}if(a.exitCode!==0)return V(i,`nonzero_exit`,`exit ${a.exitCode}${a.stderr.trim()?`: ${a.stderr.trim()}`:``}`);let o=a.stdout.trim();return o?{ok:!0,value:o}:V(i,`empty_output`,`no output`)}function z(e,t,n){try{return B(e,t,n)}catch(r){if(!te(r))throw r;return B(e,t,n)}}function B(e,t,n){n.throwIfCanceled();let r=n.attemptTimeoutMs();if(r<=0)throw k(`apple_toolchain_probe`);return a(e,t,{allowFailure:!0,timeoutMs:r,maxBuffer:131072})}function we(e){return e instanceof n&&e.details?.reason===`runner_phase_budget_exhausted`}function Te(e){if(!e.ok)return e;let t=e.value.match(/^Xcode\s+(.+)$/m)?.[1]?.trim(),n=e.value.match(/^Build version\s+(.+)$/m)?.[1]?.trim();return!t||!n?V(`xcodebuild -version`,`unparsable_output`,`unrecognized output: ${e.value.replaceAll(`
2
- `,` `)}`):{ok:!0,value:{version:t,buildVersion:n}}}function V(e,t,n){return{ok:!1,failure:{probe:e,reason:t,detail:n.length>200?`${n.slice(0,200)}…`:n}}}function Ee(e,t){let n=process.env.AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH?.trim();if(n)return p.resolve(n);let r=Oe(t),i=De(e);return p.join(i,r)}function De(e){return p.join(S,`derived`,l(e))}function Oe(e){return`cache-${h.createHash(`sha256`).update(G(H(e))).digest(`hex`).slice(0,16)}`}function H(e){let{artifacts:t,packageVersion:n,...r}=e;return r}function ke(e,t){let n=H(e),r=H(t);return[...new Set([...Object.keys(n),...Object.keys(r)])].sort((e,t)=>e.localeCompare(t)).flatMap(e=>{let t=U(n[e]),i=U(r[e]);return t===i?[]:[{key:e,expected:W(t),actual:W(i)}]})}function U(e){return e===void 0?`(absent)`:G(e)}function W(e){return e.length<=300?e:`${e.slice(0,149)}…${e.slice(-149)}`}function G(e){return JSON.stringify(K(e))}function K(e){return Array.isArray(e)?e.map(e=>K(e)):!e||typeof e!=`object`?e:Object.fromEntries(Object.entries(e).sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>[e,K(t)]))}function Ae(e){return r(e)||e.kind===`device`?`-maximum-concurrent-test-device-destinations`:`-maximum-concurrent-test-simulator-destinations`}function q(e=process.env,t=!1,n={platform:`apple`}){if(r(n))return[`CODE_SIGNING_ALLOWED=NO`,`CODE_SIGNING_REQUIRED=NO`,`CODE_SIGN_IDENTITY=`,`DEVELOPMENT_TEAM=`];if(!t)return[];let i=e.AGENT_DEVICE_IOS_TEAM_ID?.trim()||``,a=e.AGENT_DEVICE_IOS_SIGNING_IDENTITY?.trim()||``,o=e.AGENT_DEVICE_IOS_PROVISIONING_PROFILE?.trim()||``,s=[`CODE_SIGN_STYLE=${o?`Manual`:`Automatic`}`];return i&&s.push(`DEVELOPMENT_TEAM=${i}`),a&&s.push(`CODE_SIGN_IDENTITY=${a}`),o&&s.push(`PROVISIONING_PROFILE_SPECIFIER=${o}`),s}function J(e=process.env){let t=M(e),n=N(e);return[`AGENT_DEVICE_IOS_RUNNER_APP_BUNDLE_ID=${t}`,`AGENT_DEVICE_IOS_RUNNER_TEST_BUNDLE_ID=${n}`]}function Y(){return[`COMPILER_INDEX_STORE_ENABLE=NO`,`ENABLE_CODE_COVERAGE=NO`,`ONLY_ACTIVE_ARCH=YES`,`ENABLE_PREVIEWS=NO`,`ENABLE_DEBUG_DYLIB=NO`]}function X(e=process.env){let t=e.AGENT_DEVICE_XCUITEST_ARCHS?.trim();return t?[`ARCHS=${t}`]:[]}function Z(){return[...w,`OTHER_SWIFT_FLAGS=${je(process.env)}`]}function je(e){return ee(e.AGENT_DEVICE_XCUITEST_INCLUDE_UNIT_TESTS)?E:T}const Me=/^\s*Build settings from command line:\s*$/,Ne=/^\s+([A-Z][A-Z0-9_]*)\s*=\s*(.*)$/,Q=/^([A-Z][A-Z0-9_]*)=(.*)$/,Pe=/^\s*Command line invocation:\s*$/;function Fe(e){let t;try{t=m.readFileSync(e,`utf8`)}catch{return null}let n=t.split(`
3
- `),r=new Map,i=!1,a=``;for(let[e,t]of n.entries()){if(Pe.test(t)){a=n[e+1]??``;continue}if(Me.test(t)){i=!0;continue}if(!i)continue;let o=Ne.exec(t);if(!o)break;r.set(o[1],o[2].trimEnd())}return i?{settings:r,invocationLine:a}:null}function $(e){return[...e.runnerBundleBuildSettings,...e.runnerSigningBuildSettings,...e.runnerPerformanceBuildSettings,...e.runnerArchBuildSettings,...e.runnerSandboxBuildArgs]}function Ie(e){let t={};for(let n of $(e)){let e=Q.exec(n);e&&(t[e[1]]=e[2])}return t}function Le(e,t){return $(e).filter(e=>!Q.test(e)).filter(e=>!t.includes(e)).map(e=>({key:`(invocation flag)`,expected:e,actual:`absent from the "Command line invocation:" line`}))}function Re(e,t){let n=Fe(t);return n?[...Object.entries(Ie(e)).filter(([e,t])=>{let r=n.settings.get(e);return r===void 0?t!==``:r!==t}).map(([e,t])=>({key:e,expected:t,actual:n.settings.get(e)??`(absent)`})),...Le(e,n.invocationLine)]:[{key:`(build log)`,expected:`a "Build settings from command line:" block`,actual:`missing or unreadable log`}]}function ze(e,t){let r=Re(e,t);if(r.length!==0)throw new n(`COMMAND_FAILED`,`The Apple runner build did not use the settings its cache identity records`,{reason:`runner_build_settings_mismatch`,buildLogPath:t,differences:r,hint:`Align the build invocation with the runner cache identity resolvers, or rebuild without the cache.`})}export{q as _,ke as a,O as c,X as d,J as f,Z as g,Y as h,D as i,ye as l,Ae as m,C as n,xe as o,Ee as p,H as r,ze as s,F as t,M as u,G as v,_ as y};
1
+ import{F as e,G as t,N as n}from"./sdk-contracts.js";import{m as r}from"./sdk-device.js";import{F as i,O as a,c as o,f as s,g as ee,h as te,l as ne,t as c}from"./host.js";import{a as l,c as u,i as re,t as ie,u as d}from"./apple-runner-platform.js";import f from"node:path";import p from"node:fs";import m from"node:crypto";import ae from"node:os";const oe=f.join(`apple`,`runner`,`AgentDeviceRunner`),h=f.join(`dist`,`apple`,`runner`,`AgentDeviceRunner`),se=f.join(`apple`,`snapshot-presentation`),ce=f.join(`dist`,`apple`,`snapshot-presentation`);function g(e){let t=f.join(e,oe);return p.existsSync(t)?t:f.join(e,h)}function _(e){return f.join(g(e),`AgentDeviceRunner.xcodeproj`)}function v(e){let t=f.join(e,se);return p.existsSync(t)?t:f.join(e,ce)}const y=new Set([`.build`,`.swiftpm`,`xcuserdata`]),le=new Set([`.DS_Store`]),ue=new Set([`.xcuserstate`]),de=new Set([`.build`,`.swiftpm`,`SnapshotPresentationConformance`,`Tests`,`xcuserdata`]),b=new Map;function fe(e){let t=[{path:g(e),ignoredDirectoryNames:y},{path:v(e),ignoredDirectoryNames:de}],n=me(t),r=pe(e,n),i=JSON.stringify(t.map(({path:e})=>e)),a=b.get(i);if(a?.fileStatsFingerprint===r)return a.sourceFingerprint;let o=m.createHash(`sha256`);for(let t of n){let n=f.relative(e,t);o.update(n),o.update(`\0`),o.update(p.readFileSync(t)),o.update(`\0`)}let s=o.digest(`hex`);return b.set(i,{fileStatsFingerprint:r,sourceFingerprint:s}),s}function pe(e,t){let n=m.createHash(`sha256`);for(let r of t){let t=f.relative(e,r),i=p.statSync(r);n.update(t),n.update(`\0`),n.update(String(i.size)),n.update(`\0`),n.update(String(Math.trunc(i.mtimeMs))),n.update(`\0`)}return n.digest(`hex`)}function me(e){return[...new Set(e.flatMap(({path:e,ignoredDirectoryNames:t})=>he(e,t)))].sort((e,t)=>e.localeCompare(t))}function he(e,t){return p.existsSync(e)?x(e,t,!1):[]}function x(e,t,n){let r=[];for(let i of p.readdirSync(e,{withFileTypes:!0})){let a=f.join(e,i.name);if(i.isDirectory()){if(t.has(i.name))continue;let e=n||_e(i.name);r.push(...x(a,t,e))}else if(i.isFile()){if(n?ge(i.name):!ye(i.name))continue;r.push(a)}}return r}function ge(e){return le.has(e)||ue.has(f.extname(e))}function _e(e){return e.endsWith(`.xcodeproj`)||e.endsWith(`.xcworkspace`)}const ve=new Set([`.jpg`,`.json`,`.png`,`.swift`,`.m`,`.h`,`.plist`,`.entitlements`,`.xctestplan`,`.xcconfig`,`.storyboard`,`.xib`,`.xcscheme`,`.xcworkspacedata`]);function ye(e){return ve.has(f.extname(e))}const be=f.join(ae.homedir(),`.agent-device`,`apple-runner`),S=`.agent-device-runner-cache.json`,C=[`-IDEPackageSupportDisableManifestSandbox=1`,`-IDEPackageSupportDisablePluginExecutionSandbox=1`,`ENABLE_USER_SCRIPT_SANDBOXING=NO`],w=`$(inherited) -disable-sandbox -D AGENT_DEVICE_RUNNER_ISOLATION_CANARY`,T=`${w} -D AGENT_DEVICE_RUNNER_UNIT_TESTS`;function E(e,t){return{deadline:e!==void 0&&Number.isFinite(e)?c.fromTimeoutMs(Math.max(0,e)):void 0,signal:t}}function D(e,t){let n=e?.deadline;if(!n)return;let r=Math.floor(n.remainingMs());if(r<=0)throw O(t);return r}function O(e){return new n(`COMMAND_FAILED`,`The Apple runner budget ran out before this step began`,{phase:e,reason:`runner_phase_budget_exhausted`,retriable:!0})}function k(t){let n=t?.deadline,r=c.fromTimeoutMs(Math.min(45e3,n?n.remainingMs():1/0));return{attemptTimeoutMs:()=>Math.min(ie,Math.floor(r.remainingMs())),throwIfCanceled:()=>{if(t?.signal?.aborted)throw e({phase:`apple_toolchain_probe`})}}}function A(e){return e?.trim()??``}function j(e=process.env){return A(e.AGENT_DEVICE_IOS_BUNDLE_ID)||A(e.AGENT_DEVICE_IOS_RUNNER_APP_BUNDLE_ID)||`com.callstack.agentdevice.runner`}function M(e=process.env){return A(e.AGENT_DEVICE_IOS_RUNNER_TEST_BUNDLE_ID)||`${j(e)}.uitests`}function N(e=process.env){let t=j(e),n=M(e);return Array.from(new Set([A(e.AGENT_DEVICE_IOS_RUNNER_CONTAINER_BUNDLE_ID),`${n}.xctrunner`,t].filter(e=>e.length>0)))}const xe=N(process.env);function Se(e,t=s(),n){let r=u(e);return{schemaVersion:3,packageVersion:a(t),runnerSourceFingerprint:fe(t),...Ce(d(r,e.kind),n),platformName:r,deviceKind:e.kind,target:e.target??`mobile`,buildDestinationFamily:re(e),runnerBundleBuildSettings:J(process.env),runnerSigningBuildSettings:q(process.env,e.kind===`device`,e),runnerPerformanceBuildSettings:Y(),runnerArchBuildSettings:X(process.env),runnerSandboxBuildArgs:Z()}}let P;function F(){return P??=ne({ttlMs:6e5,scheduleExpiry:!0}),P}function Ce(e,t){let n=k(t);n.throwIfCanceled();let r=I(e),i=F().get(r);if(i)return F().set(r,i),i;let a=Te(e,n);if(!a.ok)throw L(a.failures);return F().set(r,a.value),a.value}function I(e){return`${o()??``}\0${e}`}function we(e){return F().get(I(d(u(e),e.kind)))?.xcodeVersion}function Te(e,t){let n=Oe(R(`xcodebuild`,[`-version`],t)),r=R(`xcrun`,[`--sdk`,e,`--show-sdk-version`],t),i=R(`xcrun`,[`--sdk`,e,`--show-sdk-build-version`],t);return!n.ok||!r.ok||!i.ok?{ok:!1,failures:[n,r,i].flatMap(e=>e.ok?[]:[e.failure])}:{ok:!0,value:{xcodeVersion:n.value.version,xcodeBuildVersion:n.value.buildVersion,sdkName:e,sdkVersion:r.value,sdkBuildVersion:i.value}}}function L(e){return new n(`COMMAND_FAILED`,`Could not read the Xcode toolchain versions the Apple runner cache is keyed on (${e.map(e=>`${e.probe}: ${e.detail}`).join(`; `)})`,{reason:`apple_toolchain_probe_unavailable`,retriable:!0,probes:e,hint:"The Apple runner cache is keyed on the toolchain version, so a cache decision cannot be made without it. Retry once the host is less loaded, or check `xcode-select -p` and `xcodebuild -version`."})}function R(e,n,r){let i=[e,...n].join(` `),a;try{a=Ee(e,n,r)}catch(e){if(r.throwIfCanceled(),t(e)||De(e))throw e;return B(i,`probe_error`,e instanceof Error?e.message:`${e}`)}if(a.exitCode!==0)return B(i,`nonzero_exit`,`exit ${a.exitCode}${a.stderr.trim()?`: ${a.stderr.trim()}`:``}`);let o=a.stdout.trim();return o?{ok:!0,value:o}:B(i,`empty_output`,`no output`)}function Ee(e,t,n){try{return z(e,t,n)}catch(r){if(!te(r))throw r;return z(e,t,n)}}function z(e,t,n){n.throwIfCanceled();let r=n.attemptTimeoutMs();if(r<=0)throw O(`apple_toolchain_probe`);return i(e,t,{allowFailure:!0,timeoutMs:r,maxBuffer:131072})}function De(e){return e instanceof n&&e.details?.reason===`runner_phase_budget_exhausted`}function Oe(e){if(!e.ok)return e;let t=e.value.match(/^Xcode\s+(.+)$/m)?.[1]?.trim(),n=e.value.match(/^Build version\s+(.+)$/m)?.[1]?.trim();return!t||!n?B(`xcodebuild -version`,`unparsable_output`,`unrecognized output: ${e.value.replaceAll(`
2
+ `,` `)}`):{ok:!0,value:{version:t,buildVersion:n}}}function B(e,t,n){return{ok:!1,failure:{probe:e,reason:t,detail:n.length>200?`${n.slice(0,200)}…`:n}}}function ke(e,t){let n=process.env.AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH?.trim();if(n)return f.resolve(n);let r=V(t),i=Ae(e);return f.join(i,r)}function Ae(e){return f.join(be,`derived`,l(e))}function V(e){return`cache-${m.createHash(`sha256`).update(G(H(e))).digest(`hex`).slice(0,16)}`}function H(e){let{artifacts:t,packageVersion:n,...r}=e;return r}function je(e,t){let n=H(e),r=H(t);return[...new Set([...Object.keys(n),...Object.keys(r)])].sort((e,t)=>e.localeCompare(t)).flatMap(e=>{let t=U(n[e]),i=U(r[e]);return t===i?[]:[{key:e,expected:W(t),actual:W(i)}]})}function U(e){return e===void 0?`(absent)`:G(e)}function W(e){return e.length<=300?e:`${e.slice(0,149)}…${e.slice(-149)}`}function G(e){return JSON.stringify(K(e))}function K(e){return Array.isArray(e)?e.map(e=>K(e)):!e||typeof e!=`object`?e:Object.fromEntries(Object.entries(e).sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>[e,K(t)]))}function Me(e){return r(e)||e.kind===`device`?`-maximum-concurrent-test-device-destinations`:`-maximum-concurrent-test-simulator-destinations`}function q(e=process.env,t=!1,n={platform:`apple`}){if(r(n))return[`CODE_SIGNING_ALLOWED=NO`,`CODE_SIGNING_REQUIRED=NO`,`CODE_SIGN_IDENTITY=`,`DEVELOPMENT_TEAM=`];if(!t)return[];let i=e.AGENT_DEVICE_IOS_TEAM_ID?.trim()||``,a=e.AGENT_DEVICE_IOS_SIGNING_IDENTITY?.trim()||``,o=e.AGENT_DEVICE_IOS_PROVISIONING_PROFILE?.trim()||``,s=[`CODE_SIGN_STYLE=${o?`Manual`:`Automatic`}`];return i&&s.push(`DEVELOPMENT_TEAM=${i}`),a&&s.push(`CODE_SIGN_IDENTITY=${a}`),o&&s.push(`PROVISIONING_PROFILE_SPECIFIER=${o}`),s}function J(e=process.env){let t=j(e),n=M(e);return[`AGENT_DEVICE_IOS_RUNNER_APP_BUNDLE_ID=${t}`,`AGENT_DEVICE_IOS_RUNNER_TEST_BUNDLE_ID=${n}`]}function Y(){return[`COMPILER_INDEX_STORE_ENABLE=NO`,`ENABLE_CODE_COVERAGE=NO`,`ONLY_ACTIVE_ARCH=YES`,`ENABLE_PREVIEWS=NO`,`ENABLE_DEBUG_DYLIB=NO`]}function X(e=process.env){let t=e.AGENT_DEVICE_XCUITEST_ARCHS?.trim();return t?[`ARCHS=${t}`]:[]}function Z(){return[...C,`OTHER_SWIFT_FLAGS=${Ne(process.env)}`]}function Ne(e){return ee(e.AGENT_DEVICE_XCUITEST_INCLUDE_UNIT_TESTS)?T:w}const Pe=/^\s*Build settings from command line:\s*$/,Fe=/^\s+([A-Z][A-Z0-9_]*)\s*=\s*(.*)$/,Q=/^([A-Z][A-Z0-9_]*)=(.*)$/,Ie=/^\s*Command line invocation:\s*$/;function Le(e){let t;try{t=p.readFileSync(e,`utf8`)}catch{return null}let n=t.split(`
3
+ `),r=new Map,i=!1,a=``;for(let[e,t]of n.entries()){if(Ie.test(t)){a=n[e+1]??``;continue}if(Pe.test(t)){i=!0;continue}if(!i)continue;let o=Fe.exec(t);if(!o)break;r.set(o[1],o[2].trimEnd())}return i?{settings:r,invocationLine:a}:null}function $(e){return[...e.runnerBundleBuildSettings,...e.runnerSigningBuildSettings,...e.runnerPerformanceBuildSettings,...e.runnerArchBuildSettings,...e.runnerSandboxBuildArgs]}function Re(e){let t={};for(let n of $(e)){let e=Q.exec(n);e&&(t[e[1]]=e[2])}return t}function ze(e,t){return $(e).filter(e=>!Q.test(e)).filter(e=>!t.includes(e)).map(e=>({key:`(invocation flag)`,expected:e,actual:`absent from the "Command line invocation:" line`}))}function Be(e,t){let n=Le(t);return n?[...Object.entries(Re(e)).filter(([e,t])=>{let r=n.settings.get(e);return r===void 0?t!==``:r!==t}).map(([e,t])=>({key:e,expected:t,actual:n.settings.get(e)??`(absent)`})),...ze(e,n.invocationLine)]:[{key:`(build log)`,expected:`a "Build settings from command line:" block`,actual:`missing or unreadable log`}]}function Ve(e,t){let r=Be(e,t);if(r.length!==0)throw new n(`COMMAND_FAILED`,`The Apple runner build did not use the settings its cache identity records`,{reason:`runner_build_settings_mismatch`,buildLogPath:t,differences:r,hint:`Align the build invocation with the runner cache identity resolvers, or rebuild without the cache.`})}export{Z as _,je as a,_ as b,D as c,X as d,J as f,Y as g,Me as h,E as i,Se as l,ke as m,S as n,we as o,V as p,H as r,Ve as s,xe as t,j as u,q as v,G as y};