agent-device 0.20.2 → 0.20.4

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 (141) hide show
  1. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.20.2.apk → agent-device-android-ime-helper-0.20.4.apk} +0 -0
  2. package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.4.apk.sha256 +1 -0
  3. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.20.2.manifest.json → agent-device-android-ime-helper-0.20.4.manifest.json} +4 -4
  4. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.4.apk +0 -0
  5. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.4.apk.sha256 +1 -0
  6. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.20.2.manifest.json → agent-device-android-snapshot-helper-0.20.4.manifest.json} +6 -6
  7. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +57 -4
  8. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +45 -185
  9. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Keyboard.swift +257 -0
  10. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift +1 -0
  11. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollGesture.swift +3 -2
  12. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +1 -1
  13. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedGesturePolicy.swift +8 -2
  14. package/dist/src/agent-browser-tool.js +1 -1
  15. package/dist/src/agent-device-client.js +2 -2
  16. package/dist/src/android-adb.d.ts +4 -27
  17. package/dist/src/android-adb.js +1 -1
  18. package/dist/src/android.js +1 -1
  19. package/dist/src/app-helpers.js +1 -0
  20. package/dist/src/app-inventory.d.ts +4 -0
  21. package/dist/src/app-lifecycle.js +7 -7
  22. package/dist/src/app-resolution.js +1 -1
  23. package/dist/src/apps.js +5 -5
  24. package/dist/src/boot-diagnostics.js +1 -1
  25. package/dist/src/cli-config.js +1 -1
  26. package/dist/src/cli-help.js +27 -19
  27. package/dist/src/cli-runner.js +1 -1
  28. package/dist/src/cli.js +15 -14
  29. package/dist/src/client-metro.js +1 -1
  30. package/dist/src/{tv-remote.d.ts → cloud-artifacts.d.ts} +109 -126
  31. package/dist/src/command-metadata.js +1 -1
  32. package/dist/src/command-schema.js +1 -1
  33. package/dist/src/command-surface.js +1 -1
  34. package/dist/src/context.js +1 -1
  35. package/dist/src/contracts.d.ts +1 -1
  36. package/dist/src/debug-symbols.js +4 -4
  37. package/dist/src/device-input-state.d.ts +26 -0
  38. package/dist/src/device-input-state.js +1 -1
  39. package/dist/src/device.js +1 -9
  40. package/dist/src/devices2.js +1 -1
  41. package/dist/src/entities.js +1 -0
  42. package/dist/src/find.js +1 -1
  43. package/dist/src/finders.d.ts +1 -1
  44. package/dist/src/finders.js +1 -1
  45. package/dist/src/generic.js +1 -1
  46. package/dist/src/helper.js +2 -2
  47. package/dist/src/ime-lifecycle.js +9 -0
  48. package/dist/src/index.d.ts +593 -556
  49. package/dist/src/input-actions.js +4 -4
  50. package/dist/src/install-artifact.js +4 -4
  51. package/dist/src/install-artifact2.js +1 -1
  52. package/dist/src/install-source2.js +1 -1
  53. package/dist/src/interaction-snapshot.js +1 -1
  54. package/dist/src/interaction.d.ts +7 -4
  55. package/dist/src/interaction.js +1 -1
  56. package/dist/src/interactor.js +2 -2
  57. package/dist/src/interactor2.js +1 -1
  58. package/dist/src/internal/bin.js +1 -1
  59. package/dist/src/internal/daemon.js +14 -14
  60. package/dist/src/internal/png-worker.js +1 -1
  61. package/dist/src/lease-context.js +1 -1
  62. package/dist/src/lease.js +1 -1
  63. package/dist/src/limrun.d.ts +171 -190
  64. package/dist/src/limrun.js +1 -1
  65. package/dist/src/linux.js +1 -1
  66. package/dist/src/logcat.js +1 -1
  67. package/dist/src/manifest.js +1 -1
  68. package/dist/src/metro.d.ts +1 -1
  69. package/dist/src/parser.js +2 -0
  70. package/dist/src/path-resolution.js +1 -1
  71. package/dist/src/physical-device-control.js +1 -1
  72. package/dist/src/plist-xml.js +1 -0
  73. package/dist/src/plist.js +1 -1
  74. package/dist/src/png-worker-contract.js +1 -1
  75. package/dist/src/process-lock.js +1 -1
  76. package/dist/src/provider-limrun-runtime.js +1 -0
  77. package/dist/src/react-native.js +1 -1
  78. package/dist/src/record-trace.js +1 -1
  79. package/dist/src/register-builtins.js +1 -1
  80. package/dist/src/registry.js +22 -22
  81. package/dist/src/remote-config2.js +1 -1
  82. package/dist/src/reporting.js +7 -7
  83. package/dist/src/runner-client.js +1 -1
  84. package/dist/src/runner-disposal.js +1 -1
  85. package/dist/src/runner-lease.js +1 -1
  86. package/dist/src/runner-provider.js +1 -1
  87. package/dist/src/runner-sequence.js +1 -1
  88. package/dist/src/runtime-transport.js +1 -1
  89. package/dist/src/runtime.js +3 -3
  90. package/dist/src/screenshot-geometry.js +1 -0
  91. package/dist/src/screenshot-result.js +6 -0
  92. package/dist/src/sdk-android-adb.d.ts +5 -6
  93. package/dist/src/sdk-android-adb.js +3 -3
  94. package/dist/src/sdk-batch-runner.d.ts +2 -2
  95. package/dist/src/sdk-batch-runner.js +3 -3
  96. package/dist/src/sdk-contracts.d.ts +21 -25
  97. package/dist/src/sdk-contracts.js +2 -2
  98. package/dist/src/sdk-finders.d.ts +1 -1
  99. package/dist/src/sdk-finders.js +1 -1
  100. package/dist/src/sdk-io.js +1 -1
  101. package/dist/src/sdk-remote-config.d.ts +19 -4
  102. package/dist/src/sdk-remote-config.js +1 -1
  103. package/dist/src/sdk-selectors.js +1 -1
  104. package/dist/src/selector-runtime.js +1 -1
  105. package/dist/src/selector-vocabulary.js +1 -1
  106. package/dist/src/selectors.d.ts +1 -1
  107. package/dist/src/selectors.js +1 -1
  108. package/dist/src/server.js +2 -2
  109. package/dist/src/session-target-evidence.js +1 -1
  110. package/dist/src/session.js +8 -8
  111. package/dist/src/setting-state.js +1 -1
  112. package/dist/src/simulator.js +1 -1
  113. package/dist/src/snapshot.js +1 -1
  114. package/dist/src/snapshot2.js +1 -1
  115. package/dist/src/src.js +1 -0
  116. package/dist/src/tool-provider.js +3 -3
  117. package/dist/src/tool-provider2.js +1 -1
  118. package/dist/src/tool-provider3.js +1 -1
  119. package/dist/src/update-check.js +1 -1
  120. package/dist/src/version.js +1 -1
  121. package/dist/src/viewport-dimension.js +14 -0
  122. package/package.json +28 -19
  123. package/server.json +2 -2
  124. package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.2.apk.sha256 +0 -1
  125. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.2.apk +0 -0
  126. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.2.apk.sha256 +0 -1
  127. package/dist/src/android-input-method-overlays.js +0 -1
  128. package/dist/src/android-input-ownership.js +0 -1
  129. package/dist/src/apple-multitouch-support.js +0 -1
  130. package/dist/src/click-button.d.ts +0 -5
  131. package/dist/src/compatibility-policy.js +0 -1
  132. package/dist/src/gesture-plan-types.js +0 -1
  133. package/dist/src/gesture-plan.js +0 -1
  134. package/dist/src/launch-console.js +0 -1
  135. package/dist/src/recording-export-quality.js +0 -15
  136. package/dist/src/rolldown-runtime.js +0 -1
  137. package/dist/src/runtime2.js +0 -1
  138. package/dist/src/sdk-app-inventory.d.ts +0 -4
  139. package/dist/src/sdk-app-inventory.js +0 -1
  140. package/dist/src/snapshot-diagnostics.js +0 -6
  141. package/dist/src/xml.js +0 -2
@@ -1 +1 @@
1
- import{t as e}from"./rolldown-runtime.js";import{_ as t}from"./sdk-contracts.js";import{c as n,s as r}from"./sdk-device.js";import{c as i,l as a}from"./install-artifact.js";import{n as o}from"./setting-state.js";import{i as s,r as c,s as l}from"./tool-provider.js";import{n as u}from"./helper.js";import{a as d,t as f}from"./physical-device-control.js";import p from"node:path";import{fileURLToPath as m}from"node:url";const h={settings:`com.apple.systempreferences`},g=/^[a-z0-9-]+(?:\.[a-z0-9-]+)+$/,_={platform:`macos`,deviceId:`host`,variant:`all`},v=i();function y(e){return g.test(e)}async function b(e){let n=e.trim(),r=h[n.toLowerCase()];if(r)return r;if(y(n))return n;let i=v.get(_,n);if(i)return i;let a=(await D(`all`)).filter(e=>e.name.toLowerCase()===n.toLowerCase()),o=a[0];if(o!==void 0&&a.length===1)return v.set(_,n,o.bundleId);throw a.length>1?new t(`INVALID_ARGS`,`Multiple apps matched "${e}"`,{matches:a}):new t(`APP_NOT_INSTALLED`,`No app found matching "${e}"`)}async function x(e,n,r){let i=r?.url?.trim();if(i){if(!a(i))throw new t(`INVALID_ARGS`,`open <app> <url> requires a valid URL target`);let e=r?.appBundleId??await b(n);await O().openBundle(e,i);return}let o=n.trim();if(a(o)){await O().openTarget(o);return}let s=r?.appBundleId??await b(o);await O().openBundle(s)}async function S(e,n){let r=await b(n),i=await u(r);if(!(!i.running||i.terminated||i.forceTerminated))throw new t(`COMMAND_FAILED`,`Failed to close macOS app ${n}`,{bundleId:r,running:i.running,terminated:i.terminated,forceTerminated:i.forceTerminated})}async function C(){return await O().readClipboard()}async function w(e){await O().writeClipboard(e)}async function T(){return await O().readDarkMode()}async function E(e){let t=o(e),n=t===`toggle`?!await T():t===`dark`;await O().setDarkMode(n)}async function D(e){return await O().listApps(e)}function O(){let e=c().macosHost;if(!e)throw new t(`UNSUPPORTED_OPERATION`,`macOS host provider is not available`);return e}var k=e({buildAppNotInstalledError:()=>F,findIosSimulatorInstalledApp:()=>I,invalidateIosAppResolutionCache:()=>N,listIosApps:()=>B,resolveIosApp:()=>P,resolveIosAppAlias:()=>L,resolveIosSimulatorDeepLinkBundleId:()=>R});const A={settings:`com.apple.Preferences`},j=i();function M(e){return{platform:`ios`,deviceId:e.id,variant:e.kind}}async function N(e,t){return await j.invalidateWhile(M(e),t)}async function P(e,r){if(n(e))return await b(r);let i=r.trim();if(i.includes(`.`))return i;let a=L(i);if(a!==i)return a;let o=M(e),s=j.get(o,i);if(s)return s;let c=(e.kind===`simulator`?await V(e):await f(e).listApps(e,`all`)).filter(e=>e.name.toLowerCase()===i.toLowerCase()),l=c[0];if(l!==void 0&&c.length===1)return j.set(o,i,l.bundleId);throw c.length>1?new t(`INVALID_ARGS`,`Multiple apps matched "${r}"`,{matches:c}):F(r)}function F(e){return new t(`APP_NOT_INSTALLED`,`No app found matching "${e}"`)}async function I(e,t){if(!r(e)||e.kind!==`simulator`||e.booted!==!0)return;let n=L(t.trim());if(!n)return;let i=await V(e);if(n.includes(`.`))return i.find(e=>e.bundleId===n)?.bundleId;let a=i.filter(e=>e.name.toLowerCase()===n.toLowerCase());return a.length===1?a[0]?.bundleId:void 0}function L(e){let t=e.trim();return A[t.toLowerCase()]??e}async function R(e,t){if(!r(e)||e.kind!==`simulator`)return;let n=z(t);if(!n)return;let i=await H(e),a=[];for(let e of i)e.bundleId.startsWith(`com.callstack.agentdevice.runner`)||e.path&&(await W(p.join(e.path,`Info.plist`))).has(n)&&a.push(e);let o=a.filter(e=>e.applicationType===`User`);if(o.length===1)return o[0]?.bundleId;if(!(o.length>1))return a.length===1?a[0]?.bundleId:void 0}function z(e){return/^([A-Za-z][A-Za-z0-9+.-]*):/.exec(e.trim())?.[1]?.toLowerCase()}async function B(e,t){return n(e)?await D(t):e.kind===`simulator`?l(await V(e),t):await f(e).listApps(e,t)}async function V(e){return(await H(e)).map(e=>({bundleId:e.bundleId,name:e.name}))}async function H(e){let t=(await d(e,[`listapps`,e.id],{allowFailure:!0})).stdout.trim();if(!t)return[];let n=null;if(t.startsWith(`{`))try{n=JSON.parse(t)}catch{n=null}if(!n&&t.startsWith(`{`))try{let e=await s(`plutil`,[`-convert`,`json`,`-o`,`-`,`-`],{allowFailure:!0,stdin:t});e.exitCode===0&&e.stdout.trim().startsWith(`{`)&&(n=JSON.parse(e.stdout))}catch{n=null}return n?Object.entries(n).map(([e,t])=>{let n=U(t);return{bundleId:e,name:t.CFBundleDisplayName??t.CFBundleName??e,...n?{path:n}:{},...t.ApplicationType?{applicationType:t.ApplicationType}:{}}}):[]}function U(e){if(e.Path)return e.Path;if(e.Bundle)try{return m(e.Bundle)}catch{return}}async function W(e){let t=await s(`plutil`,[`-convert`,`json`,`-o`,`-`,e],{allowFailure:!0});if(t.exitCode!==0)return new Set;try{let e=JSON.parse(t.stdout),n=new Set;for(let t of e.CFBundleURLTypes??[])if(Array.isArray(t.CFBundleURLSchemes))for(let e of t.CFBundleURLSchemes)typeof e==`string`&&e.trim()&&n.add(e.trim().toLowerCase());return n}catch{return new Set}}export{P as a,x as c,w as d,B as i,C as l,I as n,R as o,N as r,S as s,k as t,E as u};
1
+ import{o as e}from"./png-worker-contract.js";import{_ as t}from"./sdk-contracts.js";import{s as n}from"./sdk-batch-runner.js";import{c as r,s as i}from"./sdk-device.js";import{c as a}from"./install-artifact.js";import{n as o}from"./setting-state.js";import{i as s,r as c,s as l}from"./tool-provider.js";import{n as u}from"./helper.js";import{a as d,t as f}from"./physical-device-control.js";import p from"node:path";import{fileURLToPath as m}from"node:url";const h={settings:`com.apple.systempreferences`},g=/^[a-z0-9-]+(?:\.[a-z0-9-]+)+$/,_={platform:`macos`,deviceId:`host`,variant:`all`},v=a();function y(e){return g.test(e)}async function b(e){let n=e.trim(),r=h[n.toLowerCase()];if(r)return r;if(y(n))return n;let i=v.get(_,n);if(i)return i;let a=(await D(`all`)).filter(e=>e.name.toLowerCase()===n.toLowerCase()),o=a[0];if(o!==void 0&&a.length===1)return v.set(_,n,o.bundleId);throw a.length>1?new t(`INVALID_ARGS`,`Multiple apps matched "${e}"`,{matches:a}):new t(`APP_NOT_INSTALLED`,`No app found matching "${e}"`)}async function x(e,r,i){let a=i?.url?.trim();if(a){if(!n(a))throw new t(`INVALID_ARGS`,`open <app> <url> requires a valid URL target`);let e=i?.appBundleId??await b(r);await O().openBundle(e,a);return}let o=r.trim();if(n(o)){await O().openTarget(o);return}let s=i?.appBundleId??await b(o);await O().openBundle(s)}async function S(e,n){let r=await b(n),i=await u(r);if(!(!i.running||i.terminated||i.forceTerminated))throw new t(`COMMAND_FAILED`,`Failed to close macOS app ${n}`,{bundleId:r,running:i.running,terminated:i.terminated,forceTerminated:i.forceTerminated})}async function C(){return await O().readClipboard()}async function w(e){await O().writeClipboard(e)}async function T(){return await O().readDarkMode()}async function E(e){let t=o(e),n=t===`toggle`?!await T():t===`dark`;await O().setDarkMode(n)}async function D(e){return await O().listApps(e)}function O(){let e=c().macosHost;if(!e)throw new t(`UNSUPPORTED_OPERATION`,`macOS host provider is not available`);return e}var k=e({buildAppNotInstalledError:()=>F,findIosSimulatorInstalledApp:()=>I,invalidateIosAppResolutionCache:()=>N,listIosApps:()=>B,resolveIosApp:()=>P,resolveIosAppAlias:()=>L,resolveIosSimulatorDeepLinkBundleId:()=>R});const A={settings:`com.apple.Preferences`},j=a();function M(e){return{platform:`ios`,deviceId:e.id,variant:e.kind}}async function N(e,t){return await j.invalidateWhile(M(e),t)}async function P(e,n){if(r(e))return await b(n);let i=n.trim();if(i.includes(`.`))return i;let a=L(i);if(a!==i)return a;let o=M(e),s=j.get(o,i);if(s)return s;let c=(e.kind===`simulator`?await V(e):await f(e).listApps(e,`all`)).filter(e=>e.name.toLowerCase()===i.toLowerCase()),l=c[0];if(l!==void 0&&c.length===1)return j.set(o,i,l.bundleId);throw c.length>1?new t(`INVALID_ARGS`,`Multiple apps matched "${n}"`,{matches:c}):F(n)}function F(e){return new t(`APP_NOT_INSTALLED`,`No app found matching "${e}"`)}async function I(e,t){if(!i(e)||e.kind!==`simulator`||e.booted!==!0)return;let n=L(t.trim());if(!n)return;let r=await V(e);if(n.includes(`.`))return r.find(e=>e.bundleId===n)?.bundleId;let a=r.filter(e=>e.name.toLowerCase()===n.toLowerCase());return a.length===1?a[0]?.bundleId:void 0}function L(e){let t=e.trim();return A[t.toLowerCase()]??e}async function R(e,t){if(!i(e)||e.kind!==`simulator`)return;let n=z(t);if(!n)return;let r=await H(e),a=[];for(let e of r)e.bundleId.startsWith(`com.callstack.agentdevice.runner`)||e.path&&(await W(p.join(e.path,`Info.plist`))).has(n)&&a.push(e);let o=a.filter(e=>e.applicationType===`User`);if(o.length===1)return o[0]?.bundleId;if(!(o.length>1))return a.length===1?a[0]?.bundleId:void 0}function z(e){return/^([A-Za-z][A-Za-z0-9+.-]*):/.exec(e.trim())?.[1]?.toLowerCase()}async function B(e,t){return r(e)?await D(t):e.kind===`simulator`?l(await V(e),t):await f(e).listApps(e,t)}async function V(e){return(await H(e)).map(e=>({bundleId:e.bundleId,name:e.name}))}async function H(e){let t=(await d(e,[`listapps`,e.id],{allowFailure:!0})).stdout.trim();if(!t)return[];let n=null;if(t.startsWith(`{`))try{n=JSON.parse(t)}catch{n=null}if(!n&&t.startsWith(`{`))try{let e=await s(`plutil`,[`-convert`,`json`,`-o`,`-`,`-`],{allowFailure:!0,stdin:t});e.exitCode===0&&e.stdout.trim().startsWith(`{`)&&(n=JSON.parse(e.stdout))}catch{n=null}return n?Object.entries(n).map(([e,t])=>{let n=U(t);return{bundleId:e,name:t.CFBundleDisplayName??t.CFBundleName??e,...n?{path:n}:{},...t.ApplicationType?{applicationType:t.ApplicationType}:{}}}):[]}function U(e){if(e.Path)return e.Path;if(e.Bundle)try{return m(e.Bundle)}catch{return}}async function W(e){let t=await s(`plutil`,[`-convert`,`json`,`-o`,`-`,e],{allowFailure:!0});if(t.exitCode!==0)return new Set;try{let e=JSON.parse(t.stdout),n=new Set;for(let t of e.CFBundleURLTypes??[])if(Array.isArray(t.CFBundleURLSchemes))for(let e of t.CFBundleURLSchemes)typeof e==`string`&&e.trim()&&n.add(e.trim().toLowerCase());return n}catch{return new Set}}export{P as a,x as c,w as d,B as i,C as l,I as n,R as o,N as r,S as s,k as t,E as u};
package/dist/src/apps.js CHANGED
@@ -1,5 +1,5 @@
1
- import{t as e}from"./rolldown-runtime.js";import{_ as t}from"./sdk-contracts.js";import{h as n,j as r,p as i}from"./sdk-android-adb.js";import{ft as a}from"./recording-export-quality.js";import{c as o,s}from"./sdk-device.js";import{S as c}from"./gesture-plan-types.js";import{a as l}from"./runtime.js";import{d as u,l as d,u as ee}from"./install-artifact.js";import{l as f,o as te,s as p}from"./boot-diagnostics.js";import{a as ne,i as re,n as ie,r as ae,t as m}from"./setting-state.js";import{n as oe,t as se}from"./launch-console.js";import{a as h,i as g}from"./tool-provider.js";import{a as ce}from"./helper.js";import{L as le,R as _,_ as ue,a as v,d as y,g as de,h as fe,i as pe,l as me,m as he,n as b,o as x,r as S,t as C,v as ge,y as _e}from"./physical-device-control.js";import{a as w}from"./runner-client.js";import{St as ve,xt as ye}from"./internal/daemon.js";import{i as T,n as be,t as E}from"./simulator.js";import{a as D,c as xe,d as Se,i as Ce,l as we,n as Te,o as Ee,r as O,s as De,u as Oe}from"./app-resolution.js";import{n as ke}from"./install-artifact2.js";import{promises as k}from"node:fs";import Ae from"node:os";import A from"node:path";const je=[`--time`,`9:41`,`--dataNetwork`,`wifi`,`--wifiMode`,`active`,`--wifiBars`,`3`,`--batteryState`,`charged`,`--batteryLevel`,`100`],Me={0:`hide`,1:`wifi`,6:`3g`,7:`4g`,8:`lte`,9:`lte-a`,10:`lte+`,11:`5g`,12:`5g+`,13:`5g-uwb`,14:`5g-uc`},Ne={1:`searching`,2:`failed`,3:`active`},Pe={0:`notSupported`,1:`searching`,2:`failed`,3:`active`};function j(e,t,n){return _(e,t,n)}const M=new Set;async function Fe(e){let t=null,n=!1;if(Re(e))n=!0;else try{t=await ze(e),n=!0}catch(t){L(e,`snapshot_failed`,t)}try{(!n||t)&&await F(e),await I(e,je)}catch(t){L(e,`prepare_failed`,t)}return async()=>{await Ie(e,n?t:null)}}async function Ie(e,t){if(await F(e),!t){Le(e);return}await I(e,Ve(t)),N(e)}function N(e){M.delete(P(e))}function Le(e){M.add(P(e))}function Re(e){return M.has(P(e))}function P(e){return`${e.platform}:${e.kind}:${e.id}`}async function ze(e){return Be(n(await j(e,[`status_bar`,e.id,`list`],{allowFailure:!0}),`Failed to read simulator status bar overrides`).stdout)}async function F(e){await j(e,[`status_bar`,e.id,`clear`])}async function I(e,t){t.length!==0&&await j(e,[`status_bar`,e.id,`override`,...t])}function Be(e){let n={},r=e.split(`
2
- `).map(e=>e.trim()).filter(e=>e.length>0&&e!==`Current Status Bar Overrides:`&&!/^=+$/.test(e));for(let e of r){let r=/^DataNetworkType:\s+(\d+)$/.exec(e);if(r){let e=Number(r[1]),i=Me[e];if(!i)throw new t(`COMMAND_FAILED`,`Unsupported simulator data network type: ${e}`);n.dataNetwork=i;continue}let i=/^WiFi Mode:\s+(\d+),\s+WiFi Bars:\s+(\d+)$/.exec(e);if(i){let e=Ne[Number(i[1])];e&&(n.wifiMode=e),n.wifiBars=i[2];continue}let a=/^Cell Mode:\s+(\d+),\s+Cell Bars:\s+(\d+)$/.exec(e);if(a){let e=Number(a[1]),r=Pe[e];if(!r)throw new t(`COMMAND_FAILED`,`Unsupported simulator cellular mode: ${e}`);n.cellularMode=r,n.cellularBars=a[2];continue}let o=/^Operator Name:\s*(.*)$/.exec(e);if(o){n.operatorName=o[1]??``;continue}}return Object.keys(n).length===0?null:n}function Ve(e){let t=[];return e.dataNetwork&&t.push(`--dataNetwork`,e.dataNetwork),e.wifiMode&&t.push(`--wifiMode`,e.wifiMode),e.wifiBars!==void 0&&(e.dataNetwork===`wifi`||e.wifiMode)&&t.push(`--wifiBars`,e.wifiBars),e.cellularMode&&t.push(`--cellularMode`,e.cellularMode),e.cellularBars!==void 0&&(e.cellularMode||He(e.dataNetwork)||e.operatorName!==void 0)&&t.push(`--cellularBars`,e.cellularBars),e.operatorName!==void 0&&t.push(`--operatorName`,e.operatorName),t}function He(e){return!!(e&&e!==`hide`&&e!==`wifi`)}function L(e,t,n){r({level:`warn`,phase:`ios_screenshot_status_bar_${t}`,data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,...S(n)}})}function R(e,t,n){return _(e,t,n)}const Ue={ensureBooted:E,prepareStatusBarForScreenshot:Fe,captureWithRetry:Ke,normalizeDensity:Qe,captureWithRunner:V,shouldFallbackToRunner:W},z=new Map,B=new Map;async function We(e,t,n={}){if(o(e)){await V(e,t,n.appBundleId,n.fullscreen,n.runnerOptions);return}if(e.kind===`simulator`){await Ge(e,t,n);return}await C(e).captureScreenshot(e,t,{appBundleId:n.appBundleId,fullscreen:n.fullscreen,runnerOptions:n.runnerOptions,runRunnerCommand:w})}async function Ge(e,n,r={}){if(e.kind!==`simulator`)throw new t(`UNSUPPORTED_OPERATION`,`Simulator screenshot fallback flow supports only iOS simulators`);let i={...Ue,...r.deps??{}};r.skipIosSimulatorBootCheck||await i.ensureBooted(e);let a=async()=>{};if(r.normalizeStatusBar===!0)try{a=await i.prepareStatusBarForScreenshot(e)}catch(t){U(e,`prepare_failed`,t)}try{try{await i.captureWithRetry(e,n),await i.normalizeDensity(e,n,r.pixelDensity);return}catch(t){let a=t;if(r.skipIosSimulatorBootCheck&&Ze(t)){await i.ensureBooted(e);try{await i.captureWithRetry(e,n),await i.normalizeDensity(e,n,r.pixelDensity);return}catch(e){a=e}}if(!i.shouldFallbackToRunner(a))throw a;Ye(e,`simctl_screenshot`,a)}await i.captureWithRunner(e,n,r.appBundleId,r.fullscreen,r.runnerOptions),await i.normalizeDensity(e,n,r.pixelDensity)}finally{await a().catch(t=>U(e,`restore_failed`,t))}}async function Ke(e,t){let n=p.fromTimeoutMs(ue);await f(async({deadline:n})=>{await R(e,[`io`,e.id,`screenshot`,t],{timeoutMs:Math.max(1e3,n?.remainingMs()??2e4)})},{maxAttempts:5,baseDelayMs:he,maxDelayMs:fe,jitter:.2,shouldRetry:e=>W(e)},{deadline:n,phase:`ios_simulator_screenshot`})}async function V(e,n,r,i,a){if(e.kind===`device`&&!o(e)){await C(e).captureScreenshot(e,n,{appBundleId:r,fullscreen:i,runnerOptions:a,preferRunner:!0,runRunnerCommand:w});return}let s=(await w(e,{command:`screenshot`,appBundleId:r,fullscreen:i,inlineScreenshot:!1},a)).message;if(!s)throw new t(`COMMAND_FAILED`,`Failed to capture iOS screenshot: runner returned no file path`);if(o(e)){await k.copyFile(s,n);return}if(e.kind===`simulator`){await qe(e,s,n);return}throw new t(`COMMAND_FAILED`,`Unsupported Apple screenshot target`)}async function qe(e,n,r){let i=p.fromTimeoutMs(y),a=`Unable to locate runner container for simulator screenshot`,o=B.get(e.id);if(o){let t=await H(o,n,r);if(t.copied)return;a=t.error,B.delete(e.id)}for(let t of _e){let o=await R(e,[`get_app_container`,e.id,t,`data`],{allowFailure:!0,timeoutMs:Je(i,y,`runner screenshot container lookup`)});if(o.exitCode!==0){let e=o.stderr.trim();e&&(a=e);continue}let s=o.stdout.trim();if(!s){a=`simctl get_app_container returned empty output`;continue}let c=await H(s,n,r);if(c.copied){B.set(e.id,s);return}a=c.error}throw new t(`COMMAND_FAILED`,`Failed to capture iOS screenshot: ${a}`)}async function H(e,t,n){let r=`Runner screenshot was not found in the simulator container`;for(let i of Xe(e,t))try{return await k.copyFile(i,n),{copied:!0}}catch(e){r=e instanceof Error?e.message:String(e)}return{copied:!1,error:r}}function Je(e,n,r){let i=e.remainingMs();if(i>0)return i;throw new t(`COMMAND_FAILED`,`iOS ${r} timed out after ${n}ms`,{timeoutMs:n,step:r})}function Ye(e,t,n){let i=S(n);r({level:`warn`,phase:`ios_screenshot_fallback`,data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,from:t,to:`runner`,...i}})}function U(e,t,n){r({level:`warn`,phase:`ios_screenshot_status_bar_${t}`,data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,...S(n)}})}function Xe(e,t){let n=A.resolve(e),r=t.trim();if(!r)return[];let i=[],a=new Set,o=e=>{let t=A.normalize(e);a.has(t)||(a.add(t),i.push(t))},s=r.replace(/^\/+/,``),c=s.replace(/\\/g,`/`);if(s&&o(A.join(n,s)),A.isAbsolute(r)&&o(A.normalize(r)),c.startsWith(`tmp/`))o(A.join(n,c));else{let e=c.lastIndexOf(`/tmp/`);if(e>=0){let t=c.slice(e+1);o(A.join(n,t))}}let l=A.basename(r);return l&&o(A.join(n,`tmp`,l)),i}function W(e){if(!(e instanceof t)||e.code!==`COMMAND_FAILED`)return!1;let n=b(e);return n.includes(`timeout waiting for screen surfaces`)||n.includes(`nsposixerrordomain`)&&n.includes(`code=60`)&&n.includes(`screenshot`)||n.includes(`timed out`)&&n.includes(`screenshot`)}function Ze(e){if(!(e instanceof t)||e.code!==`COMMAND_FAILED`)return!1;let n=b(e);return n.includes(`not booted`)||n.includes(`current state: shutdown`)||n.includes(`current state is shutdown`)||n.includes(`current state=shutdown`)||n.includes(`state: shutdown`)||n.includes(`state=shutdown`)}async function Qe(e,t,n){let r=await $e(e),i=ye(await ve(t),r,n);i&&await l(t,i.width,i.height)}async function $e(e){let n=z.get(e.id);if(n!==void 0)return n;let r=await R(e,[`getenv`,e.id,`SIMULATOR_MAINSCREEN_SCALE`],{timeoutMs:de}),i=Number(r.stdout.trim());if(!Number.isFinite(i)||i<=0)throw new t(`COMMAND_FAILED`,`Failed to read iOS simulator screenshot scale from SIMULATOR_MAINSCREEN_SCALE`);return z.set(e.id,i),i}function G(e){if(!(e instanceof t)||e.code!==`COMMAND_FAILED`)return!1;let n=e.details??{};if(n.exitCode!==4)return!1;let r=String(n.stderr??``).toLowerCase();return r.includes(`fbsopenapplicationserviceerrordomain`)&&r.includes(`the request to open`)}async function et(e,t){let n=await h(le(e,[`get_app_container`,e.id,t]),{allowFailure:!0});if(n.exitCode!==0)return{installed:!1};let r=n.stdout.trim();if(!r)return{installed:!1};let i=await g(`plutil`,[`-extract`,`CFBundleExecutable`,`raw`,`-o`,`-`,`${r}/Info.plist`],{allowFailure:!0});if(i.exitCode!==0||!i.stdout.trim())return{installed:!0};let a=await g(`otool`,[`-l`,`${r}/${i.stdout.trim()}`],{allowFailure:!0});if(a.exitCode!==0)return{installed:!0};let o=a.stdout.toLowerCase();return{installed:!0,simulatorCompatible:o.includes(`iossimulator`)||o.includes(`platform 7`)}}function tt(e){return e.installed?e.simulatorCompatible===!1?`ARCH_MISMATCH`:`PERSISTENT_LAUNCH_FAIL`:`APP_NOT_INSTALLED`}function nt(e){switch(e){case`ARCH_MISMATCH`:return`The app binary was not built for the simulator platform. Rebuild with a simulator destination or use a physical device.`;case`APP_NOT_INSTALLED`:return`The app bundle is not installed on this simulator. Run install before open.`;case`PERSISTENT_LAUNCH_FAIL`:return`The simulator repeatedly refused to launch the app. Inspect crash logs in Console.app or ~/Library/Logs/DiagnosticReports/ and consider reinstalling the app.`;default:return`The simulator failed to launch the app. Retry with --debug and inspect diagnostics log for details.`}}const K=25e3;async function rt(e,n,r){let i=r?.launchConsole?.trim(),a=r?.launchArgs;if(i&&(!s(e)||e.kind!==`simulator`))throw new t(`UNSUPPORTED_OPERATION`,oe);if(o(e)){if(a&&a.length>0)throw new t(`UNSUPPORTED_OPERATION`,`--launch-args is not supported on macOS; launch arguments are currently iOS-only.`);await xe(e,n,r);return}let c=r?.url?.trim();if(c){if(i)throw new t(`INVALID_ARGS`,se);if(!d(c))throw new t(`INVALID_ARGS`,`open <app> <url> requires a valid URL target`);if(e.kind===`simulator`){(a||ee(c))&&await Y(e,r?.appBundleId??await D(e,n),{...a?{launchArgs:a}:{}}),await q(e,c,void 0);return}let o=u(r?.appBundleId??await D(e,n),c);if(!o)throw new t(`INVALID_ARGS`,`Deep link open on iOS devices requires an active app bundle ID. Open the app first, then open the URL.`);await Z(e,o,{payloadUrl:c,launchArgs:a,runnerOptions:r?.runnerOptions});return}let l=n.trim();if(d(l)){if(i)throw new t(`INVALID_ARGS`,se);if(e.kind===`simulator`){await q(e,l,a);return}let n=u(r?.appBundleId,l);if(!n)throw new t(`INVALID_ARGS`,`Deep link open on iOS devices requires an active app bundle ID. Open the app first, then open the URL.`);await Z(e,n,{payloadUrl:l,launchArgs:a,runnerOptions:r?.runnerOptions});return}let f=r?.appBundleId??await D(e,n);if(e.kind===`simulator`){await Y(e,f,{...i?{launchConsole:i}:{},...a?{launchArgs:a}:{},...r?.terminateRunningApp?{terminateRunningApp:!0}:{}});return}await Z(e,f,{launchArgs:a,runnerOptions:r?.runnerOptions})}async function q(e,n,r){if(r&&r.length>0)throw new t(`INVALID_ARGS`,`--launch-args is not supported with iOS simulator URL opens (simctl openurl ignores launch args). Launch the app first with --launch-args, then issue the URL open in a separate call.`);await E(e),await v(e,[`openurl`,e.id,n])}async function it(e){o(e)||e.kind===`simulator`&&await be(e)!==`Booted`&&await E(e)}async function J(e,n,r){if(o(e)){await De(e,n);return}let a=await D(e,n);if(e.kind===`simulator`){await E(e);let n=x(e,[`terminate`,e.id,a]),r=await h(n,{allowFailure:!0,timeoutMs:ge});if(r.exitCode!==0){if(r.stderr.toLowerCase().includes(`found nothing to terminate`))return;throw new t(`COMMAND_FAILED`,`xcrun exited with code ${r.exitCode}`,i(r,{cmd:`xcrun`,args:n}))}return}await C(e).terminateApp(e,a,{runnerOptions:r,runRunnerCommand:w})}async function Y(e,n,r){await E(e);let a=0,o=p.fromTimeoutMs(me);try{await f(async({deadline:a})=>{if(a?.isExpired())throw new t(`COMMAND_FAILED`,`App launch deadline exceeded`,{timeoutMs:me});let o=x(e,at(e.id,n,r)),s=r?.launchConsole?await ot(o,r.launchConsole):await h(o,{allowFailure:!0});if(s.exitCode!==0)throw new t(`COMMAND_FAILED`,`xcrun exited with code ${s.exitCode}`,i(s,{cmd:`xcrun`,args:o}))},{maxAttempts:10,baseDelayMs:1e3,maxDelayMs:5e3,jitter:.2,shouldRetry(e){return G(e)?(a+=1,a<3):!1}},{deadline:o})}catch(t){if(G(t)){let r=t,i=tt(await et(e,n));r.details={...r.details,hint:nt(i)}}throw t}}function at(e,t,n){let r=[`launch`];return n?.launchConsole&&r.push(`--console-pty`),n?.terminateRunningApp&&r.push(`--terminate-running-process`),r.push(e,t),n?.launchArgs&&n.launchArgs.length>0&&r.push(...n.launchArgs),r}async function ot(e,n){await k.mkdir(A.dirname(n),{recursive:!0});try{let t=await h(e,{allowFailure:!0,timeoutMs:K});return await X(n,t.stdout,t.stderr),t}catch(e){let i=(e instanceof t?e:void 0)?.details;if(i?.timeoutMs===K){let e=typeof i.stdout==`string`?i.stdout:``,t=typeof i.stderr==`string`?i.stderr:``;return await X(n,e,t),r({level:`warn`,phase:`ios_simulator_launch_console_capture_timeout`,data:{timeoutMs:K,logPath:n,stdoutBytes:Buffer.byteLength(e),stderrBytes:Buffer.byteLength(t)}}),{stdout:e,stderr:t,exitCode:0}}throw e}}async function X(e,t,n){await k.writeFile(e,st(t,n),`utf8`)}function st(e,t){return!e||!t||e.endsWith(`
3
- `)||e.endsWith(`\r`)?`${e}${t}`:`${e}\n${t}`}async function Z(e,t,n){await C(e).launchApp(e,t,{...n,runRunnerCommand:w})}async function ct(e,n){return await O(e,async()=>{let r=await D(e,n);if(e.kind!==`simulator`)return await C(e).uninstallApp(e,r),{bundleId:r};await E(e);let a=await v(e,[`uninstall`,e.id,r],{allowFailure:!0});if(a.exitCode!==0&&!pe(`${a.stdout}\n${a.stderr}`.toLowerCase()))throw new t(`COMMAND_FAILED`,`simctl uninstall failed for ${r}`,i(a));return{bundleId:r}})}async function lt(e,t,n){e.kind!==`simulator`&&C(e).assertAppInstallationSupported(e);let r=await ke({kind:`path`,path:t},n);try{return await Q(e,r.installablePath),{archivePath:r.archivePath,installablePath:r.installablePath,bundleId:r.bundleId,appName:r.appName,launchTarget:r.bundleId}}finally{await r.cleanup()}}async function ut(e,t,n){return e.kind!==`simulator`&&C(e).assertAppInstallationSupported(e),await O(e,async()=>{let{bundleId:r}=await ct(e,t);return await lt(e,n,{appIdentifierHint:t}),{bundleId:r}})}async function Q(e,t){await O(e,async()=>{if(e.kind!==`simulator`){await C(e).installApp(e,t);return}await E(e),await v(e,[`install`,e.id,t])})}async function dt(e){return o(e)?await we():(T(e,`clipboard`),await E(e),n(await v(e,[`pbpaste`,e.id],{allowFailure:!0}),`Failed to read iOS simulator clipboard`).stdout.replace(/\r\n/g,`
4
- `).replace(/\n$/,``))}async function ft(e,t){if(o(e)){await Se(t);return}T(e,`clipboard`),await E(e),n(await v(e,[`pbcopy`,e.id],{allowFailure:!0,stdin:t}),`Failed to write iOS simulator clipboard`)}async function pt(e,t,n){T(e,`push`),await E(e);let r=await k.mkdtemp(A.join(Ae.tmpdir(),`agent-device-ios-push-`)),i=A.join(r,`payload.apns`);try{await k.writeFile(i,`${JSON.stringify(n)}\n`,`utf8`),await v(e,[`push`,e.id,t,i])}finally{await k.rm(r,{recursive:!0,force:!0})}}let $=null,mt;async function ht(e,n,r,i,s){if(o(e)){let e=n.toLowerCase();if(e===`appearance`){await Oe(r);return}if(e===`permission`){let e=ae(r);if(e===`deny`)throw new t(`INVALID_ARGS`,a(`permission`));return await ce(e,_t(s?.permissionTarget))}throw new t(`INVALID_ARGS`,a(n))}T(e,`settings`),await E(e);let l=n.toLowerCase();switch(l){case`clear-app-state`:{if(r.toLowerCase()!==`clear`)throw new t(`INVALID_ARGS`,`settings clear-app-state only supports clear.`);if(!i)throw new t(`INVALID_ARGS`,`settings clear-app-state requires an app id or an active app session.`);let n=await gt(e,i);return{bundleId:n.bundleId,containerPath:n.containerPath,cleared:!0}}case`wifi`:{let t=m(r)?`active`:`failed`;await v(e,[`status_bar`,e.id,`override`,`--wifiMode`,t]),N(e);return}case`airplane`:m(r)?(await v(e,[`status_bar`,e.id,`override`,`--dataNetwork`,`hide`,`--wifiMode`,`failed`,`--wifiBars`,`0`,`--cellularMode`,`failed`,`--cellularBars`,`0`,`--operatorName`,``]),N(e)):(await v(e,[`status_bar`,e.id,`clear`]),Le(e));return;case`location`:{if(r.toLowerCase()===`set`){let{latitude:t,longitude:n}=c(s);return await v(e,[`location`,e.id,`set`,`${t},${n}`]),{latitude:t,longitude:n}}let n=m(r);if(!i)throw new t(`INVALID_ARGS`,`location setting requires an active app in session`);let a=n?`grant`:`revoke`;await v(e,[`privacy`,e.id,a,`location`,i]);return}case`faceid`:case`touchid`:{let t=l,n=bt[t];await Ot(e,Dt(r,t),{settingName:t,label:n.label,modalityAliases:n.modalityAliases});return}case`appearance`:{let t=await vt(e,r);await v(e,[`ui`,e.id,`appearance`,t]);return}case`permission`:if(!i)throw new t(`INVALID_ARGS`,`permission setting requires an active app in session`);await St(e,xt(ae(r)),Et(s?.permissionTarget,s?.permissionMode),i);return;default:throw new t(`INVALID_ARGS`,`Unsupported setting: ${n}`)}}async function gt(e,r){if(!s(e)||e.kind!==`simulator`)throw new t(`UNSUPPORTED_OPERATION`,`Clearing app state is currently supported only on iOS simulators.`);let i=await D(e,r);await E(e),await J(e,i);let a=n(await v(e,[`get_app_container`,e.id,i,`data`],{allowFailure:!0}),`simctl get_app_container failed for ${i}`).stdout.trim();if(!a)throw new t(`COMMAND_FAILED`,`simctl get_app_container returned an empty data container path for ${i}`);let o=await k.readdir(a);return await Promise.all(o.map(e=>k.rm(A.join(a,e),{recursive:!0,force:!0}))),{bundleId:i,containerPath:a}}function _t(e){let n=e?.trim().toLowerCase();if(n===`accessibility`||n===`screen-recording`||n===`input-monitoring`)return n;throw new t(`INVALID_ARGS`,`Unsupported macOS permission target. Use accessibility|screen-recording|input-monitoring.`)}async function vt(e,r){let i=ie(r);if(i!==`toggle`)return i;let a=n(await v(e,[`ui`,e.id,`appearance`],{allowFailure:!0}),`Failed to read current iOS appearance`),o=yt(a.stdout,a.stderr);if(!o)throw new t(`COMMAND_FAILED`,`Unable to determine current iOS appearance for toggle`,{stdout:a.stdout,stderr:a.stderr});return o===`dark`?`light`:`dark`}function yt(e,t){let n=/\b(light|dark|unsupported|unknown)\b/i.exec(`${e}\n${t}`);if(!n)return null;let r=n[1]?.toLowerCase();return r===`dark`?`dark`:r===`light`?`light`:null}const bt={faceid:{label:`Face ID`,modalityAliases:[`face`]},touchid:{label:`Touch ID`,modalityAliases:[`finger`,`touch`]}};function xt(e){return e===`deny`?`revoke`:e}async function St(e,n,r,i){let a=await wt(e);if(!a.has(r))throw new t(`UNSUPPORTED_OPERATION`,`iOS simctl privacy does not support service "${r}" on this runtime.`,{deviceId:e.id,appBundleId:i,hint:`Supported services: ${Array.from(a).sort().join(`, `)}`});let o=[`privacy`,e.id,n,r,i],s=r===`notifications`;if(!(n===`reset`&&s))try{await v(e,o);return}catch(n){throw s&&Ct(n)?new t(`UNSUPPORTED_OPERATION`,`iOS simulator does not support setting notifications permission via simctl privacy on this runtime.`,{deviceId:e.id,appBundleId:i,hint:`Use reset notifications for reprompt behavior, or toggle notifications manually in Settings.`}):n}try{await v(e,o);return}catch(e){if(!Ct(e))throw e}try{await v(e,[`privacy`,e.id,`reset`,`all`,i])}catch(n){throw new t(`COMMAND_FAILED`,`iOS simulator blocked direct notifications reset. Fallback reset-all also failed.`,{deviceId:e.id,appBundleId:i,hint:`Use reinstall to force a fresh notifications prompt, or reset simulator content and settings.`},n instanceof Error?n:void 0)}}function Ct(e){if(!(e instanceof t)||e.code!==`COMMAND_FAILED`)return!1;let n=String(e.details?.stderr??``).toLowerCase();return(n.includes(`failed to grant access`)||n.includes(`failed to revoke access`)||n.includes(`failed to reset access`))&&n.includes(`operation not permitted`)}async function wt(e){let n=te(e.simulatorSetPath),r=`${process.env.PATH??``}::${n??``}`;if($&&mt===r)return $;let i=await v(e,[`privacy`,`help`],{allowFailure:!0}),a=Tt(`${i.stdout}\n${i.stderr}`);if(a.size===0)throw new t(`COMMAND_FAILED`,`Unable to determine supported simctl privacy services`,{stdout:i.stdout,stderr:i.stderr,exitCode:i.exitCode,hint:"Run `xcrun simctl privacy help` manually to verify available services for this runtime."});return $=a,mt=r,a}function Tt(e){let t=new Set,n=!1;for(let r of e.split(`
5
- `)){let e=r.trim();if(!e)continue;if(e===`service`){n=!0;continue}if(!n)continue;if(e.startsWith(`bundle identifier`))break;let i=/^([a-z-]+)\s+-\s+/.exec(e)?.[1];i!==void 0&&t.add(i)}return t}function Et(e,n){let r=re(e);if(r!==`photos`&&n?.trim())throw new t(`INVALID_ARGS`,`Permission mode is only supported for photos. Received: ${n}.`);if(r===`camera`)return`camera`;if(r===`microphone`)return`microphone`;if(r===`contacts`)return`contacts`;if(r===`contacts-limited`)return`contacts-limited`;if(r===`notifications`)return`notifications`;if(r===`calendar`)return`calendar`;if(r===`location`)return`location`;if(r===`location-always`)return`location-always`;if(r===`media-library`)return`media-library`;if(r===`motion`)return`motion`;if(r===`reminders`)return`reminders`;if(r===`siri`)return`siri`;if(r===`photos`){let e=n?.trim().toLowerCase();if(!e||e===`full`)return`photos`;if(e===`limited`)return`photos-add`;throw new t(`INVALID_ARGS`,`Invalid photos mode: ${n}. Use full|limited.`)}throw new t(`INVALID_ARGS`,`Unsupported permission target: ${e}. Use camera|microphone|photos|contacts|contacts-limited|notifications|calendar|location|location-always|media-library|motion|reminders|siri.`)}function Dt(e,n){let r=e.trim().toLowerCase();if(r===`match`)return`match`;if(r===`nonmatch`)return`nonmatch`;if(r===`enroll`)return`enroll`;if(r===`unenroll`)return`unenroll`;throw new t(`INVALID_ARGS`,`Invalid ${n} state: ${e}. Use match|nonmatch|enroll|unenroll.`)}async function Ot(e,n,r){let i=kt(e.id,n,r.modalityAliases),a=[];for(let t of i){let n=x(e,t),r=await h(n,{allowFailure:!0});if(r.exitCode===0)return;a.push({args:n,stderr:r.stderr,stdout:r.stdout,exitCode:r.exitCode})}let o=ne(a);throw a.length>0&&a.every(e=>At(e.stdout,e.stderr))?new t(`UNSUPPORTED_OPERATION`,`${r.label} simulation is not supported on this simulator runtime.`,{deviceId:e.id,action:n,setting:r.settingName,attempts:o}):new t(`COMMAND_FAILED`,`Failed to simulate ${r.settingName}.`,{deviceId:e.id,action:n,setting:r.settingName,attempts:o})}function kt(e,t,n){let r=n.length>0?n:[`face`];switch(t){case`match`:return r.flatMap(t=>[[`biometric`,e,`match`,t],[`biometric`,`match`,e,t]]);case`nonmatch`:return r.flatMap(t=>[[`biometric`,e,`nonmatch`,t],[`biometric`,e,`nomatch`,t],[`biometric`,`nonmatch`,e,t],[`biometric`,`nomatch`,e,t]]);case`enroll`:return[[`biometric`,e,`enroll`,`yes`],[`biometric`,e,`enroll`,`1`],[`biometric`,`enroll`,e,`yes`],[`biometric`,`enroll`,e,`1`]];case`unenroll`:return[[`biometric`,e,`enroll`,`no`],[`biometric`,e,`enroll`,`0`],[`biometric`,`enroll`,e,`no`],[`biometric`,`enroll`,e,`0`]]}}function At(e,t){let n=`${e}\n${t}`.toLowerCase();return n.includes(`unrecognized subcommand`)||n.includes(`unknown subcommand`)||n.includes(`not supported`)||n.includes(`unavailable`)||n.includes(`biometric`)&&n.includes(`invalid`)}var jt=e({findIosSimulatorInstalledApp:()=>Te,installIosApp:()=>lt,installIosInstallablePath:()=>Q,listIosApps:()=>Ce,pushIosNotification:()=>pt,reinstallIosApp:()=>ut,resolveIosApp:()=>D,resolveIosSimulatorDeepLinkBundleId:()=>Ee});export{J as a,V as c,ft as i,We as l,ht as n,rt as o,dt as r,it as s,jt as t};
1
+ import{o as e}from"./png-worker-contract.js";import{_ as t}from"./sdk-contracts.js";import{Mt as n,Nt as r}from"./viewport-dimension.js";import{i,l as a,s as o,u as s}from"./screenshot-geometry.js";import{c,l,s as u}from"./sdk-batch-runner.js";import{c as d,s as f}from"./sdk-device.js";import{a as p}from"./runtime.js";import{h as m,j as h,p as g}from"./sdk-android-adb.js";import{l as _,o as ee,s as v}from"./boot-diagnostics.js";import{n as te,r as ne,t as y}from"./setting-state.js";import{a as b,i as re}from"./tool-provider.js";import{a as ie}from"./helper.js";import{L as ae,R as x,_ as oe,a as S,d as se,g as ce,h as le,i as ue,l as de,m as fe,n as C,o as w,r as T,t as E,v as pe,y as me}from"./physical-device-control.js";import{a as D}from"./runner-client.js";import{Ft as he,Pt as ge}from"./internal/daemon.js";import{i as O,n as _e,t as k}from"./simulator.js";import{a as A,c as ve,d as ye,i as be,l as xe,n as Se,o as Ce,r as j,s as we,u as Te}from"./app-resolution.js";import{n as Ee}from"./install-artifact2.js";import M from"node:path";import{promises as N}from"node:fs";import De from"node:os";const Oe=[`--time`,`9:41`,`--dataNetwork`,`wifi`,`--wifiMode`,`active`,`--wifiBars`,`3`,`--batteryState`,`charged`,`--batteryLevel`,`100`],ke={0:`hide`,1:`wifi`,6:`3g`,7:`4g`,8:`lte`,9:`lte-a`,10:`lte+`,11:`5g`,12:`5g+`,13:`5g-uwb`,14:`5g-uc`},Ae={1:`searching`,2:`failed`,3:`active`},je={0:`notSupported`,1:`searching`,2:`failed`,3:`active`};function P(e,t,n){return x(e,t,n)}const F=new Set;async function Me(e){let t=null,n=!1;if(Fe(e))n=!0;else try{t=await Ie(e),n=!0}catch(t){B(e,`snapshot_failed`,t)}try{(!n||t)&&await R(e),await z(e,Oe)}catch(t){B(e,`prepare_failed`,t)}return async()=>{await Ne(e,n?t:null)}}async function Ne(e,t){if(await R(e),!t){Pe(e);return}await z(e,Re(t)),I(e)}function I(e){F.delete(L(e))}function Pe(e){F.add(L(e))}function Fe(e){return F.has(L(e))}function L(e){return`${e.platform}:${e.kind}:${e.id}`}async function Ie(e){return Le(m(await P(e,[`status_bar`,e.id,`list`],{allowFailure:!0}),`Failed to read simulator status bar overrides`).stdout)}async function R(e){await P(e,[`status_bar`,e.id,`clear`])}async function z(e,t){t.length!==0&&await P(e,[`status_bar`,e.id,`override`,...t])}function Le(e){let n={},r=e.split(`
2
+ `).map(e=>e.trim()).filter(e=>e.length>0&&e!==`Current Status Bar Overrides:`&&!/^=+$/.test(e));for(let e of r){let r=/^DataNetworkType:\s+(\d+)$/.exec(e);if(r){let e=Number(r[1]),i=ke[e];if(!i)throw new t(`COMMAND_FAILED`,`Unsupported simulator data network type: ${e}`);n.dataNetwork=i;continue}let i=/^WiFi Mode:\s+(\d+),\s+WiFi Bars:\s+(\d+)$/.exec(e);if(i){let e=Ae[Number(i[1])];e&&(n.wifiMode=e),n.wifiBars=i[2];continue}let a=/^Cell Mode:\s+(\d+),\s+Cell Bars:\s+(\d+)$/.exec(e);if(a){let e=Number(a[1]),r=je[e];if(!r)throw new t(`COMMAND_FAILED`,`Unsupported simulator cellular mode: ${e}`);n.cellularMode=r,n.cellularBars=a[2];continue}let o=/^Operator Name:\s*(.*)$/.exec(e);if(o){n.operatorName=o[1]??``;continue}}return Object.keys(n).length===0?null:n}function Re(e){let t=[];return e.dataNetwork&&t.push(`--dataNetwork`,e.dataNetwork),e.wifiMode&&t.push(`--wifiMode`,e.wifiMode),e.wifiBars!==void 0&&(e.dataNetwork===`wifi`||e.wifiMode)&&t.push(`--wifiBars`,e.wifiBars),e.cellularMode&&t.push(`--cellularMode`,e.cellularMode),e.cellularBars!==void 0&&(e.cellularMode||ze(e.dataNetwork)||e.operatorName!==void 0)&&t.push(`--cellularBars`,e.cellularBars),e.operatorName!==void 0&&t.push(`--operatorName`,e.operatorName),t}function ze(e){return!!(e&&e!==`hide`&&e!==`wifi`)}function B(e,t,n){h({level:`warn`,phase:`ios_screenshot_status_bar_${t}`,data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,...T(n)}})}function V(e,t,n){return x(e,t,n)}const Be={ensureBooted:k,prepareStatusBarForScreenshot:Me,captureWithRetry:Ue,normalizeDensity:Ye,captureWithRunner:W,shouldFallbackToRunner:q},H=new Map,U=new Map;async function Ve(e,t,n={}){if(d(e)){await W(e,t,n.appBundleId,n.fullscreen,n.runnerOptions);return}if(e.kind===`simulator`){await He(e,t,n);return}await E(e).captureScreenshot(e,t,{appBundleId:n.appBundleId,fullscreen:n.fullscreen,runnerOptions:n.runnerOptions,runRunnerCommand:D})}async function He(e,n,r={}){if(e.kind!==`simulator`)throw new t(`UNSUPPORTED_OPERATION`,`Simulator screenshot fallback flow supports only iOS simulators`);let i={...Be,...r.deps??{}};r.skipIosSimulatorBootCheck||await i.ensureBooted(e);let a=async()=>{};if(r.normalizeStatusBar===!0)try{a=await i.prepareStatusBarForScreenshot(e)}catch(t){K(e,`prepare_failed`,t)}try{try{await i.captureWithRetry(e,n),await i.normalizeDensity(e,n,r.pixelDensity);return}catch(t){let a=t;if(r.skipIosSimulatorBootCheck&&Je(t)){await i.ensureBooted(e);try{await i.captureWithRetry(e,n),await i.normalizeDensity(e,n,r.pixelDensity);return}catch(e){a=e}}if(!i.shouldFallbackToRunner(a))throw a;Ke(e,`simctl_screenshot`,a)}await i.captureWithRunner(e,n,r.appBundleId,r.fullscreen,r.runnerOptions),await i.normalizeDensity(e,n,r.pixelDensity)}finally{await a().catch(t=>K(e,`restore_failed`,t))}}async function Ue(e,t){let n=v.fromTimeoutMs(oe);await _(async({deadline:n})=>{await V(e,[`io`,e.id,`screenshot`,t],{timeoutMs:Math.max(1e3,n?.remainingMs()??2e4)})},{maxAttempts:5,baseDelayMs:fe,maxDelayMs:le,jitter:.2,shouldRetry:e=>q(e)},{deadline:n,phase:`ios_simulator_screenshot`})}async function W(e,n,r,i,a){if(e.kind===`device`&&!d(e)){await E(e).captureScreenshot(e,n,{appBundleId:r,fullscreen:i,runnerOptions:a,preferRunner:!0,runRunnerCommand:D});return}let o=(await D(e,{command:`screenshot`,appBundleId:r,fullscreen:i,inlineScreenshot:!1},a)).message;if(!o)throw new t(`COMMAND_FAILED`,`Failed to capture iOS screenshot: runner returned no file path`);if(d(e)){await N.copyFile(o,n);return}if(e.kind===`simulator`){await We(e,o,n);return}throw new t(`COMMAND_FAILED`,`Unsupported Apple screenshot target`)}async function We(e,n,r){let i=v.fromTimeoutMs(se),a=`Unable to locate runner container for simulator screenshot`,o=U.get(e.id);if(o){let t=await G(o,n,r);if(t.copied)return;a=t.error,U.delete(e.id)}for(let t of me){let o=await V(e,[`get_app_container`,e.id,t,`data`],{allowFailure:!0,timeoutMs:Ge(i,se,`runner screenshot container lookup`)});if(o.exitCode!==0){let e=o.stderr.trim();e&&(a=e);continue}let s=o.stdout.trim();if(!s){a=`simctl get_app_container returned empty output`;continue}let c=await G(s,n,r);if(c.copied){U.set(e.id,s);return}a=c.error}throw new t(`COMMAND_FAILED`,`Failed to capture iOS screenshot: ${a}`)}async function G(e,t,n){let r=`Runner screenshot was not found in the simulator container`;for(let i of qe(e,t))try{return await N.copyFile(i,n),{copied:!0}}catch(e){r=e instanceof Error?e.message:String(e)}return{copied:!1,error:r}}function Ge(e,n,r){let i=e.remainingMs();if(i>0)return i;throw new t(`COMMAND_FAILED`,`iOS ${r} timed out after ${n}ms`,{timeoutMs:n,step:r})}function Ke(e,t,n){let r=T(n);h({level:`warn`,phase:`ios_screenshot_fallback`,data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,from:t,to:`runner`,...r}})}function K(e,t,n){h({level:`warn`,phase:`ios_screenshot_status_bar_${t}`,data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,...T(n)}})}function qe(e,t){let n=M.resolve(e),r=t.trim();if(!r)return[];let i=[],a=new Set,o=e=>{let t=M.normalize(e);a.has(t)||(a.add(t),i.push(t))},s=r.replace(/^\/+/,``),c=s.replace(/\\/g,`/`);if(s&&o(M.join(n,s)),M.isAbsolute(r)&&o(M.normalize(r)),c.startsWith(`tmp/`))o(M.join(n,c));else{let e=c.lastIndexOf(`/tmp/`);if(e>=0){let t=c.slice(e+1);o(M.join(n,t))}}let l=M.basename(r);return l&&o(M.join(n,`tmp`,l)),i}function q(e){if(!(e instanceof t)||e.code!==`COMMAND_FAILED`)return!1;let n=C(e);return n.includes(`timeout waiting for screen surfaces`)||n.includes(`nsposixerrordomain`)&&n.includes(`code=60`)&&n.includes(`screenshot`)||n.includes(`timed out`)&&n.includes(`screenshot`)}function Je(e){if(!(e instanceof t)||e.code!==`COMMAND_FAILED`)return!1;let n=C(e);return n.includes(`not booted`)||n.includes(`current state: shutdown`)||n.includes(`current state is shutdown`)||n.includes(`current state=shutdown`)||n.includes(`state: shutdown`)||n.includes(`state=shutdown`)}async function Ye(e,t,n){let r=await Xe(e),i=ge(await he(t),r,n);i&&await p(t,i.width,i.height)}async function Xe(e){let n=H.get(e.id);if(n!==void 0)return n;let r=await V(e,[`getenv`,e.id,`SIMULATOR_MAINSCREEN_SCALE`],{timeoutMs:ce}),i=Number(r.stdout.trim());if(!Number.isFinite(i)||i<=0)throw new t(`COMMAND_FAILED`,`Failed to read iOS simulator screenshot scale from SIMULATOR_MAINSCREEN_SCALE`);return H.set(e.id,i),i}function J(e){if(!(e instanceof t)||e.code!==`COMMAND_FAILED`)return!1;let n=e.details??{};if(n.exitCode!==4)return!1;let r=String(n.stderr??``).toLowerCase();return r.includes(`fbsopenapplicationserviceerrordomain`)&&r.includes(`the request to open`)}async function Ze(e,t){let n=await b(ae(e,[`get_app_container`,e.id,t]),{allowFailure:!0});if(n.exitCode!==0)return{installed:!1};let r=n.stdout.trim();if(!r)return{installed:!1};let i=await re(`plutil`,[`-extract`,`CFBundleExecutable`,`raw`,`-o`,`-`,`${r}/Info.plist`],{allowFailure:!0});if(i.exitCode!==0||!i.stdout.trim())return{installed:!0};let a=await re(`otool`,[`-l`,`${r}/${i.stdout.trim()}`],{allowFailure:!0});if(a.exitCode!==0)return{installed:!0};let o=a.stdout.toLowerCase();return{installed:!0,simulatorCompatible:o.includes(`iossimulator`)||o.includes(`platform 7`)}}function Qe(e){return e.installed?e.simulatorCompatible===!1?`ARCH_MISMATCH`:`PERSISTENT_LAUNCH_FAIL`:`APP_NOT_INSTALLED`}function $e(e){switch(e){case`ARCH_MISMATCH`:return`The app binary was not built for the simulator platform. Rebuild with a simulator destination or use a physical device.`;case`APP_NOT_INSTALLED`:return`The app bundle is not installed on this simulator. Run install before open.`;case`PERSISTENT_LAUNCH_FAIL`:return`The simulator repeatedly refused to launch the app. Inspect crash logs in Console.app or ~/Library/Logs/DiagnosticReports/ and consider reinstalling the app.`;default:return`The simulator failed to launch the app. Retry with --debug and inspect diagnostics log for details.`}}const Y=25e3;async function et(e,i,a){let o=a?.launchConsole?.trim(),s=a?.launchArgs;if(o&&(!f(e)||e.kind!==`simulator`))throw new t(`UNSUPPORTED_OPERATION`,r);if(d(e)){if(s&&s.length>0)throw new t(`UNSUPPORTED_OPERATION`,`--launch-args is not supported on macOS; launch arguments are currently iOS-only.`);await ve(e,i,a);return}let p=a?.url?.trim();if(p){if(o)throw new t(`INVALID_ARGS`,n);if(!u(p))throw new t(`INVALID_ARGS`,`open <app> <url> requires a valid URL target`);if(e.kind===`simulator`){let t=!!s||c(p);if(a?.terminateRunningApp||t){let n=a?.appBundleId??await A(e,i);t?await it(e,n,{...s?{launchArgs:s}:{},...a?.terminateRunningApp?{terminateRunningApp:!0}:{}}):await rt(e,n)}await X(e,p,void 0);return}let r=l(a?.appBundleId??await A(e,i),p);if(!r)throw new t(`INVALID_ARGS`,`Deep link open on iOS devices requires an active app bundle ID. Open the app first, then open the URL.`);await Z(e,r,{payloadUrl:p,launchArgs:s,runnerOptions:a?.runnerOptions});return}let m=i.trim();if(u(m)){if(o)throw new t(`INVALID_ARGS`,n);if(e.kind===`simulator`){await X(e,m,s);return}let r=l(a?.appBundleId,m);if(!r)throw new t(`INVALID_ARGS`,`Deep link open on iOS devices requires an active app bundle ID. Open the app first, then open the URL.`);await Z(e,r,{payloadUrl:m,launchArgs:s,runnerOptions:a?.runnerOptions});return}let h=a?.appBundleId??await A(e,i);if(e.kind===`simulator`){await it(e,h,{...o?{launchConsole:o}:{},...s?{launchArgs:s}:{},...a?.terminateRunningApp?{terminateRunningApp:!0}:{}});return}await Z(e,h,{launchArgs:s,runnerOptions:a?.runnerOptions})}async function X(e,n,r){if(r&&r.length>0)throw new t(`INVALID_ARGS`,`--launch-args is not supported with iOS simulator URL opens (simctl openurl ignores launch args). Launch the app first with --launch-args, then issue the URL open in a separate call.`);await k(e),await S(e,[`openurl`,e.id,n])}async function tt(e){d(e)||e.kind===`simulator`&&await _e(e)!==`Booted`&&await k(e)}async function nt(e,t,n){if(d(e)){await we(e,t);return}let r=await A(e,t);if(e.kind===`simulator`){await rt(e,r);return}await E(e).terminateApp(e,r,{runnerOptions:n,runRunnerCommand:D})}async function rt(e,n){await k(e);let r=w(e,[`terminate`,e.id,n]),i=await b(r,{allowFailure:!0,timeoutMs:pe});if(i.exitCode!==0&&!i.stderr.toLowerCase().includes(`found nothing to terminate`))throw new t(`COMMAND_FAILED`,`xcrun exited with code ${i.exitCode}`,g(i,{cmd:`xcrun`,args:r}))}async function it(e,n,r){await k(e);let i=0,a=v.fromTimeoutMs(de);try{await _(async({deadline:i})=>{if(i?.isExpired())throw new t(`COMMAND_FAILED`,`App launch deadline exceeded`,{timeoutMs:de});let a=w(e,at(e.id,n,r)),o=r?.launchConsole?await ot(a,r.launchConsole):await b(a,{allowFailure:!0});if(o.exitCode!==0)throw new t(`COMMAND_FAILED`,`xcrun exited with code ${o.exitCode}`,g(o,{cmd:`xcrun`,args:a}))},{maxAttempts:10,baseDelayMs:1e3,maxDelayMs:5e3,jitter:.2,shouldRetry(e){return J(e)?(i+=1,i<3):!1}},{deadline:a})}catch(t){if(J(t)){let r=t,i=Qe(await Ze(e,n));r.details={...r.details,hint:$e(i)}}throw t}}function at(e,t,n){let r=[`launch`];return n?.launchConsole&&r.push(`--console-pty`),n?.terminateRunningApp&&r.push(`--terminate-running-process`),r.push(e,t),n?.launchArgs&&n.launchArgs.length>0&&r.push(...n.launchArgs),r}async function ot(e,n){await N.mkdir(M.dirname(n),{recursive:!0});try{let t=await b(e,{allowFailure:!0,timeoutMs:Y});return await st(n,t.stdout,t.stderr),t}catch(e){let r=(e instanceof t?e:void 0)?.details;if(r?.timeoutMs===Y){let e=typeof r.stdout==`string`?r.stdout:``,t=typeof r.stderr==`string`?r.stderr:``;return await st(n,e,t),h({level:`warn`,phase:`ios_simulator_launch_console_capture_timeout`,data:{timeoutMs:Y,logPath:n,stdoutBytes:Buffer.byteLength(e),stderrBytes:Buffer.byteLength(t)}}),{stdout:e,stderr:t,exitCode:0}}throw e}}async function st(e,t,n){await N.writeFile(e,ct(t,n),`utf8`)}function ct(e,t){return!e||!t||e.endsWith(`
3
+ `)||e.endsWith(`\r`)?`${e}${t}`:`${e}\n${t}`}async function Z(e,t,n){await E(e).launchApp(e,t,{...n,runRunnerCommand:D})}async function lt(e,n){return await j(e,async()=>{let r=await A(e,n);if(e.kind!==`simulator`)return await E(e).uninstallApp(e,r),{bundleId:r};await k(e);let i=await S(e,[`uninstall`,e.id,r],{allowFailure:!0});if(i.exitCode!==0&&!ue(`${i.stdout}\n${i.stderr}`.toLowerCase()))throw new t(`COMMAND_FAILED`,`simctl uninstall failed for ${r}`,g(i));return{bundleId:r}})}async function Q(e,t,n){e.kind!==`simulator`&&E(e).assertAppInstallationSupported(e);let r=await Ee({kind:`path`,path:t},n);try{return await dt(e,r.installablePath),{archivePath:r.archivePath,installablePath:r.installablePath,bundleId:r.bundleId,appName:r.appName,launchTarget:r.bundleId}}finally{await r.cleanup()}}async function ut(e,t,n){return e.kind!==`simulator`&&E(e).assertAppInstallationSupported(e),await j(e,async()=>{let{bundleId:r}=await lt(e,t);return await Q(e,n,{appIdentifierHint:t}),{bundleId:r}})}async function dt(e,t){await j(e,async()=>{if(e.kind!==`simulator`){await E(e).installApp(e,t);return}await k(e),await S(e,[`install`,e.id,t])})}async function ft(e){return d(e)?await xe():(O(e,`clipboard`),await k(e),m(await S(e,[`pbpaste`,e.id],{allowFailure:!0}),`Failed to read iOS simulator clipboard`).stdout.replace(/\r\n/g,`
4
+ `).replace(/\n$/,``))}async function pt(e,t){if(d(e)){await ye(t);return}O(e,`clipboard`),await k(e),m(await S(e,[`pbcopy`,e.id],{allowFailure:!0,stdin:t}),`Failed to write iOS simulator clipboard`)}async function mt(e,t,n){O(e,`push`),await k(e);let r=await N.mkdtemp(M.join(De.tmpdir(),`agent-device-ios-push-`)),i=M.join(r,`payload.apns`);try{await N.writeFile(i,`${JSON.stringify(n)}\n`,`utf8`),await S(e,[`push`,e.id,t,i])}finally{await N.rm(r,{recursive:!0,force:!0})}}let $=null,ht;async function gt(e,n,r,s,c){if(d(e)){let e=n.toLowerCase();if(e===`appearance`){await Te(r);return}if(e===`permission`){let e=a(r);if(e===`deny`)throw new t(`INVALID_ARGS`,o(`permission`));return await ie(e,vt(c?.permissionTarget))}throw new t(`INVALID_ARGS`,o(n))}O(e,`settings`),await k(e);let l=n.toLowerCase();switch(l){case`clear-app-state`:{if(r.toLowerCase()!==`clear`)throw new t(`INVALID_ARGS`,`settings clear-app-state only supports clear.`);if(!s)throw new t(`INVALID_ARGS`,`settings clear-app-state requires an app id or an active app session.`);let n=await _t(e,s);return{bundleId:n.bundleId,containerPath:n.containerPath,cleared:!0}}case`wifi`:{let t=y(r)?`active`:`failed`;await S(e,[`status_bar`,e.id,`override`,`--wifiMode`,t]),I(e);return}case`airplane`:y(r)?(await S(e,[`status_bar`,e.id,`override`,`--dataNetwork`,`hide`,`--wifiMode`,`failed`,`--wifiBars`,`0`,`--cellularMode`,`failed`,`--cellularBars`,`0`,`--operatorName`,``]),I(e)):(await S(e,[`status_bar`,e.id,`clear`]),Pe(e));return;case`location`:{if(r.toLowerCase()===`set`){let{latitude:t,longitude:n}=i(c);return await S(e,[`location`,e.id,`set`,`${t},${n}`]),{latitude:t,longitude:n}}let n=y(r);if(!s)throw new t(`INVALID_ARGS`,`location setting requires an active app in session`);let a=n?`grant`:`revoke`;await S(e,[`privacy`,e.id,a,`location`,s]);return}case`faceid`:case`touchid`:{let t=l,n=xt[t];await kt(e,Ot(r,t),{settingName:t,label:n.label,modalityAliases:n.modalityAliases});return}case`appearance`:{let t=await yt(e,r);await S(e,[`ui`,e.id,`appearance`,t]);return}case`permission`:if(!s)throw new t(`INVALID_ARGS`,`permission setting requires an active app in session`);await Ct(e,St(a(r)),Dt(c?.permissionTarget,c?.permissionMode),s);return;default:throw new t(`INVALID_ARGS`,`Unsupported setting: ${n}`)}}async function _t(e,n){if(!f(e)||e.kind!==`simulator`)throw new t(`UNSUPPORTED_OPERATION`,`Clearing app state is currently supported only on iOS simulators.`);let r=await A(e,n);await k(e),await nt(e,r);let i=m(await S(e,[`get_app_container`,e.id,r,`data`],{allowFailure:!0}),`simctl get_app_container failed for ${r}`).stdout.trim();if(!i)throw new t(`COMMAND_FAILED`,`simctl get_app_container returned an empty data container path for ${r}`);let a=await N.readdir(i);return await Promise.all(a.map(e=>N.rm(M.join(i,e),{recursive:!0,force:!0}))),{bundleId:r,containerPath:i}}function vt(e){let n=e?.trim().toLowerCase();if(n===`accessibility`||n===`screen-recording`||n===`input-monitoring`)return n;throw new t(`INVALID_ARGS`,`Unsupported macOS permission target. Use accessibility|screen-recording|input-monitoring.`)}async function yt(e,n){let r=te(n);if(r!==`toggle`)return r;let i=m(await S(e,[`ui`,e.id,`appearance`],{allowFailure:!0}),`Failed to read current iOS appearance`),a=bt(i.stdout,i.stderr);if(!a)throw new t(`COMMAND_FAILED`,`Unable to determine current iOS appearance for toggle`,{stdout:i.stdout,stderr:i.stderr});return a===`dark`?`light`:`dark`}function bt(e,t){let n=/\b(light|dark|unsupported|unknown)\b/i.exec(`${e}\n${t}`);if(!n)return null;let r=n[1]?.toLowerCase();return r===`dark`?`dark`:r===`light`?`light`:null}const xt={faceid:{label:`Face ID`,modalityAliases:[`face`]},touchid:{label:`Touch ID`,modalityAliases:[`finger`,`touch`]}};function St(e){return e===`deny`?`revoke`:e}async function Ct(e,n,r,i){let a=await Tt(e);if(!a.has(r))throw new t(`UNSUPPORTED_OPERATION`,`iOS simctl privacy does not support service "${r}" on this runtime.`,{deviceId:e.id,appBundleId:i,hint:`Supported services: ${Array.from(a).sort().join(`, `)}`});let o=[`privacy`,e.id,n,r,i],s=r===`notifications`;if(!(n===`reset`&&s))try{await S(e,o);return}catch(n){throw s&&wt(n)?new t(`UNSUPPORTED_OPERATION`,`iOS simulator does not support setting notifications permission via simctl privacy on this runtime.`,{deviceId:e.id,appBundleId:i,hint:`Use reset notifications for reprompt behavior, or toggle notifications manually in Settings.`}):n}try{await S(e,o);return}catch(e){if(!wt(e))throw e}try{await S(e,[`privacy`,e.id,`reset`,`all`,i])}catch(n){throw new t(`COMMAND_FAILED`,`iOS simulator blocked direct notifications reset. Fallback reset-all also failed.`,{deviceId:e.id,appBundleId:i,hint:`Use reinstall to force a fresh notifications prompt, or reset simulator content and settings.`},n instanceof Error?n:void 0)}}function wt(e){if(!(e instanceof t)||e.code!==`COMMAND_FAILED`)return!1;let n=String(e.details?.stderr??``).toLowerCase();return(n.includes(`failed to grant access`)||n.includes(`failed to revoke access`)||n.includes(`failed to reset access`))&&n.includes(`operation not permitted`)}async function Tt(e){let n=ee(e.simulatorSetPath),r=`${process.env.PATH??``}::${n??``}`;if($&&ht===r)return $;let i=await S(e,[`privacy`,`help`],{allowFailure:!0}),a=Et(`${i.stdout}\n${i.stderr}`);if(a.size===0)throw new t(`COMMAND_FAILED`,`Unable to determine supported simctl privacy services`,{stdout:i.stdout,stderr:i.stderr,exitCode:i.exitCode,hint:"Run `xcrun simctl privacy help` manually to verify available services for this runtime."});return $=a,ht=r,a}function Et(e){let t=new Set,n=!1;for(let r of e.split(`
5
+ `)){let e=r.trim();if(!e)continue;if(e===`service`){n=!0;continue}if(!n)continue;if(e.startsWith(`bundle identifier`))break;let i=/^([a-z-]+)\s+-\s+/.exec(e)?.[1];i!==void 0&&t.add(i)}return t}function Dt(e,n){let r=s(e);if(r!==`photos`&&n?.trim())throw new t(`INVALID_ARGS`,`Permission mode is only supported for photos. Received: ${n}.`);if(r===`camera`)return`camera`;if(r===`microphone`)return`microphone`;if(r===`contacts`)return`contacts`;if(r===`contacts-limited`)return`contacts-limited`;if(r===`notifications`)return`notifications`;if(r===`calendar`)return`calendar`;if(r===`location`)return`location`;if(r===`location-always`)return`location-always`;if(r===`media-library`)return`media-library`;if(r===`motion`)return`motion`;if(r===`reminders`)return`reminders`;if(r===`siri`)return`siri`;if(r===`photos`){let e=n?.trim().toLowerCase();if(!e||e===`full`)return`photos`;if(e===`limited`)return`photos-add`;throw new t(`INVALID_ARGS`,`Invalid photos mode: ${n}. Use full|limited.`)}throw new t(`INVALID_ARGS`,`Unsupported permission target: ${e}. Use camera|microphone|photos|contacts|contacts-limited|notifications|calendar|location|location-always|media-library|motion|reminders|siri.`)}function Ot(e,n){let r=e.trim().toLowerCase();if(r===`match`)return`match`;if(r===`nonmatch`)return`nonmatch`;if(r===`enroll`)return`enroll`;if(r===`unenroll`)return`unenroll`;throw new t(`INVALID_ARGS`,`Invalid ${n} state: ${e}. Use match|nonmatch|enroll|unenroll.`)}async function kt(e,n,r){let i=At(e.id,n,r.modalityAliases),a=[];for(let t of i){let n=w(e,t),r=await b(n,{allowFailure:!0});if(r.exitCode===0)return;a.push({args:n,stderr:r.stderr,stdout:r.stdout,exitCode:r.exitCode})}let o=ne(a);throw a.length>0&&a.every(e=>jt(e.stdout,e.stderr))?new t(`UNSUPPORTED_OPERATION`,`${r.label} simulation is not supported on this simulator runtime.`,{deviceId:e.id,action:n,setting:r.settingName,attempts:o}):new t(`COMMAND_FAILED`,`Failed to simulate ${r.settingName}.`,{deviceId:e.id,action:n,setting:r.settingName,attempts:o})}function At(e,t,n){let r=n.length>0?n:[`face`];switch(t){case`match`:return r.flatMap(t=>[[`biometric`,e,`match`,t],[`biometric`,`match`,e,t]]);case`nonmatch`:return r.flatMap(t=>[[`biometric`,e,`nonmatch`,t],[`biometric`,e,`nomatch`,t],[`biometric`,`nonmatch`,e,t],[`biometric`,`nomatch`,e,t]]);case`enroll`:return[[`biometric`,e,`enroll`,`yes`],[`biometric`,e,`enroll`,`1`],[`biometric`,`enroll`,e,`yes`],[`biometric`,`enroll`,e,`1`]];case`unenroll`:return[[`biometric`,e,`enroll`,`no`],[`biometric`,e,`enroll`,`0`],[`biometric`,`enroll`,e,`no`],[`biometric`,`enroll`,e,`0`]]}}function jt(e,t){let n=`${e}\n${t}`.toLowerCase();return n.includes(`unrecognized subcommand`)||n.includes(`unknown subcommand`)||n.includes(`not supported`)||n.includes(`unavailable`)||n.includes(`biometric`)&&n.includes(`invalid`)}var Mt=e({findIosSimulatorInstalledApp:()=>Se,installIosApp:()=>Q,installIosInstallablePath:()=>dt,listIosApps:()=>be,pushIosNotification:()=>mt,reinstallIosApp:()=>ut,resolveIosApp:()=>A,resolveIosSimulatorDeepLinkBundleId:()=>Ce});export{nt as a,W as c,pt as i,Ve as l,gt as n,et as o,ft as r,tt as s,Mt as t};
@@ -1,2 +1,2 @@
1
1
  import{_ as e,v as t}from"./sdk-contracts.js";import{j as n}from"./sdk-android-adb.js";function r(e){return[`1`,`true`,`yes`,`on`].includes((e??``).trim().toLowerCase())}const i={maxAttempts:3,baseDelayMs:200,maxDelayMs:2e3,jitter:.2};var a=class e{startedAtMs;expiresAtMs;constructor(e,t){this.startedAtMs=e,this.expiresAtMs=e+Math.max(0,t)}static fromTimeoutMs(t,n=Date.now()){return new e(n,t)}remainingMs(e=Date.now()){return Math.max(0,this.expiresAtMs-e)}elapsedMs(e=Date.now()){return Math.max(0,e-this.startedAtMs)}isExpired(e=Date.now()){return this.remainingMs(e)<=0}};async function o(t,n={},r={}){let a={maxAttempts:n.maxAttempts??i.maxAttempts,baseDelayMs:n.baseDelayMs??i.baseDelayMs,maxDelayMs:n.maxDelayMs??i.maxDelayMs,jitter:n.jitter??i.jitter,shouldRetry:n.shouldRetry},o;for(let n=1;n<=a.maxAttempts;n+=1){if(r.signal?.aborted)throw new e(`COMMAND_FAILED`,`request canceled`,{reason:`request_canceled`});if(r.deadline?.isExpired()&&n>1)break;try{let e=await t({attempt:n,maxAttempts:a.maxAttempts,deadline:r.deadline});return r.onEvent?.({phase:r.phase,event:`succeeded`,attempt:n,maxAttempts:a.maxAttempts,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs()}),l({phase:r.phase,event:`succeeded`,attempt:n,maxAttempts:a.maxAttempts,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs()}),e}catch(e){o=e;let t=r.classifyReason?.(e),i={phase:r.phase,event:`attempt_failed`,attempt:n,maxAttempts:a.maxAttempts,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs(),reason:t};if(r.onEvent?.(i),l(i),n>=a.maxAttempts||a.shouldRetry&&!a.shouldRetry(e,n))break;let u=s(a.baseDelayMs,a.maxDelayMs,a.jitter,n),d=r.deadline?Math.min(u,r.deadline.remainingMs()):u;if(d<=0)break;let f={phase:r.phase,event:`retry_scheduled`,attempt:n,maxAttempts:a.maxAttempts,delayMs:d,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs(),reason:t};r.onEvent?.(f),l(f),await c(d,r.signal)}}let u={phase:r.phase,event:`exhausted`,attempt:a.maxAttempts,maxAttempts:a.maxAttempts,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs(),reason:r.classifyReason?.(o)};throw r.onEvent?.(u),l(u),o||new e(`COMMAND_FAILED`,`retry failed`)}function s(e,t,n,r){let i=Math.min(t,e*2**(r-1)),a=i*n;return Math.max(0,i+(Math.random()*2-1)*a)}function c(e,t){return new Promise(n=>{if(t?.aborted){n();return}let r=!1,i=()=>{r||(r=!0,t&&t.removeEventListener(`abort`,o),n())},a=setTimeout(i,e);function o(){clearTimeout(a),i()}t&&t.addEventListener(`abort`,o,{once:!0})})}function l(e){n({level:e.event===`attempt_failed`||e.event===`exhausted`?`warn`:`debug`,phase:`retry`,data:{...e}})}function u(e){return e?.trim()||void 0}function d(e){return u(e)}function f(e){return new Set(e.split(/[\s,]+/).map(e=>e.trim()).filter(Boolean))}function p(e,t=process.env){let n=u(e)??u(t.AGENT_DEVICE_ANDROID_DEVICE_ALLOWLIST);if(n)return f(n)}const m=new Set([`IOS_BOOT_TIMEOUT`,`IOS_RUNNER_CONNECT_TIMEOUT`,`IOS_TOOL_MISSING`,`ANDROID_BOOT_TIMEOUT`,`ADB_TRANSPORT_UNAVAILABLE`,`CI_RESOURCE_STARVATION_SUSPECTED`]);function h(e){return m.has(e.toUpperCase())}function g(e){let n=e.error?t(e.error):null,r=e.context?.platform,i=e.context?.phase;if(n?.code===`TOOL_MISSING`)return r===`android`?`ADB_TRANSPORT_UNAVAILABLE`:`IOS_TOOL_MISSING`;let a=n?.details??{},o=typeof a.message==`string`?a.message:void 0,s=typeof a.stdout==`string`?a.stdout:void 0,c=typeof a.stderr==`string`?a.stderr:void 0,l=a.boot&&typeof a.boot==`object`?a.boot:null,u=a.bootstatus&&typeof a.bootstatus==`object`?a.bootstatus:null,d=[e.message,n?.message,e.stdout,e.stderr,o,s,c,typeof l?.stdout==`string`?l.stdout:void 0,typeof l?.stderr==`string`?l.stderr:void 0,typeof u?.stdout==`string`?u.stdout:void 0,typeof u?.stderr==`string`?u.stderr:void 0].filter(Boolean).join(`
2
- `).toLowerCase();return r===`ios`&&(d.includes(`runner did not accept connection`)||i===`connect`&&(d.includes(`timed out`)||d.includes(`timeout`)||d.includes(`econnrefused`)||d.includes(`connection refused`)||d.includes(`fetch failed`)||d.includes(`socket hang up`)))?`IOS_RUNNER_CONNECT_TIMEOUT`:r===`ios`&&i===`boot`&&(d.includes(`timed out`)||d.includes(`timeout`))?`IOS_BOOT_TIMEOUT`:r===`android`&&i===`boot`&&(d.includes(`timed out`)||d.includes(`timeout`))?`ANDROID_BOOT_TIMEOUT`:d.includes(`resource temporarily unavailable`)||d.includes(`killed: 9`)||d.includes(`cannot allocate memory`)||d.includes(`system is low on memory`)?`CI_RESOURCE_STARVATION_SUSPECTED`:r===`android`&&(d.includes(`device not found`)||d.includes(`no devices`)||d.includes(`device offline`)||d.includes(`offline`)||d.includes(`unauthorized`)||d.includes(`not authorized`)||d.includes(`unable to locate device`)||d.includes(`invalid device`))?`ADB_TRANSPORT_UNAVAILABLE`:n?.code===`COMMAND_FAILED`||d.length>0?`BOOT_COMMAND_FAILED`:`UNKNOWN`}function _(e){switch(e){case`IOS_BOOT_TIMEOUT`:return`Retry simulator boot and inspect simctl bootstatus logs; in CI reduce parallel jobs or use a larger runner.`;case`IOS_RUNNER_CONNECT_TIMEOUT`:return`Retry runner startup, inspect xcodebuild logs, and verify simulator responsiveness before command execution.`;case`ANDROID_BOOT_TIMEOUT`:return`Retry emulator startup and verify sys.boot_completed reaches 1; consider increasing startup budget in CI.`;case`ADB_TRANSPORT_UNAVAILABLE`:return`Check adb server/device transport (adb devices -l), restart adb, and ensure the target device is online and authorized.`;case`CI_RESOURCE_STARVATION_SUSPECTED`:return`CI machine may be resource constrained; reduce parallel jobs or use a larger runner.`;case`IOS_TOOL_MISSING`:return`Xcode command-line tools are missing or not in PATH; run xcode-select --install and verify xcrun works.`;case`BOOT_COMMAND_FAILED`:return`Inspect command stderr/stdout for the failing boot phase and retry after environment validation.`;default:return`Retry once and inspect verbose logs for the failing phase.`}}export{p as a,r as c,f as i,o as l,g as n,d as o,h as r,a as s,_ as t};
2
+ `).toLowerCase();return r===`ios`&&d.includes(`0xe8008012`)?`IOS_RUNNER_DEVICE_NOT_PROVISIONED`:r===`ios`&&(d.includes(`provisioning profile`)||d.includes(`embedded profile`))?`BOOT_COMMAND_FAILED`:r===`ios`&&(d.includes(`runner did not accept connection`)||i===`connect`&&(d.includes(`timed out`)||d.includes(`timeout`)||d.includes(`econnrefused`)||d.includes(`connection refused`)||d.includes(`fetch failed`)||d.includes(`socket hang up`)))?`IOS_RUNNER_CONNECT_TIMEOUT`:r===`ios`&&i===`boot`&&(d.includes(`timed out`)||d.includes(`timeout`))?`IOS_BOOT_TIMEOUT`:r===`android`&&i===`boot`&&(d.includes(`timed out`)||d.includes(`timeout`))?`ANDROID_BOOT_TIMEOUT`:d.includes(`resource temporarily unavailable`)||d.includes(`killed: 9`)||d.includes(`cannot allocate memory`)||d.includes(`system is low on memory`)?`CI_RESOURCE_STARVATION_SUSPECTED`:r===`android`&&(d.includes(`device not found`)||d.includes(`no devices`)||d.includes(`device offline`)||d.includes(`offline`)||d.includes(`unauthorized`)||d.includes(`not authorized`)||d.includes(`unable to locate device`)||d.includes(`invalid device`))?`ADB_TRANSPORT_UNAVAILABLE`:n?.code===`COMMAND_FAILED`||d.length>0?`BOOT_COMMAND_FAILED`:`UNKNOWN`}function _(e){switch(e){case`IOS_BOOT_TIMEOUT`:return`Retry simulator boot and inspect simctl bootstatus logs; in CI reduce parallel jobs or use a larger runner.`;case`IOS_RUNNER_CONNECT_TIMEOUT`:return`Retry runner startup, inspect xcodebuild logs, and verify simulator responsiveness before command execution.`;case`IOS_RUNNER_DEVICE_NOT_PROVISIONED`:return`The XCTest runner cannot be installed on this device: its provisioning profile does not cover it. Register the device with the signing team (Xcode > Settings > Accounts, or add its UDID to the provisioning profile) and retry. Retrying without that will keep failing.`;case`ANDROID_BOOT_TIMEOUT`:return`Retry emulator startup and verify sys.boot_completed reaches 1; consider increasing startup budget in CI.`;case`ADB_TRANSPORT_UNAVAILABLE`:return`Check adb server/device transport (adb devices -l), restart adb, and ensure the target device is online and authorized.`;case`CI_RESOURCE_STARVATION_SUSPECTED`:return`CI machine may be resource constrained; reduce parallel jobs or use a larger runner.`;case`IOS_TOOL_MISSING`:return`Xcode command-line tools are missing or not in PATH; run xcode-select --install and verify xcrun works.`;case`BOOT_COMMAND_FAILED`:return`Inspect command stderr/stdout for the failing boot phase and retry after environment validation.`;default:return`Retry once and inspect verbose logs for the failing phase.`}}export{p as a,r as c,f as i,o as l,g as n,d as o,h as r,a as s,_ as t};
@@ -1 +1 @@
1
- import{_ as e}from"./sdk-contracts.js";import{E as t,O as n}from"./sdk-android-adb.js";import{An as r}from"./recording-export-quality.js";import{n as i,t as a}from"./path-resolution.js";import{f as o,t as s,u as c,v as l}from"./registry.js";import{r as u}from"./command-schema.js";import d from"node:fs";import f from"node:path";function p(e,t){for(let[n,r]of Object.entries(t))r!==void 0&&(e[n]=r);return e}const m=new Set([`config`,`remoteConfig`,`help`,`version`,`batchSteps`,`githubActionsArtifact`]),h=new Set([`appsFilter`,`iosSimulatorDeviceSet`]),g=S(),_=new Map(g.map(e=>[e.key,e]));function v(e){return _.get(e)}function y(e){return g.filter(t=>t.config.enabled&&t.supportsCommand(e))}function b(e,t){return v(e)?.supportsCommand(t)??!1}function x(e,t,r,i){return n(w(e),t,r,i)}function S(){let e=new Map;for(let t of l()){let n=e.get(t.key);n?n.push(t):e.set(t.key,[t])}let n=new Map;for(let e of o)n.set(e,new Set([`*`]));for(let e of r()){let t=u(e);for(let r of[...t.allowedFlags??[],...t.supportedFlags??[]]){let t=n.get(r);t&&t.has(`*`)||(t?t.add(e):n.set(r,new Set([e])))}}return[...e.entries()].map(([e,r])=>({key:e,flagDefinitions:r,config:{enabled:!m.has(e),key:e},env:{names:h.has(e)?[]:[t(e)]},supportsCommand(t){let r=n.get(e);return r?r.has(`*`)?!0:t?r.has(t):!1:!1}})).sort((e,t)=>e.key.localeCompare(t.key))}function C(e){let t=e.flagDefinitions[0];if(!t)throw Error(`Missing flag definition for option ${e.key}`);return t}function w(e){let t=e.flagDefinitions.find(e=>e.setValue===void 0);if(t)return t;let n=C(e);if(n.type===`enum`){let t=n.enumValues??e.flagDefinitions.map(e=>e.setValue).filter(e=>e!==void 0);return{...n,setValue:void 0,enumValues:t}}return n}const T=s();function E(e){return T[e.name]?.({input:e.input??{},result:e.result})}function D(e){let t=e.env??process.env;return p(p({},j(O(e.cwd,e.cliFlags.config,t))),P(t,e.command))}function O(e,t,n){let r=t??n.AGENT_DEVICE_CONFIG;return r?[{path:A(r,e,n),required:!0}]:[{path:k(n),required:!1},{path:f.resolve(e,`agent-device.json`),required:!1}]}function k(e){return f.join(a(`~`,{env:e}),`.agent-device`,`config.json`)}function A(e,t,n){return i(e,{cwd:t,env:n})}function j(e){let t={};for(let n of e)p(t,M(n.path,n.required));return t}function M(t,n){if(!d.existsSync(t)){if(n)throw new e(`INVALID_ARGS`,`Config file not found: ${t}`);return{}}let r;try{r=d.readFileSync(t,`utf8`)}catch(n){throw new e(`INVALID_ARGS`,`Failed to read config file: ${t}`,{cause:n instanceof Error?n.message:String(n)})}let i;try{i=JSON.parse(r)}catch(n){throw new e(`INVALID_ARGS`,`Invalid JSON in config file: ${t}`,{cause:n instanceof Error?n.message:String(n)})}if(!i||typeof i!=`object`||Array.isArray(i))throw new e(`INVALID_ARGS`,`Config file must contain a JSON object: ${t}`);return N(i,`config file ${t}`)}function N(t,n){let r={};for(let[i,a]of Object.entries(t)){if(i===`installSource`){r.installSource=c(a,n);continue}let t=i,o=v(t);if(!o)throw new e(`INVALID_ARGS`,`Unknown config key "${i}" in ${n}.`);if(!o.config.enabled)throw new e(`INVALID_ARGS`,`Unsupported config key "${i}" in ${n}.`);r[t]=x(o,a,n,i)}return r}function P(e,t){let n={};for(let r of y(t)){if(r.key===`installSource`)continue;let t=r.env.names.map(t=>({name:t,value:e[t]})).find(e=>typeof e.value==`string`&&e.value.trim().length>0);t&&(n[r.key]=x(r,t.value,`environment variable ${t.name}`,t.name))}return n}export{p as i,E as n,b as r,D as t};
1
+ import{_ as e}from"./sdk-contracts.js";import{Wt as t}from"./viewport-dimension.js";import{S as n,p as r,t as i,u as a}from"./registry.js";import{E as o,O as s}from"./sdk-android-adb.js";import{n as c,t as l}from"./path-resolution.js";import{r as u}from"./command-schema.js";import d from"node:path";import f from"node:fs";function p(e,t){for(let[n,r]of Object.entries(t))r!==void 0&&(e[n]=r);return e}const m=new Set([`config`,`remoteConfig`,`help`,`version`,`batchSteps`,`githubActionsArtifact`]),h=new Set([`appsFilter`,`iosSimulatorDeviceSet`]),g=S(),_=new Map(g.map(e=>[e.key,e]));function v(e){return _.get(e)}function y(e){return g.filter(t=>t.configurable&&t.supportsCommand(e))}function b(e,t){return v(e)?.supportsCommand(t)??!1}function x(e,t,n,r){return s(w(e),t,n,r)}function S(){let e=new Map;for(let t of n()){let n=e.get(t.key);n?n.push(t):e.set(t.key,[t])}let i=new Map;for(let e of r)i.set(e,new Set([`*`]));for(let e of t()){let t=u(e);for(let n of[...t.allowedFlags??[],...t.supportedFlags??[]]){let t=i.get(n);t&&t.has(`*`)||(t?t.add(e):i.set(n,new Set([e])))}}return[...e.entries()].map(([e,t])=>({key:e,flagDefinitions:t,configurable:!m.has(e),env:{names:h.has(e)?[]:[o(e)]},supportsCommand(t){let n=i.get(e);return n?n.has(`*`)?!0:t?n.has(t):!1:!1}})).sort((e,t)=>e.key.localeCompare(t.key))}function C(e){let t=e.flagDefinitions[0];if(!t)throw Error(`Missing flag definition for option ${e.key}`);return t}function w(e){let t=e.flagDefinitions.find(e=>e.setValue===void 0);if(t)return t;let n=C(e);if(n.type===`enum`){let t=n.enumValues??e.flagDefinitions.map(e=>e.setValue).filter(e=>e!==void 0);return{...n,setValue:void 0,enumValues:t}}return n}const T=i();function E(e){return T[e.name]?.({input:e.input??{},result:e.result})}function D(e){let t=e.env??process.env;return p(p({},M(k(e.cwd,e.cliFlags.config,t))),F(t,e.command))}const O=new Set(`json.platform.target.device.udid.serial.session.sessionLock.activity.launchArgs.launchUrl.remote.deviceHub.testIme.appsFilter.clean.force.stale.relaunch.shutdown.surface.headless.restart.noRecord.record.recordAs.snapshotInteractiveOnly.snapshotDiff.snapshotDepth.snapshotScope.snapshotRaw.snapshotForceFull.screenshotPixelDensity.screenshotFullscreen.screenshotMaxSize.screenshotNoStabilize.screenshotNormalizeStatusBar.overlayRefs.networkInclude.baseline.threshold.count.pointerCount.fps.quality.hideTouches.recordingScope.intervalMs.delayMs.durationMs.holdMs.jitterPx.pixels.doubleTap.verify.settle.settleQuietMs.clickButton.backMode.pauseMs.pattern.kind.perfTemplate.responseLevel.verbose.cost.timeoutMs.retries.failFast.recordVideo.replayUpdate.replayMaestro.replayFrom.replayPlanDigest.replayKeepSession.findFirst.findLast.batchOnError.batchMaxSteps.retainPaths.retentionMs.shardAll.shardSplit.noLogin`.split(`.`));function k(e,t,n){let r=t??n.AGENT_DEVICE_CONFIG;return r?[{path:j(r,e,n),required:!0,source:`explicit`}]:[{path:A(n),required:!1,source:`user`},{path:d.resolve(e,`agent-device.json`),required:!1,source:`project`}]}function A(e){return d.join(l(`~`,{env:e}),`.agent-device`,`config.json`)}function j(e,t,n){return c(e,{cwd:t,env:n})}function M(e){let t={};for(let n of e)p(t,N(n));return t}function N(t){let{path:n,required:r}=t;if(!f.existsSync(n)){if(r)throw new e(`INVALID_ARGS`,`Config file not found: ${n}`);return{}}let i;try{i=f.readFileSync(n,`utf8`)}catch(t){throw new e(`INVALID_ARGS`,`Failed to read config file: ${n}`,{cause:t instanceof Error?t.message:String(t)})}let a;try{a=JSON.parse(i)}catch(t){throw new e(`INVALID_ARGS`,`Invalid JSON in config file: ${n}`,{cause:t instanceof Error?t.message:String(t)})}if(!a||typeof a!=`object`||Array.isArray(a))throw new e(`INVALID_ARGS`,`Config file must contain a JSON object: ${n}`);return P(a,{source:t.source,label:`${t.source===`project`?`project `:``}config file ${n}`})}function P(t,n){let r={};for(let[i,o]of Object.entries(t)){let t=i,s=v(t);if(!s)throw new e(`INVALID_ARGS`,`Unknown config key "${i}" in ${n.label}.`);if(!s.configurable)throw new e(`INVALID_ARGS`,`Config key "${i}" is not allowed in ${n.label}. This key is not supported in config files.`);if(n.source===`project`&&!O.has(t))throw new e(`INVALID_ARGS`,`Config key "${i}" is not allowed in ${n.label}. Move it to ~/.agent-device/config.json, pass it with --config or AGENT_DEVICE_CONFIG, or provide it through CLI flags/environment variables.`);if(t===`installSource`){r.installSource=a(o,n.label);continue}r[t]=x(s,o,n.label,i)}return r}function F(e,t){let n={};for(let r of y(t)){if(r.key===`installSource`)continue;let t=r.env.names.map(t=>({name:t,value:e[t]})).find(e=>typeof e.value==`string`&&e.value.trim().length>0);t&&(n[r.key]=x(r,t.value,`environment variable ${t.name}`,t.name))}return n}export{p as i,E as n,b as r,D as t};
@@ -1,5 +1,5 @@
1
- import{An as e}from"./recording-export-quality.js";import{f as t,v as n}from"./registry.js";import{i as r,r as i}from"./command-schema.js";const a=[`Flows: launchApp; runFlow file/inline with platform, visibility, and limited boolean conditions; onFlowStart/onFlowComplete; repeat.times and retry.`,`Interactions: tapOn, doubleTapOn, longPressOn, inputText, eraseText, openLink, hideKeyboard, basic pressKey, and back; targets support index, childOf, label, points, and optional.`,`Assertions and navigation: assertVisible, assertNotVisible, extendedWaitUntil, scroll, scrollUntilVisible, absolute/percentage/target swipe, takeScreenshot, waitForAnimationToEnd, and stopApp.`,`Scripts: ordered runScript file/env scripts with http.post, json, and output variables.`],o=[`Runtime: iOS and Android only; launchApp.clearState supports Android and iOS simulators, launch arguments are Apple-only, and standalone device utility/state commands are unsupported.`,`Expressions: when.true supports boolean literals and maestro.platform comparisons; repeat.while, evalScript, and broader JavaScript expressions are unsupported.`,`Environment: flow env is the default, AD_VAR_* overrides it, and CLI -e KEY=VALUE wins over both.`,`Failure diagnostics: resolved targets and runFlow paths are rendered, while inputText payloads remain hidden; do not place secrets in diagnostic identifiers.`,`Trust: runScript executes trusted scripts, may make http.post network requests, and is not a security sandbox; output keys cannot contain a dot.`,`Errors and tracking: unsupported commands and fields fail with source context when available; open a focused issue only when implementation work is planned.`];function s(){return[`Supported subset:`,...a.map(e=>` - ${e}`),``,`Boundaries:`,...o.map(e=>` - ${e}`)].join(`
2
- `)}function c(e){let t=e.endsWith(`?`),n=t?e.slice(0,-1):e;return t?`[${n}]`:`<${n}>`}function l(e){return n().filter(t=>t.key===e)}function u(e,t){if(t.usageOverride)return t.usageOverride;let n=(t.positionalArgs??[]).map(c),r=(t.allowedFlags??[]).flatMap(e=>l(e).map(e=>e.usageLabel??e.names[0])).map(e=>`[${e}]`);return[e,...n,...r].join(` `)}const d=[{label:`agent-device help manual-qa`,description:`Follow a manual test script with exact interactions and verification`},{label:`agent-device help dogfood`,description:`Explore an app and report issues with evidence`},{label:`agent-device help validate`,description:`Validate code changes, perf, visuals, logs, and cleanup`},{label:`agent-device help workflow`,description:`Full app automation reference for commands, refs, selectors, and waits`},{label:`agent-device help debugging`,description:`Use when logs, network, audio, perf memory, traces, alerts, or diagnostics matter`},{label:`agent-device help tv`,description:`Use when navigating Android TV, tvOS, or Vega VVD focus-first surfaces`},{label:`agent-device help react-native`,description:`Use when the target app is React Native, Expo, or a dev client`},{label:`agent-device help react-devtools`,description:`Use when inspecting components, props/state/hooks, renders, or profiles`},{label:`agent-device help cdp`,description:`Use when investigating JS heap growth, heap snapshots, or retainers`},{label:`agent-device help physical-device`,description:`Use when using a connected phone/tablet or iOS signing setup`},{label:`agent-device help remote`,description:`Use when working through cloud config, tenants, leases, or local tunnels`},{label:`agent-device help web`,description:`Use when automating a browser through agent-device sessions`},{label:`agent-device help macos`,description:`Use when targeting desktop, frontmost app, or menu bar surfaces`}],f=[`Write full command lines starting with agent-device; do not output pseudo commands, helper prose, pipes, grep, jq, or hidden stderr.`,`Default app loop: agent-device open <app> -> agent-device snapshot -i -> mutate a current target with --settle -> continue from that settled diff -> agent-device close.`,`Use --settle only on planned press, click, fill, or longpress commands; never add it to open, snapshot, or close.`,`Follow structured command hints before choosing a recovery action.`,`Targets are concrete refs or selectors: @e12, label="Query", role=button label="Submit".`,`Selector keys are only: id, role, text, label, value, appname, windowtitle, visible, hidden, editable, selected, focused, enabled, hittable. placeholder, index, and key are not selector keys.`,`A literal @ handle is a label such as label="@account.example", never a bare @ref.`,`Boundary shapes: agent-device fill 'label="Query"' "text" --settle; agent-device press 'role=button label="Submit"' --settle.`,`fill takes a target then text. press targets an accessible element; key=Enter is not a supported target.`,`Pick the help mode below when the task is manual QA, dogfooding, engineering validation, or debugging.`],p=[`Planning output contract: when asked to plan commands, output command lines only: no prose, numbering, Markdown fences, pipes, or shell helpers.`,`If you did not use --settle, verify a mutation with diff snapshot (or diff snapshot -i), not a full snapshot: it prints only the added/removed/changed lines since the last snapshot in this session.`,`Network-backed or debounced results may arrive after the --settle quiet window; follow the settled action with wait text "Expected result" or wait <selector> instead of polling full snapshots.`,`Pin a raw CLI ref to the response that minted it with ~s<n> (n = refsGeneration): press @e12~s4. On iOS, stale refs are rejected for mutations; refresh refs or use a stable selector.`,`Plain snapshot reads state; snapshot -i refreshes current interactive refs only.`,`Default snapshot text is an agent-facing, token-efficient view for planning and targeting actions.`,`Read-only visible/state question: use snapshot/get/is/find; use snapshot -i only when refs are needed.`,`Truncated text/input preview: expand first with snapshot -s @e12, not get text.`,`React Native apps: read help react-native.`,`Text fields: use fill <target> <text> --settle to replace a field value. Use type <text> only to append after focusing a field with press.`,`Clearing text: do not use fill <target> ""; use a visible clear/reset control or report that clearing is unsupported.`,`Implicit default sessions are scoped to the current worktree; if a prompt names a Session, include --session <name> on every command in that flow.`,`Run mutating commands serially within one session; parallelize only read-only commands or separate sessions/devices.`,`After mutation: refs are stale. If the next target is known, use its selector directly; otherwise refresh with snapshot -i, scoped with -s when a stable container is known. Use press/click for taps.`,`macOS context menus use click <ref> --button secondary, then snapshot -i. Longpress is for mobile hold gestures, not macOS secondary-click menus.`,`Remote lifecycle: use connect, then open, commands, close, and disconnect. Read help remote for proxy, cloud, and device-cloud provider flows.`,`TV/D-pad targets: read help tv. Web browser sessions: read help web.`,`Debug evidence: Session state contains request diagnostics and runner.log; use logs clear --restart/mark/path, trace, and network dump --include headers for app evidence.`,`Routine QA loop with concrete command shapes: agent-device help manual-qa. Full operating guide: agent-device help workflow. Exploratory QA: agent-device help dogfood.`],m=[`Default config files: ~/.agent-device/config.json, ./agent-device.json`,`Use --config <path> or AGENT_DEVICE_CONFIG to load one explicit config file.`],h=[{label:`AGENT_DEVICE_SESSION`,description:`Explicit session name`},{label:`AGENT_DEVICE_PLATFORM`,description:`Default platform binding`},{label:`AGENT_DEVICE_SESSION_LOCK`,description:`Bound-session conflict mode`},{label:`AGENT_DEVICE_DAEMON_BASE_URL`,description:`Connect to remote daemon`},{label:`AGENT_DEVICE_DAEMON_AUTH_TOKEN`,description:`Remote daemon service/API token`},{label:`AGENT_DEVICE_CLOUD_BASE_URL`,description:`Bridge/control-plane API origin for cloud auth and /api-keys`}],g=[`agent-device open Settings --platform ios`,`agent-device open https://example.com --platform web`,`agent-device snapshot -i`,`agent-device fill @e3 "test@example.com"`,`agent-device replay ./session.ad`,`agent-device test ./suite --platform android`],_={"manual-qa":{summary:`Follow manual test scripts with exact interactions and verification`,body:`agent-device help manual-qa
1
+ import{Wt as e}from"./viewport-dimension.js";import{S as t,p as n}from"./registry.js";import{i as r,r as i}from"./command-schema.js";const a=[`Flows: launchApp; runFlow file/inline with platform, visibility, and limited boolean conditions; onFlowStart/onFlowComplete; repeat.times and retry.`,`Interactions: tapOn, doubleTapOn, longPressOn, inputText, eraseText, openLink, hideKeyboard, basic pressKey, and back; targets support index, childOf, label, points, and optional.`,`Assertions and navigation: assertVisible, assertNotVisible, extendedWaitUntil, scroll, scrollUntilVisible, absolute/percentage/target swipe, takeScreenshot, waitForAnimationToEnd, and stopApp.`,`Scripts: ordered runScript file/env scripts with http.post, json, and output variables.`],o=[`Runtime: iOS and Android only; launchApp.clearState supports Android and iOS simulators, launch arguments are Apple-only, and standalone device utility/state commands are unsupported.`,`Expressions: when.true supports boolean literals and maestro.platform comparisons; repeat.while, evalScript, and broader JavaScript expressions are unsupported.`,`Environment: flow env is the default, AD_VAR_* overrides it, and CLI -e KEY=VALUE wins over both.`,`Failure diagnostics: resolved targets and runFlow paths are rendered, while inputText payloads remain hidden; do not place secrets in diagnostic identifiers.`,`Trust: runScript executes trusted scripts, may make http.post network requests, and is not a security sandbox; output keys cannot contain a dot.`,`Errors and tracking: unsupported commands and fields fail with source context when available; open a focused issue only when implementation work is planned.`];function s(){return[`Supported subset:`,...a.map(e=>` - ${e}`),``,`Boundaries:`,...o.map(e=>` - ${e}`)].join(`
2
+ `)}function c(e){let t=e.endsWith(`?`),n=t?e.slice(0,-1):e;return t?`[${n}]`:`<${n}>`}function l(e){return t().filter(t=>t.key===e)}function u(e,t){if(t.usageOverride)return t.usageOverride;let n=(t.positionalArgs??[]).map(c),r=(t.allowedFlags??[]).flatMap(e=>l(e).map(e=>e.usageLabel??e.names[0])).map(e=>`[${e}]`);return[e,...n,...r].join(` `)}const d=[{label:`agent-device help manual-qa`,description:`Follow a manual test script with exact interactions and verification`},{label:`agent-device help dogfood`,description:`Explore an app and report issues with evidence`},{label:`agent-device help validate`,description:`Validate code changes, perf, visuals, logs, and cleanup`},{label:`agent-device help workflow`,description:`Full app automation reference for commands, refs, selectors, and waits`},{label:`agent-device help debugging`,description:`Use when logs, network, audio, perf memory, traces, alerts, or diagnostics matter`},{label:`agent-device help tv`,description:`Use when navigating Android TV, tvOS, or Vega VVD focus-first surfaces`},{label:`agent-device help react-native`,description:`Use when the target app is React Native, Expo, or a dev client`},{label:`agent-device help react-devtools`,description:`Use when inspecting components, props/state/hooks, renders, or profiles`},{label:`agent-device help cdp`,description:`Use when investigating JS heap growth, heap snapshots, or retainers`},{label:`agent-device help physical-device`,description:`Use when using a connected phone/tablet or iOS signing setup`},{label:`agent-device help remote`,description:`Use when working through cloud config, tenants, leases, or local tunnels`},{label:`agent-device help web`,description:`Use when automating a browser through agent-device sessions`},{label:`agent-device help macos`,description:`Use when targeting desktop, frontmost app, or menu bar surfaces`}],f=[`Write full command lines starting with agent-device; do not output pseudo commands, helper prose, pipes, grep, jq, or hidden stderr.`,`Default app loop: agent-device open <app> -> agent-device snapshot -i -> mutate a current target with --settle -> continue from that settled diff -> agent-device close.`,`Use --settle only on planned press, click, fill, or longpress commands; never add it to open, snapshot, or close.`,`Follow structured command hints before choosing a recovery action.`,`Targets are concrete refs or selectors: @e12, label="Query", role=button label="Submit".`,`Selector keys are only: id, role, text, label, value, appname, windowtitle, visible, hidden, editable, selected, focused, enabled, hittable. placeholder, index, and key are not selector keys.`,`A literal @ handle is a label such as label="@account.example", never a bare @ref.`,`Boundary shapes: agent-device fill 'label="Query"' "text" --settle; agent-device press 'role=button label="Submit"' --settle.`,`fill takes a target then text. press targets an accessible element; key=Enter is not a supported target.`,`Pick the help mode below when the task is manual QA, dogfooding, engineering validation, or debugging.`],p=[`Planning output contract: when asked to plan commands, output command lines only: no prose, numbering, Markdown fences, pipes, or shell helpers.`,`If you did not use --settle, verify a mutation with diff snapshot (or diff snapshot -i), not a full snapshot: it prints only the added/removed/changed lines since the last snapshot in this session.`,`Network-backed or debounced results may arrive after the --settle quiet window; follow the settled action with wait text "Expected result" or wait <selector> instead of polling full snapshots.`,`Pin a raw CLI ref to the response that minted it with ~s<n> (n = refsGeneration): press @e12~s4. On iOS, stale refs are rejected for mutations; refresh refs or use a stable selector.`,`Plain snapshot reads state; snapshot -i refreshes current interactive refs only.`,`Default snapshot text is an agent-facing, token-efficient view for planning and targeting actions.`,`Read-only visible/state question: use snapshot/get/is/find; use snapshot -i only when refs are needed.`,`Truncated text/input preview: expand first with snapshot -s @e12, not get text.`,`React Native apps: read help react-native.`,`Text fields: use fill <target> <text> --settle to replace a field value. Use type <text> only to append after focusing a field with press.`,`Clearing text: do not use fill <target> ""; use a visible clear/reset control or report that clearing is unsupported.`,`Implicit default sessions are scoped to the current worktree; if a prompt names a Session, include --session <name> on every command in that flow.`,`Run mutating commands serially within one session; parallelize only read-only commands or separate sessions/devices.`,`After mutation: refs are stale. If the next target is known, use its selector directly; otherwise refresh with snapshot -i, scoped with -s when a stable container is known. Use press/click for taps.`,`macOS context menus use click <ref> --button secondary, then snapshot -i. Longpress is for mobile hold gestures, not macOS secondary-click menus.`,`Remote lifecycle: use connect, then open, commands, close, and disconnect. Read help remote for proxy, cloud, and device-cloud provider flows.`,`TV/D-pad targets: read help tv. Web browser sessions: read help web.`,`Debug evidence: Session state contains request diagnostics and runner.log; use logs clear --restart/mark/path, trace, and network dump --include headers for app evidence.`,`Routine QA loop with concrete command shapes: agent-device help manual-qa. Full operating guide: agent-device help workflow. Exploratory QA: agent-device help dogfood.`],m=[`Default config files: ~/.agent-device/config.json, ./agent-device.json (project-safe defaults only).`,`Use --config <path> or AGENT_DEVICE_CONFIG for explicit connection/provider defaults; project config cannot select endpoints or credentials.`],h=[{label:`AGENT_DEVICE_SESSION`,description:`Explicit session name`},{label:`AGENT_DEVICE_PLATFORM`,description:`Default platform binding`},{label:`AGENT_DEVICE_SESSION_LOCK`,description:`Bound-session conflict mode`},{label:`AGENT_DEVICE_DAEMON_BASE_URL`,description:`Connect to remote daemon`},{label:`AGENT_DEVICE_DAEMON_AUTH_TOKEN`,description:`Remote daemon service/API token`},{label:`AGENT_DEVICE_CLOUD_BASE_URL`,description:`Bridge/control-plane API origin for cloud auth and /api-keys`}],g=[`agent-device open Settings --platform ios`,`agent-device open https://example.com --platform web`,`agent-device snapshot -i`,`agent-device fill @e3 "test@example.com"`,`agent-device replay ./session.ad`,`agent-device test ./suite --platform android`],_={"manual-qa":{summary:`Follow manual test scripts with exact interactions and verification`,body:`agent-device help manual-qa
3
3
 
4
4
  Use this when asked to follow a manual QA script, test case, checklist, acceptance flow, or user-provided instructions.
5
5
 
@@ -207,7 +207,7 @@ Validation and evidence:
207
207
  agent-device screenshot
208
208
  agent-device press 124 817
209
209
  agent-device snapshot -i
210
- Startup/CPU/memory/frame first pass: perf metrics --json (bare perf and metrics are aliases). Focused frame/jank health: perf frames --json. Memory-only sample: perf memory sample --json returns compact JSON with bounded top offenders. Heap/memgraph artifact escalation: perf memory snapshot --out heap.artifact; use --kind android-hprof on Android or --kind memgraph on supported Apple simulator/macOS app sessions. Android native profiling: perf cpu profile start|stop|report --kind simpleperf --out <path>; Android native traces: perf trace start|stop --kind perfetto --out <path>. Artifact collectors return compact state/path/size metadata only; raw heap/profile/trace files stay on disk. Treat native perf output as the agent evidence: for example, a Perfetto stop can return state=stopped, outPath=/tmp/app.perfetto-trace, sizeBytes=5392410, and method=adb-shell-perfetto while the 5.3 MB raw trace stays in the artifact. This is better than raw dumps for agents because it is stable, bounded, and keeps large artifacts out of context. heapprofd is deferred until Perfetto plumbing is available. Replay divergence and resume: a failing replay/test step returns REPLAY_DIVERGENCE with a bounded report (screen digest, ranked selector suggestions, resume). Repair app state, then resume with replay --from <n> --plan-digest <sha256> (both from the report's resume field) to continue from the failed step without re-running earlier ones; resume never re-executes skipped steps, so app state is the caller's responsibility, and it is rejected with INVALID_ARGS when the plan digest is stale, --from is out of range, or the skipped range/target touches runtime control flow. The plan digest binds the script, its includes, the effective --platform/--target, and per-action runtime/identity. Native .ad interpolation is late-bound after planning, so changing only its values keeps the digest; Maestro environment substitution occurs during compatibility parsing and can change action inputs, includes, or control expansion, so it can change the digest. --from is replay-only; test rejects it. --update/-u no longer rewrites the script (ADR 0012) — it is a no-op kept for compatibility; every divergence already carries the same ranked suggestions. Agent-supervised repair (heal-by-doing): arm replay <file>.ad --save-script[=<out>] before step 1 (armed once; --from continuations do not need it again). Every divergence carries a repairHint: record-and-heal means press the correct control via a blessed @ref from the divergence's screen.refs, recorded (no --no-record), then continue with replay --from <n+1> --plan-digest <sha256>; state-repair means the script is correct but app state is not, so fix state with --no-record actions, then replay --from <n> --plan-digest <sha256> to re-run the unchanged step; caution means something already matches the recorded selector, so a blind re-press may repeat the mistake; manual means no safe automated repair could be proven. (If close --save-script is run before a required resume, the repair is aborted and no script is written). While armed, read-only inspection YOU run to locate the repair target (snapshot -i, get attrs, find, is) is excluded from the healed script by default — no --no-record needed; the script's own authored get/is/find steps are unaffected and stay in the heal. If the step you are repairing is itself a read, pass --record on that one command so it lands in the heal (--record is accepted only on snapshot/get/is and a read-only find; it is mutually exclusive with --no-record). Ending the repair with close --save-script[=<out>] writes only the steps recorded since the arming replay as <out>, defaulting to the <file> sibling <stem>.healed.ad — review its diff before promoting it over the original.
210
+ Startup/CPU/memory/frame first pass: perf metrics --json (bare perf and metrics are aliases). Focused frame/jank health: perf frames --json. Memory-only sample: perf memory sample --json returns compact JSON with bounded top offenders. Heap/memgraph artifact escalation: perf memory snapshot --out heap.artifact; use --kind android-hprof on Android or --kind memgraph on supported Apple simulator/macOS app sessions. Android native profiling: perf cpu profile start|stop|report --kind simpleperf --out <path>; Android native traces: perf trace start|stop --kind perfetto --out <path>. Artifact collectors return compact state/path/size metadata only; raw heap/profile/trace files stay on disk. Treat native perf output as the agent evidence: for example, a Perfetto stop can return state=stopped, outPath=/tmp/app.perfetto-trace, sizeBytes=5392410, and method=adb-shell-perfetto while the 5.3 MB raw trace stays in the artifact. This is better than raw dumps for agents because it is stable, bounded, and keeps large artifacts out of context. heapprofd is deferred until Perfetto plumbing is available. Replay divergence and resume: a failing replay/test step returns REPLAY_DIVERGENCE with a bounded report (screen digest, ranked selector suggestions, resume). Repair app state, then resume with replay --from <n> --plan-digest <sha256> (both from the report's resume field) to continue from the failed step without re-running earlier ones; resume never re-executes skipped steps, so app state is the caller's responsibility, and it is rejected with INVALID_ARGS when the plan digest is stale, --from is out of range, or the skipped range/target touches runtime control flow. The plan digest binds the script, its includes, the effective --platform/--target, and per-action runtime/identity. Native .ad interpolation is late-bound after planning, so changing only its values keeps the digest; Maestro environment substitution occurs during compatibility parsing and can change action inputs, includes, or control expansion, so it can change the digest. --from is replay-only; test rejects it. Native .ad session takeover: replay <file>.ad --keep-session suppresses exactly an authored terminal close and returns the surviving session for continued commands; interior closes still run, close-less scripts are unchanged, and test/Maestro reject the option. --update/-u no longer rewrites the script (ADR 0012) — it is a no-op kept for compatibility; every divergence already carries the same ranked suggestions. Agent-supervised repair (heal-by-doing): arm replay <file>.ad --save-script[=<out>] before step 1 (armed once; --from continuations do not need it again). Every divergence carries a repairHint: record-and-heal means press the correct control via a blessed @ref from the divergence's screen.refs, recorded (no --no-record), then continue with replay --from <n+1> --plan-digest <sha256>; state-repair means the script is correct but app state is not, so fix state with --no-record actions, then replay --from <n> --plan-digest <sha256> to re-run the unchanged step; caution means something already matches the recorded selector, so a blind re-press may repeat the mistake; manual means no safe automated repair could be proven. (If close --save-script is run before a required resume, the repair is aborted and no script is written). While armed, read-only inspection YOU run to locate the repair target (snapshot -i, get attrs, find, is) is excluded from the healed script by default — no --no-record needed; the script's own authored get/is/find steps are unaffected and stay in the heal. If the step you are repairing is itself a read, pass --record on that one command so it lands in the heal (--record is accepted only on snapshot/get/is and a read-only find; it is mutually exclusive with --no-record). Ending the repair with close --save-script[=<out>] writes only the steps recorded since the arming replay as <out>, defaulting to the <file> sibling <stem>.healed.ad — review its diff before promoting it over the original.
211
211
  Recording: record start/stop. The default scope is app and expects an active session created by open <app>; this keeps app proof videos tied to the intended app session. Use record start --scope device/system to explicitly request whole-screen capture where the selected backend supports it, such as recordings that intentionally span multiple apps, home screen, settings, or app transitions. Use --max-size to cap the longest edge and --quality medium|high to choose output quality across Android and Apple targets. By default, stop burns touch overlays into the video; use record start --hide-touches for the fastest raw recording. Android record start publishes a durable device manifest. Android adb screenrecord has a 180s platform limit, so longer Android recordings are returned as multiple MP4 chunks while the daemon stays alive; after daemon restart, record stop recovers only manifest-owned chunks and warns when gesture overlays are unavailable. For gesture-heavy iOS simulator proof videos, prefer --hide-touches because overlay timing depends on a stable runner session while gestures are executing. Tracing: trace start ./trace.log, trace stop ./trace.log. Paths are positional.
212
212
  Stable known flow: batch ./steps.json, not workflow batch.
213
213
  Inline batch JSON example:
@@ -699,15 +699,22 @@ Rules:
699
699
  This topic covers what already works by opening SpringBoard as the session app. It does not yet cover keeping an app session open while alternating individual commands against SpringBoard, or Live Activity/Dynamic Island semantics; those land separately.`},remote:{summary:`Direct proxy, cloud profiles, and remote config`,body:`agent-device help remote
700
700
 
701
701
  Remote connection providers use the same lifecycle:
702
- connect -> open -> commands -> close -> disconnect
702
+ connect -> install/open -> commands -> close -> disconnect
703
703
 
704
704
  Providers:
705
705
  Cloud: agent-device connect or agent-device connect cloud discovers the agent-device cloud profile.
706
706
  Remote config: agent-device connect --remote-config ./remote-config.json uses a local profile.
707
707
  Direct proxy: agent-device connect proxy --daemon-base-url <proxy-agent-device-url> stores the shared proxy profile and client identity.
708
- BrowserStack: agent-device connect browserstack stores a local provider profile and creates the App Automate session on first open.
709
- AWS Device Farm: agent-device connect aws-device-farm stores a local provider profile and creates the remote access session on first open.
710
- Limrun: agent-device connect limrun stores a local provider profile and creates a direct iOS or Android instance on first open.
708
+ BrowserStack: agent-device connect browserstack verifies credentials, the exact device, and a bs:// app reference, then stores a local provider profile. It does not create an App Automate session.
709
+ AWS Device Farm: agent-device connect aws-device-farm verifies credentials and the exact project, device, and optional app upload, then stores a local provider profile. It does not create a remote access session.
710
+ Limrun: agent-device connect limrun verifies access to the selected iOS or Android instance service, then stores a local provider profile. It does not create an instance.
711
+
712
+ After direct-provider connect:
713
+ Read the printed Device, App, Next, and workflow-note lines. They are also available as verification/device/app/liveSession/nextSteps/notes in --json output.
714
+ BrowserStack and AWS Device Farm create the hosted session on open. open needs the installed package or bundle identifier, not the app artifact name or ARN.
715
+ A new Limrun instance has no user app. Run install <package-or-bundle-id> <app-path-or-url> first; install allocates the instance, then open launches the installed id.
716
+ AWS Device Farm cannot install after allocation. If connect reports no attached app, reconnect with --aws-app-arn <arn> --force before open.
717
+ Do not run devices or apps as a pre-open catalog probe for direct providers; those commands can allocate the deferred provider session and only inspect that live device.
711
718
 
712
719
  Device cloud interfaces:
713
720
  CLI is the canonical bootstrap path: connect limrun/browserstack/aws-device-farm, then use normal open/snapshot/click/close/artifacts/disconnect commands.
@@ -780,10 +787,11 @@ Rules:
780
787
  Use agent-device proxy for direct tunnel access to a Mac you control. Expose the printed proxy URL through cloudflared/ngrok, then run agent-device connect proxy with the tunnel URL and printed token before normal commands.
781
788
  Use Limrun, BrowserStack, and AWS Device Farm through local provider profiles; they do not accept a remote agent-device daemon URL.
782
789
  Device cloud credentials must be available before the command starts. Limrun uses LIMRUN_API_KEY. BrowserStack uses BROWSERSTACK_USERNAME and BROWSERSTACK_ACCESS_KEY. AWS Device Farm uses the AWS CLI credential chain, including CI-provided AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY/AWS_SESSION_TOKEN, AWS profiles, or web identity role variables.
790
+ Direct-provider connect performs read-only provider calls and saves active connection state only after verification succeeds. It never creates a device, instance, App Automate session, or AWS remote access session.
783
791
  Prefer short-lived AWS role credentials in CI. Generated connection profiles store app/device selectors and ARNs, not Limrun API keys, BrowserStack access keys, or AWS credentials.
784
792
  Limrun Android supports direct ADB port reverse for local Metro. Limrun iOS requires a public Metro/React DevTools URL because it cannot reach local host ports directly.
785
793
  After closing a device cloud session, run agent-device artifacts --json to retrieve provider video/log/dashboard URLs when the provider has made them available.
786
- connect proxy stores the connection profile and client identity. Device leases are acquired on open and expire after five minutes without commands.
794
+ connect proxy stores the connection profile and client identity. Proxy device leases are acquired on open and expire after five minutes without commands; devices may inspect proxy inventory without allocating.
787
795
  Multiple agents can share one proxy when each uses connect proxy, open, commands, close, and disconnect.
788
796
  disconnect releases local connection state; close releases the active session and device lease.
789
797
  A busy direct-proxy device error means another agent owns the device until it closes or its inactivity lease expires.
@@ -959,28 +967,28 @@ Evidence:
959
967
 
960
968
  Report:
961
969
  Summarize what changed, exact validation commands, pass/fail observations, artifact paths, and residual risk.
962
- If live validation is blocked, state the blocker, device/session, and exact next command needed.`}};function v(e,t,n){let r=n.endsWith(`?`),i=r?n.slice(0,-1):n,a=/^[a-z-]+(?:\|[a-z-]+)+$/i.test(i),o=a||t.usageOverride!==void 0&&t.usageOverride.startsWith(`${e} ${i}`);return r?a?`[${i}]`:o?i:`[${i}]`:o?i:`<${i}>`}function y(e,t){return t.listUsageOverride?t.listUsageOverride:[e,...(t.positionalArgs??[]).map(n=>v(e,t,n))].join(` `)}function b(){let n=E(e().map(e=>{let t=i(e);return{name:e,schema:t,usage:y(e,t)}})),r=C(`Global Flags:`,S(t)),a=T(`Agent Starting Point:`,f),o=T(`Agent Quickstart:`,p);return`agent-device <command> [args] [--json]
970
+ If live validation is blocked, state the blocker, device/session, and exact next command needed.`}};function v(e,t,n){let r=n.endsWith(`?`),i=r?n.slice(0,-1):n,a=/^[a-z-]+(?:\|[a-z-]+)+$/i.test(i),o=a||t.usageOverride!==void 0&&t.usageOverride.startsWith(`${e} ${i}`);return r?a?`[${i}]`:o?i:`[${i}]`:o?i:`<${i}>`}function y(e,t){return t.listUsageOverride?t.listUsageOverride:[e,...(t.positionalArgs??[]).map(n=>v(e,t,n))].join(` `)}function b(){let t=D(e().map(e=>{let t=i(e);return{name:e,schema:t,usage:y(e,t)}})),r=w(`Global Flags:`,S(n)),a=E(`Agent Starting Point:`,f),o=E(`Agent Quickstart:`,p);return`agent-device <command> [args] [--json]
963
971
 
964
972
  CLI to automate supported app, device, desktop, and web targets for AI agents.
965
973
 
966
974
  ${a}
967
975
 
968
- ${w(`Agent Workflows:`,d)}
976
+ ${T(`Agent Workflows:`,d)}
969
977
 
970
- ${n}
978
+ ${t}
971
979
 
972
980
  ${r}
973
981
 
974
982
  ${o}
975
983
 
976
- ${T(`Configuration:`,m)}
984
+ ${E(`Configuration:`,m)}
977
985
 
978
- ${w(`Environment:`,h)}
986
+ ${T(`Environment:`,h)}
979
987
 
980
- ${T(`Examples:`,g)}
981
- `}function x(){return b()}function S(e){return n().filter(t=>e.has(t.key)&&t.usageLabel!==void 0&&t.usageDescription!==void 0)}function C(e,t){return w(e,t.map(e=>({label:e.usageLabel??``,description:e.usageDescription??``})))}function w(e,t){if(t.length===0)return`${e}\n (none)`;let n=Math.max(...t.map(e=>Math.min(e.label.length,26)))+2,r=[e];for(let e of t){let t=e.label.length<=26?e.label.padEnd(n):`${e.label} `;r.push(` ${t}${e.description}`)}return r.join(`
982
- `)}function T(e,t){return t.length===0?`${e}\n (none)`:[e,...t.map(e=>` ${e}`)].join(`
983
- `)}function E(e){return w(`Commands:`,e.map(e=>({label:e.usage,description:e.schema.summary??e.schema.helpDescription})))}function D(e){let t=O(e);if(t)return t;let n=r(e);if(!n)return null;let i=u(e,n),a=S(new Set(n.allowedFlags??[])),o=[];a.length>0&&o.push(C(`Command flags:`,a));let s=o.length>0?`\n\n${o.join(`
988
+ ${E(`Examples:`,g)}
989
+ `}function x(){return b()}function S(e){return t().filter(t=>e.has(t.key)&&t.usageLabel!==void 0&&t.usageDescription!==void 0)}function C(e,t){return t?e.map(e=>{let n=t[e.key];return n===void 0?e:{...e,usageDescription:n}}):e}function w(e,t){return T(e,t.map(e=>({label:e.usageLabel??``,description:e.usageDescription??``})))}function T(e,t){if(t.length===0)return`${e}\n (none)`;let n=Math.max(...t.map(e=>Math.min(e.label.length,26)))+2,r=[e];for(let e of t){let t=e.label.length<=26?e.label.padEnd(n):`${e.label} `;r.push(` ${t}${e.description}`)}return r.join(`
990
+ `)}function E(e,t){return t.length===0?`${e}\n (none)`:[e,...t.map(e=>` ${e}`)].join(`
991
+ `)}function D(e){return T(`Commands:`,e.map(e=>({label:e.usage,description:e.schema.summary??e.schema.helpDescription})))}function O(e){let t=k(e);if(t)return t;let n=r(e);if(!n)return null;let i=u(e,n),a=C(S(new Set(n.allowedFlags??[])),n.flagDescriptionOverrides),o=[];a.length>0&&o.push(w(`Command flags:`,a));let s=o.length>0?`\n\n${o.join(`
984
992
 
985
993
  `)}`:``;return`agent-device ${i}
986
994
 
@@ -988,11 +996,11 @@ ${n.helpDescription}
988
996
 
989
997
  Usage:
990
998
  agent-device ${i}${s}
991
- `}function O(e){let t=_[e];return t?`${t.body}
999
+ `}function k(e){let t=_[e];return t?`${t.body}
992
1000
 
993
1001
  Related:
994
1002
  agent-device help command list and global flags
995
1003
  agent-device help <command> command-specific flags
996
1004
  agent-device help manual-qa routine QA loop with concrete command shapes
997
1005
  agent-device help workflow full app automation reference
998
- `:null}export{D as buildCommandUsageText,x as buildUsageText};
1006
+ `:null}export{O as buildCommandUsageText,x as buildUsageText};
@@ -1,2 +1,2 @@
1
- import{Qt as e}from"./recording-export-quality.js";import{n as t}from"./registry.js";import{n}from"./cli-config.js";import{n as r}from"./command-surface.js";function i(t,n,r){if(t.json){e({success:!0,data:n});return}let i=r?.();i&&a(i)}function a(e){process.stdout.write(e.endsWith(`
1
+ import{lt as e}from"./viewport-dimension.js";import{n as t}from"./registry.js";import{n}from"./cli-config.js";import{n as r}from"./command-surface.js";function i(t,n,r){if(t.json){e({success:!0,data:n});return}let i=r?.();i&&a(i)}function a(e){process.stdout.write(e.endsWith(`
2
2
  `)?e:`${e}\n`)}const o=t();function s(e,t,n){let r=o[e];if(!r)throw Error(`Missing CLI reader for command: ${e}`);return r(t,n)}async function c(e){return(await l(e)).result}async function l(e){let t=s(e.command,e.positionals,e.flags),i=await r(e.client,e.command,t);return{result:i,cliOutput:n({name:e.command,input:t,result:i})}}export{i,l as n,s as r,c as t};