agent-device 0.19.0 → 0.19.1

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 (84) hide show
  1. package/README.md +5 -1
  2. package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.19.0.apk → agent-device-android-multitouch-helper-0.19.1.apk} +0 -0
  3. package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.19.1.apk.sha256 +1 -0
  4. package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.19.0.manifest.json → agent-device-android-multitouch-helper-0.19.1.manifest.json} +4 -4
  5. package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.19.0.apk → agent-device-android-snapshot-helper-0.19.1.apk} +0 -0
  6. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.19.1.apk.sha256 +1 -0
  7. package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.19.0.manifest.json → agent-device-android-snapshot-helper-0.19.1.manifest.json} +6 -6
  8. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AXSnapshotFallback.swift +24 -7
  9. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +506 -119
  10. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +153 -48
  11. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift +172 -0
  12. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +76 -5
  13. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +60 -52
  14. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +159 -56
  15. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedGesturePolicy.swift +142 -0
  16. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +16 -14
  17. package/dist/src/android-adb.d.ts +1 -2
  18. package/dist/src/android-adb.js +1 -1
  19. package/dist/src/android-input-ownership.js +6 -0
  20. package/dist/src/android.js +1 -1
  21. package/dist/src/app-lifecycle.js +3 -3
  22. package/dist/src/apple-runner-options.js +1 -1
  23. package/dist/src/apps.js +5 -5
  24. package/dist/src/cli-help.js +131 -34
  25. package/dist/src/cli-runner.js +1 -1
  26. package/dist/src/cli.js +11 -11
  27. package/dist/src/client.js +2 -2
  28. package/dist/src/command-metadata.js +1 -1
  29. package/dist/src/command-schema.js +3 -3
  30. package/dist/src/device-input-state.js +2 -2
  31. package/dist/src/devices2.js +1 -1
  32. package/dist/src/find.js +1 -1
  33. package/dist/src/generic.js +1 -1
  34. package/dist/src/index.d.ts +199 -20
  35. package/dist/src/input-actions.js +3 -3
  36. package/dist/src/interaction-snapshot.js +1 -1
  37. package/dist/src/interaction.js +1 -1
  38. package/dist/src/interactor.js +2 -2
  39. package/dist/src/internal/bin.js +1 -1
  40. package/dist/src/internal/daemon.js +17 -15
  41. package/dist/src/lease.js +1 -1
  42. package/dist/src/linux.js +1 -1
  43. package/dist/src/metro.d.ts +0 -1
  44. package/dist/src/notifications.js +1 -1
  45. package/dist/src/points.js +1 -1
  46. package/dist/src/project-runtime.js +1 -1
  47. package/dist/src/providers.js +1 -1
  48. package/dist/src/react-native.js +1 -1
  49. package/dist/src/record-trace-recording.js +2 -2
  50. package/dist/src/record-trace.js +1 -1
  51. package/dist/src/registry.js +24 -23
  52. package/dist/src/reporting.js +3 -3
  53. package/dist/src/runner-client.js +1 -1
  54. package/dist/src/runtime.js +2 -2
  55. package/dist/src/sdk-android-adb.d.ts +2 -2
  56. package/dist/src/sdk-batch-runner.d.ts +0 -1
  57. package/dist/src/sdk-batch.js +2 -2
  58. package/dist/src/sdk-finders.d.ts +1 -2
  59. package/dist/src/sdk-finders.js +1 -1
  60. package/dist/src/sdk-io.d.ts +0 -1
  61. package/dist/src/sdk-remote-config.d.ts +1 -2
  62. package/dist/src/sdk-remote-config.js +1 -1
  63. package/dist/src/sdk-selectors.js +1 -1
  64. package/dist/src/selector-runtime.js +1 -1
  65. package/dist/src/selectors.d.ts +0 -1
  66. package/dist/src/selectors.js +1 -1
  67. package/dist/src/selectors2.js +1 -1
  68. package/dist/src/server.js +3 -3
  69. package/dist/src/session.js +10 -10
  70. package/dist/src/simctl.js +1 -1
  71. package/dist/src/simulator.js +1 -1
  72. package/dist/src/snapshot-diagnostics.js +2 -2
  73. package/dist/src/snapshot-helper.js +4 -4
  74. package/dist/src/snapshot-quality.js +1 -1
  75. package/dist/src/snapshot2.js +2 -2
  76. package/dist/src/tv-remote.js +14 -0
  77. package/dist/src/web.js +1 -1
  78. package/package.json +5 -4
  79. package/server.json +2 -2
  80. package/skills/agent-device/SKILL.md +8 -7
  81. package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.19.0.apk.sha256 +0 -1
  82. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.19.0.apk.sha256 +0 -1
  83. package/dist/src/device-rotation.js +0 -14
  84. package/dist/src/logcat.js +0 -6
@@ -52,6 +52,7 @@ final class RunnerTests: XCTestCase {
52
52
  let maxRecordingFps = 120
53
53
  var needsPostSnapshotInteractionDelay = false
54
54
  var needsFirstInteractionDelay = false
55
+ var runnerAccessibilityHealth: RunnerAccessibilityHealth = .unknown
55
56
  var activeRecording: ScreenRecorder?
56
57
  let commandJournal = RunnerCommandJournal()
57
58
  // Coalesces duplicate transport sends of the same commandId onto the single in-flight
@@ -67,21 +68,22 @@ final class RunnerTests: XCTestCase {
67
68
  // Past this age the runner stops claiming "busy, retry soon" and reports itself wedged so
68
69
  // the daemon recycles it — the only cure once the main thread is stuck for good.
69
70
  let mainThreadWedgeThreshold: TimeInterval = 120
70
- // Sticky per-bundle hint: after the XCTest tree backend ground past its slice (or a snapshot
71
- // was abandoned by the watchdog), later capture plans lead with the private AX backend
72
- // instead of re-grinding the tree on the same screen class (#1105).
73
- let snapshotTreePenaltyLock = NSLock()
74
- var snapshotTreePenaltyBundleId: String?
75
- var snapshotTreePenaltyUntil = Date.distantPast
76
- let snapshotTreePenaltyDuration: TimeInterval = 120
77
- // Bluesky-class screens grind ~4-8s before the tree backend fails; anything past this
78
- // threshold marks the screen hostile so the next capture leads with private AX.
79
- let snapshotTreeSlowCaptureThreshold: TimeInterval = 3
80
- // The blocking XCTest tree snapshot XPC runs on a worker thread with this slice so a
71
+ // Sticky per-bundle hint: after an XCTest-backed snapshot tier ground past its slice (or a
72
+ // snapshot was abandoned by the watchdog), later capture plans avoid the XCTest accessibility
73
+ // channel when an independent recovery backend exists, or use a bounded XCTest probe when it
74
+ // does not, for the same screen class (#1105/#1156).
75
+ let snapshotXCTestChannelPenaltyLock = NSLock()
76
+ var snapshotXCTestChannelPenaltyBundleId: String?
77
+ var snapshotXCTestChannelPenaltyUntil = Date.distantPast
78
+ let snapshotXCTestChannelPenaltyDuration: TimeInterval = 120
79
+ // Bluesky-class screens can grind ~4-8s before an XCTest-backed snapshot tier fails; anything
80
+ // past this threshold marks the screen hostile so the next capture uses non-XCTest recovery.
81
+ let snapshotXCTestSlowCaptureThreshold: TimeInterval = 3
82
+ // The blocking XCTest tree snapshot XPC runs on the main thread with this slice so a
81
83
  // content-dependent grind (#1105: seconds to minutes on live Bluesky screens) cannot pin
82
- // the capture plan. On timeout the XPC keeps grinding on its worker; while any abandoned
83
- // tree capture is outstanding, plans skip the XCTest-backed tiers (tree, query sweep) and
84
- // use the private AX backend, which does not go through testmanagerd.
84
+ // the capture plan. On timeout the XPC keeps grinding on main; while any abandoned
85
+ // tree capture is outstanding, plans skip XCTest-backed tiers (tree, query sweep) until the
86
+ // abandoned work drains.
85
87
  let treeCaptureLock = NSLock()
86
88
  var abandonedTreeCaptureCount = 0
87
89
  let treeCaptureSliceBudget: TimeInterval = 8
@@ -1,7 +1,6 @@
1
1
  import { i as createAndroidPortReverseManager, n as AndroidAdbExecutorOptions, r as AndroidPortReverseEndpoint, t as AndroidAdbExecutor } from "./sdk-android-adb.js";
2
2
  import { t as AppsFilter } from "./sdk-app-inventory.js";
3
3
  import fs from "node:fs";
4
-
5
4
  //#region src/platforms/android/app-parsers.d.ts
6
5
  type AndroidForegroundApp = {
7
6
  package?: string;
@@ -37,7 +36,7 @@ type AndroidLogcatCaptureOptions = {
37
36
  };
38
37
  declare function captureAndroidLogcatWithAdb(adb: AndroidAdbExecutor, options?: AndroidLogcatCaptureOptions): Promise<string>;
39
38
  //#endregion
40
- //#region src/platforms/android/input-ownership.d.ts
39
+ //#region src/core/android-input-ownership.d.ts
41
40
  type AndroidInputOwner = 'app' | 'ime' | 'unknown';
42
41
  //#endregion
43
42
  //#region src/platforms/android/device-input-state.d.ts
@@ -1 +1 @@
1
- import{F as e}from"./sdk-contracts.js";import{a as t,n}from"./sdk-android-adb.js";import{r}from"./sdk-app-inventory.js";import{a as i,g as a,h as o,m as s,o as c,u as l}from"./app-lifecycle.js";import{t as u}from"./logcat.js";import{c as d,i as f,n as p,o as m}from"./device-input-state.js";const h=`android.intent.category.LAUNCHER`,g=`android.intent.category.LEANBACK_LAUNCHER`;async function _(e,t={}){let n=await y(e,t.target??`auto`);return(r(t.filter)===`user-installed`?(await C(e)).filter(e=>n.has(e)):Array.from(n)).map(e=>({package:e,name:i(e)})).sort((e,t)=>e.package.localeCompare(t.package))}async function v(e){return await w(e,[[`shell`,`dumpsys`,`window`,`windows`],[`shell`,`dumpsys`,`window`]])||await w(e,[[`shell`,`dumpsys`,`activity`,`activities`],[`shell`,`dumpsys`,`activity`]])||{}}async function y(e,t){let n=(await Promise.all(b(t).map(async t=>{let n=await e(x(t),{allowFailure:!0});return n.exitCode===0?S(n.stdout):[]}))).flat();return new Set(n)}function b(e){switch(e){case`mobile`:return[h];case`tv`:return[g];default:return[h,g]}}function x(e){return[`shell`,`cmd`,`package`,`query-activities`,`--brief`,`-a`,`android.intent.action.MAIN`,`-c`,e]}function S(e){return e.trim().length===0?[]:o(e)}async function C(e){let t=await e([`shell`,`pm`,`list`,`packages`,`-3`],{allowFailure:!0});if(t.exitCode!==0)throw n(`Failed to list Android user-installed apps`,t);return a(t.stdout)}async function w(e,t){for(let n of t){let t=s((await e(n,{allowFailure:!0})).stdout??``);if(t)return t}return null}const T=`android.intent.category.LAUNCHER`,E=`android.intent.category.DEFAULT`;async function D(e,t){await e([`shell`,`am`,`force-stop`,t])}async function O(e,t,n=[T]){for(let r of n){let n=await e([`shell`,`cmd`,`package`,`resolve-activity`,`--brief`,`-a`,`android.intent.action.MAIN`,`-c`,r,t],{allowFailure:!0});if(n.exitCode!==0)continue;let i=l(n.stdout);if(i)return i}return null}async function k(t,n,r={}){let i=r.category??T;if(r.activity){await t([`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.MAIN`,`-c`,E,`-c`,i,`-n`,A(n,r.activity)]);return}let a=await t([`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.MAIN`,`-c`,E,`-c`,i,`-p`,n],{allowFailure:!0});if(a.exitCode===0&&!c(a.stdout,a.stderr))return;let o=await O(t,n,[i]);if(!o)throw new e(`COMMAND_FAILED`,`Failed to resolve Android launch component for ${n}`,{stdout:a.stdout,stderr:a.stderr,exitCode:a.exitCode});await t([`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.MAIN`,`-c`,E,`-c`,i,`-n`,o])}function A(e,t){return t.includes(`/`)?t:`${e}/${t.startsWith(`.`)?t:`.${t}`}`}export{u as captureAndroidLogcatWithAdb,t as createAndroidPortReverseManager,p as dismissAndroidKeyboardWithAdb,D as forceStopAndroidAppWithAdb,v as getAndroidAppStateWithAdb,f as getAndroidKeyboardStatusWithAdb,_ as listAndroidAppsWithAdb,k as openAndroidAppWithAdb,m as readAndroidClipboardWithAdb,d as writeAndroidClipboardWithAdb};
1
+ import{F as e}from"./sdk-contracts.js";import{a as t,n}from"./sdk-android-adb.js";import{r}from"./sdk-app-inventory.js";import{a as i,g as a,h as o,m as s,o as c,u as l}from"./app-lifecycle.js";import{s as u}from"./android-input-ownership.js";import{c as d,i as f,n as p,o as m}from"./device-input-state.js";const h=`android.intent.category.LAUNCHER`,g=`android.intent.category.LEANBACK_LAUNCHER`;async function _(e,t={}){let n=await y(e,t.target??`auto`);return(r(t.filter)===`user-installed`?(await C(e)).filter(e=>n.has(e)):Array.from(n)).map(e=>({package:e,name:i(e)})).sort((e,t)=>e.package.localeCompare(t.package))}async function v(e){return await w(e,[[`shell`,`dumpsys`,`window`,`windows`],[`shell`,`dumpsys`,`window`]])||await w(e,[[`shell`,`dumpsys`,`activity`,`activities`],[`shell`,`dumpsys`,`activity`]])||{}}async function y(e,t){let n=(await Promise.all(b(t).map(async t=>{let n=await e(x(t),{allowFailure:!0});return n.exitCode===0?S(n.stdout):[]}))).flat();return new Set(n)}function b(e){switch(e){case`mobile`:return[h];case`tv`:return[g];default:return[h,g]}}function x(e){return[`shell`,`cmd`,`package`,`query-activities`,`--brief`,`-a`,`android.intent.action.MAIN`,`-c`,e]}function S(e){return e.trim().length===0?[]:o(e)}async function C(e){let t=await e([`shell`,`pm`,`list`,`packages`,`-3`],{allowFailure:!0});if(t.exitCode!==0)throw n(`Failed to list Android user-installed apps`,t);return a(t.stdout)}async function w(e,t){for(let n of t){let t=s((await e(n,{allowFailure:!0})).stdout??``);if(t)return t}return null}const T=`android.intent.category.LAUNCHER`,E=`android.intent.category.DEFAULT`;async function D(e,t){await e([`shell`,`am`,`force-stop`,t])}async function O(e,t,n=[T]){for(let r of n){let n=await e([`shell`,`cmd`,`package`,`resolve-activity`,`--brief`,`-a`,`android.intent.action.MAIN`,`-c`,r,t],{allowFailure:!0});if(n.exitCode!==0)continue;let i=l(n.stdout);if(i)return i}return null}async function k(t,n,r={}){let i=r.category??T;if(r.activity){await t([`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.MAIN`,`-c`,E,`-c`,i,`-n`,A(n,r.activity)]);return}let a=await t([`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.MAIN`,`-c`,E,`-c`,i,`-p`,n],{allowFailure:!0});if(a.exitCode===0&&!c(a.stdout,a.stderr))return;let o=await O(t,n,[i]);if(!o)throw new e(`COMMAND_FAILED`,`Failed to resolve Android launch component for ${n}`,{stdout:a.stdout,stderr:a.stderr,exitCode:a.exitCode});await t([`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.MAIN`,`-c`,E,`-c`,i,`-n`,o])}function A(e,t){return t.includes(`/`)?t:`${e}/${t.startsWith(`.`)?t:`.${t}`}`}export{u as captureAndroidLogcatWithAdb,t as createAndroidPortReverseManager,p as dismissAndroidKeyboardWithAdb,D as forceStopAndroidAppWithAdb,v as getAndroidAppStateWithAdb,f as getAndroidKeyboardStatusWithAdb,_ as listAndroidAppsWithAdb,k as openAndroidAppWithAdb,m as readAndroidClipboardWithAdb,d as writeAndroidClipboardWithAdb};
@@ -0,0 +1,6 @@
1
+ import{t as e}from"./rolldown-runtime.js";import{F as t,I as n}from"./sdk-contracts.js";import{b as r,c as i,h as a,j as o,n as s,r as c,v as l,w as u}from"./sdk-android-adb.js";import{t as d}from"./timeouts.js";import{n as f,t as p}from"./manifest.js";import{n as m,t as h}from"./install-source2.js";import"node:fs";import g from"node:path";function ee(e){let t=e.trim();if(!t||/\s/.test(t))return!1;let n=/^([A-Za-z][A-Za-z0-9+.-]*):(.+)$/.exec(t);if(!n)return!1;let r=n[1]?.toLowerCase(),i=n[2]??``;return r===`http`||r===`https`||r===`ws`||r===`wss`||r===`ftp`||r===`ftps`?i.startsWith(`//`):!0}function _(e){let t=e.trim().split(`:`)[0]?.toLowerCase();return t===`http`||t===`https`}function te(e,t){let n=e?.trim();if(n)return n;if(_(t))return`com.apple.mobilesafari`}function ne(e={}){let t=e.ttlMs??3e4,n=e.nowMs??Date.now,r=new Map,i=e=>{let t=y(e);for(let e of r.keys())e.startsWith(t)&&r.delete(e)};return{get(e,t){let i=v(e,t),a=r.get(i);if(a){if(a.expiresAtMs<=n()){r.delete(i);return}return a.value}},set(e,i,a){return r.set(v(e,i),{value:a,expiresAtMs:n()+t}),a},clear(e){i(e)},async invalidateWhile(e,t){i(e);try{return await t()}finally{i(e)}}}}function v(e,t){return[e.platform,e.deviceId,e.variant??``,t.trim().toLowerCase()].join(`\0`)}function y(e){return[e.platform,e.deviceId,``].join(`\0`)}function b(e){return[`1`,`true`,`yes`,`on`].includes((e??``).trim().toLowerCase())}const x={attempts:3,baseDelayMs:200,maxDelayMs:2e3,jitter:.2};var S=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 C(e,n={},r={}){let i={maxAttempts:n.maxAttempts??x.attempts,baseDelayMs:n.baseDelayMs??x.baseDelayMs,maxDelayMs:n.maxDelayMs??x.maxDelayMs,jitter:n.jitter??x.jitter,shouldRetry:n.shouldRetry},a;for(let n=1;n<=i.maxAttempts;n+=1){if(r.signal?.aborted)throw new t(`COMMAND_FAILED`,`request canceled`,{reason:`request_canceled`});if(r.deadline?.isExpired()&&n>1)break;try{let t=await e({attempt:n,maxAttempts:i.maxAttempts,deadline:r.deadline});return r.onEvent?.({phase:r.phase,event:`succeeded`,attempt:n,maxAttempts:i.maxAttempts,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs()}),w({phase:r.phase,event:`succeeded`,attempt:n,maxAttempts:i.maxAttempts,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs()}),t}catch(e){a=e;let t=r.classifyReason?.(e),o={phase:r.phase,event:`attempt_failed`,attempt:n,maxAttempts:i.maxAttempts,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs(),reason:t};if(r.onEvent?.(o),w(o),n>=i.maxAttempts||i.shouldRetry&&!i.shouldRetry(e,n))break;let s=ie(i.baseDelayMs,i.maxDelayMs,i.jitter,n),c=r.deadline?Math.min(s,r.deadline.remainingMs()):s;if(c<=0)break;let l={phase:r.phase,event:`retry_scheduled`,attempt:n,maxAttempts:i.maxAttempts,delayMs:c,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs(),reason:t};r.onEvent?.(l),w(l),await ae(c,r.signal)}}let o={phase:r.phase,event:`exhausted`,attempt:i.maxAttempts,maxAttempts:i.maxAttempts,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs(),reason:r.classifyReason?.(a)};throw r.onEvent?.(o),w(o),a||new t(`COMMAND_FAILED`,`retry failed`)}async function re(e,t={}){return C(()=>e(),{maxAttempts:t.attempts,baseDelayMs:t.baseDelayMs,maxDelayMs:t.maxDelayMs,jitter:t.jitter,shouldRetry:t.shouldRetry})}function ie(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 ae(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 w(e){o({level:e.event===`attempt_failed`||e.event===`exhausted`?`warn`:`debug`,phase:`retry`,data:{...e}})}function T(e){return e?.trim()||void 0}function oe(e){return T(e)}function E(e){return new Set(e.split(/[\s,]+/).map(e=>e.trim()).filter(Boolean))}function D(e,t=process.env){let n=T(e)??T(t.AGENT_DEVICE_ANDROID_DEVICE_ALLOWLIST);if(n)return E(n)}const se=new Set([`IOS_BOOT_TIMEOUT`,`IOS_RUNNER_CONNECT_TIMEOUT`,`IOS_TOOL_MISSING`,`ANDROID_BOOT_TIMEOUT`,`ADB_TRANSPORT_UNAVAILABLE`,`CI_RESOURCE_STARVATION_SUSPECTED`]);function ce(e){return se.has(e.toUpperCase())}function O(e){let t=e.error?n(e.error):null,r=e.context?.platform,i=e.context?.phase;if(t?.code===`TOOL_MISSING`)return r===`android`?`ADB_TRANSPORT_UNAVAILABLE`:`IOS_TOOL_MISSING`;let a=t?.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,t?.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`:t?.code===`COMMAND_FAILED`||d.length>0?`BOOT_COMMAND_FAILED`:`UNKNOWN`}function k(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.`}}var le=e({ensureAndroidEmulatorBooted:()=>Se,listAndroidDevices:()=>U,parseAndroidAvdList:()=>G,parseAndroidEmulatorAvdNameOutput:()=>I,parseAndroidFeatureListForTv:()=>H,parseAndroidTargetFromCharacteristics:()=>V,resolveAndroidAvdName:()=>K,resolveAndroidEmulatorAvdName:()=>B,waitForAndroidBoot:()=>J});const A=1e4,ue=[`android.software.leanback`,`android.software.leanback_only`,`android.hardware.type.television`];async function j(e,t,n){let r=Array(e.length),i=0,a=Math.min(t,e.length);return await Promise.all(Array.from({length:a},async()=>{for(;i<e.length;){let t=i;i+=1,r[t]=await n(e[t])}})),r}function M(e){return`${e.stdout}\n${e.stderr}`}function N(e,t){return[`-s`,e,...t]}function P(e){return e.startsWith(`emulator-`)}function F(e){return e.toLowerCase().replace(/_/g,` `).replace(/\s+/g,` `).trim()}function I(e){let t=e.split(`
3
+ `).map(e=>e.trim()).filter(e=>e.length>0);if(t.length!==0)return t.at(-1)===`OK`&&t.pop(),t.join(`
4
+ `).trim()||void 0}async function L(e,t=A){return l(`adb`,N(e,[`shell`,`getprop`,`sys.boot_completed`]),{allowFailure:!0,timeoutMs:t})}async function R(e,t){let n=t.replace(/_/g,` `).trim();if(!P(e))return n||e;let r=await B(e);return r?r.replace(/_/g,` `):n||e}async function z(e,t,r){try{return await r(`adb`,N(e,t),{allowFailure:!0,timeoutMs:1e4})}catch(e){if(de(n(e)))return;throw e}}function de(e){return e.code===`COMMAND_FAILED`&&typeof e.details?.timeoutMs==`number`}async function B(e,t=l){for(let n of[`ro.boot.qemu.avd_name`,`persist.sys.avd_name`]){let r=await z(e,[`shell`,`getprop`,n],t);if(!r)continue;let i=r.stdout.trim();if(r.exitCode===0&&i.length>0)return i}let n=await z(e,[`emu`,`avd`,`name`],t);if(!n)return;let r=I(n.stdout);if(n.exitCode===0&&r)return r}function V(e){let t=e.toLowerCase();return t.includes(`tv`)||t.includes(`leanback`)?`tv`:null}function H(e){return/feature:android\.(software\.leanback(_only)?|hardware\.type\.television)\b/i.test(e)}async function fe(e,t){let n=M(await l(`adb`,N(e,[`shell`,`cmd`,`package`,`has-feature`,t]),{allowFailure:!0,timeoutMs:A})).toLowerCase();return n.includes(`true`)?!0:n.includes(`false`)?!1:null}async function pe(e){return(await j(ue,2,async t=>await fe(e,t))).some(e=>e===!0)}async function me(e){return V(M(await l(`adb`,N(e,[`shell`,`getprop`,`ro.build.characteristics`]),{allowFailure:!0,timeoutMs:A})))===`tv`||await pe(e)||H(M(await l(`adb`,N(e,[`shell`,`pm`,`list`,`features`]),{allowFailure:!0,timeoutMs:A})))?`tv`:`mobile`}async function U(e={}){if(await f(),!await u(`adb`))throw new t(`TOOL_MISSING`,`adb not found in PATH`);let n=e.serialAllowlist??D(void 0),r=await j((await W()).filter(e=>!n||n.has(e.serial)),3,async({serial:e,rawModel:t})=>{let[n,r,i]=await Promise.all([R(e,t),xe(e),me(e)]);return{platform:`android`,id:e,name:n,kind:P(e)?`emulator`:`device`,target:i,booted:r}});return[...r,...await ge(r)]}function he(e){return e.split(`
5
+ `).map(e=>e.trim()).filter(e=>e.length>0&&!e.startsWith(`List of devices`)).map(e=>e.split(/\s+/)).flatMap(e=>{let t=e[0];return t===void 0||e[1]!==`device`?[]:[{serial:t,rawModel:(e.find(e=>e.startsWith(`model:`))??``).replace(`model:`,``)}]})}async function W(){try{return he((await l(`adb`,[`devices`,`-l`],{timeoutMs:A})).stdout)}catch(e){throw c(e)}}function G(e){return e.split(`
6
+ `).map(e=>e.trim()).filter(e=>e.length>0)}function K(e,t){let n=e.find(e=>e===t);if(n)return n;let r=F(t);return e.find(e=>F(e)===r)}async function q(){return G(a(await l(`emulator`,[`-list-avds`],{allowFailure:!0,timeoutMs:A}),`Failed to list Android emulator AVDs`,{hint:`Verify Android emulator tooling is installed and available in PATH.`}).stdout)}async function ge(e){let t=await q().catch(()=>[]),n=new Set(e.filter(e=>e.kind===`emulator`).map(e=>F(e.name)));return t.filter(e=>!n.has(F(e))).map(e=>({platform:`android`,id:e,name:e,kind:`emulator`,target:_e(e),booted:!1}))}function _e(e){return/\b(tv|television)\b/i.test(F(e))?`tv`:`mobile`}function ve(e,t,n){let r=F(t);return e.find(e=>e.platform!==`android`||e.kind!==`emulator`||n&&e.id!==n?!1:F(e.name)===r)}async function ye(e){let n=Date.now(),r=Math.min(1e3,Math.max(50,Math.floor(e.timeoutMs/20)));for(;Date.now()-n<e.timeoutMs;){try{let t=await be(e.avdName,e.serial);if(t)return{platform:`android`,id:t,name:e.avdName,kind:`emulator`,target:`mobile`,booted:!1}}catch{}await d(r)}throw new t(`COMMAND_FAILED`,`Android emulator did not appear in time`,{avdName:e.avdName,serial:e.serial,timeoutMs:e.timeoutMs,hint:`Check emulator logs and verify the AVD can start from command line.`})}async function be(e,t){let n=F(e),r=(await W()).filter(e=>t&&e.serial!==t?!1:P(e.serial));for(let e of r)if(F(e.rawModel)===n||F(await R(e.serial,e.rawModel))===n)return e.serial}async function xe(e){try{return(await L(e)).stdout.trim()===`1`}catch{return!1}}async function Se(e){await f();let n=e.avdName.trim();if(!n)throw new t(`INVALID_ARGS`,`Android emulator boot requires a non-empty AVD name.`);let i=e.timeoutMs??12e4;if(!await u(`adb`))throw new t(`TOOL_MISSING`,`adb not found in PATH`);if(!await u(`emulator`))throw new t(`TOOL_MISSING`,`emulator not found in PATH`);let a=await q(),o=K(a,n);if(!o)throw new t(`DEVICE_NOT_FOUND`,`No Android emulator AVD named ${e.avdName}`,{requestedAvdName:n,availableAvds:a,hint:"Run `emulator -list-avds` and pass an existing AVD name to --device."});let s=Date.now(),c=ve(await U(),o,e.serial),l=c&&P(c.id)?c:void 0;if(!l){let t=[`-avd`,o];e.headless&&t.push(`-no-window`,`-no-audio`),r(`emulator`,t)}let d=l??await ye({avdName:o,serial:e.serial,timeoutMs:i}),p=Date.now()-s,m=Math.max(1e3,i-p);await J(d.id,m);let h=(await U()).find(e=>e.id===d.id);return h?{...h,name:o,booted:!0}:{...d,name:o,booted:!0}}async function J(e,r=6e4){let i=r,a=S.fromTimeoutMs(i),o=Math.min(1e3,Math.max(50,Math.floor(i/20))),s=Math.max(1,Math.ceil(i/o)),c,l=!1;try{await C(async({deadline:n})=>{if(n?.isExpired())throw l=!0,new t(`COMMAND_FAILED`,`Android boot deadline exceeded`,{serial:e,timeoutMs:r,elapsedMs:a.elapsedMs(),message:`timeout`});let o=Math.max(1e3,n?.remainingMs()??i),s=await L(e,Math.min(o,A));if(c=s,s.stdout.trim()!==`1`)throw new t(`COMMAND_FAILED`,`Android device is still booting`,{serial:e,stdout:s.stdout,stderr:s.stderr,exitCode:s.exitCode})},{maxAttempts:s,baseDelayMs:o,maxDelayMs:o,jitter:0,shouldRetry:e=>{let t=O({error:e,stdout:c?.stdout,stderr:c?.stderr,context:{platform:`android`,phase:`boot`}});return t!==`ADB_TRANSPORT_UNAVAILABLE`&&t!==`ANDROID_BOOT_TIMEOUT`}},{deadline:a,phase:`boot`,classifyReason:e=>O({error:e,stdout:c?.stdout,stderr:c?.stderr,context:{platform:`android`,phase:`boot`}})})}catch(r){let o=n(r),s=c?.stdout,u=c?.stderr,d=c?.exitCode,f=O({error:r,stdout:s,stderr:u,context:{platform:`android`,phase:`boot`}});f===`BOOT_COMMAND_FAILED`&&o.message===`Android device is still booting`&&(f=`ANDROID_BOOT_TIMEOUT`);let p={serial:e,timeoutMs:i,elapsedMs:a.elapsedMs(),reason:f,hint:k(f),stdout:s,stderr:u,exitCode:d};throw l||f===`ANDROID_BOOT_TIMEOUT`?new t(`COMMAND_FAILED`,`Android device did not finish booting in time`,p):o.code===`TOOL_MISSING`?new t(`TOOL_MISSING`,o.message,{...p,...o.details??{}}):f===`ADB_TRANSPORT_UNAVAILABLE`?new t(`COMMAND_FAILED`,o.message,{...p,...o.details??{}}):new t(o.code,o.message,{...p,...o.details??{}},o.cause)}}async function Ce(e,t,n){return await i(e)(t,n)}function we(e){return{platform:`android`,id:e,name:e,kind:e.startsWith(`emulator-`)?`emulator`:`device`,booted:!0}}function Te(e,t){let n=`${e}\n${t}`.toLowerCase();return n.includes(`no shell command implementation`)||n.includes(`unknown command`)}var Ee=e({prepareAndroidInstallArtifact:()=>Y});async function Y(e,t){let n=e.kind===`url`&&h(e.url),r=await m({source:e,isInstallablePath:(e,t)=>t.isFile()&&De(e),installableLabel:`Android installable (.apk or .aab)`,allowArchiveExtraction:e.kind!==`url`||n,signal:t?.signal});try{let e=t?.resolveIdentity===!1?{}:await Oe(r.installablePath);return{archivePath:r.archivePath,installablePath:r.installablePath,packageName:e.packageName,cleanup:r.cleanup}}catch(e){throw await r.cleanup(),e}}function De(e){let t=g.extname(e).toLowerCase();return t===`.apk`||t===`.aab`}async function Oe(e){let t=g.extname(e).toLowerCase();return t!==`.apk`&&t!==`.aab`?{}:{packageName:await p(e)}}async function ke(e,t={}){let n=[`logcat`,`-d`,`-v`,`time`];t.lines!==void 0&&n.push(`-t`,String(Math.max(1,Math.floor(t.lines))));let r=await e(n,{allowFailure:!0,timeoutMs:t.timeoutMs,signal:t.signal});if(r.exitCode!==0)throw s(`Failed to capture Android logcat`,r);return r.stdout}function X(e,n={}){if(!e.spawn)throw new t(`UNSUPPORTED_OPERATION`,`Android ADB provider does not support streams`,{capability:`adb.spawn`});let r=[`logcat`,`-v`,`time`];n.pid&&r.push(`--pid`,n.pid);let i=e.spawn(r,{stdio:[`ignore`,`pipe`,`pipe`],signal:n.signal});return n.output&&i.stdout&&i.stdout.pipe(n.output,{end:!1}),i}const Z=new Set([`com.google.android.inputmethod.latin`,`com.samsung.android.honeyboard`,`com.touchtype.swiftkey`,`com.microsoft.swiftkey`]);function Q(e){let t=$(e.packageName),n=(e.resourceId??``).toLowerCase(),r=$(e.activeInputMethodPackage);if(t&&r&&t===r)return{inputMethodOwned:!0,source:`active-input-method`};if(r&&n.startsWith(`${r}:id/`))return{inputMethodOwned:!0,source:`active-input-method-resource`};if(t&&Z.has(t))return{inputMethodOwned:!0,source:`known-ime-package`};for(let e of Z)if(n.startsWith(`${e}:id/`))return{inputMethodOwned:!0,source:`known-ime-resource`};return{inputMethodOwned:!1,source:`app`}}function Ae(e,t,n){return Q({packageName:e,resourceId:t,activeInputMethodPackage:n}).inputMethodOwned}function je(e){return Q(e).inputMethodOwned}function Me(e){let t=$(e);return!!(t&&Z.has(t))}function Ne(e){let t=(e??``).toLowerCase();for(let e of Z)if(t.startsWith(`${e}:id/`))return!0;return!1}function Pe(e,t,n){return!e&&!t?`unknown`:Ae(e,t,n)?`ime`:`app`}function Fe(e){for(let t of[/\bmCurMethodId=([^\s]+)/i,/\bmCurId=([^\s]+)/i,/\bmCurrentInputMethodId=([^\s]+)/i,/\bcurMethodId=([^\s]+)/i]){let n=Ie(e.match(t)?.[1]);if(n)return n}}function Ie(e){let t=(e??``).trim();if(t)return $((t.split(`/`)[0]??``).match(/[a-zA-Z0-9_.]+/)?.[0])}function $(e){return(e??``).trim().toLowerCase()||void 0}export{b as C,ee as D,ne as E,_ as O,S,re as T,O as _,Ne as a,D as b,X as c,we as d,Te as f,k as g,J as h,Me as i,te as k,Ee as l,le as m,Q as n,Fe as o,Ce as p,je as r,ke as s,Pe as t,Y as u,ce as v,C as w,oe as x,E as y};
@@ -1 +1 @@
1
- import{B as e,F as t}from"./sdk-contracts.js";import{F as n,c as r,d as i,j as a,l as o,n as s,o as c,p as l}from"./sdk-android-adb.js";import{n as u,t as d}from"./version.js";import{t as f}from"./timeouts.js";import{sn as p,yt as m}from"./device-rotation.js";import{c as h,d as ee,l as te,n as ne}from"./app-lifecycle.js";import{s as g}from"./logcat.js";import{a as re,s as ie}from"./device-input-state.js";import{d as ae,f as _,l as oe,u as v}from"./snapshot-helper.js";import{wt as y}from"./internal/daemon.js";import{a as se,c as ce,d as le,f as ue,i as de,l as b,n as x,o as S,p as C,r as w,t as T,u as E}from"./input-actions.js";import{a as D,i as O,n as k,r as A,t as j}from"./setting-state.js";import M from"node:crypto";import{promises as N}from"node:fs";import P from"node:path";import F from"node:fs/promises";const I=`android-multitouch-helper-v1`,L=`ANDROID_MULTITOUCH_HELPER_NO_FINAL_RESULT`,R=`ANDROID_MULTITOUCH_HELPER_REPORTED_FAILURE`;async function z(t,n){let r=await G(t,{kind:`swipe`,...n});if(r)return r;try{return await K(t,{kind:`swipe`,...n})}catch(r){return a({level:`warn`,phase:`android_swipe_helper_fallback`,data:{error:e(r).message}}),await ue(t,n.x1,n.y1,n.x2,n.y2,n.durationMs),{backend:`adb-input-swipe-fallback`}}}async function B(e,n){if(!Number.isFinite(n.scale)||n.scale<=0)throw new t(`INVALID_ARGS`,`gesture pinch requires scale > 0`);let r=await U(e,n.x,n.y);return await W(e,{kind:`pinch`,x:r.x,y:r.y,scale:n.scale,durationMs:n.durationMs})}async function V(e,n){if(!Number.isFinite(n.degrees))throw new t(`INVALID_ARGS`,`gesture rotate requires finite degrees`);if(n.velocity!==void 0&&(!Number.isFinite(n.velocity)||n.velocity===0))throw new t(`INVALID_ARGS`,`gesture rotate velocity must be a non-zero number`);let r=await U(e,n.x,n.y),i=n.degrees;return await W(e,{kind:`rotate`,x:r.x,y:r.y,degrees:i,durationMs:n.durationMs})}async function H(e,n){if(!Number.isFinite(n.scale)||n.scale<=0)throw new t(`INVALID_ARGS`,`gesture transform requires scale > 0`);if(!Number.isFinite(n.degrees))throw new t(`INVALID_ARGS`,`gesture transform requires finite degrees`);if(![n.x,n.y,n.dx,n.dy].every(Number.isFinite))throw new t(`INVALID_ARGS`,`gesture transform requires finite x y dx dy`);return await W(e,{kind:`transform`,x:n.x,y:n.y,dx:n.dx,dy:n.dy,scale:n.scale,degrees:n.degrees,durationMs:n.durationMs})}async function U(e,t,n){if(t!==void 0&&n!==void 0)return{x:t,y:n};let r=await se(e);return{x:Math.round(r.width/2),y:Math.round(r.height/2)}}async function W(e,t){return await G(e,t)||await K(e,t)}async function G(e,t){let n=i(e);if(n)return{backend:`provider-native-touch`,...await n(t)??{}}}async function K(e,t){let i=r(e),s=await q(),c=o(e),l=await n(`android_multitouch_helper_install`,async()=>await ve({adb:i,adbProvider:c,artifact:s,deviceKey:Se(e)}),{packageName:s.manifest.packageName,versionCode:s.manifest.versionCode});a({phase:`android_multitouch_helper_install_decision`,data:l});let u=await n(`android_multitouch_helper_gesture`,async()=>await me({adb:i,request:fe(t),packageName:s.manifest.packageName,instrumentationRunner:s.manifest.instrumentationRunner}),{packageName:s.manifest.packageName,version:s.manifest.version});return{backend:`android-multitouch-helper`,helperVersion:s.manifest.version,installReason:l.reason,...u}}function fe(e){let t=Math.round(pe(e));switch(e.kind){case`swipe`:return{kind:`swipe`,x1:Math.round(e.x1),y1:Math.round(e.y1),x2:Math.round(e.x2),y2:Math.round(e.y2),durationMs:t};case`pinch`:return{kind:`pinch`,x:Math.round(e.x),y:Math.round(e.y),scale:e.scale,radius:160,durationMs:t};case`rotate`:return{kind:`rotate`,x:Math.round(e.x),y:Math.round(e.y),degrees:e.degrees,radius:160,durationMs:t};case`transform`:return{kind:`transform`,x:Math.round(e.x),y:Math.round(e.y),dx:Math.round(e.dx),dy:Math.round(e.dy),scale:e.scale,degrees:e.degrees,durationMs:t}}}function pe(e){if(e.durationMs!==void 0)return e.durationMs;if(e.kind===`swipe`||e.kind===`pinch`)return 300;let t=Math.ceil(Math.abs(e.degrees)/3)*16;return Math.min(Math.max(300,t),2400)}async function me(e){let n=Buffer.from(JSON.stringify({protocol:I,...e.request})).toString(`base64`),r=await e.adb([`shell`,`am`,`instrument`,`-w`,`-e`,`payloadBase64`,n,e.instrumentationRunner],{allowFailure:!0,timeoutMs:45e3}),i;try{i=he(`${r.stdout}\n${r.stderr}`)}catch(e){if(e instanceof t){if(e.code===R)throw new t(`COMMAND_FAILED`,e.message,e.details,e);if(e.code!==L)throw e}throw new t(`COMMAND_FAILED`,r.exitCode===0?`Android multi-touch helper output could not be parsed`:`Android multi-touch helper failed before returning parseable output`,{stdout:r.stdout,stderr:r.stderr,exitCode:r.exitCode},e)}if(r.exitCode!==0)throw new t(`COMMAND_FAILED`,`Android multi-touch helper failed`,l(r,{helper:i}));return i}function he(e){let n=oe(e).results.find(e=>e.agentDeviceProtocol===I);if(!n)throw new t(L,`Android multi-touch helper did not return a final result`);if(n.ok!==`true`)throw new t(R,ge(n),{errorType:n.errorType,helper:n});return{kind:n.kind,helperApiVersion:n.helperApiVersion,injectedEvents:_(n.injectedEvents),elapsedMs:_(n.elapsedMs)}}function ge(e){return e.message&&e.message!==`null`?e.message:e.errorType||`Android multi-touch helper returned an error`}async function q(){let n=u(),r=P.join(d(),`android-multitouch-helper`,`dist`),i=P.join(r,`agent-device-android-multitouch-helper-${n}.manifest.json`);try{let e=_e(JSON.parse(await F.readFile(i,`utf8`))),t=P.join(r,e.assetName);return await F.access(t),{apkPath:t,manifest:e}}catch(n){throw new t(`UNSUPPORTED_OPERATION`,`Android touch gestures require the bundled Android touch helper artifact, but it was not found or could not be read`,{manifestPath:i,error:e(n).message},n)}}function _e(e){if(!e||typeof e!=`object`||Array.isArray(e))throw new t(`INVALID_ARGS`,`Android multi-touch helper manifest must be an object.`);let n=e;return{name:X(n.name,`name`,`android-multitouch-helper`),version:Y(n.version,`version`),assetName:Y(n.assetName,`assetName`),sha256:we(n.sha256),packageName:X(n.packageName,`packageName`,`com.callstack.agentdevice.multitouchhelper`),versionCode:Ce(n.versionCode,`versionCode`),instrumentationRunner:X(n.instrumentationRunner,`instrumentationRunner`,`com.callstack.agentdevice.multitouchhelper/.MultiTouchInstrumentation`),statusProtocol:X(n.statusProtocol,`statusProtocol`,I)}}async function ve(e){let{adb:t,artifact:n}=e,r=n.manifest.packageName,i=n.manifest.versionCode,a=`${e.deviceKey}\0${r}\0${i}`;if(J.has(a))return{packageName:r,versionCode:i,installed:!1,reason:`current`};let o=await ye(t,r);if(o!==void 0&&o>=i)return J.add(a),{packageName:r,versionCode:i,installedVersionCode:o,installed:!1,reason:`current`};await be(n);let l=await c(n.apkPath,{provider:e.adbProvider,replace:!0,allowTestPackages:!0,allowFailure:!0,timeoutMs:3e4});if(l.exitCode!==0)throw s(`Failed to install Android multi-touch helper`,l,{packageName:r,versionCode:i});return J.add(a),{packageName:r,versionCode:i,installedVersionCode:o,installed:!0,reason:o===void 0?`missing`:`outdated`}}const J=new Set;async function ye(e,t){let n=await e([`shell`,`cmd`,`package`,`list`,`packages`,`--show-versioncode`,t],{allowFailure:!0,timeoutMs:5e3});if(n.exitCode!==0)return;let r=RegExp(`package:${Te(t)}(?:\\s|$).*versionCode:(\\d+)`).exec(`${n.stdout}\n${n.stderr}`);return r?Number(r[1]):void 0}async function be(e){let n=await xe(e.apkPath);if(n!==e.manifest.sha256)throw new t(`COMMAND_FAILED`,`Android multi-touch helper APK checksum mismatch`,{apkPath:e.apkPath,expectedSha256:e.manifest.sha256,actualSha256:n})}async function xe(e){let t=M.createHash(`sha256`);return t.update(await F.readFile(e)),t.digest(`hex`)}function Se(e){return`${e.platform}:${e.id}`}function Y(e,n){if(typeof e!=`string`||e.length===0)throw new t(`INVALID_ARGS`,`Android multi-touch helper manifest ${n} is required.`);return e}function Ce(e,t){return v(e,t,`multi-touch helper`)}function X(e,t,n){return ae(e,t,n,`multi-touch helper`)}function we(e){let n=Y(e,`sha256`).trim().toLowerCase();if(n.length!==64||!/^[0-9a-f]+$/.test(n))throw new t(`INVALID_ARGS`,`Android multi-touch helper manifest sha256 must be a 64-character hex string.`);return n}function Te(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}const Z=[`window_animation_scale`,`transition_animation_scale`,`animator_duration_scale`];async function Ee(e,n,r,i,a){switch(n.toLowerCase()){case`wifi`:await g(e,[`shell`,`svc`,`wifi`,j(r)?`enable`:`disable`]);return;case`airplane`:{let t=j(r),n=t?`1`:`0`,i=t?`true`:`false`;await g(e,[`shell`,`settings`,`put`,`global`,`airplane_mode_on`,n]),await g(e,[`shell`,`am`,`broadcast`,`-a`,`android.intent.action.AIRPLANE_MODE`,`--ez`,`state`,i]);return}case`location`:if(r.toLowerCase()===`set`){if(e.kind!==`emulator`)throw new t(`UNSUPPORTED_OPERATION`,`Android precise location coordinates are supported only on emulators.`,{deviceId:e.id,hint:`Use an Android emulator for adb emu geo fix, or configure location through device/provider tooling.`});let{latitude:n,longitude:r}=m(a);return await g(e,[`emu`,`geo`,`fix`,String(r),String(n)]),{latitude:n,longitude:r}}await g(e,[`shell`,`settings`,`put`,`secure`,`location_mode`,j(r)?`3`:`0`]);return;case`animations`:{let t=j(r)?`1`:`0`;for(let n of Z)await g(e,[`shell`,`settings`,`put`,`global`,n,t]);return{scale:t,keys:[...Z]}}case`appearance`:await g(e,[`shell`,`cmd`,`uimode`,`night`,await je(e,r)===`dark`?`yes`:`no`]);return;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 ee(e,i);if(n.type===`intent`)throw new t(`INVALID_ARGS`,`settings clear-app-state requires a package name, not an intent.`);await g(e,[`shell`,`am`,`force-stop`,n.value],{allowFailure:!0});let a=await g(e,[`shell`,`pm`,`clear`,n.value],{allowFailure:!0});if(a.exitCode!==0||!/\bSuccess\b/i.test(a.stdout))throw new t(`COMMAND_FAILED`,`Failed to clear Android app data for ${n.value}`,{package:n.value,stdout:a.stdout,stderr:a.stderr,exitCode:a.exitCode});return{package:n.value,cleared:!0}}case`fingerprint`:await Oe(e,De(r));return;case`permission`:{if(!i)throw new t(`INVALID_ARGS`,`permission setting requires an active app in session`);let n=A(r),o=Ne(a?.permissionTarget,a?.permissionMode);if(o.kind===`notifications`){await Fe(e,i,n,o);return}let s=n===`grant`?`grant`:`revoke`;if(o.type===`photos`){await Pe(e,i,s);return}await g(e,[`shell`,`pm`,s,i,o.value]);return}default:throw new t(`INVALID_ARGS`,`Unsupported setting: ${n}`)}}function De(e){let n=e.trim().toLowerCase();if(n===`match`)return`match`;if(n===`nonmatch`)return`nonmatch`;throw new t(`INVALID_ARGS`,`Invalid fingerprint state: ${e}. Use match|nonmatch.`)}async function Oe(e,n){let r=ke(e,n),i=[];for(let t of r){let n=await g(e,t,{allowFailure:!0});if(n.exitCode===0)return;i.push({args:t,stdout:n.stdout,stderr:n.stderr,exitCode:n.exitCode})}let a=D(i);throw i.length>0&&i.every(e=>Ae(e.stdout,e.stderr))?new t(`UNSUPPORTED_OPERATION`,`Android fingerprint simulation is not supported on this target/runtime.`,{deviceId:e.id,action:n,hint:`Use an Android emulator with biometric support, or a device/runtime that exposes cmd fingerprint.`,attempts:a}):new t(`COMMAND_FAILED`,`Failed to simulate Android fingerprint.`,{deviceId:e.id,action:n,attempts:a})}function ke(e,t){let n=t===`match`?`1`:`9999`,r=[[`shell`,`cmd`,`fingerprint`,`touch`,n],[`shell`,`cmd`,`fingerprint`,`finger`,n]];return e.kind===`emulator`&&r.push([`emu`,`finger`,`touch`,n]),r}function Ae(e,t){let n=`${e}\n${t}`.toLowerCase();return n.includes(`unknown command`)||n.includes(`can't find service: fingerprint`)||n.includes(`service fingerprint was not found`)||n.includes(`fingerprint cmd unavailable`)||n.includes(`emu command is not supported`)||n.includes(`emulator console is not running`)||n.includes(`fingerprint`)&&n.includes(`not found`)}async function je(e,n){let r=k(n);if(r!==`toggle`)return r;let i=await g(e,[`shell`,`cmd`,`uimode`,`night`],{allowFailure:!0});if(i.exitCode!==0)throw s(`Failed to read current Android appearance`,i);let a=Me(i.stdout,i.stderr);if(!a)throw new t(`COMMAND_FAILED`,`Unable to determine current Android appearance for toggle`,{stdout:i.stdout,stderr:i.stderr});return a===`auto`?`dark`:a===`dark`?`light`:`dark`}function Me(e,t){let n=/night mode:\s*(yes|no|auto)\b/i.exec(`${e}\n${t}`);if(!n)return null;let r=n[1]?.toLowerCase();return r===`yes`?`dark`:r===`no`?`light`:r===`auto`?`auto`:null}function Ne(e,n){let r=O(e);if(n?.trim())throw new t(`INVALID_ARGS`,`Permission mode is only supported for photos. Received: ${n}.`);if(r===`camera`)return{kind:`pm`,value:`android.permission.CAMERA`,type:`camera`};if(r===`microphone`)return{kind:`pm`,value:`android.permission.RECORD_AUDIO`,type:`microphone`};if(r===`photos`)return{kind:`pm`,value:`android.permission.READ_MEDIA_IMAGES`,type:`photos`};if(r===`contacts`)return{kind:`pm`,value:`android.permission.READ_CONTACTS`,type:`contacts`};if(r===`notifications`)return{kind:`notifications`,appOps:`POST_NOTIFICATION`,permission:`android.permission.POST_NOTIFICATIONS`};throw new t(`INVALID_ARGS`,`Unsupported permission target on Android: ${e}. Use camera|microphone|photos|contacts|notifications.`)}async function Pe(e,n,r){let i=await Ie(e),a=i!==null&&i>=33?[`android.permission.READ_MEDIA_IMAGES`,`android.permission.READ_EXTERNAL_STORAGE`]:[`android.permission.READ_EXTERNAL_STORAGE`,`android.permission.READ_MEDIA_IMAGES`],o=[];for(let t of a){let i=await g(e,[`shell`,`pm`,r,n,t],{allowFailure:!0});if(i.exitCode===0)return;o.push({permission:t,stderr:i.stderr,exitCode:i.exitCode})}throw new t(`COMMAND_FAILED`,`Failed to ${r} Android photos permission`,{appPackage:n,sdkInt:i,attempts:o})}async function Fe(e,t,n,r){let i=n===`grant`?`allow`:n===`deny`?`deny`:`default`;n===`grant`?await g(e,[`shell`,`pm`,`grant`,t,r.permission],{allowFailure:!0}):(await g(e,[`shell`,`pm`,`revoke`,t,r.permission],{allowFailure:!0}),n===`reset`&&(await g(e,[`shell`,`pm`,`clear-permission-flags`,t,r.permission,`user-set`],{allowFailure:!0}),await g(e,[`shell`,`pm`,`clear-permission-flags`,t,r.permission,`user-fixed`],{allowFailure:!0}))),await g(e,[`shell`,`appops`,`set`,t,r.appOps,i])}async function Ie(e){let t=await g(e,[`shell`,`getprop`,`ro.build.version.sdk`],{allowFailure:!0});if(t.exitCode!==0)return null;let n=Number.parseInt(t.stdout.trim(),10);return!Number.isFinite(n)||n<=0?null:n}const Q=Buffer.from([137,80,78,71,13,10,26,10]);async function Le(e,t,n={}){if(n.stabilize===!1){await $(e,t);return}await Re(e);try{await f(1e3),await $(e,t)}finally{await ze(e).catch(()=>{})}}async function Re(e){let t=t=>g(e,[`shell`,t],{allowFailure:!0});await t(`settings put global sysui_demo_allowed 1`);let n=e=>t(`am broadcast -a com.android.systemui.demo -e command ${e}`);await n(`clock -e hhmm 0941`),await n(`notifications -e visible false`)}async function ze(e){await g(e,[`shell`,`am broadcast -a com.android.systemui.demo -e command exit`],{allowFailure:!0})}async function $(e,n){let r=await g(e,[`exec-out`,`screencap`,`-p`],{binaryStdout:!0});if(!r.stdoutBuffer)throw new t(`COMMAND_FAILED`,`Failed to capture screenshot`);let i=r.stdoutBuffer.indexOf(Q);if(i<0)throw new t(`COMMAND_FAILED`,`Screenshot data does not contain a valid PNG header`);let a=Be(r.stdoutBuffer,i);if(!a)throw new t(`COMMAND_FAILED`,`Screenshot data does not contain a complete PNG payload`);await N.writeFile(n,r.stdoutBuffer.subarray(i,a))}function Be(e,t){let n=t+Q.length;for(;n+8<=e.length;){let t=e.readUInt32BE(n),r=n+4,i=e.toString(`ascii`,r,r+4),a=n+12+t;if(a>e.length)return null;if(i===`IEND`)return a;n=a}return null}function Ve(e){return{open:(t,n)=>h(e,t,{activity:n?.activity,appBundleId:n?.appBundleId,launchArgs:n?.launchArgs,url:n?.url}),openDevice:()=>te(e),close:t=>ne(e,t),tap:(t,n)=>b(e,t,n),doubleTap:async(t,n)=>{await b(e,t,n),await b(e,t,n)},swipe:(t,n,r,i,a)=>z(e,{x1:t,y1:n,x2:r,y2:i,durationMs:a}),pan:(t,n,r,i,a)=>z(e,{x1:t,y1:n,x2:r,y2:i,durationMs:a}),fling:(t,n,r,i,a)=>z(e,{x1:t,y1:n,x2:r,y2:i,durationMs:a}),longPress:(t,n,r)=>ce(e,t,n,r),focus:(t,n)=>de(e,t,n),type:(t,n)=>C(e,t,n),fill:(t,n,r,i)=>w(e,t,n,r,i),scroll:(t,n)=>le(e,t,n),pinch:(t,n,r)=>B(e,{scale:t,x:n,y:r}),screenshot:(t,n)=>Le(e,t,n),snapshot:async t=>{let r=await n(`snapshot_capture`,async()=>await y(e,{appBundleId:t?.appBundleId,interactiveOnly:t?.interactiveOnly,depth:t?.depth,scope:t?.scope,raw:t?.raw,helperSessionScope:t?.appBundleId?`daemon-session`:`command`}),{backend:`android`});return{nodes:r.nodes??[],truncated:r.truncated??!1,backend:`android`,...p(r)}},back:t=>x(e),home:()=>S(e),rotate:t=>E(e,t),rotateGesture:(t,n,r,i)=>V(e,{degrees:t,x:n,y:r,velocity:i}),transformGesture:t=>H(e,t),appSwitcher:()=>T(e),readClipboard:()=>re(e),writeClipboard:t=>ie(e,t),setSetting:(t,n,r,i)=>Ee(e,t,n,r,i)}}export{Ve as createAndroidInteractor};
1
+ import{B as e,F as t}from"./sdk-contracts.js";import{F as n,c as r,d as i,j as a,l as o,n as s,o as c,p as l}from"./sdk-android-adb.js";import{Tt as u,hn as d}from"./tv-remote.js";import{n as ee,t as te}from"./version.js";import{t as ne}from"./timeouts.js";import{c as f,d as p,l as re,n as m}from"./app-lifecycle.js";import{p as h}from"./android-input-ownership.js";import{a as g,s as _}from"./device-input-state.js";import{d as v,f as y,l as ie,u as ae}from"./snapshot-helper.js";import{Dt as oe}from"./internal/daemon.js";import{a as se,c as ce,d as le,f as ue,i as de,l as b,m as x,n as S,o as C,p as w,r as T,t as E,u as D}from"./input-actions.js";import{a as O,i as k,n as A,r as j,t as M}from"./setting-state.js";import N from"node:crypto";import{promises as P}from"node:fs";import F from"node:path";import I from"node:fs/promises";const L=`android-multitouch-helper-v1`,R=`ANDROID_MULTITOUCH_HELPER_NO_FINAL_RESULT`,z=`ANDROID_MULTITOUCH_HELPER_REPORTED_FAILURE`;async function B(t,n){let r=await G(t,{kind:`swipe`,...n});if(r)return r;try{return await K(t,{kind:`swipe`,...n})}catch(r){return a({level:`warn`,phase:`android_swipe_helper_fallback`,data:{error:e(r).message}}),await w(t,n.x1,n.y1,n.x2,n.y2,n.durationMs),{backend:`adb-input-swipe-fallback`}}}async function V(e,n){if(!Number.isFinite(n.scale)||n.scale<=0)throw new t(`INVALID_ARGS`,`gesture pinch requires scale > 0`);let r=await U(e,n.x,n.y);return await W(e,{kind:`pinch`,x:r.x,y:r.y,scale:n.scale,durationMs:n.durationMs})}async function H(e,n){if(!Number.isFinite(n.degrees))throw new t(`INVALID_ARGS`,`gesture rotate requires finite degrees`);if(n.velocity!==void 0&&(!Number.isFinite(n.velocity)||n.velocity===0))throw new t(`INVALID_ARGS`,`gesture rotate velocity must be a non-zero number`);let r=await U(e,n.x,n.y),i=n.degrees;return await W(e,{kind:`rotate`,x:r.x,y:r.y,degrees:i,durationMs:n.durationMs})}async function fe(e,n){if(!Number.isFinite(n.scale)||n.scale<=0)throw new t(`INVALID_ARGS`,`gesture transform requires scale > 0`);if(!Number.isFinite(n.degrees))throw new t(`INVALID_ARGS`,`gesture transform requires finite degrees`);if(![n.x,n.y,n.dx,n.dy].every(Number.isFinite))throw new t(`INVALID_ARGS`,`gesture transform requires finite x y dx dy`);return await W(e,{kind:`transform`,x:n.x,y:n.y,dx:n.dx,dy:n.dy,scale:n.scale,degrees:n.degrees,durationMs:n.durationMs})}async function U(e,t,n){if(t!==void 0&&n!==void 0)return{x:t,y:n};let r=await se(e);return{x:Math.round(r.width/2),y:Math.round(r.height/2)}}async function W(e,t){return await G(e,t)||await K(e,t)}async function G(e,t){let n=i(e);if(n)return{backend:`provider-native-touch`,...await n(t)??{}}}async function K(e,t){let i=r(e),s=await _e(),c=o(e),l=await n(`android_multitouch_helper_install`,async()=>await ye({adb:i,adbProvider:c,artifact:s,deviceKey:Ce(e)}),{packageName:s.manifest.packageName,versionCode:s.manifest.versionCode});a({phase:`android_multitouch_helper_install_decision`,data:l});let u=await n(`android_multitouch_helper_gesture`,async()=>await me({adb:i,request:q(t),packageName:s.manifest.packageName,instrumentationRunner:s.manifest.instrumentationRunner}),{packageName:s.manifest.packageName,version:s.manifest.version});return{backend:`android-multitouch-helper`,helperVersion:s.manifest.version,installReason:l.reason,...u}}function q(e){let t=Math.round(pe(e));switch(e.kind){case`swipe`:return{kind:`swipe`,x1:Math.round(e.x1),y1:Math.round(e.y1),x2:Math.round(e.x2),y2:Math.round(e.y2),durationMs:t};case`pinch`:return{kind:`pinch`,x:Math.round(e.x),y:Math.round(e.y),scale:e.scale,radius:160,durationMs:t};case`rotate`:return{kind:`rotate`,x:Math.round(e.x),y:Math.round(e.y),degrees:e.degrees,radius:160,durationMs:t};case`transform`:return{kind:`transform`,x:Math.round(e.x),y:Math.round(e.y),dx:Math.round(e.dx),dy:Math.round(e.dy),scale:e.scale,degrees:e.degrees,durationMs:t}}}function pe(e){if(e.durationMs!==void 0)return e.durationMs;if(e.kind===`swipe`||e.kind===`pinch`)return 300;let t=Math.ceil(Math.abs(e.degrees)/3)*16;return Math.min(Math.max(300,t),2400)}async function me(e){let n=Buffer.from(JSON.stringify({protocol:L,...e.request})).toString(`base64`),r=await e.adb([`shell`,`am`,`instrument`,`-w`,`-e`,`payloadBase64`,n,e.instrumentationRunner],{allowFailure:!0,timeoutMs:45e3}),i;try{i=he(`${r.stdout}\n${r.stderr}`)}catch(e){if(e instanceof t){if(e.code===z)throw new t(`COMMAND_FAILED`,e.message,e.details,e);if(e.code!==R)throw e}throw new t(`COMMAND_FAILED`,r.exitCode===0?`Android multi-touch helper output could not be parsed`:`Android multi-touch helper failed before returning parseable output`,{stdout:r.stdout,stderr:r.stderr,exitCode:r.exitCode},e)}if(r.exitCode!==0)throw new t(`COMMAND_FAILED`,`Android multi-touch helper failed`,l(r,{helper:i}));return i}function he(e){let n=ie(e).results.find(e=>e.agentDeviceProtocol===L);if(!n)throw new t(R,`Android multi-touch helper did not return a final result`);if(n.ok!==`true`)throw new t(z,ge(n),{errorType:n.errorType,helper:n});return{kind:n.kind,helperApiVersion:n.helperApiVersion,injectedEvents:y(n.injectedEvents),elapsedMs:y(n.elapsedMs)}}function ge(e){return e.message&&e.message!==`null`?e.message:e.errorType||`Android multi-touch helper returned an error`}async function _e(){let n=ee(),r=F.join(te(),`android-multitouch-helper`,`dist`),i=F.join(r,`agent-device-android-multitouch-helper-${n}.manifest.json`);try{let e=ve(JSON.parse(await I.readFile(i,`utf8`))),t=F.join(r,e.assetName);return await I.access(t),{apkPath:t,manifest:e}}catch(n){throw new t(`UNSUPPORTED_OPERATION`,`Android touch gestures require the bundled Android touch helper artifact, but it was not found or could not be read`,{manifestPath:i,error:e(n).message},n)}}function ve(e){if(!e||typeof e!=`object`||Array.isArray(e))throw new t(`INVALID_ARGS`,`Android multi-touch helper manifest must be an object.`);let n=e;return{name:X(n.name,`name`,`android-multitouch-helper`),version:Y(n.version,`version`),assetName:Y(n.assetName,`assetName`),sha256:Te(n.sha256),packageName:X(n.packageName,`packageName`,`com.callstack.agentdevice.multitouchhelper`),versionCode:we(n.versionCode,`versionCode`),instrumentationRunner:X(n.instrumentationRunner,`instrumentationRunner`,`com.callstack.agentdevice.multitouchhelper/.MultiTouchInstrumentation`),statusProtocol:X(n.statusProtocol,`statusProtocol`,L)}}async function ye(e){let{adb:t,artifact:n}=e,r=n.manifest.packageName,i=n.manifest.versionCode,a=`${e.deviceKey}\0${r}\0${i}`;if(J.has(a))return{packageName:r,versionCode:i,installed:!1,reason:`current`};let o=await be(t,r);if(o!==void 0&&o>=i)return J.add(a),{packageName:r,versionCode:i,installedVersionCode:o,installed:!1,reason:`current`};await xe(n);let l=await c(n.apkPath,{provider:e.adbProvider,replace:!0,allowTestPackages:!0,allowFailure:!0,timeoutMs:3e4});if(l.exitCode!==0)throw s(`Failed to install Android multi-touch helper`,l,{packageName:r,versionCode:i});return J.add(a),{packageName:r,versionCode:i,installedVersionCode:o,installed:!0,reason:o===void 0?`missing`:`outdated`}}const J=new Set;async function be(e,t){let n=await e([`shell`,`cmd`,`package`,`list`,`packages`,`--show-versioncode`,t],{allowFailure:!0,timeoutMs:5e3});if(n.exitCode!==0)return;let r=RegExp(`package:${Ee(t)}(?:\\s|$).*versionCode:(\\d+)`).exec(`${n.stdout}\n${n.stderr}`);return r?Number(r[1]):void 0}async function xe(e){let n=await Se(e.apkPath);if(n!==e.manifest.sha256)throw new t(`COMMAND_FAILED`,`Android multi-touch helper APK checksum mismatch`,{apkPath:e.apkPath,expectedSha256:e.manifest.sha256,actualSha256:n})}async function Se(e){let t=N.createHash(`sha256`);return t.update(await I.readFile(e)),t.digest(`hex`)}function Ce(e){return`${e.platform}:${e.id}`}function Y(e,n){if(typeof e!=`string`||e.length===0)throw new t(`INVALID_ARGS`,`Android multi-touch helper manifest ${n} is required.`);return e}function we(e,t){return ae(e,t,`multi-touch helper`)}function X(e,t,n){return v(e,t,n,`multi-touch helper`)}function Te(e){let n=Y(e,`sha256`).trim().toLowerCase();if(n.length!==64||!/^[0-9a-f]+$/.test(n))throw new t(`INVALID_ARGS`,`Android multi-touch helper manifest sha256 must be a 64-character hex string.`);return n}function Ee(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}const Z=[`window_animation_scale`,`transition_animation_scale`,`animator_duration_scale`];async function De(e,n,r,i,a){switch(n.toLowerCase()){case`wifi`:await h(e,[`shell`,`svc`,`wifi`,M(r)?`enable`:`disable`]);return;case`airplane`:{let t=M(r),n=t?`1`:`0`,i=t?`true`:`false`;await h(e,[`shell`,`settings`,`put`,`global`,`airplane_mode_on`,n]),await h(e,[`shell`,`am`,`broadcast`,`-a`,`android.intent.action.AIRPLANE_MODE`,`--ez`,`state`,i]);return}case`location`:if(r.toLowerCase()===`set`){if(e.kind!==`emulator`)throw new t(`UNSUPPORTED_OPERATION`,`Android precise location coordinates are supported only on emulators.`,{deviceId:e.id,hint:`Use an Android emulator for adb emu geo fix, or configure location through device/provider tooling.`});let{latitude:n,longitude:r}=u(a);return await h(e,[`emu`,`geo`,`fix`,String(r),String(n)]),{latitude:n,longitude:r}}await h(e,[`shell`,`settings`,`put`,`secure`,`location_mode`,M(r)?`3`:`0`]);return;case`animations`:{let t=M(r)?`1`:`0`;for(let n of Z)await h(e,[`shell`,`settings`,`put`,`global`,n,t]);return{scale:t,keys:[...Z]}}case`appearance`:await h(e,[`shell`,`cmd`,`uimode`,`night`,await Me(e,r)===`dark`?`yes`:`no`]);return;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 p(e,i);if(n.type===`intent`)throw new t(`INVALID_ARGS`,`settings clear-app-state requires a package name, not an intent.`);await h(e,[`shell`,`am`,`force-stop`,n.value],{allowFailure:!0});let a=await h(e,[`shell`,`pm`,`clear`,n.value],{allowFailure:!0});if(a.exitCode!==0||!/\bSuccess\b/i.test(a.stdout))throw new t(`COMMAND_FAILED`,`Failed to clear Android app data for ${n.value}`,{package:n.value,stdout:a.stdout,stderr:a.stderr,exitCode:a.exitCode});return{package:n.value,cleared:!0}}case`fingerprint`:await ke(e,Oe(r));return;case`permission`:{if(!i)throw new t(`INVALID_ARGS`,`permission setting requires an active app in session`);let n=j(r),o=Pe(a?.permissionTarget,a?.permissionMode);if(o.kind===`notifications`){await Ie(e,i,n,o);return}let s=n===`grant`?`grant`:`revoke`;if(o.type===`photos`){await Fe(e,i,s);return}await h(e,[`shell`,`pm`,s,i,o.value]);return}default:throw new t(`INVALID_ARGS`,`Unsupported setting: ${n}`)}}function Oe(e){let n=e.trim().toLowerCase();if(n===`match`)return`match`;if(n===`nonmatch`)return`nonmatch`;throw new t(`INVALID_ARGS`,`Invalid fingerprint state: ${e}. Use match|nonmatch.`)}async function ke(e,n){let r=Ae(e,n),i=[];for(let t of r){let n=await h(e,t,{allowFailure:!0});if(n.exitCode===0)return;i.push({args:t,stdout:n.stdout,stderr:n.stderr,exitCode:n.exitCode})}let a=O(i);throw i.length>0&&i.every(e=>je(e.stdout,e.stderr))?new t(`UNSUPPORTED_OPERATION`,`Android fingerprint simulation is not supported on this target/runtime.`,{deviceId:e.id,action:n,hint:`Use an Android emulator with biometric support, or a device/runtime that exposes cmd fingerprint.`,attempts:a}):new t(`COMMAND_FAILED`,`Failed to simulate Android fingerprint.`,{deviceId:e.id,action:n,attempts:a})}function Ae(e,t){let n=t===`match`?`1`:`9999`,r=[[`shell`,`cmd`,`fingerprint`,`touch`,n],[`shell`,`cmd`,`fingerprint`,`finger`,n]];return e.kind===`emulator`&&r.push([`emu`,`finger`,`touch`,n]),r}function je(e,t){let n=`${e}\n${t}`.toLowerCase();return n.includes(`unknown command`)||n.includes(`can't find service: fingerprint`)||n.includes(`service fingerprint was not found`)||n.includes(`fingerprint cmd unavailable`)||n.includes(`emu command is not supported`)||n.includes(`emulator console is not running`)||n.includes(`fingerprint`)&&n.includes(`not found`)}async function Me(e,n){let r=A(n);if(r!==`toggle`)return r;let i=await h(e,[`shell`,`cmd`,`uimode`,`night`],{allowFailure:!0});if(i.exitCode!==0)throw s(`Failed to read current Android appearance`,i);let a=Ne(i.stdout,i.stderr);if(!a)throw new t(`COMMAND_FAILED`,`Unable to determine current Android appearance for toggle`,{stdout:i.stdout,stderr:i.stderr});return a===`auto`?`dark`:a===`dark`?`light`:`dark`}function Ne(e,t){let n=/night mode:\s*(yes|no|auto)\b/i.exec(`${e}\n${t}`);if(!n)return null;let r=n[1]?.toLowerCase();return r===`yes`?`dark`:r===`no`?`light`:r===`auto`?`auto`:null}function Pe(e,n){let r=k(e);if(n?.trim())throw new t(`INVALID_ARGS`,`Permission mode is only supported for photos. Received: ${n}.`);if(r===`camera`)return{kind:`pm`,value:`android.permission.CAMERA`,type:`camera`};if(r===`microphone`)return{kind:`pm`,value:`android.permission.RECORD_AUDIO`,type:`microphone`};if(r===`photos`)return{kind:`pm`,value:`android.permission.READ_MEDIA_IMAGES`,type:`photos`};if(r===`contacts`)return{kind:`pm`,value:`android.permission.READ_CONTACTS`,type:`contacts`};if(r===`notifications`)return{kind:`notifications`,appOps:`POST_NOTIFICATION`,permission:`android.permission.POST_NOTIFICATIONS`};throw new t(`INVALID_ARGS`,`Unsupported permission target on Android: ${e}. Use camera|microphone|photos|contacts|notifications.`)}async function Fe(e,n,r){let i=await Le(e),a=i!==null&&i>=33?[`android.permission.READ_MEDIA_IMAGES`,`android.permission.READ_EXTERNAL_STORAGE`]:[`android.permission.READ_EXTERNAL_STORAGE`,`android.permission.READ_MEDIA_IMAGES`],o=[];for(let t of a){let i=await h(e,[`shell`,`pm`,r,n,t],{allowFailure:!0});if(i.exitCode===0)return;o.push({permission:t,stderr:i.stderr,exitCode:i.exitCode})}throw new t(`COMMAND_FAILED`,`Failed to ${r} Android photos permission`,{appPackage:n,sdkInt:i,attempts:o})}async function Ie(e,t,n,r){let i=n===`grant`?`allow`:n===`deny`?`deny`:`default`;n===`grant`?await h(e,[`shell`,`pm`,`grant`,t,r.permission],{allowFailure:!0}):(await h(e,[`shell`,`pm`,`revoke`,t,r.permission],{allowFailure:!0}),n===`reset`&&(await h(e,[`shell`,`pm`,`clear-permission-flags`,t,r.permission,`user-set`],{allowFailure:!0}),await h(e,[`shell`,`pm`,`clear-permission-flags`,t,r.permission,`user-fixed`],{allowFailure:!0}))),await h(e,[`shell`,`appops`,`set`,t,r.appOps,i])}async function Le(e){let t=await h(e,[`shell`,`getprop`,`ro.build.version.sdk`],{allowFailure:!0});if(t.exitCode!==0)return null;let n=Number.parseInt(t.stdout.trim(),10);return!Number.isFinite(n)||n<=0?null:n}const Q=Buffer.from([137,80,78,71,13,10,26,10]);async function Re(e,t,n={}){if(n.stabilize===!1){await $(e,t);return}await ze(e);try{await ne(1e3),await $(e,t)}finally{await Be(e).catch(()=>{})}}async function ze(e){let t=t=>h(e,[`shell`,t],{allowFailure:!0});await t(`settings put global sysui_demo_allowed 1`);let n=e=>t(`am broadcast -a com.android.systemui.demo -e command ${e}`);await n(`clock -e hhmm 0941`),await n(`notifications -e visible false`)}async function Be(e){await h(e,[`shell`,`am broadcast -a com.android.systemui.demo -e command exit`],{allowFailure:!0})}async function $(e,n){let r=await h(e,[`exec-out`,`screencap`,`-p`],{binaryStdout:!0});if(!r.stdoutBuffer)throw new t(`COMMAND_FAILED`,`Failed to capture screenshot`);let i=r.stdoutBuffer.indexOf(Q);if(i<0)throw new t(`COMMAND_FAILED`,`Screenshot data does not contain a valid PNG header`);let a=Ve(r.stdoutBuffer,i);if(!a)throw new t(`COMMAND_FAILED`,`Screenshot data does not contain a complete PNG payload`);await P.writeFile(n,r.stdoutBuffer.subarray(i,a))}function Ve(e,t){let n=t+Q.length;for(;n+8<=e.length;){let t=e.readUInt32BE(n),r=n+4,i=e.toString(`ascii`,r,r+4),a=n+12+t;if(a>e.length)return null;if(i===`IEND`)return a;n=a}return null}function He(e){return{open:(t,n)=>f(e,t,{activity:n?.activity,appBundleId:n?.appBundleId,launchArgs:n?.launchArgs,url:n?.url}),openDevice:()=>re(e),close:t=>m(e,t),tap:(t,n)=>b(e,t,n),doubleTap:async(t,n)=>{await b(e,t,n),await b(e,t,n)},swipe:(t,n,r,i,a)=>B(e,{x1:t,y1:n,x2:r,y2:i,durationMs:a}),pan:(t,n,r,i,a)=>B(e,{x1:t,y1:n,x2:r,y2:i,durationMs:a}),fling:(t,n,r,i,a)=>B(e,{x1:t,y1:n,x2:r,y2:i,durationMs:a}),longPress:(t,n,r)=>ce(e,t,n,r),focus:(t,n)=>de(e,t,n),type:(t,n)=>x(e,t,n),fill:(t,n,r,i)=>T(e,t,n,r,i),scroll:(t,n)=>ue(e,t,n),pinch:(t,n,r)=>V(e,{scale:t,x:n,y:r}),screenshot:(t,n)=>Re(e,t,n),snapshot:async t=>{let r=await n(`snapshot_capture`,async()=>await oe(e,{appBundleId:t?.appBundleId,interactiveOnly:t?.interactiveOnly,depth:t?.depth,scope:t?.scope,raw:t?.raw,helperSessionScope:t?.appBundleId?`daemon-session`:`command`}),{backend:`android`});return{nodes:r.nodes??[],truncated:r.truncated??!1,backend:`android`,...d(r)}},back:t=>S(e),home:()=>C(e),rotate:t=>le(e,t),rotateGesture:(t,n,r,i)=>H(e,{degrees:t,x:n,y:r,velocity:i}),transformGesture:t=>fe(e,t),appSwitcher:()=>E(e),tvRemote:(t,n)=>D(e,t,n),readClipboard:()=>g(e),writeClipboard:t=>_(e,t),setSetting:(t,n,r,i)=>De(e,t,n,r,i)}}export{He as createAndroidInteractor};
@@ -1,7 +1,7 @@
1
- import{t as e}from"./rolldown-runtime.js";import{F as t}from"./sdk-contracts.js";import{_ as n,a as r,l as i,n as a,o,v as s,w as c}from"./sdk-android-adb.js";import{t as l}from"./timeouts.js";import{b as ee,i as u,l as d,s as f,x as p}from"./logcat.js";import{promises as m}from"node:fs";import te from"node:os";import h from"node:path";const ne=[`mCurrentFocus=Window{`,`mFocusedApp=AppWindowToken{`,`mResumedActivity:`,`ResumedActivity:`],re=/\bApplication Not Responding:\s*([A-Za-z0-9_.]+)/i,ie=/([^{}]*\bis(?:n't| not)\s+responding[^{}]*)/i,ae=/\b([A-Za-z][A-Za-z0-9_]*(?:\.[A-Za-z0-9_]+)+)\b/;function g(e){let t=new Set;for(let n of e.split(`
1
+ import{t as e}from"./rolldown-runtime.js";import{F as t}from"./sdk-contracts.js";import{_ as n,a as r,l as i,n as a,o,v as s,w as c}from"./sdk-android-adb.js";import{t as l}from"./timeouts.js";import{D as u,E as ee,h as d,p as f,u as p}from"./android-input-ownership.js";import{promises as m}from"node:fs";import te from"node:os";import h from"node:path";const ne=[`mCurrentFocus=Window{`,`mFocusedApp=AppWindowToken{`,`mResumedActivity:`,`ResumedActivity:`],re=/\bApplication Not Responding:\s*([A-Za-z0-9_.]+)/i,ie=/([^{}]*\bis(?:n't| not)\s+responding[^{}]*)/i,ae=/\b([A-Za-z][A-Za-z0-9_]*(?:\.[A-Za-z0-9_]+)+)\b/;function g(e){let t=new Set;for(let n of e.split(`
2
2
  `)){let e=n.trim();if(!e)continue;let r=e.split(/\s+/)[0]??``;if(!r.includes(`/`))continue;let i=r.split(`/`)[0]??``;i.includes(`.`)&&i&&t.add(i)}return Array.from(t)}function _(e){return e.split(`
3
3
  `).map(e=>{let t=e.trim();return t.startsWith(`package:`)?t.slice(8):t}).filter(Boolean)}function v(e){return y(e,e=>ce(e))}function oe(e){return y(e,(e,t)=>se(e,t))}function y(e,t){let n=e.split(`
4
4
  `);for(let e of ne)for(let r of n){let n=r.indexOf(e);if(n===-1)continue;let i=r.trim(),a=t(r.slice(n+e.length),i);if(a)return a}return null}function se(e,t){let n=e.split(`}`)[0]?.trim()??e.trim(),r=re.exec(n);if(r){let e=r[1];return{package:e,focusedWindow:`Application Not Responding: ${e}`,raw:t}}let i=ie.exec(n);if(!i)return null;let a=i[1];if(a===void 0)return null;let o=a.trim().replace(/\s+/g,` `),s=ae.exec(o)?.[1];return{...s?{package:s}:{},focusedWindow:o,raw:t}}function ce(e){for(let t of e.trim().split(/\s+/)){let e=t.indexOf(`/`);if(e<=0)continue;let n=b(t.slice(0,e),!1),r=b(t.slice(e+1),!0);if(n&&r&&n.length===e)return{package:n,activity:r}}return null}function b(e,t){let n=0;for(;n<e.length&&le(e[n],t);)n+=1;return e.slice(0,n)}function le(e,t){if(!e)return!1;let n=e.charCodeAt(0);return n>=48&&n<=57||n>=65&&n<=90||n>=97&&n<=122||e===`_`||e===`.`||t&&e===`$`}const ue=/\.(?:apk|aab)$/i,de=/^[A-Za-z_][\w]*(\.[A-Za-z_][\w]*)+$/;function x(e){let t=e.trim();return t.length===0?`other`:ue.test(t)?t.includes(`/`)||t.includes(`\\`)||t.startsWith(`.`)||t.startsWith(`~`)||!S(t)?`binary`:`package`:S(t)?`package`:`other`}function S(e){return de.test(e)}function fe(e){return`Android runtime hints require an installed package name, not "${e}". Install or reinstall the app first, then relaunch by package.`}var pe=e({closeAndroidApp:()=>G,getAndroidAppState:()=>N,getAndroidBlockingDialogFocus:()=>P,inferAndroidAppName:()=>M,installAndroidApp:()=>He,installAndroidInstallablePath:()=>Q,installAndroidInstallablePathAndResolvePackageName:()=>$,isAmStartError:()=>H,listAndroidApps:()=>A,openAndroidApp:()=>L,openAndroidDevice:()=>W,parseAndroidLaunchComponent:()=>U,reinstallAndroidApp:()=>Ue,resolveAndroidApp:()=>k});const me={settings:{type:`intent`,value:`android.settings.SETTINGS`}},C=`android.intent.category.LAUNCHER`,w=`android.intent.category.LEANBACK_LAUNCHER`,T=`android.intent.category.DEFAULT`,E=`Run agent-device apps --platform android to discover the installed package name, then retry open with that exact package.`,he=new Set([`localhost`,`127.0.0.1`,`::1`,`[::1]`]),D=ee();function O(e){return{platform:`android`,deviceId:e.id,variant:e.target??``}}async function k(e,n){let r=n.trim();if(x(r)===`package`)return{type:`package`,value:r};let i=me[r.toLowerCase()];if(i)return i;let a=O(e),o=D.get(a,r);if(o)return o;let s=(await f(e,[`shell`,`pm`,`list`,`packages`])).stdout.split(`
5
- `).map(e=>e.replace(`package:`,``).trim()).filter(Boolean).filter(e=>e.toLowerCase().includes(r.toLowerCase())),c=s[0];if(c!==void 0&&s.length===1)return D.set(a,r,{type:`package`,value:c});throw s.length>1?new t(`INVALID_ARGS`,`Multiple packages matched "${n}"`,{matches:s,hint:`Run agent-device apps --platform android to see the exact installed package names before retrying open.`}):new t(`APP_NOT_INSTALLED`,`No package found matching "${n}"`,{hint:E})}async function A(e,t){let n=await ge(e);return(t===`user-installed`?(await ve(e)).filter(e=>n.has(e)):Array.from(n)).sort((e,t)=>e.localeCompare(t)).map(e=>({package:e,name:M(e)}))}async function ge(e){let t=new Set;for(let n of j(e,{includeFallbackWhenUnknown:!0})){let r=await f(e,[`shell`,`cmd`,`package`,`query-activities`,`--brief`,`-a`,`android.intent.action.MAIN`,`-c`,n],{allowFailure:!0});if(!(r.exitCode!==0||r.stdout.trim().length===0))for(let e of g(r.stdout))t.add(e)}return t}function _e(e){return j(e)[0]??C}function j(e,t={}){return e.target===`tv`?[w]:e.target===`mobile`?[C]:t.includeFallbackWhenUnknown?[C,w]:[C]}async function ve(e){return _((await f(e,[`shell`,`pm`,`list`,`packages`,`-3`])).stdout)}function M(e){let t=new Set([`com`,`android`,`google`,`app`,`apps`,`service`,`services`,`mobile`,`client`]),n=e.split(`.`).flatMap(e=>e.split(/[_-]+/)).map(e=>e.trim().toLowerCase()).filter(e=>e.length>0),r=n[n.length-1]??e;for(let e=n.length-1;e>=0;--e){let i=n[e];if(i&&!t.has(i)){r=i;break}}return r.split(/[^a-z0-9]+/i).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `)}async function N(e){return await F(e,[[`shell`,`dumpsys`,`window`,`windows`],[`shell`,`dumpsys`,`window`]])||await F(e,[[`shell`,`dumpsys`,`activity`,`activities`],[`shell`,`dumpsys`,`activity`]])||{}}async function P(e){return await ye(e,[[`shell`,`dumpsys`,`window`,`windows`],[`shell`,`dumpsys`,`window`]])}async function F(e,t){for(let n of t){let t=v((await f(e,n,{allowFailure:!0})).stdout??``);if(t)return t}return null}async function ye(e,t){for(let n of t){let t=oe((await f(e,n,{allowFailure:!0})).stdout??``);if(t)return t}return null}function be(e){let t;try{t=new URL(e)}catch{return null}let n=t.hostname.toLowerCase();if(!he.has(n)||!t.port)return null;let r=Number(t.port);return Number.isInteger(r)?`tcp:${r}`:null}async function xe(e,n){let a=be(n);if(!a)return;let o=r(i(e));try{await o.ensure({local:a,remote:a})}catch(e){let n={localPort:a.replace(`tcp:`,``),operation:`adb reverse ${a} ${a}`};throw e instanceof t&&Object.assign(n,{hint:e.details?.hint,diagnosticId:e.details?.diagnosticId,logPath:e.details?.logPath}),new t(`COMMAND_FAILED`,`Failed to ensure Android port reverse ${a} before opening localhost URL`,n,e)}}function Se(e){return/^[A-Za-z0-9_@%+=:,./-]+$/.test(e)?e:`'${e.replace(/'/g,`'\\''`)}'`}function I(e){return(e.launchArgs??[]).map(Se)}async function L(e,t,n){e.booted||await d(e.id);let r=ke(n),i=r.activity,a=t.trim();if(p(a)){await Ce(e,a,r);return}if(r.url!==void 0){await we(e,t,r);return}let o=await k(e,t),s=_e(e);if(o.type===`intent`){await Te(e,o.value,r);return}if(i){await Ee(e,o.value,i,s,r);return}await De(e,o.value,s,r)}async function Ce(e,n,r){if(r.activity)throw new t(`INVALID_ARGS`,`Activity override is not supported when opening a deep link URL`);await xe(e,n),await f(e,[`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.VIEW`,`-d`,n,...Ae(r.appBundleId),...I(r)])}async function we(e,n,r){if(r.activity)throw new t(`INVALID_ARGS`,`Activity override is not supported when opening an app-bound deep link URL`);let i=r.url?.trim()??``;if(!p(i))throw new t(`INVALID_ARGS`,`Android app-bound open requires a valid URL target`);await f(e,[`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.VIEW`,`-d`,i,`-p`,await Oe(e,n,`app-bound open`),...I(r)])}async function Te(e,n,r){if(r.activity)throw new t(`INVALID_ARGS`,`Activity override requires a package name, not an intent`);await f(e,[`shell`,`am`,`start`,`-W`,`-a`,n,...I(r)])}async function Ee(e,t,n,r,i){let a=n.includes(`/`)?n:`${t}/${n.startsWith(`.`)?n:`.${n}`}`;try{await f(e,R(a,r,i))}catch(n){throw await je(e,t,n),n}}async function De(e,t,n,r){let i=await f(e,[`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.MAIN`,`-c`,T,`-c`,n,`-p`,t,...I(r)],{allowFailure:!0});if(i.exitCode===0&&!H(i.stdout,i.stderr))return;let o=await Me(e,t);if(!o)throw await B(e,t)?a(`Failed to launch ${t}`,i):z(t);await f(e,R(o,n,r))}function R(e,t,n){return[`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.MAIN`,`-c`,T,`-c`,t,`-n`,e,...I(n)]}async function Oe(e,n,r){let i=await k(e,n);if(i.type===`intent`)throw new t(`INVALID_ARGS`,`Android ${r} requires a package name, not an intent`);return i.value}function ke(e){return typeof e==`string`?{activity:e}:e??{}}function Ae(e){let t=e?.trim();return t?[`-p`,t]:[]}function z(e){return new t(`APP_NOT_INSTALLED`,`No package found matching "${e}"`,{package:e,hint:E})}async function B(e,t){let n=await f(e,[`shell`,`pm`,`path`,t],{allowFailure:!0}),r=`${n.stdout}\n${n.stderr}`;return n.exitCode===0&&/\bpackage:/i.test(r)?!0:(V(r),!1)}async function je(e,n,r){if(V(r instanceof t?`${String(r.details?.stdout??``)}\n${String(r.details?.stderr??``)}`:``)||!await B(e,n))throw z(n)}function V(e){return/\bunknown package\b/i.test(e)||/\bpackage .* (?:was|is) not found\b/i.test(e)||/\bpackage .* does not exist\b/i.test(e)||/\bcould not find package\b/i.test(e)}async function Me(e,t){let n=Array.from(new Set(j(e,{includeFallbackWhenUnknown:!0})));for(let r of n){let n=await f(e,[`shell`,`cmd`,`package`,`resolve-activity`,`--brief`,`-a`,`android.intent.action.MAIN`,`-c`,r,t],{allowFailure:!0});if(n.exitCode!==0)continue;let i=U(n.stdout);if(i)return i}return null}function H(e,t){let n=`${e}\n${t}`;return/Error:.*(?:Activity not started|unable to resolve Intent)/i.test(n)}function U(e){let t=e.split(`
5
+ `).map(e=>e.replace(`package:`,``).trim()).filter(Boolean).filter(e=>e.toLowerCase().includes(r.toLowerCase())),c=s[0];if(c!==void 0&&s.length===1)return D.set(a,r,{type:`package`,value:c});throw s.length>1?new t(`INVALID_ARGS`,`Multiple packages matched "${n}"`,{matches:s,hint:`Run agent-device apps --platform android to see the exact installed package names before retrying open.`}):new t(`APP_NOT_INSTALLED`,`No package found matching "${n}"`,{hint:E})}async function A(e,t){let n=await ge(e);return(t===`user-installed`?(await ve(e)).filter(e=>n.has(e)):Array.from(n)).sort((e,t)=>e.localeCompare(t)).map(e=>({package:e,name:M(e)}))}async function ge(e){let t=new Set;for(let n of j(e,{includeFallbackWhenUnknown:!0})){let r=await f(e,[`shell`,`cmd`,`package`,`query-activities`,`--brief`,`-a`,`android.intent.action.MAIN`,`-c`,n],{allowFailure:!0});if(!(r.exitCode!==0||r.stdout.trim().length===0))for(let e of g(r.stdout))t.add(e)}return t}function _e(e){return j(e)[0]??C}function j(e,t={}){return e.target===`tv`?[w]:e.target===`mobile`?[C]:t.includeFallbackWhenUnknown?[C,w]:[C]}async function ve(e){return _((await f(e,[`shell`,`pm`,`list`,`packages`,`-3`])).stdout)}function M(e){let t=new Set([`com`,`android`,`google`,`app`,`apps`,`service`,`services`,`mobile`,`client`]),n=e.split(`.`).flatMap(e=>e.split(/[_-]+/)).map(e=>e.trim().toLowerCase()).filter(e=>e.length>0),r=n[n.length-1]??e;for(let e=n.length-1;e>=0;--e){let i=n[e];if(i&&!t.has(i)){r=i;break}}return r.split(/[^a-z0-9]+/i).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `)}async function N(e){return await F(e,[[`shell`,`dumpsys`,`window`,`windows`],[`shell`,`dumpsys`,`window`]])||await F(e,[[`shell`,`dumpsys`,`activity`,`activities`],[`shell`,`dumpsys`,`activity`]])||{}}async function P(e){return await ye(e,[[`shell`,`dumpsys`,`window`,`windows`],[`shell`,`dumpsys`,`window`]])}async function F(e,t){for(let n of t){let t=v((await f(e,n,{allowFailure:!0})).stdout??``);if(t)return t}return null}async function ye(e,t){for(let n of t){let t=oe((await f(e,n,{allowFailure:!0})).stdout??``);if(t)return t}return null}function be(e){let t;try{t=new URL(e)}catch{return null}let n=t.hostname.toLowerCase();if(!he.has(n)||!t.port)return null;let r=Number(t.port);return Number.isInteger(r)?`tcp:${r}`:null}async function xe(e,n){let a=be(n);if(!a)return;let o=r(i(e));try{await o.ensure({local:a,remote:a})}catch(e){let n={localPort:a.replace(`tcp:`,``),operation:`adb reverse ${a} ${a}`};throw e instanceof t&&Object.assign(n,{hint:e.details?.hint,diagnosticId:e.details?.diagnosticId,logPath:e.details?.logPath}),new t(`COMMAND_FAILED`,`Failed to ensure Android port reverse ${a} before opening localhost URL`,n,e)}}function Se(e){return/^[A-Za-z0-9_@%+=:,./-]+$/.test(e)?e:`'${e.replace(/'/g,`'\\''`)}'`}function I(e){return(e.launchArgs??[]).map(Se)}async function L(e,t,n){e.booted||await d(e.id);let r=ke(n),i=r.activity,a=t.trim();if(u(a)){await Ce(e,a,r);return}if(r.url!==void 0){await we(e,t,r);return}let o=await k(e,t),s=_e(e);if(o.type===`intent`){await Te(e,o.value,r);return}if(i){await Ee(e,o.value,i,s,r);return}await De(e,o.value,s,r)}async function Ce(e,n,r){if(r.activity)throw new t(`INVALID_ARGS`,`Activity override is not supported when opening a deep link URL`);await xe(e,n),await f(e,[`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.VIEW`,`-d`,n,...Ae(r.appBundleId),...I(r)])}async function we(e,n,r){if(r.activity)throw new t(`INVALID_ARGS`,`Activity override is not supported when opening an app-bound deep link URL`);let i=r.url?.trim()??``;if(!u(i))throw new t(`INVALID_ARGS`,`Android app-bound open requires a valid URL target`);await f(e,[`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.VIEW`,`-d`,i,`-p`,await Oe(e,n,`app-bound open`),...I(r)])}async function Te(e,n,r){if(r.activity)throw new t(`INVALID_ARGS`,`Activity override requires a package name, not an intent`);await f(e,[`shell`,`am`,`start`,`-W`,`-a`,n,...I(r)])}async function Ee(e,t,n,r,i){let a=n.includes(`/`)?n:`${t}/${n.startsWith(`.`)?n:`.${n}`}`;try{await f(e,R(a,r,i))}catch(n){throw await je(e,t,n),n}}async function De(e,t,n,r){let i=await f(e,[`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.MAIN`,`-c`,T,`-c`,n,`-p`,t,...I(r)],{allowFailure:!0});if(i.exitCode===0&&!H(i.stdout,i.stderr))return;let o=await Me(e,t);if(!o)throw await B(e,t)?a(`Failed to launch ${t}`,i):z(t);await f(e,R(o,n,r))}function R(e,t,n){return[`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.MAIN`,`-c`,T,`-c`,t,`-n`,e,...I(n)]}async function Oe(e,n,r){let i=await k(e,n);if(i.type===`intent`)throw new t(`INVALID_ARGS`,`Android ${r} requires a package name, not an intent`);return i.value}function ke(e){return typeof e==`string`?{activity:e}:e??{}}function Ae(e){let t=e?.trim();return t?[`-p`,t]:[]}function z(e){return new t(`APP_NOT_INSTALLED`,`No package found matching "${e}"`,{package:e,hint:E})}async function B(e,t){let n=await f(e,[`shell`,`pm`,`path`,t],{allowFailure:!0}),r=`${n.stdout}\n${n.stderr}`;return n.exitCode===0&&/\bpackage:/i.test(r)?!0:(V(r),!1)}async function je(e,n,r){if(V(r instanceof t?`${String(r.details?.stdout??``)}\n${String(r.details?.stderr??``)}`:``)||!await B(e,n))throw z(n)}function V(e){return/\bunknown package\b/i.test(e)||/\bpackage .* (?:was|is) not found\b/i.test(e)||/\bpackage .* does not exist\b/i.test(e)||/\bcould not find package\b/i.test(e)}async function Me(e,t){let n=Array.from(new Set(j(e,{includeFallbackWhenUnknown:!0})));for(let r of n){let n=await f(e,[`shell`,`cmd`,`package`,`resolve-activity`,`--brief`,`-a`,`android.intent.action.MAIN`,`-c`,r,t],{allowFailure:!0});if(n.exitCode!==0)continue;let i=U(n.stdout);if(i)return i}return null}function H(e,t){let n=`${e}\n${t}`;return/Error:.*(?:Activity not started|unable to resolve Intent)/i.test(n)}function U(e){let t=e.split(`
6
6
  `).map(e=>e.trim()).filter(Boolean);for(let e=t.length-1;e>=0;--e){let n=t[e];if(n===void 0||!n.includes(`/`))continue;let r=n.split(/\s+/)[0];if(r!==void 0)return r}return null}async function W(e){e.booted||await d(e.id)}async function G(e,n){if(n.trim().toLowerCase()===`settings`){await f(e,[`shell`,`am`,`force-stop`,`com.android.settings`]),await K(e,`com.android.settings`);return}let r=await k(e,n);if(r.type===`intent`)throw new t(`INVALID_ARGS`,`Close requires a package name, not an intent`);await f(e,[`shell`,`am`,`force-stop`,r.value]),await K(e,r.value)}async function K(e,t){await Ne(e,t),await Fe(e,t)}async function Ne(e,t){let n=Date.now()+2e3;for(;Date.now()<n;){if((await Pe(e))?.package!==t)return;await l(50)}}async function Pe(e){for(let t of[[`shell`,`dumpsys`,`window`,`windows`],[`shell`,`dumpsys`,`window`],[`shell`,`dumpsys`,`activity`,`activities`],[`shell`,`dumpsys`,`activity`]]){let n=v((await f(e,t,{allowFailure:!0})).stdout??``);if(n)return n}return null}async function Fe(e,t){let n=Date.now()+2e3;for(;Date.now()<n;){if(!await q(e,t)&&(await l(150),!await q(e,t)))return;await l(50)}}async function q(e,t){return((await f(e,[`shell`,`pidof`,t],{allowFailure:!0})).stdout??``).trim().length>0}async function Ie(e,n){let r=await k(e,n);if(r.type===`intent`)throw new t(`INVALID_ARGS`,`App uninstall requires a package name, not an intent`);let i=await f(e,[`uninstall`,r.value],{allowFailure:!0});if(i.exitCode!==0){let e=`${i.stdout}\n${i.stderr}`.toLowerCase();if(!e.includes(`unknown package`)&&!e.includes(`not installed`))throw a(`adb uninstall failed for ${r.value}`,i)}return{package:r.value}}let J=null;function Le(){return`${process.env.PATH??``}::${process.env.AGENT_DEVICE_BUNDLETOOL_JAR??``}`}async function Re(){let e=Le();if(J?.key===e)return J.invocation;if(await c(`bundletool`)){let t={cmd:`bundletool`,prefixArgs:[]};return J={key:e,invocation:t},t}let r=await n(process.env.AGENT_DEVICE_BUNDLETOOL_JAR,`AGENT_DEVICE_BUNDLETOOL_JAR`);if(!r)throw new t(`TOOL_MISSING`,`bundletool not found in PATH. Install bundletool or set AGENT_DEVICE_BUNDLETOOL_JAR to a bundletool-all.jar path.`);let i={cmd:`java`,prefixArgs:[`-jar`,r]};return J={key:e,invocation:i},i}async function Y(e){let t=await Re();await s(t.cmd,[...t.prefixArgs,...e])}function ze(e){return h.extname(e).toLowerCase()===`.aab`}async function Be(e,t){let n=i(e),r=`universal`;if(n.installBundle){await n.installBundle(t,{mode:r});return}let a=await m.mkdtemp(h.join(te.tmpdir(),`agent-device-aab-`)),o=h.join(a,`bundle.apks`);try{await Y([`build-apks`,`--bundle`,t,`--output`,o,`--mode`,r]),await Y([`install-apks`,`--apks`,o,`--device-id`,e.id])}finally{await m.rm(a,{recursive:!0,force:!0})}}async function X(e,t){if(ze(t)){await Be(e,t);return}await o(t,{device:e,replace:!0})}async function Z(e){let t=await f(e,[`shell`,`pm`,`list`,`packages`]);return new Set(t.stdout.split(`
7
- `).map(e=>e.replace(`package:`,``).trim()).filter(Boolean))}async function Ve(e,t){let n=await Z(e),r=Array.from(n).filter(e=>!t.has(e));if(r.length===1)return r[0]}async function Q(e,t){await D.invalidateWhile(O(e),async()=>{e.booted||await d(e.id),await X(e,t)})}async function $(e,t,n){let r=n?void 0:await Z(e);return await Q(e,t),n??(r?await Ve(e,r):void 0)}async function He(e,t){e.booted||await d(e.id);let n=await u({kind:`path`,path:t});try{let t=await $(e,n.installablePath,n.packageName),r=t?M(t):void 0;return{archivePath:n.archivePath,installablePath:n.installablePath,packageName:t,appName:r,launchTarget:t}}finally{await n.cleanup()}}async function Ue(e,t,n){return await D.invalidateWhile(O(e),async()=>{e.booted||await d(e.id);let{package:r}=await Ie(e,t),i=await u({kind:`path`,path:n},{resolveIdentity:!1});try{await Q(e,i.installablePath)}finally{await i.cleanup()}return{package:r}})}export{M as a,L as c,k as d,x as f,_ as g,g as h,P as i,W as l,v as m,G as n,H as o,fe as p,N as r,A as s,pe as t,U as u};
7
+ `).map(e=>e.replace(`package:`,``).trim()).filter(Boolean))}async function Ve(e,t){let n=await Z(e),r=Array.from(n).filter(e=>!t.has(e));if(r.length===1)return r[0]}async function Q(e,t){await D.invalidateWhile(O(e),async()=>{e.booted||await d(e.id),await X(e,t)})}async function $(e,t,n){let r=n?void 0:await Z(e);return await Q(e,t),n??(r?await Ve(e,r):void 0)}async function He(e,t){e.booted||await d(e.id);let n=await p({kind:`path`,path:t});try{let t=await $(e,n.installablePath,n.packageName),r=t?M(t):void 0;return{archivePath:n.archivePath,installablePath:n.installablePath,packageName:t,appName:r,launchTarget:t}}finally{await n.cleanup()}}async function Ue(e,t,n){return await D.invalidateWhile(O(e),async()=>{e.booted||await d(e.id);let{package:r}=await Ie(e,t),i=await p({kind:`path`,path:n},{resolveIdentity:!1});try{await Q(e,i.installablePath)}finally{await i.cleanup()}return{package:r}})}export{M as a,L as c,k as d,x as f,_ as g,g as h,P as i,W as l,v as m,G as n,H as o,fe as p,N as r,A as s,pe as t,U as u};
@@ -1 +1 @@
1
- import{F as e,p as t}from"./sdk-contracts.js";import{p as n}from"./sdk-android-adb.js";import{x as r}from"./logcat.js";import{n as i}from"./runner-client.js";import{o as a}from"./tool-provider.js";import{t as o}from"./ttl-memo.js";import{H as s,It as c,W as l,pn as u}from"./internal/daemon.js";import{promises as d}from"node:fs";import f from"node:os";import p from"node:path";const m=o({ttlMs:5e3});async function h(e,n={}){if(u(e))return;let r=_(e);if(m.get(r)===!0){if(!n.focusExisting)return;m.delete(r)}if(t(e)){if(e.kind===`simulator`){let{ensureBootedSimulator:t}=await import(`./simulator.js`).then(e=>e.o);await t(e,{deviceHub:n.deviceHub,focusExisting:n.focusExisting,onColdBootStart:n.onIosSimulatorColdBootStart}),g(r);return}if(e.kind===`device`){await v(e.id),g(r);return}}if(e.platform===`android`){let{waitForAndroidBoot:t}=await import(`./logcat.js`).then(e=>e.c);await t(e.id),g(r)}}function g(e){m.set(e,!0)}function _(e){let t=e.kind===`simulator`?e.simulatorSetPath??``:``;return JSON.stringify([e.platform,e.kind,e.id,e.target??``,t])}async function v(t){let r=p.join(f.tmpdir(),`agent-device-ready-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);try{let i=await a([`devicectl`,`device`,`info`,`details`,`--device`,t,`--json-output`,r,`--timeout`,`15`],{allowFailure:!0,timeoutMs:18e3}),{stdout:o,stderr:s}=i,c=await b(r);if(i.exitCode===0){if(!c.parsed)throw new e(`COMMAND_FAILED`,`iOS device readiness probe failed`,{kind:`probe_inconclusive`,deviceId:t,stdout:o,stderr:s,hint:`CoreDevice returned success but readiness JSON output was missing or invalid. Retry; if it persists restart Xcode and the iOS device.`});let n=c?.tunnelState?.toLowerCase();if(n===`connecting`)throw new e(`COMMAND_FAILED`,`iOS device is not ready for automation`,{kind:`not_ready`,deviceId:t,tunnelState:n,hint:`Device tunnel is still connecting. Keep the device unlocked and connected by cable until it is fully available in Xcode Devices, then retry.`});return}throw new e(`COMMAND_FAILED`,`iOS device is not ready for automation`,n(i,{kind:`not_ready`,deviceId:t,tunnelState:c?.tunnelState,hint:x(o,s)}))}catch(n){if(n instanceof e&&n.code===`COMMAND_FAILED`){if((typeof n.details?.kind==`string`?n.details.kind:``)===`not_ready`)throw n;let r=n.details??{},i=String(r.stdout??``),a=String(r.stderr??``),o=Number(r.timeoutMs??15e3),s=`CoreDevice did not respond within ${o}ms. Keep the device unlocked and trusted, then retry; if it persists restart Xcode and the iOS device.`;throw new e(`COMMAND_FAILED`,`iOS device readiness probe failed`,{deviceId:t,cause:n.message,timeoutMs:o,stdout:i,stderr:a,hint:i||a?x(i,a):s},n)}throw new e(`COMMAND_FAILED`,`iOS device readiness probe failed`,{deviceId:t,hint:`Reconnect the device, keep it unlocked, and retry.`},n instanceof Error?n:void 0)}finally{await d.rm(r,{force:!0}).catch(()=>{})}}function y(e){let t=e?.result;if(!t||typeof t!=`object`)return{};let n=t.connectionProperties?.tunnelState,r=t.device?.connectionProperties?.tunnelState,i=typeof n==`string`?n:typeof r==`string`?r:void 0;return i?{tunnelState:i}:{}}async function b(e){try{let t=await d.readFile(e,`utf8`);return{parsed:!0,tunnelState:y(JSON.parse(t)).tunnelState}}catch{return{parsed:!1}}}function x(e,t){return l(e,t)||(`${e}\n${t}`.toLowerCase().includes(`timed out waiting for all destinations`)?`Xcode destination did not become available in time. Keep device unlocked and retry.`:s)}function S(e){let{req:t,logPath:n,traceLogPath:r}=e;return{verbose:t.flags?.verbose,logPath:n,traceLogPath:r,requestId:t.meta?.requestId,iosXctestrunFile:t.flags?.iosXctestrunFile,iosXctestDerivedDataPath:t.flags?.iosXctestDerivedDataPath,iosXctestEnvDir:t.flags?.iosXctestEnvDir}}function C(e){let{req:t,logPath:n,appBundleId:r,traceLogPath:i}=e;return{...S({req:t,logPath:n,traceLogPath:i}),runnerLeaseContext:c(n,t.flags,r,i,t.meta?.requestId,t.meta).runnerLeaseContext}}function w(e){let{req:n,logPath:r,device:a,traceLogPath:o,enabled:s}=e;if(!(!s||!t(a)||a.kind!==`simulator`))return e=>i(e,S({req:n,logPath:r,traceLogPath:o}))}function T(e){let{req:t,logPath:n,device:i,surface:a,openTarget:o,traceLogPath:s}=e;return w({req:t,logPath:n,device:i,traceLogPath:s,enabled:a===`app`&&!!o&&!r(o??``)})}export{h as a,w as i,C as n,T as r,S as t};
1
+ import{F as e,p as t}from"./sdk-contracts.js";import{p as n}from"./sdk-android-adb.js";import{D as r}from"./android-input-ownership.js";import{n as i}from"./runner-client.js";import{o as a}from"./tool-provider.js";import{t as o}from"./ttl-memo.js";import{H as s,W as c,gn as l,zt as u}from"./internal/daemon.js";import{promises as d}from"node:fs";import f from"node:os";import p from"node:path";const m=o({ttlMs:5e3});async function h(e,n={}){if(l(e))return;let r=_(e);if(m.get(r)===!0){if(!n.focusExisting)return;m.delete(r)}if(t(e)){if(e.kind===`simulator`){let{ensureBootedSimulator:t}=await import(`./simulator.js`).then(e=>e.o);await t(e,{deviceHub:n.deviceHub,focusExisting:n.focusExisting,onColdBootStart:n.onIosSimulatorColdBootStart}),g(r);return}if(e.kind===`device`){await v(e.id),g(r);return}}if(e.platform===`android`){let{waitForAndroidBoot:t}=await import(`./android-input-ownership.js`).then(e=>e.m);await t(e.id),g(r)}}function g(e){m.set(e,!0)}function _(e){let t=e.kind===`simulator`?e.simulatorSetPath??``:``;return JSON.stringify([e.platform,e.kind,e.id,e.target??``,t])}async function v(t){let r=p.join(f.tmpdir(),`agent-device-ready-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);try{let i=await a([`devicectl`,`device`,`info`,`details`,`--device`,t,`--json-output`,r,`--timeout`,`15`],{allowFailure:!0,timeoutMs:18e3}),{stdout:o,stderr:s}=i,c=await b(r);if(i.exitCode===0){if(!c.parsed)throw new e(`COMMAND_FAILED`,`iOS device readiness probe failed`,{kind:`probe_inconclusive`,deviceId:t,stdout:o,stderr:s,hint:`CoreDevice returned success but readiness JSON output was missing or invalid. Retry; if it persists restart Xcode and the iOS device.`});let n=c?.tunnelState?.toLowerCase();if(n===`connecting`)throw new e(`COMMAND_FAILED`,`iOS device is not ready for automation`,{kind:`not_ready`,deviceId:t,tunnelState:n,hint:`Device tunnel is still connecting. Keep the device unlocked and connected by cable until it is fully available in Xcode Devices, then retry.`});return}throw new e(`COMMAND_FAILED`,`iOS device is not ready for automation`,n(i,{kind:`not_ready`,deviceId:t,tunnelState:c?.tunnelState,hint:x(o,s)}))}catch(n){if(n instanceof e&&n.code===`COMMAND_FAILED`){if((typeof n.details?.kind==`string`?n.details.kind:``)===`not_ready`)throw n;let r=n.details??{},i=String(r.stdout??``),a=String(r.stderr??``),o=Number(r.timeoutMs??15e3),s=`CoreDevice did not respond within ${o}ms. Keep the device unlocked and trusted, then retry; if it persists restart Xcode and the iOS device.`;throw new e(`COMMAND_FAILED`,`iOS device readiness probe failed`,{deviceId:t,cause:n.message,timeoutMs:o,stdout:i,stderr:a,hint:i||a?x(i,a):s},n)}throw new e(`COMMAND_FAILED`,`iOS device readiness probe failed`,{deviceId:t,hint:`Reconnect the device, keep it unlocked, and retry.`},n instanceof Error?n:void 0)}finally{await d.rm(r,{force:!0}).catch(()=>{})}}function y(e){let t=e?.result;if(!t||typeof t!=`object`)return{};let n=t.connectionProperties?.tunnelState,r=t.device?.connectionProperties?.tunnelState,i=typeof n==`string`?n:typeof r==`string`?r:void 0;return i?{tunnelState:i}:{}}async function b(e){try{let t=await d.readFile(e,`utf8`);return{parsed:!0,tunnelState:y(JSON.parse(t)).tunnelState}}catch{return{parsed:!1}}}function x(e,t){return c(e,t)||(`${e}\n${t}`.toLowerCase().includes(`timed out waiting for all destinations`)?`Xcode destination did not become available in time. Keep device unlocked and retry.`:s)}function S(e){let{req:t,logPath:n,traceLogPath:r}=e;return{verbose:t.flags?.verbose,logPath:n,traceLogPath:r,requestId:t.meta?.requestId,iosXctestrunFile:t.flags?.iosXctestrunFile,iosXctestDerivedDataPath:t.flags?.iosXctestDerivedDataPath,iosXctestEnvDir:t.flags?.iosXctestEnvDir}}function C(e){let{req:t,logPath:n,appBundleId:r,traceLogPath:i}=e;return{...S({req:t,logPath:n,traceLogPath:i}),runnerLeaseContext:u(n,t.flags,r,i,t.meta?.requestId,t.meta).runnerLeaseContext}}function w(e){let{req:n,logPath:r,device:a,traceLogPath:o,enabled:s}=e;if(!(!s||!t(a)||a.kind!==`simulator`))return e=>i(e,S({req:n,logPath:r,traceLogPath:o}))}function T(e){let{req:t,logPath:n,device:i,surface:a,openTarget:o,traceLogPath:s}=e;return w({req:t,logPath:n,device:i,traceLogPath:s,enabled:a===`app`&&!!o&&!r(o??``)})}export{h as a,w as i,C as n,T as r,S as t};
package/dist/src/apps.js CHANGED
@@ -1,5 +1,5 @@
1
- import{t as e}from"./rolldown-runtime.js";import{F as t,m as n,p as r}from"./sdk-contracts.js";import{h as i,j as a,p as o,v as s}from"./sdk-android-adb.js";import{St as c,yt as l}from"./device-rotation.js";import{n as u,t as ee}from"./install-source2.js";import{C as te,S as ne,b as d,g as f,h as re,v as ie,x as p}from"./logcat.js";import{d as ae,i as oe}from"./runner-client.js";import{a as m,i as se,l as ce,o as h}from"./tool-provider.js";import{a as le,n as ue}from"./helper.js";import{n as g,r as de}from"./simctl.js";import{$ as fe,G as _,Gt as v,J as pe,K as y,Kt as me,Q as he,U as ge,V as b,Y as x,et as _e,tt as ve}from"./internal/daemon.js";import{a as ye,i as be,n as S,r as C,t as w}from"./setting-state.js";import{i as T,n as xe,t as E}from"./simulator.js";import{promises as D}from"node:fs";import Se from"node:os";import O from"node:path";import{fileURLToPath as Ce}from"node:url";function k(e){if(!(e instanceof t))return{reason:e instanceof Error?e.message:String(e)};let n=e.details??{},r=Array.isArray(n.args)?n.args.filter(e=>typeof e==`string`).join(` `):void 0;return{errorCode:e.code,reason:e.message,timeoutMs:typeof n.timeoutMs==`number`?n.timeoutMs:void 0,exitCode:typeof n.exitCode==`number`?n.exitCode:void 0,stderr:typeof n.stderr==`string`&&n.stderr.trim()?n.stderr:void 0,stdout:typeof n.stdout==`string`&&n.stdout.trim()?n.stdout:void 0,commandArgs:r}}const we=[`--time`,`9:41`,`--dataNetwork`,`wifi`,`--wifiMode`,`active`,`--wifiBars`,`3`,`--batteryState`,`charged`,`--batteryLevel`,`100`],Te={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`},Ee={1:`searching`,2:`failed`,3:`active`},De={0:`notSupported`,1:`searching`,2:`failed`,3:`active`};function A(e,t,n){return de(e,t,n)}const j=new Set;async function Oe(e){let t=null,n=!1;if(Ae(e))n=!0;else try{t=await je(e),n=!0}catch(t){L(e,`snapshot_failed`,t)}try{(!n||t)&&await F(e),await I(e,we)}catch(t){L(e,`prepare_failed`,t)}return async()=>{await ke(e,n?t:null)}}async function ke(e,t){if(await F(e),!t){N(e);return}await I(e,Ne(t)),M(e)}function M(e){j.delete(P(e))}function N(e){j.add(P(e))}function Ae(e){return j.has(P(e))}function P(e){return`${e.platform}:${e.kind}:${e.id}`}async function je(e){return Me(i(await A(e,[`status_bar`,e.id,`list`],{allowFailure:!0}),`Failed to read simulator status bar overrides`).stdout)}async function F(e){await A(e,[`status_bar`,e.id,`clear`])}async function I(e,t){t.length!==0&&await A(e,[`status_bar`,e.id,`override`,...t])}function Me(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=Te[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=Ee[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=De[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 Ne(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||Pe(e.dataNetwork)||e.operatorName!==void 0)&&t.push(`--cellularBars`,e.cellularBars),e.operatorName!==void 0&&t.push(`--operatorName`,e.operatorName),t}function Pe(e){return!!(e&&e!==`hide`&&e!==`wifi`)}function L(e,t,n){a({level:`warn`,phase:`ios_screenshot_status_bar_${t}`,data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,...k(n)}})}function R(e,t,n){return de(e,t,n)}const Fe={ensureBooted:E,prepareStatusBarForScreenshot:Oe,captureWithRetry:Re,captureWithRunner:z,shouldFallbackToRunner:Ue};async function Ie(e,t,r={}){if(n(e)){await z(e,t,r.appBundleId,r.fullscreen,r.runnerOptions);return}if(e.kind===`simulator`){await Le(e,t,r);return}try{await _([`device`,`screenshot`,`--device`,e.id,t],{action:`capture iOS screenshot`,deviceId:e.id});return}catch(t){if(!He(t))throw t;V(e,`devicectl_screenshot`,t)}await z(e,t,r.appBundleId,r.fullscreen,r.runnerOptions)}async function Le(e,n,r={}){if(e.kind!==`simulator`)throw new t(`UNSUPPORTED_OPERATION`,`Simulator screenshot fallback flow supports only iOS simulators`);let i=r.deps??Fe;r.skipIosSimulatorBootCheck||await i.ensureBooted(e);let a=async()=>{};if(r.normalizeStatusBar===!0)try{a=await i.prepareStatusBarForScreenshot(e)}catch(t){H(e,`prepare_failed`,t)}try{try{await i.captureWithRetry(e,n);return}catch(t){let a=t;if(r.skipIosSimulatorBootCheck&&We(t)){await i.ensureBooted(e);try{await i.captureWithRetry(e,n);return}catch(e){a=e}}if(!i.shouldFallbackToRunner(a))throw a;V(e,`simctl_screenshot`,a)}await i.captureWithRunner(e,n,r.appBundleId,r.fullscreen,r.runnerOptions)}finally{await a().catch(t=>H(e,`restore_failed`,t))}}async function Re(e,t){let n=f.fromTimeoutMs(_e);await ie(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=>Ue(e)},{deadline:n,phase:`ios_simulator_screenshot`})}async function z(e,r,i,a,o){let s=(await oe(e,{command:`screenshot`,appBundleId:i,fullscreen:a},o)).message;if(!s)throw new t(`COMMAND_FAILED`,`Failed to capture iOS screenshot: runner returned no file path`);if(n(e)){await D.copyFile(s,r);return}if(e.kind===`simulator`){await Be(e,s,r);return}await ze(e,s,r)}async function ze(e,n,r){let i=f.fromTimeoutMs(x),a={exitCode:1,stdout:``,stderr:``};for(let t of ae)if(a=await h([`devicectl`,`device`,`copy`,`from`,`--device`,e.id,`--source`,n,`--destination`,r,`--domain-type`,`appDataContainer`,`--domain-identifier`,t],{allowFailure:!0,timeoutMs:B(i,x,`runner screenshot copy`)}),a.exitCode===0)return;throw new t(`COMMAND_FAILED`,`Failed to capture iOS screenshot: ${a.stderr.trim()||a.stdout.trim()||`devicectl exited with code ${a.exitCode}`}`)}async function Be(e,n,r){let i=f.fromTimeoutMs(x),a=`Unable to locate runner container for simulator screenshot`;for(let t of ae){let o=await R(e,[`get_app_container`,e.id,t,`data`],{allowFailure:!0,timeoutMs:B(i,x,`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=Ve(s,n);for(let e of c)try{await D.copyFile(e,r);return}catch(e){a=e instanceof Error?e.message:String(e)}}throw new t(`COMMAND_FAILED`,`Failed to capture iOS screenshot: ${a}`)}function B(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 V(e,t,n){let r=k(n);a({level:`warn`,phase:`ios_screenshot_fallback`,data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,from:t,to:`runner`,...r}})}function H(e,t,n){a({level:`warn`,phase:`ios_screenshot_status_bar_${t}`,data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,...k(n)}})}function Ve(e,t){let n=O.resolve(e),r=t.trim();if(!r)return[];let i=[],a=new Set,o=e=>{let t=O.normalize(e);a.has(t)||(a.add(t),i.push(t))},s=r.replace(/^\/+/,``),c=s.replace(/\\/g,`/`);if(s&&o(O.join(n,s)),O.isAbsolute(r)&&o(O.normalize(r)),c.startsWith(`tmp/`))o(O.join(n,c));else{let e=c.lastIndexOf(`/tmp/`);if(e>=0){let t=c.slice(e+1);o(O.join(n,t))}}let l=O.basename(r);return l&&o(O.join(n,`tmp`,l)),i}function He(e){if(!(e instanceof t)||e.code!==`COMMAND_FAILED`)return!1;let n=U(e);return n.includes(`unknown option '--device'`)||n.includes(`unknown subcommand`)&&n.includes(`screenshot`)||n.includes(`unrecognized subcommand`)&&n.includes(`screenshot`)}function Ue(e){if(!(e instanceof t)||e.code!==`COMMAND_FAILED`)return!1;let n=U(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 We(e){if(!(e instanceof t)||e.code!==`COMMAND_FAILED`)return!1;let n=U(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`)}function U(e){let t=e.details??{},n=typeof t.stdout==`string`?t.stdout:``,r=typeof t.stderr==`string`?t.stderr:``,i=Array.isArray(t.args)?t.args.filter(e=>typeof e==`string`).join(` `):``;return`${e.message}\n${n}\n${r}\n${i}`.toLowerCase()}const Ge={settings:`com.apple.systempreferences`},Ke=/^[a-z0-9-]+(?:\.[a-z0-9-]+)+$/,qe={platform:`macos`,deviceId:`host`,variant:`all`},Je=d();function Ye(e){return Ke.test(e)}async function W(e){let n=e.trim(),r=Ge[n.toLowerCase()];if(r)return r;if(Ye(n))return n;let i=Je.get(qe,n);if(i)return i;let a=(await nt(`all`)).filter(e=>e.name.toLowerCase()===n.toLowerCase()),o=a[0];if(o!==void 0&&a.length===1)return Je.set(qe,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 Xe(e,n,r){let i=r?.url?.trim();if(i){if(!p(i))throw new t(`INVALID_ARGS`,`open <app> <url> requires a valid URL target`);let e=r?.appBundleId??await W(n);await G().openBundle(e,i);return}let a=n.trim();if(p(a)){await G().openTarget(a);return}let o=r?.appBundleId??await W(a);await G().openBundle(o)}async function Ze(e,n){let r=await W(n),i=await ue(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 Qe(){return await G().readClipboard()}async function $e(e){await G().writeClipboard(e)}async function et(){return await G().readDarkMode()}async function tt(e){let t=S(e),n=t===`toggle`?!await et():t===`dark`;await G().setDarkMode(n)}async function nt(e){return await G().listApps(e)}function G(){let e=se().macosHost;if(!e)throw new t(`UNSUPPORTED_OPERATION`,`macOS host provider is not available`);return e}function K(e,t){return g(e,t)}function q(e,t,n){return h(K(e,t),n)}function rt(e){return e.includes(`not installed`)||e.includes(`not found`)||e.includes(`no such file`)}const it={settings:`com.apple.Preferences`},J=d();function at(e){return{platform:`ios`,deviceId:e.id,variant:e.kind}}async function Y(e,t){return await J.invalidateWhile(at(e),t)}async function X(e,r){if(n(e))return await W(r);let i=r.trim();if(i.includes(`.`))return i;let a=it[i.toLowerCase()];if(a)return a;let o=at(e),s=J.get(o,i);if(s)return s;let c=(e.kind===`simulator`?await lt(e):await ge(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}):new t(`APP_NOT_INSTALLED`,`No app found matching "${r}"`)}async function ot(e,t){if(!r(e)||e.kind!==`simulator`)return;let n=st(t);if(!n)return;let i=await ut(e),a=[];for(let e of i)e.bundleId.startsWith(`com.callstack.agentdevice.runner`)||e.path&&(await ft(O.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 st(e){return/^([A-Za-z][A-Za-z0-9+.-]*):/.exec(e.trim())?.[1]?.toLowerCase()}async function ct(e,t){return n(e)?await nt(t):e.kind===`simulator`?ce(await lt(e),t):await ge(e,t)}async function lt(e){return(await ut(e)).map(e=>({bundleId:e.bundleId,name:e.name}))}async function ut(e){let t=(await q(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 m(`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=dt(t);return{bundleId:e,name:t.CFBundleDisplayName??t.CFBundleName??e,...n?{path:n}:{},...t.ApplicationType?{applicationType:t.ApplicationType}:{}}}):[]}function dt(e){if(e.Path)return e.Path;if(e.Bundle)try{return Ce(e.Bundle)}catch{return}}async function ft(e){let t=await m(`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}}function pt(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 mt(e,t){let n=await h(g(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 m(`plutil`,[`-extract`,`CFBundleExecutable`,`raw`,`-o`,`-`,`${r}/Info.plist`],{allowFailure:!0});if(i.exitCode!==0||!i.stdout.trim())return{installed:!0};let a=await m(`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 ht(e){return e.installed?e.simulatorCompatible===!1?`ARCH_MISMATCH`:`PERSISTENT_LAUNCH_FAIL`:`APP_NOT_INSTALLED`}function gt(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 Z=25e3;async function _t(e,i,a){let o=a?.launchConsole?.trim(),s=a?.launchArgs;if(o&&(!r(e)||e.kind!==`simulator`))throw new t(`UNSUPPORTED_OPERATION`,me);if(n(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 Xe(e,i,a);return}let c=a?.url?.trim();if(c){if(o)throw new t(`INVALID_ARGS`,v);if(!p(c))throw new t(`INVALID_ARGS`,`open <app> <url> requires a valid URL target`);if(e.kind===`simulator`){(s||ne(c))&&await xt(e,a?.appBundleId??await X(e,i),{...s?{launchArgs:s}:{}}),await vt(e,c,void 0);return}let n=te(a?.appBundleId??await X(e,i),c);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 Q(e,n,{payloadUrl:c,launchArgs:s});return}let l=i.trim();if(p(l)){if(o)throw new t(`INVALID_ARGS`,v);if(e.kind===`simulator`){await vt(e,l,s);return}let n=te(a?.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 Q(e,n,{payloadUrl:l,launchArgs:s});return}let u=a?.appBundleId??await X(e,i);if(e.kind===`simulator`){await xt(e,u,{...o?{launchConsole:o}:{},...s?{launchArgs:s}:{},...a?.terminateRunningApp?{terminateRunningApp:!0}:{}});return}await Q(e,u,{launchArgs:s})}async function vt(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 q(e,[`openurl`,e.id,n])}async function yt(e){n(e)||e.kind===`simulator`&&await xe(e)!==`Booted`&&await E(e)}async function bt(e,r){if(n(e)){await Ze(e,r);return}let i=await X(e,r);if(e.kind===`simulator`){await E(e);let n=K(e,[`terminate`,e.id,i]),r=await h(n,{allowFailure:!0,timeoutMs:ve});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}`,o(r,{cmd:`xcrun`,args:n}))}return}await _([`device`,`process`,`terminate`,`--device`,e.id,i],{action:`terminate iOS app`,deviceId:e.id})}async function xt(e,n,r){await E(e);let i=0,a=f.fromTimeoutMs(y);try{await ie(async({deadline:i})=>{if(i?.isExpired())throw new t(`COMMAND_FAILED`,`App launch deadline exceeded`,{timeoutMs:y});let a=K(e,St(e.id,n,r)),s=r?.launchConsole?await Ct(a,r.launchConsole):await h(a,{allowFailure:!0});if(s.exitCode!==0)throw new t(`COMMAND_FAILED`,`xcrun exited with code ${s.exitCode}`,o(s,{cmd:`xcrun`,args:a}))},{maxAttempts:10,baseDelayMs:1e3,maxDelayMs:5e3,jitter:.2,shouldRetry(e){return pt(e)?(i+=1,i<3):!1}},{deadline:a})}catch(t){if(pt(t)){let r=t,i=ht(await mt(e,n));r.details={...r.details,hint:gt(i)}}throw t}}function St(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 Ct(e,n){await D.mkdir(O.dirname(n),{recursive:!0});try{let t=await h(e,{allowFailure:!0,timeoutMs:Z});return await wt(n,t.stdout,t.stderr),t}catch(e){let r=(e instanceof t?e:void 0)?.details;if(r?.timeoutMs===Z){let e=typeof r.stdout==`string`?r.stdout:``,t=typeof r.stderr==`string`?r.stderr:``;return await wt(n,e,t),a({level:`warn`,phase:`ios_simulator_launch_console_capture_timeout`,data:{timeoutMs:Z,logPath:n,stdoutBytes:Buffer.byteLength(e),stderrBytes:Buffer.byteLength(t)}}),{stdout:e,stderr:t,exitCode:0}}throw e}}async function wt(e,t,n){await D.writeFile(e,Tt(t,n),`utf8`)}function Tt(e,t){return!e||!t||e.endsWith(`
3
- `)||e.endsWith(`\r`)?`${e}${t}`:`${e}\n${t}`}async function Q(e,t,n){let r=[`device`,`process`,`launch`,`--device`,e.id,t];n?.payloadUrl&&r.push(`--payload-url`,n.payloadUrl),n?.launchArgs&&n.launchArgs.length>0&&r.push(`--`,...n.launchArgs),await _(r,{action:`launch iOS app`,deviceId:e.id})}var Et=e({prepareIosInstallArtifact:()=>Dt});async function Dt(e,n){if(e.kind===`url`&&!ee(e.url))throw new t(`INVALID_ARGS`,`iOS install_from_source URL sources are only supported for trusted artifact services such as GitHub Actions and EAS. Use a path source for other hosts.`);let r=await u({source:e,isInstallablePath:(e,t)=>t.isDirectory()&&e.toLowerCase().endsWith(`.app`)||t.isFile()&&e.toLowerCase().endsWith(`.ipa`),installableLabel:`iOS installable (.app or .ipa)`,allowArchiveExtraction:e.kind!==`url`||ee(e.url),signal:n?.signal}),i;try{i=await kt(r.installablePath,n);let e=i,t=await Ot(e.installPath);return{archivePath:r.archivePath??(r.installablePath.toLowerCase().endsWith(`.ipa`)?r.installablePath:void 0),installablePath:e.installPath,bundleId:t.bundleId,appName:t.appName,cleanup:async()=>{await e.cleanup(),await r.cleanup()}}}catch(e){try{await i?.cleanup()}finally{await r.cleanup()}throw e}}async function Ot(e){let t=O.join(e,`Info.plist`),[n,r,i]=await Promise.all([b(t,`CFBundleIdentifier`),b(t,`CFBundleDisplayName`),b(t,`CFBundleName`)]);return{bundleId:n,appName:r??i}}async function kt(e,n){if(!e.toLowerCase().endsWith(`.ipa`))return{installPath:e,cleanup:async()=>{}};let r=await D.mkdtemp(O.join(Se.tmpdir(),`agent-device-ios-ipa-`)),i=async()=>{await D.rm(r,{recursive:!0,force:!0})};try{await s(`unzip`,[`-q`,e,`-d`,r]);let a=O.join(r,`Payload`),o=(await D.readdir(a,{withFileTypes:!0}).catch(()=>{throw new t(`INVALID_ARGS`,`Invalid IPA: missing Payload directory`)})).filter(e=>e.isDirectory()&&e.name.toLowerCase().endsWith(`.app`)).map(e=>({installPath:O.join(a,e.name),bundleName:e.name.replace(/\.app$/i,``)})),c=o[0];if(c!==void 0&&o.length===1)return{installPath:c.installPath,cleanup:i};if(o.length===0)throw new t(`INVALID_ARGS`,`Invalid IPA: expected at least one .app under Payload, found 0`);await At(o);let l=n?.appIdentifierHint?.trim();if(l){let e=jt(o,l);if(e)return{installPath:e.installPath,cleanup:i};throw new t(`INVALID_ARGS`,`Invalid IPA: found ${o.length} .app bundles under Payload and none matched "${l}". Available bundles: ${o.map(Mt).join(`, `)}`)}throw new t(`INVALID_ARGS`,`Invalid IPA: found ${o.length} .app bundles under Payload. Pass an app identifier or bundle name matching one of: ${o.map(Mt).join(`, `)}`)}catch(e){throw await i(),e}}async function At(e){await Promise.all(e.map(async e=>{if(e.bundleId&&e.appName)return;let t=await Ot(e.installPath);e.bundleId=e.bundleId??t.bundleId,e.appName=e.appName??t.appName}))}function jt(e,n){let r=n.toLowerCase(),i=e.filter(e=>e.bundleName.toLowerCase()===r);if(i.length===1)return i[0];if(i.length>1)throw new t(`INVALID_ARGS`,`Invalid IPA: multiple app bundles matched "${n}" by name. Use a bundle id hint instead.`);if(n.includes(`.`)){let t=e.filter(e=>e.bundleId?.toLowerCase()===r);if(t.length===1)return t[0]}}function Mt(e){let t=e.bundleId??e.appName;return t?`${e.bundleName}.app (${t})`:`${e.bundleName}.app`}async function Nt(e,n){return await Y(e,async()=>{let r=await X(e,n);if(e.kind!==`simulator`)return await _([`device`,`uninstall`,`app`,`--device`,e.id,r],{action:`uninstall iOS app ${r}`,deviceId:e.id},{tolerateOutput:(e,t)=>rt(`${e}\n${t}`.toLowerCase())}),{bundleId:r};await E(e);let i=await q(e,[`uninstall`,e.id,r],{allowFailure:!0});if(i.exitCode!==0&&!rt(`${i.stdout}\n${i.stderr}`.toLowerCase()))throw new t(`COMMAND_FAILED`,`simctl uninstall failed for ${r}`,o(i));return{bundleId:r}})}async function Pt(e,t,n){let r=await Dt({kind:`path`,path:t},n);try{return await It(e,r.installablePath),{archivePath:r.archivePath,installablePath:r.installablePath,bundleId:r.bundleId,appName:r.appName,launchTarget:r.bundleId}}finally{await r.cleanup()}}async function Ft(e,t,n){return await Y(e,async()=>{let{bundleId:r}=await Nt(e,t);return await Pt(e,n,{appIdentifierHint:t}),{bundleId:r}})}async function It(e,t){await Y(e,async()=>{if(e.kind!==`simulator`){await _([`device`,`install`,`app`,`--device`,e.id,t],{action:`install iOS app`,deviceId:e.id},{timeoutMs:pe});return}await E(e),await q(e,[`install`,e.id,t])})}async function Lt(e){return n(e)?await Qe():(T(e,`clipboard`),await E(e),i(await q(e,[`pbpaste`,e.id],{allowFailure:!0}),`Failed to read iOS simulator clipboard`).stdout.replace(/\r\n/g,`
4
- `).replace(/\n$/,``))}async function Rt(e,t){if(n(e)){await $e(t);return}T(e,`clipboard`),await E(e),i(await q(e,[`pbcopy`,e.id],{allowFailure:!0,stdin:t}),`Failed to write iOS simulator clipboard`)}async function zt(e,t,n){T(e,`push`),await E(e);let r=await D.mkdtemp(O.join(Se.tmpdir(),`agent-device-ios-push-`)),i=O.join(r,`payload.apns`);try{await D.writeFile(i,`${JSON.stringify(n)}\n`,`utf8`),await q(e,[`push`,e.id,t,i])}finally{await D.rm(r,{recursive:!0,force:!0})}}let $=null,Bt;async function Vt(e,r,i,a,o){if(n(e)){let e=r.toLowerCase();if(e===`appearance`){await tt(i);return}if(e===`permission`){let e=C(i);if(e===`deny`)throw new t(`INVALID_ARGS`,c(`permission`));return await le(e,Ut(o?.permissionTarget))}throw new t(`INVALID_ARGS`,c(r))}T(e,`settings`),await E(e);let s=r.toLowerCase();switch(s){case`clear-app-state`:{if(i.toLowerCase()!==`clear`)throw new t(`INVALID_ARGS`,`settings clear-app-state only supports clear.`);if(!a)throw new t(`INVALID_ARGS`,`settings clear-app-state requires an app id or an active app session.`);let n=await Ht(e,a);return{bundleId:n.bundleId,containerPath:n.containerPath,cleared:!0}}case`wifi`:{let t=w(i)?`active`:`failed`;await q(e,[`status_bar`,e.id,`override`,`--wifiMode`,t]),M(e);return}case`airplane`:w(i)?(await q(e,[`status_bar`,e.id,`override`,`--dataNetwork`,`hide`,`--wifiMode`,`failed`,`--wifiBars`,`0`,`--cellularMode`,`failed`,`--cellularBars`,`0`,`--operatorName`,``]),M(e)):(await q(e,[`status_bar`,e.id,`clear`]),N(e));return;case`location`:{if(i.toLowerCase()===`set`){let{latitude:t,longitude:n}=l(o);return await q(e,[`location`,e.id,`set`,`${t},${n}`]),{latitude:t,longitude:n}}let n=w(i);if(!a)throw new t(`INVALID_ARGS`,`location setting requires an active app in session`);let r=n?`grant`:`revoke`;await q(e,[`privacy`,e.id,r,`location`,a]);return}case`faceid`:case`touchid`:{let t=s,n=Kt[t];await en(e,$t(i,t),{settingName:t,label:n.label,modalityAliases:n.modalityAliases});return}case`appearance`:{let t=await Wt(e,i);await q(e,[`ui`,e.id,`appearance`,t]);return}case`permission`:if(!a)throw new t(`INVALID_ARGS`,`permission setting requires an active app in session`);await Jt(e,qt(C(i)),Qt(o?.permissionTarget,o?.permissionMode),a);return;default:throw new t(`INVALID_ARGS`,`Unsupported setting: ${r}`)}}async function Ht(e,n){if(!r(e)||e.kind!==`simulator`)throw new t(`UNSUPPORTED_OPERATION`,`Clearing app state is currently supported only on iOS simulators.`);let a=await X(e,n);await E(e),await bt(e,a);let o=i(await q(e,[`get_app_container`,e.id,a,`data`],{allowFailure:!0}),`simctl get_app_container failed for ${a}`).stdout.trim();if(!o)throw new t(`COMMAND_FAILED`,`simctl get_app_container returned an empty data container path for ${a}`);let s=await D.readdir(o);return await Promise.all(s.map(e=>D.rm(O.join(o,e),{recursive:!0,force:!0}))),{bundleId:a,containerPath:o}}function Ut(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 Wt(e,n){let r=S(n);if(r!==`toggle`)return r;let a=i(await q(e,[`ui`,e.id,`appearance`],{allowFailure:!0}),`Failed to read current iOS appearance`),o=Gt(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 Gt(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 Kt={faceid:{label:`Face ID`,modalityAliases:[`face`]},touchid:{label:`Touch ID`,modalityAliases:[`finger`,`touch`]}};function qt(e){return e===`deny`?`revoke`:e}async function Jt(e,n,r,i){let a=await Xt(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 q(e,o);return}catch(n){throw s&&Yt(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 q(e,o);return}catch(e){if(!Yt(e))throw e}try{await q(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 Yt(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 Xt(e){let n=re(e.simulatorSetPath),r=`${process.env.PATH??``}::${n??``}`;if($&&Bt===r)return $;let i=await q(e,[`privacy`,`help`],{allowFailure:!0}),a=Zt(`${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,Bt=r,a}function Zt(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 Qt(e,n){let r=be(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 $t(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 en(e,n,r){let i=tn(e.id,n,r.modalityAliases),a=[];for(let t of i){let n=K(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=ye(a);throw a.length>0&&a.every(e=>nn(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 tn(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 nn(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 rn=e({installIosApp:()=>Pt,installIosInstallablePath:()=>It,listIosApps:()=>ct,pushIosNotification:()=>zt,reinstallIosApp:()=>Ft,resolveIosApp:()=>X,resolveIosSimulatorDeepLinkBundleId:()=>ot});export{Et as a,yt as c,Rt as i,ct as l,Vt as n,bt as o,Lt as r,_t as s,rn as t,Ie as u};
1
+ import{t as e}from"./rolldown-runtime.js";import{F as t,m as n,p as r}from"./sdk-contracts.js";import{h as i,j as a,p as o,v as s}from"./sdk-android-adb.js";import{Ot as c,Tt as l}from"./tv-remote.js";import{i as u}from"./runtime.js";import{n as ee,t as te}from"./install-source2.js";import{D as d,E as f,O as ne,S as p,k as re,w as m,x as ie}from"./android-input-ownership.js";import{d as h,i as ae}from"./runner-client.js";import{a as g,i as oe,l as se,o as _}from"./tool-provider.js";import{a as ce,n as le}from"./helper.js";import{n as v,r as ue}from"./simctl.js";import{$ as de,G as y,J as fe,Jt as pe,K as me,Q as he,U as ge,V as b,Y as x,Yt as _e,bt as ve,et as ye,tt as be,yt as xe}from"./internal/daemon.js";import{a as Se,i as Ce,n as S,r as C,t as w}from"./setting-state.js";import{i as T,n as we,t as E}from"./simulator.js";import{promises as D}from"node:fs";import O from"node:os";import k from"node:path";import{fileURLToPath as Te}from"node:url";function A(e){if(!(e instanceof t))return{reason:e instanceof Error?e.message:String(e)};let n=e.details??{},r=Array.isArray(n.args)?n.args.filter(e=>typeof e==`string`).join(` `):void 0;return{errorCode:e.code,reason:e.message,timeoutMs:typeof n.timeoutMs==`number`?n.timeoutMs:void 0,exitCode:typeof n.exitCode==`number`?n.exitCode:void 0,stderr:typeof n.stderr==`string`&&n.stderr.trim()?n.stderr:void 0,stdout:typeof n.stdout==`string`&&n.stdout.trim()?n.stdout:void 0,commandArgs:r}}const Ee=[`--time`,`9:41`,`--dataNetwork`,`wifi`,`--wifiMode`,`active`,`--wifiBars`,`3`,`--batteryState`,`charged`,`--batteryLevel`,`100`],De={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`},Oe={1:`searching`,2:`failed`,3:`active`},ke={0:`notSupported`,1:`searching`,2:`failed`,3:`active`};function j(e,t,n){return ue(e,t,n)}const M=new Set;async function Ae(e){let t=null,n=!1;if(Me(e))n=!0;else try{t=await Ne(e),n=!0}catch(t){R(e,`snapshot_failed`,t)}try{(!n||t)&&await I(e),await L(e,Ee)}catch(t){R(e,`prepare_failed`,t)}return async()=>{await je(e,n?t:null)}}async function je(e,t){if(await I(e),!t){P(e);return}await L(e,Fe(t)),N(e)}function N(e){M.delete(F(e))}function P(e){M.add(F(e))}function Me(e){return M.has(F(e))}function F(e){return`${e.platform}:${e.kind}:${e.id}`}async function Ne(e){return Pe(i(await j(e,[`status_bar`,e.id,`list`],{allowFailure:!0}),`Failed to read simulator status bar overrides`).stdout)}async function I(e){await j(e,[`status_bar`,e.id,`clear`])}async function L(e,t){t.length!==0&&await j(e,[`status_bar`,e.id,`override`,...t])}function Pe(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=De[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=Oe[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=ke[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 Fe(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||Ie(e.dataNetwork)||e.operatorName!==void 0)&&t.push(`--cellularBars`,e.cellularBars),e.operatorName!==void 0&&t.push(`--operatorName`,e.operatorName),t}function Ie(e){return!!(e&&e!==`hide`&&e!==`wifi`)}function R(e,t,n){a({level:`warn`,phase:`ios_screenshot_status_bar_${t}`,data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,...A(n)}})}function z(e,t,n){return ue(e,t,n)}const Le={ensureBooted:E,prepareStatusBarForScreenshot:Ae,captureWithRetry:Be,normalizeDensity:Xe,captureWithRunner:V,shouldFallbackToRunner:Je},B=new Map;async function Re(e,t,r={}){if(n(e)){await V(e,t,r.appBundleId,r.fullscreen,r.runnerOptions);return}if(e.kind===`simulator`){await ze(e,t,r);return}try{await y([`device`,`screenshot`,`--device`,e.id,t],{action:`capture iOS screenshot`,deviceId:e.id});return}catch(t){if(!qe(t))throw t;We(e,`devicectl_screenshot`,t)}await V(e,t,r.appBundleId,r.fullscreen,r.runnerOptions)}async function ze(e,n,r={}){if(e.kind!==`simulator`)throw new t(`UNSUPPORTED_OPERATION`,`Simulator screenshot fallback flow supports only iOS simulators`);let i={...Le,...r.deps??{}};r.skipIosSimulatorBootCheck||await i.ensureBooted(e);let a=async()=>{};if(r.normalizeStatusBar===!0)try{a=await i.prepareStatusBarForScreenshot(e)}catch(t){Ge(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&&Ye(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;We(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=>Ge(e,`restore_failed`,t))}}async function Be(e,t){let n=p.fromTimeoutMs(ye);await m(async({deadline:n})=>{await z(e,[`io`,e.id,`screenshot`,t],{timeoutMs:Math.max(1e3,n?.remainingMs()??2e4)})},{maxAttempts:5,baseDelayMs:he,maxDelayMs:de,jitter:.2,shouldRetry:e=>Je(e)},{deadline:n,phase:`ios_simulator_screenshot`})}async function V(e,r,i,a,o){let s=(await ae(e,{command:`screenshot`,appBundleId:i,fullscreen:a},o)).message;if(!s)throw new t(`COMMAND_FAILED`,`Failed to capture iOS screenshot: runner returned no file path`);if(n(e)){await D.copyFile(s,r);return}if(e.kind===`simulator`){await He(e,s,r);return}await Ve(e,s,r)}async function Ve(e,n,r){let i=p.fromTimeoutMs(x),a={exitCode:1,stdout:``,stderr:``};for(let t of h)if(a=await _([`devicectl`,`device`,`copy`,`from`,`--device`,e.id,`--source`,n,`--destination`,r,`--domain-type`,`appDataContainer`,`--domain-identifier`,t],{allowFailure:!0,timeoutMs:Ue(i,x,`runner screenshot copy`)}),a.exitCode===0)return;throw new t(`COMMAND_FAILED`,`Failed to capture iOS screenshot: ${a.stderr.trim()||a.stdout.trim()||`devicectl exited with code ${a.exitCode}`}`)}async function He(e,n,r){let i=p.fromTimeoutMs(x),a=`Unable to locate runner container for simulator screenshot`;for(let t of h){let o=await z(e,[`get_app_container`,e.id,t,`data`],{allowFailure:!0,timeoutMs:Ue(i,x,`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=Ke(s,n);for(let e of c)try{await D.copyFile(e,r);return}catch(e){a=e instanceof Error?e.message:String(e)}}throw new t(`COMMAND_FAILED`,`Failed to capture iOS screenshot: ${a}`)}function Ue(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 We(e,t,n){let r=A(n);a({level:`warn`,phase:`ios_screenshot_fallback`,data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,from:t,to:`runner`,...r}})}function Ge(e,t,n){a({level:`warn`,phase:`ios_screenshot_status_bar_${t}`,data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,...A(n)}})}function Ke(e,t){let n=k.resolve(e),r=t.trim();if(!r)return[];let i=[],a=new Set,o=e=>{let t=k.normalize(e);a.has(t)||(a.add(t),i.push(t))},s=r.replace(/^\/+/,``),c=s.replace(/\\/g,`/`);if(s&&o(k.join(n,s)),k.isAbsolute(r)&&o(k.normalize(r)),c.startsWith(`tmp/`))o(k.join(n,c));else{let e=c.lastIndexOf(`/tmp/`);if(e>=0){let t=c.slice(e+1);o(k.join(n,t))}}let l=k.basename(r);return l&&o(k.join(n,`tmp`,l)),i}function qe(e){if(!(e instanceof t)||e.code!==`COMMAND_FAILED`)return!1;let n=H(e);return n.includes(`unknown option '--device'`)||n.includes(`unknown subcommand`)&&n.includes(`screenshot`)||n.includes(`unrecognized subcommand`)&&n.includes(`screenshot`)}function Je(e){if(!(e instanceof t)||e.code!==`COMMAND_FAILED`)return!1;let n=H(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 Ye(e){if(!(e instanceof t)||e.code!==`COMMAND_FAILED`)return!1;let n=H(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`)}function H(e){let t=e.details??{},n=typeof t.stdout==`string`?t.stdout:``,r=typeof t.stderr==`string`?t.stderr:``,i=Array.isArray(t.args)?t.args.filter(e=>typeof e==`string`).join(` `):``;return`${e.message}\n${n}\n${r}\n${i}`.toLowerCase()}async function Xe(e,t,n){let r=await Ze(e),i=xe(await ve(t),r,n);i&&await u(t,i.width,i.height)}async function Ze(e){let n=B.get(e.id);if(n!==void 0)return n;let r=await z(e,[`getenv`,e.id,`SIMULATOR_MAINSCREEN_SCALE`],{timeoutMs:5e3}),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 B.set(e.id,i),i}const Qe={settings:`com.apple.systempreferences`},$e=/^[a-z0-9-]+(?:\.[a-z0-9-]+)+$/,et={platform:`macos`,deviceId:`host`,variant:`all`},U=f();function tt(e){return $e.test(e)}async function W(e){let n=e.trim(),r=Qe[n.toLowerCase()];if(r)return r;if(tt(n))return n;let i=U.get(et,n);if(i)return i;let a=(await ct(`all`)).filter(e=>e.name.toLowerCase()===n.toLowerCase()),o=a[0];if(o!==void 0&&a.length===1)return U.set(et,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 nt(e,n,r){let i=r?.url?.trim();if(i){if(!d(i))throw new t(`INVALID_ARGS`,`open <app> <url> requires a valid URL target`);let e=r?.appBundleId??await W(n);await G().openBundle(e,i);return}let a=n.trim();if(d(a)){await G().openTarget(a);return}let o=r?.appBundleId??await W(a);await G().openBundle(o)}async function rt(e,n){let r=await W(n),i=await le(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 it(){return await G().readClipboard()}async function at(e){await G().writeClipboard(e)}async function ot(){return await G().readDarkMode()}async function st(e){let t=S(e),n=t===`toggle`?!await ot():t===`dark`;await G().setDarkMode(n)}async function ct(e){return await G().listApps(e)}function G(){let e=oe().macosHost;if(!e)throw new t(`UNSUPPORTED_OPERATION`,`macOS host provider is not available`);return e}function K(e,t){return v(e,t)}function q(e,t,n){return _(K(e,t),n)}function lt(e){return e.includes(`not installed`)||e.includes(`not found`)||e.includes(`no such file`)}const ut={settings:`com.apple.Preferences`},J=f();function dt(e){return{platform:`ios`,deviceId:e.id,variant:e.kind}}async function Y(e,t){return await J.invalidateWhile(dt(e),t)}async function X(e,r){if(n(e))return await W(r);let i=r.trim();if(i.includes(`.`))return i;let a=ut[i.toLowerCase()];if(a)return a;let o=dt(e),s=J.get(o,i);if(s)return s;let c=(e.kind===`simulator`?await ht(e):await ge(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}):new t(`APP_NOT_INSTALLED`,`No app found matching "${r}"`)}async function ft(e,t){if(!r(e)||e.kind!==`simulator`)return;let n=pt(t);if(!n)return;let i=await gt(e),a=[];for(let e of i)e.bundleId.startsWith(`com.callstack.agentdevice.runner`)||e.path&&(await vt(k.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 pt(e){return/^([A-Za-z][A-Za-z0-9+.-]*):/.exec(e.trim())?.[1]?.toLowerCase()}async function mt(e,t){return n(e)?await ct(t):e.kind===`simulator`?se(await ht(e),t):await ge(e,t)}async function ht(e){return(await gt(e)).map(e=>({bundleId:e.bundleId,name:e.name}))}async function gt(e){let t=(await q(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 g(`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=_t(t);return{bundleId:e,name:t.CFBundleDisplayName??t.CFBundleName??e,...n?{path:n}:{},...t.ApplicationType?{applicationType:t.ApplicationType}:{}}}):[]}function _t(e){if(e.Path)return e.Path;if(e.Bundle)try{return Te(e.Bundle)}catch{return}}async function vt(e){let t=await g(`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}}function yt(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 bt(e,t){let n=await _(v(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 xt(e){return e.installed?e.simulatorCompatible===!1?`ARCH_MISMATCH`:`PERSISTENT_LAUNCH_FAIL`:`APP_NOT_INSTALLED`}function St(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 Z=25e3;async function Ct(e,i,a){let o=a?.launchConsole?.trim(),s=a?.launchArgs;if(o&&(!r(e)||e.kind!==`simulator`))throw new t(`UNSUPPORTED_OPERATION`,_e);if(n(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 nt(e,i,a);return}let c=a?.url?.trim();if(c){if(o)throw new t(`INVALID_ARGS`,pe);if(!d(c))throw new t(`INVALID_ARGS`,`open <app> <url> requires a valid URL target`);if(e.kind===`simulator`){(s||ne(c))&&await Dt(e,a?.appBundleId??await X(e,i),{...s?{launchArgs:s}:{}}),await wt(e,c,void 0);return}let n=re(a?.appBundleId??await X(e,i),c);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 Q(e,n,{payloadUrl:c,launchArgs:s});return}let l=i.trim();if(d(l)){if(o)throw new t(`INVALID_ARGS`,pe);if(e.kind===`simulator`){await wt(e,l,s);return}let n=re(a?.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 Q(e,n,{payloadUrl:l,launchArgs:s});return}let u=a?.appBundleId??await X(e,i);if(e.kind===`simulator`){await Dt(e,u,{...o?{launchConsole:o}:{},...s?{launchArgs:s}:{},...a?.terminateRunningApp?{terminateRunningApp:!0}:{}});return}await Q(e,u,{launchArgs:s})}async function wt(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 q(e,[`openurl`,e.id,n])}async function Tt(e){n(e)||e.kind===`simulator`&&await we(e)!==`Booted`&&await E(e)}async function Et(e,r){if(n(e)){await rt(e,r);return}let i=await X(e,r);if(e.kind===`simulator`){await E(e);let n=K(e,[`terminate`,e.id,i]),r=await _(n,{allowFailure:!0,timeoutMs:be});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}`,o(r,{cmd:`xcrun`,args:n}))}return}await y([`device`,`process`,`terminate`,`--device`,e.id,i],{action:`terminate iOS app`,deviceId:e.id})}async function Dt(e,n,r){await E(e);let i=0,a=p.fromTimeoutMs(me);try{await m(async({deadline:i})=>{if(i?.isExpired())throw new t(`COMMAND_FAILED`,`App launch deadline exceeded`,{timeoutMs:me});let a=K(e,Ot(e.id,n,r)),s=r?.launchConsole?await kt(a,r.launchConsole):await _(a,{allowFailure:!0});if(s.exitCode!==0)throw new t(`COMMAND_FAILED`,`xcrun exited with code ${s.exitCode}`,o(s,{cmd:`xcrun`,args:a}))},{maxAttempts:10,baseDelayMs:1e3,maxDelayMs:5e3,jitter:.2,shouldRetry(e){return yt(e)?(i+=1,i<3):!1}},{deadline:a})}catch(t){if(yt(t)){let r=t,i=xt(await bt(e,n));r.details={...r.details,hint:St(i)}}throw t}}function Ot(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 kt(e,n){await D.mkdir(k.dirname(n),{recursive:!0});try{let t=await _(e,{allowFailure:!0,timeoutMs:Z});return await At(n,t.stdout,t.stderr),t}catch(e){let r=(e instanceof t?e:void 0)?.details;if(r?.timeoutMs===Z){let e=typeof r.stdout==`string`?r.stdout:``,t=typeof r.stderr==`string`?r.stderr:``;return await At(n,e,t),a({level:`warn`,phase:`ios_simulator_launch_console_capture_timeout`,data:{timeoutMs:Z,logPath:n,stdoutBytes:Buffer.byteLength(e),stderrBytes:Buffer.byteLength(t)}}),{stdout:e,stderr:t,exitCode:0}}throw e}}async function At(e,t,n){await D.writeFile(e,jt(t,n),`utf8`)}function jt(e,t){return!e||!t||e.endsWith(`
3
+ `)||e.endsWith(`\r`)?`${e}${t}`:`${e}\n${t}`}async function Q(e,t,n){let r=[`device`,`process`,`launch`,`--device`,e.id,t];n?.payloadUrl&&r.push(`--payload-url`,n.payloadUrl),n?.launchArgs&&n.launchArgs.length>0&&r.push(`--`,...n.launchArgs),await y(r,{action:`launch iOS app`,deviceId:e.id})}var Mt=e({prepareIosInstallArtifact:()=>Nt});async function Nt(e,n){if(e.kind===`url`&&!te(e.url))throw new t(`INVALID_ARGS`,`iOS install_from_source URL sources are only supported for trusted artifact services such as GitHub Actions and EAS. Use a path source for other hosts.`);let r=await ee({source:e,isInstallablePath:(e,t)=>t.isDirectory()&&e.toLowerCase().endsWith(`.app`)||t.isFile()&&e.toLowerCase().endsWith(`.ipa`),installableLabel:`iOS installable (.app or .ipa)`,allowArchiveExtraction:e.kind!==`url`||te(e.url),signal:n?.signal}),i;try{i=await Ft(r.installablePath,n);let e=i,t=await Pt(e.installPath);return{archivePath:r.archivePath??(r.installablePath.toLowerCase().endsWith(`.ipa`)?r.installablePath:void 0),installablePath:e.installPath,bundleId:t.bundleId,appName:t.appName,cleanup:async()=>{await e.cleanup(),await r.cleanup()}}}catch(e){try{await i?.cleanup()}finally{await r.cleanup()}throw e}}async function Pt(e){let t=k.join(e,`Info.plist`),[n,r,i]=await Promise.all([b(t,`CFBundleIdentifier`),b(t,`CFBundleDisplayName`),b(t,`CFBundleName`)]);return{bundleId:n,appName:r??i}}async function Ft(e,n){if(!e.toLowerCase().endsWith(`.ipa`))return{installPath:e,cleanup:async()=>{}};let r=await D.mkdtemp(k.join(O.tmpdir(),`agent-device-ios-ipa-`)),i=async()=>{await D.rm(r,{recursive:!0,force:!0})};try{await s(`unzip`,[`-q`,e,`-d`,r]);let a=k.join(r,`Payload`),o=(await D.readdir(a,{withFileTypes:!0}).catch(()=>{throw new t(`INVALID_ARGS`,`Invalid IPA: missing Payload directory`)})).filter(e=>e.isDirectory()&&e.name.toLowerCase().endsWith(`.app`)).map(e=>({installPath:k.join(a,e.name),bundleName:e.name.replace(/\.app$/i,``)})),c=o[0];if(c!==void 0&&o.length===1)return{installPath:c.installPath,cleanup:i};if(o.length===0)throw new t(`INVALID_ARGS`,`Invalid IPA: expected at least one .app under Payload, found 0`);await It(o);let l=n?.appIdentifierHint?.trim();if(l){let e=Lt(o,l);if(e)return{installPath:e.installPath,cleanup:i};throw new t(`INVALID_ARGS`,`Invalid IPA: found ${o.length} .app bundles under Payload and none matched "${l}". Available bundles: ${o.map(Rt).join(`, `)}`)}throw new t(`INVALID_ARGS`,`Invalid IPA: found ${o.length} .app bundles under Payload. Pass an app identifier or bundle name matching one of: ${o.map(Rt).join(`, `)}`)}catch(e){throw await i(),e}}async function It(e){await Promise.all(e.map(async e=>{if(e.bundleId&&e.appName)return;let t=await Pt(e.installPath);e.bundleId=e.bundleId??t.bundleId,e.appName=e.appName??t.appName}))}function Lt(e,n){let r=n.toLowerCase(),i=e.filter(e=>e.bundleName.toLowerCase()===r);if(i.length===1)return i[0];if(i.length>1)throw new t(`INVALID_ARGS`,`Invalid IPA: multiple app bundles matched "${n}" by name. Use a bundle id hint instead.`);if(n.includes(`.`)){let t=e.filter(e=>e.bundleId?.toLowerCase()===r);if(t.length===1)return t[0]}}function Rt(e){let t=e.bundleId??e.appName;return t?`${e.bundleName}.app (${t})`:`${e.bundleName}.app`}async function zt(e,n){return await Y(e,async()=>{let r=await X(e,n);if(e.kind!==`simulator`)return await y([`device`,`uninstall`,`app`,`--device`,e.id,r],{action:`uninstall iOS app ${r}`,deviceId:e.id},{tolerateOutput:(e,t)=>lt(`${e}\n${t}`.toLowerCase())}),{bundleId:r};await E(e);let i=await q(e,[`uninstall`,e.id,r],{allowFailure:!0});if(i.exitCode!==0&&!lt(`${i.stdout}\n${i.stderr}`.toLowerCase()))throw new t(`COMMAND_FAILED`,`simctl uninstall failed for ${r}`,o(i));return{bundleId:r}})}async function Bt(e,t,n){let r=await Nt({kind:`path`,path:t},n);try{return await Ht(e,r.installablePath),{archivePath:r.archivePath,installablePath:r.installablePath,bundleId:r.bundleId,appName:r.appName,launchTarget:r.bundleId}}finally{await r.cleanup()}}async function Vt(e,t,n){return await Y(e,async()=>{let{bundleId:r}=await zt(e,t);return await Bt(e,n,{appIdentifierHint:t}),{bundleId:r}})}async function Ht(e,t){await Y(e,async()=>{if(e.kind!==`simulator`){await y([`device`,`install`,`app`,`--device`,e.id,t],{action:`install iOS app`,deviceId:e.id},{timeoutMs:fe});return}await E(e),await q(e,[`install`,e.id,t])})}async function Ut(e){return n(e)?await it():(T(e,`clipboard`),await E(e),i(await q(e,[`pbpaste`,e.id],{allowFailure:!0}),`Failed to read iOS simulator clipboard`).stdout.replace(/\r\n/g,`
4
+ `).replace(/\n$/,``))}async function Wt(e,t){if(n(e)){await at(t);return}T(e,`clipboard`),await E(e),i(await q(e,[`pbcopy`,e.id],{allowFailure:!0,stdin:t}),`Failed to write iOS simulator clipboard`)}async function Gt(e,t,n){T(e,`push`),await E(e);let r=await D.mkdtemp(k.join(O.tmpdir(),`agent-device-ios-push-`)),i=k.join(r,`payload.apns`);try{await D.writeFile(i,`${JSON.stringify(n)}\n`,`utf8`),await q(e,[`push`,e.id,t,i])}finally{await D.rm(r,{recursive:!0,force:!0})}}let $=null,Kt;async function qt(e,r,i,a,o){if(n(e)){let e=r.toLowerCase();if(e===`appearance`){await st(i);return}if(e===`permission`){let e=C(i);if(e===`deny`)throw new t(`INVALID_ARGS`,c(`permission`));return await ce(e,Yt(o?.permissionTarget))}throw new t(`INVALID_ARGS`,c(r))}T(e,`settings`),await E(e);let s=r.toLowerCase();switch(s){case`clear-app-state`:{if(i.toLowerCase()!==`clear`)throw new t(`INVALID_ARGS`,`settings clear-app-state only supports clear.`);if(!a)throw new t(`INVALID_ARGS`,`settings clear-app-state requires an app id or an active app session.`);let n=await Jt(e,a);return{bundleId:n.bundleId,containerPath:n.containerPath,cleared:!0}}case`wifi`:{let t=w(i)?`active`:`failed`;await q(e,[`status_bar`,e.id,`override`,`--wifiMode`,t]),N(e);return}case`airplane`:w(i)?(await q(e,[`status_bar`,e.id,`override`,`--dataNetwork`,`hide`,`--wifiMode`,`failed`,`--wifiBars`,`0`,`--cellularMode`,`failed`,`--cellularBars`,`0`,`--operatorName`,``]),N(e)):(await q(e,[`status_bar`,e.id,`clear`]),P(e));return;case`location`:{if(i.toLowerCase()===`set`){let{latitude:t,longitude:n}=l(o);return await q(e,[`location`,e.id,`set`,`${t},${n}`]),{latitude:t,longitude:n}}let n=w(i);if(!a)throw new t(`INVALID_ARGS`,`location setting requires an active app in session`);let r=n?`grant`:`revoke`;await q(e,[`privacy`,e.id,r,`location`,a]);return}case`faceid`:case`touchid`:{let t=s,n=Qt[t];await sn(e,on(i,t),{settingName:t,label:n.label,modalityAliases:n.modalityAliases});return}case`appearance`:{let t=await Xt(e,i);await q(e,[`ui`,e.id,`appearance`,t]);return}case`permission`:if(!a)throw new t(`INVALID_ARGS`,`permission setting requires an active app in session`);await en(e,$t(C(i)),an(o?.permissionTarget,o?.permissionMode),a);return;default:throw new t(`INVALID_ARGS`,`Unsupported setting: ${r}`)}}async function Jt(e,n){if(!r(e)||e.kind!==`simulator`)throw new t(`UNSUPPORTED_OPERATION`,`Clearing app state is currently supported only on iOS simulators.`);let a=await X(e,n);await E(e),await Et(e,a);let o=i(await q(e,[`get_app_container`,e.id,a,`data`],{allowFailure:!0}),`simctl get_app_container failed for ${a}`).stdout.trim();if(!o)throw new t(`COMMAND_FAILED`,`simctl get_app_container returned an empty data container path for ${a}`);let s=await D.readdir(o);return await Promise.all(s.map(e=>D.rm(k.join(o,e),{recursive:!0,force:!0}))),{bundleId:a,containerPath:o}}function Yt(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 Xt(e,n){let r=S(n);if(r!==`toggle`)return r;let a=i(await q(e,[`ui`,e.id,`appearance`],{allowFailure:!0}),`Failed to read current iOS appearance`),o=Zt(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 Zt(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 Qt={faceid:{label:`Face ID`,modalityAliases:[`face`]},touchid:{label:`Touch ID`,modalityAliases:[`finger`,`touch`]}};function $t(e){return e===`deny`?`revoke`:e}async function en(e,n,r,i){let a=await nn(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 q(e,o);return}catch(n){throw s&&tn(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 q(e,o);return}catch(e){if(!tn(e))throw e}try{await q(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 tn(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 nn(e){let n=ie(e.simulatorSetPath),r=`${process.env.PATH??``}::${n??``}`;if($&&Kt===r)return $;let i=await q(e,[`privacy`,`help`],{allowFailure:!0}),a=rn(`${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,Kt=r,a}function rn(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 an(e,n){let r=Ce(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 on(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 sn(e,n,r){let i=cn(e.id,n,r.modalityAliases),a=[];for(let t of i){let n=K(e,t),r=await _(n,{allowFailure:!0});if(r.exitCode===0)return;a.push({args:n,stderr:r.stderr,stdout:r.stdout,exitCode:r.exitCode})}let o=Se(a);throw a.length>0&&a.every(e=>ln(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 cn(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 ln(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 un=e({installIosApp:()=>Bt,installIosInstallablePath:()=>Ht,listIosApps:()=>mt,pushIosNotification:()=>Gt,reinstallIosApp:()=>Vt,resolveIosApp:()=>X,resolveIosSimulatorDeepLinkBundleId:()=>ft});export{Mt as a,Tt as c,Wt as i,mt as l,qt as n,Et as o,Ut as r,Ct as s,un as t,Re as u};