agent-device 0.20.2 → 0.20.3

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 (35) hide show
  1. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.20.2.apk → agent-device-android-ime-helper-0.20.3.apk} +0 -0
  2. package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.3.apk.sha256 +1 -0
  3. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.20.2.manifest.json → agent-device-android-ime-helper-0.20.3.manifest.json} +4 -4
  4. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.20.2.apk → agent-device-android-snapshot-helper-0.20.3.apk} +0 -0
  5. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.3.apk.sha256 +1 -0
  6. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.20.2.manifest.json → agent-device-android-snapshot-helper-0.20.3.manifest.json} +6 -6
  7. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +0 -176
  8. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Keyboard.swift +184 -0
  9. package/dist/src/android-adb.d.ts +3 -26
  10. package/dist/src/android-adb.js +1 -1
  11. package/dist/src/app-helpers.js +1 -0
  12. package/dist/src/apps.js +5 -5
  13. package/dist/src/context.js +1 -1
  14. package/dist/src/device-input-state.d.ts +26 -0
  15. package/dist/src/find.js +1 -1
  16. package/dist/src/index.d.ts +1 -1
  17. package/dist/src/interaction-snapshot.js +1 -1
  18. package/dist/src/interaction.js +1 -1
  19. package/dist/src/internal/bin.js +1 -1
  20. package/dist/src/internal/daemon.js +14 -14
  21. package/dist/src/lease.js +1 -1
  22. package/dist/src/limrun.d.ts +78 -2
  23. package/dist/src/logcat.js +1 -1
  24. package/dist/src/recording-export-quality.js +2 -3
  25. package/dist/src/runtime2.js +1 -1
  26. package/dist/src/sdk-android-adb.d.ts +1 -1
  27. package/dist/src/selector-runtime.js +1 -1
  28. package/dist/src/session-target-evidence.js +1 -1
  29. package/dist/src/session.js +8 -8
  30. package/dist/src/snapshot2.js +1 -1
  31. package/dist/src/tv-remote.d.ts +1 -1
  32. package/package.json +14 -15
  33. package/server.json +2 -2
  34. package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.2.apk.sha256 +0 -1
  35. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.2.apk.sha256 +0 -1
@@ -0,0 +1 @@
1
+ 02777b8c7e55044210eafa255ac92e627977679d75ba4a97f563f3e23dea3267 agent-device-android-ime-helper-0.20.3.apk
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "android-ime-helper",
3
- "version": "0.20.2",
4
- "assetName": "agent-device-android-ime-helper-0.20.2.apk",
5
- "sha256": "941f2997c4315e753fb2f5411562a2907b3a188370c3d52e762692843be1b50f",
3
+ "version": "0.20.3",
4
+ "assetName": "agent-device-android-ime-helper-0.20.3.apk",
5
+ "sha256": "02777b8c7e55044210eafa255ac92e627977679d75ba4a97f563f3e23dea3267",
6
6
  "packageName": "com.callstack.agentdevice.imehelper",
7
- "versionCode": 20002,
7
+ "versionCode": 20003,
8
8
  "serviceComponent": "com.callstack.agentdevice.imehelper/.TestInputMethodService",
9
9
  "broadcastProtocol": "android-ime-helper-v1"
10
10
  }
@@ -0,0 +1 @@
1
+ de7ecffc80d54dd1c173d3bf445ff8c9a02de2d124bcf39abeafeb5a6a08c95e agent-device-android-snapshot-helper-0.20.3.apk
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "android-snapshot-helper",
3
- "version": "0.20.2",
4
- "releaseTag": "v0.20.2",
5
- "assetName": "agent-device-android-snapshot-helper-0.20.2.apk",
3
+ "version": "0.20.3",
4
+ "releaseTag": "v0.20.3",
5
+ "assetName": "agent-device-android-snapshot-helper-0.20.3.apk",
6
6
  "apkUrl": null,
7
- "sha256": "003859ea5dc5d1ed8ddc5daf786eacb790ff471bbb0c3b0e1239712f73f37684",
8
- "checksumName": "agent-device-android-snapshot-helper-0.20.2.apk.sha256",
7
+ "sha256": "de7ecffc80d54dd1c173d3bf445ff8c9a02de2d124bcf39abeafeb5a6a08c95e",
8
+ "checksumName": "agent-device-android-snapshot-helper-0.20.3.apk.sha256",
9
9
  "packageName": "com.callstack.agentdevice.snapshothelper",
10
- "versionCode": 20002,
10
+ "versionCode": 20003,
11
11
  "instrumentationRunner": "com.callstack.agentdevice.snapshothelper/.SnapshotInstrumentation",
12
12
  "minSdk": 23,
13
13
  "targetSdk": 36,
@@ -335,168 +335,6 @@ extension RunnerTests {
335
335
  && point.y <= frame.maxY + tolerance
336
336
  }
337
337
 
338
- func isKeyboardVisible(app: XCUIApplication) -> Bool {
339
- return visibleKeyboardFrame(app: app) != nil
340
- }
341
-
342
- func dismissKeyboard(app: XCUIApplication) -> (wasVisible: Bool, dismissed: Bool, visible: Bool) {
343
- let wasVisible = isKeyboardVisible(app: app)
344
- guard wasVisible else {
345
- return (wasVisible: false, dismissed: false, visible: false)
346
- }
347
-
348
- #if os(tvOS)
349
- _ = pressTvRemote(.menu)
350
- sleepFor(0.2)
351
- let visible = isKeyboardVisible(app: app)
352
- return (wasVisible: true, dismissed: !visible, visible: visible)
353
- #else
354
- if tapKeyboardDismissControl(app: app) {
355
- sleepFor(0.2)
356
- let visible = isKeyboardVisible(app: app)
357
- return (wasVisible: true, dismissed: !visible, visible: visible)
358
- }
359
-
360
- return (wasVisible: true, dismissed: false, visible: isKeyboardVisible(app: app))
361
- #endif
362
- }
363
-
364
- func pressKeyboardReturn(app: XCUIApplication) -> (wasVisible: Bool, pressed: Bool, visible: Bool) {
365
- #if os(tvOS)
366
- return (wasVisible: false, pressed: pressTvRemote(.select), visible: false)
367
- #elseif os(iOS)
368
- let wasVisible = isKeyboardVisible(app: app)
369
- if tapKeyboardReturnControl(app: app) {
370
- sleepFor(0.2)
371
- return (wasVisible: wasVisible, pressed: true, visible: isKeyboardVisible(app: app))
372
- }
373
-
374
- var typed = false
375
- let exceptionMessage = RunnerObjCExceptionCatcher.catchException({
376
- app.typeText(XCUIKeyboardKey.return.rawValue)
377
- typed = true
378
- })
379
- if let exceptionMessage {
380
- NSLog(
381
- "AGENT_DEVICE_RUNNER_KEYBOARD_RETURN_IGNORED_EXCEPTION=%@",
382
- exceptionMessage
383
- )
384
- if let singleTarget = singleTextEntryElement(app: app) {
385
- return pressKeyboardReturn(on: singleTarget, app: app, wasVisible: wasVisible)
386
- }
387
- return (wasVisible: wasVisible, pressed: false, visible: isKeyboardVisible(app: app))
388
- }
389
- sleepFor(0.2)
390
- return (wasVisible: wasVisible, pressed: typed, visible: isKeyboardVisible(app: app))
391
- #else
392
- return (wasVisible: false, pressed: false, visible: false)
393
- #endif
394
- }
395
-
396
- private func pressKeyboardReturn(
397
- on element: XCUIElement,
398
- app: XCUIApplication,
399
- wasVisible: Bool
400
- ) -> (wasVisible: Bool, pressed: Bool, visible: Bool) {
401
- #if os(iOS)
402
- let exceptionMessage = RunnerObjCExceptionCatcher.catchException({
403
- element.tap()
404
- element.typeText(XCUIKeyboardKey.return.rawValue)
405
- })
406
- if let exceptionMessage {
407
- NSLog(
408
- "AGENT_DEVICE_RUNNER_KEYBOARD_RETURN_TARGET_IGNORED_EXCEPTION=%@",
409
- exceptionMessage
410
- )
411
- return (wasVisible: wasVisible, pressed: false, visible: isKeyboardVisible(app: app))
412
- }
413
- sleepFor(0.2)
414
- return (wasVisible: wasVisible, pressed: true, visible: isKeyboardVisible(app: app))
415
- #else
416
- return (wasVisible: wasVisible, pressed: false, visible: false)
417
- #endif
418
- }
419
-
420
- private func singleTextEntryElement(app: XCUIApplication) -> XCUIElement? {
421
- #if os(iOS)
422
- let matches = safely("KEYBOARD_RETURN_TEXT_ENTRY_QUERY", []) {
423
- app.descendants(matching: .any).allElementsBoundByIndex.filter { element in
424
- guard element.exists else { return false }
425
- switch element.elementType {
426
- case .textField, .secureTextField, .searchField, .textView:
427
- return true
428
- default:
429
- return false
430
- }
431
- }
432
- }
433
- return matches.count == 1 ? matches[0] : nil
434
- #else
435
- return nil
436
- #endif
437
- }
438
-
439
- private func tapKeyboardDismissControl(app: XCUIApplication) -> Bool {
440
- #if os(tvOS)
441
- return false
442
- #else
443
- guard let keyboardFrame = visibleKeyboardFrame(app: app) else {
444
- return false
445
- }
446
- for label in ["Hide keyboard", "Dismiss keyboard", "Done"] {
447
- let candidates = [
448
- app.keyboards.buttons[label],
449
- app.keyboards.keys[label],
450
- app.keyboards.toolbars.buttons[label],
451
- ]
452
- if let hittable = candidates.first(where: { $0.exists && $0.isHittable }) {
453
- hittable.tap()
454
- return true
455
- }
456
-
457
- let toolbarButtonPredicate = NSPredicate(
458
- format: "label == %@ OR identifier == %@",
459
- label,
460
- label
461
- )
462
- let toolbarButtons = app.descendants(matching: .button)
463
- .matching(toolbarButtonPredicate)
464
- .allElementsBoundByIndex
465
- if let hittable = toolbarButtons.first(where: {
466
- $0.exists && $0.isHittable && isKeyboardAccessoryControl($0, keyboardFrame: keyboardFrame)
467
- }) {
468
- hittable.tap()
469
- return true
470
- }
471
- }
472
- return false
473
- #endif
474
- }
475
-
476
- private func tapKeyboardReturnControl(app: XCUIApplication) -> Bool {
477
- #if os(iOS)
478
- for label in ["return", "Return", "Enter", "Go", "Search", "Next", "Done", "Send", "Join"] {
479
- let candidates = [
480
- app.keyboards.buttons[label],
481
- app.keyboards.keys[label],
482
- ]
483
- if let hittable = candidates.first(where: { $0.exists && $0.isHittable }) {
484
- hittable.tap()
485
- return true
486
- }
487
- }
488
- #endif
489
- return false
490
- }
491
-
492
- private func isKeyboardAccessoryControl(_ element: XCUIElement, keyboardFrame: CGRect) -> Bool {
493
- let frame = element.frame
494
- guard !frame.isEmpty && !keyboardFrame.isEmpty else {
495
- return false
496
- }
497
- return frame.intersects(keyboardFrame) || abs(frame.maxY - keyboardFrame.minY) <= 80
498
- }
499
-
500
338
  private func readableText(for element: XCUIElement) -> String? {
501
339
  let label = element.label.trimmingCharacters(in: .whitespacesAndNewlines)
502
340
  let identifier = element.identifier.trimmingCharacters(in: .whitespacesAndNewlines)
@@ -1005,20 +843,6 @@ extension RunnerTests {
1005
843
  #endif
1006
844
  }
1007
845
 
1008
- private func visibleKeyboardFrame(app: XCUIApplication) -> CGRect? {
1009
- #if os(iOS)
1010
- return safely("KEYBOARD_FRAME") {
1011
- let keyboard = app.keyboards.firstMatch
1012
- guard keyboard.exists else { return nil }
1013
- let keyboardFrame = keyboard.frame
1014
- guard !keyboardFrame.isEmpty else { return nil }
1015
- return keyboardFrame
1016
- }
1017
- #else
1018
- return nil
1019
- #endif
1020
- }
1021
-
1022
846
  func axFreeSynthesizedDragPlan(
1023
847
  app: XCUIApplication,
1024
848
  x: Double,
@@ -0,0 +1,184 @@
1
+ import XCTest
2
+
3
+ private enum KeyboardDismissObservationTiming {
4
+ static let timeout: TimeInterval = 2
5
+ }
6
+
7
+ extension RunnerTests {
8
+ func isKeyboardVisible(app: XCUIApplication) -> Bool {
9
+ return visibleKeyboardFrame(app: app) != nil
10
+ }
11
+
12
+ func dismissKeyboard(app: XCUIApplication) -> (wasVisible: Bool, dismissed: Bool, visible: Bool) {
13
+ let keyboard = app.keyboards.firstMatch
14
+ let wasVisible = isKeyboardVisible(app: app)
15
+ guard wasVisible else {
16
+ return (wasVisible: false, dismissed: false, visible: false)
17
+ }
18
+
19
+ #if os(tvOS)
20
+ _ = pressTvRemote(.menu)
21
+ sleepFor(0.2)
22
+ let visible = isKeyboardVisible(app: app)
23
+ return (wasVisible: true, dismissed: !visible, visible: visible)
24
+ #else
25
+ if tapKeyboardDismissControl(app: app) {
26
+ _ = keyboard.waitForNonExistence(timeout: KeyboardDismissObservationTiming.timeout)
27
+ let visible = isKeyboardVisible(app: app)
28
+ return (wasVisible: true, dismissed: !visible, visible: visible)
29
+ }
30
+
31
+ return (wasVisible: true, dismissed: false, visible: isKeyboardVisible(app: app))
32
+ #endif
33
+ }
34
+
35
+ func pressKeyboardReturn(app: XCUIApplication) -> (wasVisible: Bool, pressed: Bool, visible: Bool) {
36
+ #if os(tvOS)
37
+ return (wasVisible: false, pressed: pressTvRemote(.select), visible: false)
38
+ #elseif os(iOS)
39
+ let wasVisible = isKeyboardVisible(app: app)
40
+ if tapKeyboardReturnControl(app: app) {
41
+ sleepFor(0.2)
42
+ return (wasVisible: wasVisible, pressed: true, visible: isKeyboardVisible(app: app))
43
+ }
44
+
45
+ var typed = false
46
+ let exceptionMessage = RunnerObjCExceptionCatcher.catchException({
47
+ app.typeText(XCUIKeyboardKey.return.rawValue)
48
+ typed = true
49
+ })
50
+ if let exceptionMessage {
51
+ NSLog(
52
+ "AGENT_DEVICE_RUNNER_KEYBOARD_RETURN_IGNORED_EXCEPTION=%@",
53
+ exceptionMessage
54
+ )
55
+ if let singleTarget = singleTextEntryElement(app: app) {
56
+ return pressKeyboardReturn(on: singleTarget, app: app, wasVisible: wasVisible)
57
+ }
58
+ return (wasVisible: wasVisible, pressed: false, visible: isKeyboardVisible(app: app))
59
+ }
60
+ sleepFor(0.2)
61
+ return (wasVisible: wasVisible, pressed: typed, visible: isKeyboardVisible(app: app))
62
+ #else
63
+ return (wasVisible: false, pressed: false, visible: false)
64
+ #endif
65
+ }
66
+
67
+ func visibleKeyboardFrame(app: XCUIApplication) -> CGRect? {
68
+ #if os(iOS)
69
+ return safely("KEYBOARD_FRAME") {
70
+ let keyboard = app.keyboards.firstMatch
71
+ guard keyboard.exists else { return nil }
72
+ let keyboardFrame = keyboard.frame
73
+ guard !keyboardFrame.isEmpty else { return nil }
74
+ return keyboardFrame
75
+ }
76
+ #else
77
+ return nil
78
+ #endif
79
+ }
80
+
81
+ private func pressKeyboardReturn(
82
+ on element: XCUIElement,
83
+ app: XCUIApplication,
84
+ wasVisible: Bool
85
+ ) -> (wasVisible: Bool, pressed: Bool, visible: Bool) {
86
+ #if os(iOS)
87
+ let exceptionMessage = RunnerObjCExceptionCatcher.catchException({
88
+ element.tap()
89
+ element.typeText(XCUIKeyboardKey.return.rawValue)
90
+ })
91
+ if let exceptionMessage {
92
+ NSLog(
93
+ "AGENT_DEVICE_RUNNER_KEYBOARD_RETURN_TARGET_IGNORED_EXCEPTION=%@",
94
+ exceptionMessage
95
+ )
96
+ return (wasVisible: wasVisible, pressed: false, visible: isKeyboardVisible(app: app))
97
+ }
98
+ sleepFor(0.2)
99
+ return (wasVisible: wasVisible, pressed: true, visible: isKeyboardVisible(app: app))
100
+ #else
101
+ return (wasVisible: wasVisible, pressed: false, visible: false)
102
+ #endif
103
+ }
104
+
105
+ private func singleTextEntryElement(app: XCUIApplication) -> XCUIElement? {
106
+ #if os(iOS)
107
+ let matches = safely("KEYBOARD_RETURN_TEXT_ENTRY_QUERY", []) {
108
+ app.descendants(matching: .any).allElementsBoundByIndex.filter { element in
109
+ guard element.exists else { return false }
110
+ switch element.elementType {
111
+ case .textField, .secureTextField, .searchField, .textView:
112
+ return true
113
+ default:
114
+ return false
115
+ }
116
+ }
117
+ }
118
+ return matches.count == 1 ? matches[0] : nil
119
+ #else
120
+ return nil
121
+ #endif
122
+ }
123
+
124
+ private func tapKeyboardDismissControl(app: XCUIApplication) -> Bool {
125
+ #if os(tvOS)
126
+ return false
127
+ #else
128
+ guard let keyboardFrame = visibleKeyboardFrame(app: app) else {
129
+ return false
130
+ }
131
+ for label in ["Hide keyboard", "Dismiss keyboard", "Done"] {
132
+ let candidates = [
133
+ app.keyboards.buttons[label],
134
+ app.keyboards.keys[label],
135
+ app.keyboards.toolbars.buttons[label],
136
+ ]
137
+ if let hittable = candidates.first(where: { $0.exists && $0.isHittable }) {
138
+ hittable.tap()
139
+ return true
140
+ }
141
+
142
+ let toolbarButtonPredicate = NSPredicate(
143
+ format: "label == %@ OR identifier == %@",
144
+ label,
145
+ label
146
+ )
147
+ let toolbarButtons = app.descendants(matching: .button)
148
+ .matching(toolbarButtonPredicate)
149
+ .allElementsBoundByIndex
150
+ if let hittable = toolbarButtons.first(where: {
151
+ $0.exists && $0.isHittable && isKeyboardAccessoryControl($0, keyboardFrame: keyboardFrame)
152
+ }) {
153
+ hittable.tap()
154
+ return true
155
+ }
156
+ }
157
+ return false
158
+ #endif
159
+ }
160
+
161
+ private func tapKeyboardReturnControl(app: XCUIApplication) -> Bool {
162
+ #if os(iOS)
163
+ for label in ["return", "Return", "Enter", "Go", "Search", "Next", "Done", "Send", "Join"] {
164
+ let candidates = [
165
+ app.keyboards.buttons[label],
166
+ app.keyboards.keys[label],
167
+ ]
168
+ if let hittable = candidates.first(where: { $0.exists && $0.isHittable }) {
169
+ hittable.tap()
170
+ return true
171
+ }
172
+ }
173
+ #endif
174
+ return false
175
+ }
176
+
177
+ private func isKeyboardAccessoryControl(_ element: XCUIElement, keyboardFrame: CGRect) -> Bool {
178
+ let frame = element.frame
179
+ guard !frame.isEmpty && !keyboardFrame.isEmpty else {
180
+ return false
181
+ }
182
+ return frame.intersects(keyboardFrame) || abs(frame.maxY - keyboardFrame.minY) <= 80
183
+ }
184
+ }
@@ -1,5 +1,6 @@
1
- import { i as createAndroidPortReverseManager, n as AndroidAdbExecutorOptions, r as AndroidPortReverseEndpoint, t as AndroidAdbExecutor } from "./sdk-android-adb.js";
1
+ import { a as createAndroidPortReverseManager, i as AndroidPortReverseEndpoint, n as AndroidAdbExecutorOptions, r as AndroidAdbProvider, t as AndroidAdbExecutor } from "./sdk-android-adb.js";
2
2
  import { t as AppsFilter } from "./sdk-app-inventory.js";
3
+ import { a as readAndroidClipboardWithAdb, i as getAndroidKeyboardStatusWithAdb, n as AndroidKeyboardState, o as writeAndroidClipboardWithAdb, r as dismissAndroidKeyboardWithAdb, t as AndroidKeyboardDismissResult } from "./device-input-state.js";
3
4
  import fs from "node:fs";
4
5
  //#region src/platforms/android/app-parsers.d.ts
5
6
  type AndroidForegroundApp = {
@@ -36,28 +37,4 @@ type AndroidLogcatCaptureOptions = {
36
37
  };
37
38
  declare function captureAndroidLogcatWithAdb(adb: AndroidAdbExecutor, options?: AndroidLogcatCaptureOptions): Promise<string>;
38
39
  //#endregion
39
- //#region src/contracts/android-input-ownership.d.ts
40
- type AndroidInputOwner = 'app' | 'ime' | 'unknown';
41
- //#endregion
42
- //#region src/platforms/android/device-input-state.d.ts
43
- type AndroidKeyboardType = 'text' | 'number' | 'email' | 'phone' | 'password' | 'datetime' | 'unknown';
44
- type AndroidKeyboardState = {
45
- visible: boolean;
46
- inputType?: string;
47
- type?: AndroidKeyboardType;
48
- inputMethodPackage?: string;
49
- focusedPackage?: string;
50
- focusedResourceId?: string;
51
- inputOwner: AndroidInputOwner;
52
- };
53
- type AndroidKeyboardDismissResult = AndroidKeyboardState & {
54
- attempts: number;
55
- wasVisible: boolean;
56
- dismissed: boolean;
57
- };
58
- declare function getAndroidKeyboardStatusWithAdb(adb: AndroidAdbExecutor): Promise<AndroidKeyboardState>;
59
- declare function dismissAndroidKeyboardWithAdb(adb: AndroidAdbExecutor): Promise<AndroidKeyboardDismissResult>;
60
- declare function readAndroidClipboardWithAdb(adb: AndroidAdbExecutor): Promise<string>;
61
- declare function writeAndroidClipboardWithAdb(adb: AndroidAdbExecutor, text: string): Promise<void>;
62
- //#endregion
63
- export { type AndroidAdbExecutor, type AndroidAdbExecutorOptions, type AndroidPortReverseEndpoint, captureAndroidLogcatWithAdb, createAndroidPortReverseManager, dismissAndroidKeyboardWithAdb, forceStopAndroidAppWithAdb, getAndroidAppStateWithAdb, getAndroidKeyboardStatusWithAdb, listAndroidAppsWithAdb, openAndroidAppWithAdb, readAndroidClipboardWithAdb, writeAndroidClipboardWithAdb };
40
+ export { type AndroidAdbExecutor, type AndroidAdbExecutorOptions, type AndroidAdbProvider, type AndroidKeyboardDismissResult, type AndroidKeyboardState, type AndroidPortReverseEndpoint, captureAndroidLogcatWithAdb, createAndroidPortReverseManager, dismissAndroidKeyboardWithAdb, forceStopAndroidAppWithAdb, getAndroidAppStateWithAdb, getAndroidKeyboardStatusWithAdb, listAndroidAppsWithAdb, openAndroidAppWithAdb, readAndroidClipboardWithAdb, writeAndroidClipboardWithAdb };
@@ -1 +1 @@
1
- import{_ as e}from"./sdk-contracts.js";import{i 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{_ as e}from"./sdk-contracts.js";import{i as t}from"./sdk-android-adb.js";import{o as n,u as r}from"./app-lifecycle.js";import{n as i,r as a}from"./app-helpers.js";import{t as o}from"./logcat.js";import{c as s,i as c,n as l,o as u}from"./device-input-state.js";const d=`android.intent.category.LAUNCHER`,f=`android.intent.category.DEFAULT`;async function p(e,t){await e([`shell`,`am`,`force-stop`,t])}async function m(e,t,n=[d]){for(let i of n){let n=await e([`shell`,`cmd`,`package`,`resolve-activity`,`--brief`,`-a`,`android.intent.action.MAIN`,`-c`,i,t],{allowFailure:!0});if(n.exitCode!==0)continue;let a=r(n.stdout);if(a)return a}return null}async function h(t,r,i={}){let a=i.category??d;if(i.activity){await t([`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.MAIN`,`-c`,f,`-c`,a,`-n`,g(r,i.activity)]);return}let o=await t([`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.MAIN`,`-c`,f,`-c`,a,`-p`,r],{allowFailure:!0});if(o.exitCode===0&&!n(o.stdout,o.stderr))return;let s=await m(t,r,[a]);if(!s)throw new e(`COMMAND_FAILED`,`Failed to resolve Android launch component for ${r}`,{stdout:o.stdout,stderr:o.stderr,exitCode:o.exitCode});await t([`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.MAIN`,`-c`,f,`-c`,a,`-n`,s])}function g(e,t){return t.includes(`/`)?t:`${e}/${t.startsWith(`.`)?t:`.${t}`}`}export{o as captureAndroidLogcatWithAdb,t as createAndroidPortReverseManager,l as dismissAndroidKeyboardWithAdb,p as forceStopAndroidAppWithAdb,i as getAndroidAppStateWithAdb,c as getAndroidKeyboardStatusWithAdb,a as listAndroidAppsWithAdb,h as openAndroidAppWithAdb,u as readAndroidClipboardWithAdb,s as writeAndroidClipboardWithAdb};
@@ -0,0 +1 @@
1
+ import{t as e}from"./rolldown-runtime.js";import{n as t}from"./sdk-android-adb.js";import{r as n}from"./sdk-app-inventory.js";import{a as r,g as i,h as a,m as o}from"./app-lifecycle.js";var s=e({getAndroidAppStateWithAdb:()=>d,listAndroidAppsWithAdb:()=>u});const c=`android.intent.category.LAUNCHER`,l=`android.intent.category.LEANBACK_LAUNCHER`;async function u(e,t={}){let i=await f(e,t.target??`auto`);return(n(t.filter)===`user-installed`?(await g(e)).filter(e=>i.has(e)):Array.from(i)).map(e=>({package:e,name:r(e)})).sort((e,t)=>e.package.localeCompare(t.package))}async function d(e){return await _(e,[[`shell`,`dumpsys`,`window`,`windows`],[`shell`,`dumpsys`,`window`]])||await _(e,[[`shell`,`dumpsys`,`activity`,`activities`],[`shell`,`dumpsys`,`activity`]])||{}}async function f(e,t){let n=(await Promise.all(p(t).map(async t=>{let n=await e(m(t),{allowFailure:!0});return n.exitCode===0?h(n.stdout):[]}))).flat();return new Set(n)}function p(e){switch(e){case`mobile`:return[c];case`tv`:return[l];default:return[c,l]}}function m(e){return[`shell`,`cmd`,`package`,`query-activities`,`--brief`,`-a`,`android.intent.action.MAIN`,`-c`,e]}function h(e){return e.trim().length===0?[]:a(e)}async function g(e){let n=await e([`shell`,`pm`,`list`,`packages`,`-3`],{allowFailure:!0});if(n.exitCode!==0)throw t(`Failed to list Android user-installed apps`,n);return i(n.stdout)}async function _(e,t){for(let n of t){let t=o((await e(n,{allowFailure:!0})).stdout??``);if(t)return t}return null}export{d as n,u 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{_ as t}from"./sdk-contracts.js";import{h as n,j as r,p as i}from"./sdk-android-adb.js";import{ft as a}from"./recording-export-quality.js";import{c as o,s}from"./sdk-device.js";import{S as c}from"./gesture-plan-types.js";import{a as l}from"./runtime.js";import{d as u,l as d,u as ee}from"./install-artifact.js";import{l as f,o as te,s as p}from"./boot-diagnostics.js";import{a as ne,i as re,n as ie,r as ae,t as m}from"./setting-state.js";import{n as oe,t as se}from"./launch-console.js";import{a as h,i as g}from"./tool-provider.js";import{a as ce}from"./helper.js";import{L as le,R as _,_ as ue,a as v,d as y,g as de,h as fe,i as pe,l as me,m as he,n as b,o as x,r as S,t as C,v as ge,y as _e}from"./physical-device-control.js";import{a as w}from"./runner-client.js";import{St as ve,xt as ye}from"./internal/daemon.js";import{i as T,n as be,t as E}from"./simulator.js";import{a as D,c as xe,d as Se,i as Ce,l as we,n as Te,o as Ee,r as O,s as De,u as Oe}from"./app-resolution.js";import{n as ke}from"./install-artifact2.js";import{promises as k}from"node:fs";import Ae from"node:os";import A from"node:path";const je=[`--time`,`9:41`,`--dataNetwork`,`wifi`,`--wifiMode`,`active`,`--wifiBars`,`3`,`--batteryState`,`charged`,`--batteryLevel`,`100`],Me={0:`hide`,1:`wifi`,6:`3g`,7:`4g`,8:`lte`,9:`lte-a`,10:`lte+`,11:`5g`,12:`5g+`,13:`5g-uwb`,14:`5g-uc`},Ne={1:`searching`,2:`failed`,3:`active`},Pe={0:`notSupported`,1:`searching`,2:`failed`,3:`active`};function j(e,t,n){return _(e,t,n)}const M=new Set;async function Fe(e){let t=null,n=!1;if(Re(e))n=!0;else try{t=await ze(e),n=!0}catch(t){L(e,`snapshot_failed`,t)}try{(!n||t)&&await F(e),await I(e,je)}catch(t){L(e,`prepare_failed`,t)}return async()=>{await Ie(e,n?t:null)}}async function Ie(e,t){if(await F(e),!t){Le(e);return}await I(e,Ve(t)),N(e)}function N(e){M.delete(P(e))}function Le(e){M.add(P(e))}function Re(e){return M.has(P(e))}function P(e){return`${e.platform}:${e.kind}:${e.id}`}async function ze(e){return Be(n(await j(e,[`status_bar`,e.id,`list`],{allowFailure:!0}),`Failed to read simulator status bar overrides`).stdout)}async function F(e){await j(e,[`status_bar`,e.id,`clear`])}async function I(e,t){t.length!==0&&await j(e,[`status_bar`,e.id,`override`,...t])}function Be(e){let n={},r=e.split(`
2
- `).map(e=>e.trim()).filter(e=>e.length>0&&e!==`Current Status Bar Overrides:`&&!/^=+$/.test(e));for(let e of r){let r=/^DataNetworkType:\s+(\d+)$/.exec(e);if(r){let e=Number(r[1]),i=Me[e];if(!i)throw new t(`COMMAND_FAILED`,`Unsupported simulator data network type: ${e}`);n.dataNetwork=i;continue}let i=/^WiFi Mode:\s+(\d+),\s+WiFi Bars:\s+(\d+)$/.exec(e);if(i){let e=Ne[Number(i[1])];e&&(n.wifiMode=e),n.wifiBars=i[2];continue}let a=/^Cell Mode:\s+(\d+),\s+Cell Bars:\s+(\d+)$/.exec(e);if(a){let e=Number(a[1]),r=Pe[e];if(!r)throw new t(`COMMAND_FAILED`,`Unsupported simulator cellular mode: ${e}`);n.cellularMode=r,n.cellularBars=a[2];continue}let o=/^Operator Name:\s*(.*)$/.exec(e);if(o){n.operatorName=o[1]??``;continue}}return Object.keys(n).length===0?null:n}function Ve(e){let t=[];return e.dataNetwork&&t.push(`--dataNetwork`,e.dataNetwork),e.wifiMode&&t.push(`--wifiMode`,e.wifiMode),e.wifiBars!==void 0&&(e.dataNetwork===`wifi`||e.wifiMode)&&t.push(`--wifiBars`,e.wifiBars),e.cellularMode&&t.push(`--cellularMode`,e.cellularMode),e.cellularBars!==void 0&&(e.cellularMode||He(e.dataNetwork)||e.operatorName!==void 0)&&t.push(`--cellularBars`,e.cellularBars),e.operatorName!==void 0&&t.push(`--operatorName`,e.operatorName),t}function He(e){return!!(e&&e!==`hide`&&e!==`wifi`)}function L(e,t,n){r({level:`warn`,phase:`ios_screenshot_status_bar_${t}`,data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,...S(n)}})}function R(e,t,n){return _(e,t,n)}const Ue={ensureBooted:E,prepareStatusBarForScreenshot:Fe,captureWithRetry:Ke,normalizeDensity:Qe,captureWithRunner:V,shouldFallbackToRunner:W},z=new Map,B=new Map;async function We(e,t,n={}){if(o(e)){await V(e,t,n.appBundleId,n.fullscreen,n.runnerOptions);return}if(e.kind===`simulator`){await Ge(e,t,n);return}await C(e).captureScreenshot(e,t,{appBundleId:n.appBundleId,fullscreen:n.fullscreen,runnerOptions:n.runnerOptions,runRunnerCommand:w})}async function Ge(e,n,r={}){if(e.kind!==`simulator`)throw new t(`UNSUPPORTED_OPERATION`,`Simulator screenshot fallback flow supports only iOS simulators`);let i={...Ue,...r.deps??{}};r.skipIosSimulatorBootCheck||await i.ensureBooted(e);let a=async()=>{};if(r.normalizeStatusBar===!0)try{a=await i.prepareStatusBarForScreenshot(e)}catch(t){U(e,`prepare_failed`,t)}try{try{await i.captureWithRetry(e,n),await i.normalizeDensity(e,n,r.pixelDensity);return}catch(t){let a=t;if(r.skipIosSimulatorBootCheck&&Ze(t)){await i.ensureBooted(e);try{await i.captureWithRetry(e,n),await i.normalizeDensity(e,n,r.pixelDensity);return}catch(e){a=e}}if(!i.shouldFallbackToRunner(a))throw a;Ye(e,`simctl_screenshot`,a)}await i.captureWithRunner(e,n,r.appBundleId,r.fullscreen,r.runnerOptions),await i.normalizeDensity(e,n,r.pixelDensity)}finally{await a().catch(t=>U(e,`restore_failed`,t))}}async function Ke(e,t){let n=p.fromTimeoutMs(ue);await f(async({deadline:n})=>{await R(e,[`io`,e.id,`screenshot`,t],{timeoutMs:Math.max(1e3,n?.remainingMs()??2e4)})},{maxAttempts:5,baseDelayMs:he,maxDelayMs:fe,jitter:.2,shouldRetry:e=>W(e)},{deadline:n,phase:`ios_simulator_screenshot`})}async function V(e,n,r,i,a){if(e.kind===`device`&&!o(e)){await C(e).captureScreenshot(e,n,{appBundleId:r,fullscreen:i,runnerOptions:a,preferRunner:!0,runRunnerCommand:w});return}let s=(await w(e,{command:`screenshot`,appBundleId:r,fullscreen:i,inlineScreenshot:!1},a)).message;if(!s)throw new t(`COMMAND_FAILED`,`Failed to capture iOS screenshot: runner returned no file path`);if(o(e)){await k.copyFile(s,n);return}if(e.kind===`simulator`){await qe(e,s,n);return}throw new t(`COMMAND_FAILED`,`Unsupported Apple screenshot target`)}async function qe(e,n,r){let i=p.fromTimeoutMs(y),a=`Unable to locate runner container for simulator screenshot`,o=B.get(e.id);if(o){let t=await H(o,n,r);if(t.copied)return;a=t.error,B.delete(e.id)}for(let t of _e){let o=await R(e,[`get_app_container`,e.id,t,`data`],{allowFailure:!0,timeoutMs:Je(i,y,`runner screenshot container lookup`)});if(o.exitCode!==0){let e=o.stderr.trim();e&&(a=e);continue}let s=o.stdout.trim();if(!s){a=`simctl get_app_container returned empty output`;continue}let c=await H(s,n,r);if(c.copied){B.set(e.id,s);return}a=c.error}throw new t(`COMMAND_FAILED`,`Failed to capture iOS screenshot: ${a}`)}async function H(e,t,n){let r=`Runner screenshot was not found in the simulator container`;for(let i of Xe(e,t))try{return await k.copyFile(i,n),{copied:!0}}catch(e){r=e instanceof Error?e.message:String(e)}return{copied:!1,error:r}}function Je(e,n,r){let i=e.remainingMs();if(i>0)return i;throw new t(`COMMAND_FAILED`,`iOS ${r} timed out after ${n}ms`,{timeoutMs:n,step:r})}function Ye(e,t,n){let i=S(n);r({level:`warn`,phase:`ios_screenshot_fallback`,data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,from:t,to:`runner`,...i}})}function U(e,t,n){r({level:`warn`,phase:`ios_screenshot_status_bar_${t}`,data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,...S(n)}})}function Xe(e,t){let n=A.resolve(e),r=t.trim();if(!r)return[];let i=[],a=new Set,o=e=>{let t=A.normalize(e);a.has(t)||(a.add(t),i.push(t))},s=r.replace(/^\/+/,``),c=s.replace(/\\/g,`/`);if(s&&o(A.join(n,s)),A.isAbsolute(r)&&o(A.normalize(r)),c.startsWith(`tmp/`))o(A.join(n,c));else{let e=c.lastIndexOf(`/tmp/`);if(e>=0){let t=c.slice(e+1);o(A.join(n,t))}}let l=A.basename(r);return l&&o(A.join(n,`tmp`,l)),i}function W(e){if(!(e instanceof t)||e.code!==`COMMAND_FAILED`)return!1;let n=b(e);return n.includes(`timeout waiting for screen surfaces`)||n.includes(`nsposixerrordomain`)&&n.includes(`code=60`)&&n.includes(`screenshot`)||n.includes(`timed out`)&&n.includes(`screenshot`)}function Ze(e){if(!(e instanceof t)||e.code!==`COMMAND_FAILED`)return!1;let n=b(e);return n.includes(`not booted`)||n.includes(`current state: shutdown`)||n.includes(`current state is shutdown`)||n.includes(`current state=shutdown`)||n.includes(`state: shutdown`)||n.includes(`state=shutdown`)}async function Qe(e,t,n){let r=await $e(e),i=ye(await ve(t),r,n);i&&await l(t,i.width,i.height)}async function $e(e){let n=z.get(e.id);if(n!==void 0)return n;let r=await R(e,[`getenv`,e.id,`SIMULATOR_MAINSCREEN_SCALE`],{timeoutMs:de}),i=Number(r.stdout.trim());if(!Number.isFinite(i)||i<=0)throw new t(`COMMAND_FAILED`,`Failed to read iOS simulator screenshot scale from SIMULATOR_MAINSCREEN_SCALE`);return z.set(e.id,i),i}function G(e){if(!(e instanceof t)||e.code!==`COMMAND_FAILED`)return!1;let n=e.details??{};if(n.exitCode!==4)return!1;let r=String(n.stderr??``).toLowerCase();return r.includes(`fbsopenapplicationserviceerrordomain`)&&r.includes(`the request to open`)}async function et(e,t){let n=await h(le(e,[`get_app_container`,e.id,t]),{allowFailure:!0});if(n.exitCode!==0)return{installed:!1};let r=n.stdout.trim();if(!r)return{installed:!1};let i=await g(`plutil`,[`-extract`,`CFBundleExecutable`,`raw`,`-o`,`-`,`${r}/Info.plist`],{allowFailure:!0});if(i.exitCode!==0||!i.stdout.trim())return{installed:!0};let a=await g(`otool`,[`-l`,`${r}/${i.stdout.trim()}`],{allowFailure:!0});if(a.exitCode!==0)return{installed:!0};let o=a.stdout.toLowerCase();return{installed:!0,simulatorCompatible:o.includes(`iossimulator`)||o.includes(`platform 7`)}}function tt(e){return e.installed?e.simulatorCompatible===!1?`ARCH_MISMATCH`:`PERSISTENT_LAUNCH_FAIL`:`APP_NOT_INSTALLED`}function nt(e){switch(e){case`ARCH_MISMATCH`:return`The app binary was not built for the simulator platform. Rebuild with a simulator destination or use a physical device.`;case`APP_NOT_INSTALLED`:return`The app bundle is not installed on this simulator. Run install before open.`;case`PERSISTENT_LAUNCH_FAIL`:return`The simulator repeatedly refused to launch the app. Inspect crash logs in Console.app or ~/Library/Logs/DiagnosticReports/ and consider reinstalling the app.`;default:return`The simulator failed to launch the app. Retry with --debug and inspect diagnostics log for details.`}}const K=25e3;async function rt(e,n,r){let i=r?.launchConsole?.trim(),a=r?.launchArgs;if(i&&(!s(e)||e.kind!==`simulator`))throw new t(`UNSUPPORTED_OPERATION`,oe);if(o(e)){if(a&&a.length>0)throw new t(`UNSUPPORTED_OPERATION`,`--launch-args is not supported on macOS; launch arguments are currently iOS-only.`);await xe(e,n,r);return}let c=r?.url?.trim();if(c){if(i)throw new t(`INVALID_ARGS`,se);if(!d(c))throw new t(`INVALID_ARGS`,`open <app> <url> requires a valid URL target`);if(e.kind===`simulator`){(a||ee(c))&&await Y(e,r?.appBundleId??await D(e,n),{...a?{launchArgs:a}:{}}),await q(e,c,void 0);return}let o=u(r?.appBundleId??await D(e,n),c);if(!o)throw new t(`INVALID_ARGS`,`Deep link open on iOS devices requires an active app bundle ID. Open the app first, then open the URL.`);await Z(e,o,{payloadUrl:c,launchArgs:a,runnerOptions:r?.runnerOptions});return}let l=n.trim();if(d(l)){if(i)throw new t(`INVALID_ARGS`,se);if(e.kind===`simulator`){await q(e,l,a);return}let n=u(r?.appBundleId,l);if(!n)throw new t(`INVALID_ARGS`,`Deep link open on iOS devices requires an active app bundle ID. Open the app first, then open the URL.`);await Z(e,n,{payloadUrl:l,launchArgs:a,runnerOptions:r?.runnerOptions});return}let f=r?.appBundleId??await D(e,n);if(e.kind===`simulator`){await Y(e,f,{...i?{launchConsole:i}:{},...a?{launchArgs:a}:{},...r?.terminateRunningApp?{terminateRunningApp:!0}:{}});return}await Z(e,f,{launchArgs:a,runnerOptions:r?.runnerOptions})}async function q(e,n,r){if(r&&r.length>0)throw new t(`INVALID_ARGS`,`--launch-args is not supported with iOS simulator URL opens (simctl openurl ignores launch args). Launch the app first with --launch-args, then issue the URL open in a separate call.`);await E(e),await v(e,[`openurl`,e.id,n])}async function it(e){o(e)||e.kind===`simulator`&&await be(e)!==`Booted`&&await E(e)}async function J(e,n,r){if(o(e)){await De(e,n);return}let a=await D(e,n);if(e.kind===`simulator`){await E(e);let n=x(e,[`terminate`,e.id,a]),r=await h(n,{allowFailure:!0,timeoutMs:ge});if(r.exitCode!==0){if(r.stderr.toLowerCase().includes(`found nothing to terminate`))return;throw new t(`COMMAND_FAILED`,`xcrun exited with code ${r.exitCode}`,i(r,{cmd:`xcrun`,args:n}))}return}await C(e).terminateApp(e,a,{runnerOptions:r,runRunnerCommand:w})}async function Y(e,n,r){await E(e);let a=0,o=p.fromTimeoutMs(me);try{await f(async({deadline:a})=>{if(a?.isExpired())throw new t(`COMMAND_FAILED`,`App launch deadline exceeded`,{timeoutMs:me});let o=x(e,at(e.id,n,r)),s=r?.launchConsole?await ot(o,r.launchConsole):await h(o,{allowFailure:!0});if(s.exitCode!==0)throw new t(`COMMAND_FAILED`,`xcrun exited with code ${s.exitCode}`,i(s,{cmd:`xcrun`,args:o}))},{maxAttempts:10,baseDelayMs:1e3,maxDelayMs:5e3,jitter:.2,shouldRetry(e){return G(e)?(a+=1,a<3):!1}},{deadline:o})}catch(t){if(G(t)){let r=t,i=tt(await et(e,n));r.details={...r.details,hint:nt(i)}}throw t}}function at(e,t,n){let r=[`launch`];return n?.launchConsole&&r.push(`--console-pty`),n?.terminateRunningApp&&r.push(`--terminate-running-process`),r.push(e,t),n?.launchArgs&&n.launchArgs.length>0&&r.push(...n.launchArgs),r}async function ot(e,n){await k.mkdir(A.dirname(n),{recursive:!0});try{let t=await h(e,{allowFailure:!0,timeoutMs:K});return await X(n,t.stdout,t.stderr),t}catch(e){let i=(e instanceof t?e:void 0)?.details;if(i?.timeoutMs===K){let e=typeof i.stdout==`string`?i.stdout:``,t=typeof i.stderr==`string`?i.stderr:``;return await X(n,e,t),r({level:`warn`,phase:`ios_simulator_launch_console_capture_timeout`,data:{timeoutMs:K,logPath:n,stdoutBytes:Buffer.byteLength(e),stderrBytes:Buffer.byteLength(t)}}),{stdout:e,stderr:t,exitCode:0}}throw e}}async function X(e,t,n){await k.writeFile(e,st(t,n),`utf8`)}function st(e,t){return!e||!t||e.endsWith(`
3
- `)||e.endsWith(`\r`)?`${e}${t}`:`${e}\n${t}`}async function Z(e,t,n){await C(e).launchApp(e,t,{...n,runRunnerCommand:w})}async function ct(e,n){return await O(e,async()=>{let r=await D(e,n);if(e.kind!==`simulator`)return await C(e).uninstallApp(e,r),{bundleId:r};await E(e);let a=await v(e,[`uninstall`,e.id,r],{allowFailure:!0});if(a.exitCode!==0&&!pe(`${a.stdout}\n${a.stderr}`.toLowerCase()))throw new t(`COMMAND_FAILED`,`simctl uninstall failed for ${r}`,i(a));return{bundleId:r}})}async function lt(e,t,n){e.kind!==`simulator`&&C(e).assertAppInstallationSupported(e);let r=await ke({kind:`path`,path:t},n);try{return await Q(e,r.installablePath),{archivePath:r.archivePath,installablePath:r.installablePath,bundleId:r.bundleId,appName:r.appName,launchTarget:r.bundleId}}finally{await r.cleanup()}}async function ut(e,t,n){return e.kind!==`simulator`&&C(e).assertAppInstallationSupported(e),await O(e,async()=>{let{bundleId:r}=await ct(e,t);return await lt(e,n,{appIdentifierHint:t}),{bundleId:r}})}async function Q(e,t){await O(e,async()=>{if(e.kind!==`simulator`){await C(e).installApp(e,t);return}await E(e),await v(e,[`install`,e.id,t])})}async function dt(e){return o(e)?await we():(T(e,`clipboard`),await E(e),n(await v(e,[`pbpaste`,e.id],{allowFailure:!0}),`Failed to read iOS simulator clipboard`).stdout.replace(/\r\n/g,`
4
- `).replace(/\n$/,``))}async function ft(e,t){if(o(e)){await Se(t);return}T(e,`clipboard`),await E(e),n(await v(e,[`pbcopy`,e.id],{allowFailure:!0,stdin:t}),`Failed to write iOS simulator clipboard`)}async function pt(e,t,n){T(e,`push`),await E(e);let r=await k.mkdtemp(A.join(Ae.tmpdir(),`agent-device-ios-push-`)),i=A.join(r,`payload.apns`);try{await k.writeFile(i,`${JSON.stringify(n)}\n`,`utf8`),await v(e,[`push`,e.id,t,i])}finally{await k.rm(r,{recursive:!0,force:!0})}}let $=null,mt;async function ht(e,n,r,i,s){if(o(e)){let e=n.toLowerCase();if(e===`appearance`){await Oe(r);return}if(e===`permission`){let e=ae(r);if(e===`deny`)throw new t(`INVALID_ARGS`,a(`permission`));return await ce(e,_t(s?.permissionTarget))}throw new t(`INVALID_ARGS`,a(n))}T(e,`settings`),await E(e);let l=n.toLowerCase();switch(l){case`clear-app-state`:{if(r.toLowerCase()!==`clear`)throw new t(`INVALID_ARGS`,`settings clear-app-state only supports clear.`);if(!i)throw new t(`INVALID_ARGS`,`settings clear-app-state requires an app id or an active app session.`);let n=await gt(e,i);return{bundleId:n.bundleId,containerPath:n.containerPath,cleared:!0}}case`wifi`:{let t=m(r)?`active`:`failed`;await v(e,[`status_bar`,e.id,`override`,`--wifiMode`,t]),N(e);return}case`airplane`:m(r)?(await v(e,[`status_bar`,e.id,`override`,`--dataNetwork`,`hide`,`--wifiMode`,`failed`,`--wifiBars`,`0`,`--cellularMode`,`failed`,`--cellularBars`,`0`,`--operatorName`,``]),N(e)):(await v(e,[`status_bar`,e.id,`clear`]),Le(e));return;case`location`:{if(r.toLowerCase()===`set`){let{latitude:t,longitude:n}=c(s);return await v(e,[`location`,e.id,`set`,`${t},${n}`]),{latitude:t,longitude:n}}let n=m(r);if(!i)throw new t(`INVALID_ARGS`,`location setting requires an active app in session`);let a=n?`grant`:`revoke`;await v(e,[`privacy`,e.id,a,`location`,i]);return}case`faceid`:case`touchid`:{let t=l,n=bt[t];await Ot(e,Dt(r,t),{settingName:t,label:n.label,modalityAliases:n.modalityAliases});return}case`appearance`:{let t=await vt(e,r);await v(e,[`ui`,e.id,`appearance`,t]);return}case`permission`:if(!i)throw new t(`INVALID_ARGS`,`permission setting requires an active app in session`);await St(e,xt(ae(r)),Et(s?.permissionTarget,s?.permissionMode),i);return;default:throw new t(`INVALID_ARGS`,`Unsupported setting: ${n}`)}}async function gt(e,r){if(!s(e)||e.kind!==`simulator`)throw new t(`UNSUPPORTED_OPERATION`,`Clearing app state is currently supported only on iOS simulators.`);let i=await D(e,r);await E(e),await J(e,i);let a=n(await v(e,[`get_app_container`,e.id,i,`data`],{allowFailure:!0}),`simctl get_app_container failed for ${i}`).stdout.trim();if(!a)throw new t(`COMMAND_FAILED`,`simctl get_app_container returned an empty data container path for ${i}`);let o=await k.readdir(a);return await Promise.all(o.map(e=>k.rm(A.join(a,e),{recursive:!0,force:!0}))),{bundleId:i,containerPath:a}}function _t(e){let n=e?.trim().toLowerCase();if(n===`accessibility`||n===`screen-recording`||n===`input-monitoring`)return n;throw new t(`INVALID_ARGS`,`Unsupported macOS permission target. Use accessibility|screen-recording|input-monitoring.`)}async function vt(e,r){let i=ie(r);if(i!==`toggle`)return i;let a=n(await v(e,[`ui`,e.id,`appearance`],{allowFailure:!0}),`Failed to read current iOS appearance`),o=yt(a.stdout,a.stderr);if(!o)throw new t(`COMMAND_FAILED`,`Unable to determine current iOS appearance for toggle`,{stdout:a.stdout,stderr:a.stderr});return o===`dark`?`light`:`dark`}function yt(e,t){let n=/\b(light|dark|unsupported|unknown)\b/i.exec(`${e}\n${t}`);if(!n)return null;let r=n[1]?.toLowerCase();return r===`dark`?`dark`:r===`light`?`light`:null}const bt={faceid:{label:`Face ID`,modalityAliases:[`face`]},touchid:{label:`Touch ID`,modalityAliases:[`finger`,`touch`]}};function xt(e){return e===`deny`?`revoke`:e}async function St(e,n,r,i){let a=await wt(e);if(!a.has(r))throw new t(`UNSUPPORTED_OPERATION`,`iOS simctl privacy does not support service "${r}" on this runtime.`,{deviceId:e.id,appBundleId:i,hint:`Supported services: ${Array.from(a).sort().join(`, `)}`});let o=[`privacy`,e.id,n,r,i],s=r===`notifications`;if(!(n===`reset`&&s))try{await v(e,o);return}catch(n){throw s&&Ct(n)?new t(`UNSUPPORTED_OPERATION`,`iOS simulator does not support setting notifications permission via simctl privacy on this runtime.`,{deviceId:e.id,appBundleId:i,hint:`Use reset notifications for reprompt behavior, or toggle notifications manually in Settings.`}):n}try{await v(e,o);return}catch(e){if(!Ct(e))throw e}try{await v(e,[`privacy`,e.id,`reset`,`all`,i])}catch(n){throw new t(`COMMAND_FAILED`,`iOS simulator blocked direct notifications reset. Fallback reset-all also failed.`,{deviceId:e.id,appBundleId:i,hint:`Use reinstall to force a fresh notifications prompt, or reset simulator content and settings.`},n instanceof Error?n:void 0)}}function Ct(e){if(!(e instanceof t)||e.code!==`COMMAND_FAILED`)return!1;let n=String(e.details?.stderr??``).toLowerCase();return(n.includes(`failed to grant access`)||n.includes(`failed to revoke access`)||n.includes(`failed to reset access`))&&n.includes(`operation not permitted`)}async function wt(e){let n=te(e.simulatorSetPath),r=`${process.env.PATH??``}::${n??``}`;if($&&mt===r)return $;let i=await v(e,[`privacy`,`help`],{allowFailure:!0}),a=Tt(`${i.stdout}\n${i.stderr}`);if(a.size===0)throw new t(`COMMAND_FAILED`,`Unable to determine supported simctl privacy services`,{stdout:i.stdout,stderr:i.stderr,exitCode:i.exitCode,hint:"Run `xcrun simctl privacy help` manually to verify available services for this runtime."});return $=a,mt=r,a}function Tt(e){let t=new Set,n=!1;for(let r of e.split(`
5
- `)){let e=r.trim();if(!e)continue;if(e===`service`){n=!0;continue}if(!n)continue;if(e.startsWith(`bundle identifier`))break;let i=/^([a-z-]+)\s+-\s+/.exec(e)?.[1];i!==void 0&&t.add(i)}return t}function Et(e,n){let r=re(e);if(r!==`photos`&&n?.trim())throw new t(`INVALID_ARGS`,`Permission mode is only supported for photos. Received: ${n}.`);if(r===`camera`)return`camera`;if(r===`microphone`)return`microphone`;if(r===`contacts`)return`contacts`;if(r===`contacts-limited`)return`contacts-limited`;if(r===`notifications`)return`notifications`;if(r===`calendar`)return`calendar`;if(r===`location`)return`location`;if(r===`location-always`)return`location-always`;if(r===`media-library`)return`media-library`;if(r===`motion`)return`motion`;if(r===`reminders`)return`reminders`;if(r===`siri`)return`siri`;if(r===`photos`){let e=n?.trim().toLowerCase();if(!e||e===`full`)return`photos`;if(e===`limited`)return`photos-add`;throw new t(`INVALID_ARGS`,`Invalid photos mode: ${n}. Use full|limited.`)}throw new t(`INVALID_ARGS`,`Unsupported permission target: ${e}. Use camera|microphone|photos|contacts|contacts-limited|notifications|calendar|location|location-always|media-library|motion|reminders|siri.`)}function Dt(e,n){let r=e.trim().toLowerCase();if(r===`match`)return`match`;if(r===`nonmatch`)return`nonmatch`;if(r===`enroll`)return`enroll`;if(r===`unenroll`)return`unenroll`;throw new t(`INVALID_ARGS`,`Invalid ${n} state: ${e}. Use match|nonmatch|enroll|unenroll.`)}async function Ot(e,n,r){let i=kt(e.id,n,r.modalityAliases),a=[];for(let t of i){let n=x(e,t),r=await h(n,{allowFailure:!0});if(r.exitCode===0)return;a.push({args:n,stderr:r.stderr,stdout:r.stdout,exitCode:r.exitCode})}let o=ne(a);throw a.length>0&&a.every(e=>At(e.stdout,e.stderr))?new t(`UNSUPPORTED_OPERATION`,`${r.label} simulation is not supported on this simulator runtime.`,{deviceId:e.id,action:n,setting:r.settingName,attempts:o}):new t(`COMMAND_FAILED`,`Failed to simulate ${r.settingName}.`,{deviceId:e.id,action:n,setting:r.settingName,attempts:o})}function kt(e,t,n){let r=n.length>0?n:[`face`];switch(t){case`match`:return r.flatMap(t=>[[`biometric`,e,`match`,t],[`biometric`,`match`,e,t]]);case`nonmatch`:return r.flatMap(t=>[[`biometric`,e,`nonmatch`,t],[`biometric`,e,`nomatch`,t],[`biometric`,`nonmatch`,e,t],[`biometric`,`nomatch`,e,t]]);case`enroll`:return[[`biometric`,e,`enroll`,`yes`],[`biometric`,e,`enroll`,`1`],[`biometric`,`enroll`,e,`yes`],[`biometric`,`enroll`,e,`1`]];case`unenroll`:return[[`biometric`,e,`enroll`,`no`],[`biometric`,e,`enroll`,`0`],[`biometric`,`enroll`,e,`no`],[`biometric`,`enroll`,e,`0`]]}}function At(e,t){let n=`${e}\n${t}`.toLowerCase();return n.includes(`unrecognized subcommand`)||n.includes(`unknown subcommand`)||n.includes(`not supported`)||n.includes(`unavailable`)||n.includes(`biometric`)&&n.includes(`invalid`)}var jt=e({findIosSimulatorInstalledApp:()=>Te,installIosApp:()=>lt,installIosInstallablePath:()=>Q,listIosApps:()=>Ce,pushIosNotification:()=>pt,reinstallIosApp:()=>ut,resolveIosApp:()=>D,resolveIosSimulatorDeepLinkBundleId:()=>Ee});export{J as a,V as c,ft as i,We as l,ht as n,rt as o,dt as r,it as s,jt as t};
1
+ import{t as e}from"./rolldown-runtime.js";import{_ as t}from"./sdk-contracts.js";import{h as n,j as r,p as i}from"./sdk-android-adb.js";import{ft as a}from"./recording-export-quality.js";import{c as o,s}from"./sdk-device.js";import{S as c}from"./gesture-plan-types.js";import{a as l}from"./runtime.js";import{d as u,l as d,u as ee}from"./install-artifact.js";import{l as f,o as te,s as p}from"./boot-diagnostics.js";import{a as ne,i as re,n as ie,r as m,t as h}from"./setting-state.js";import{n as ae,t as oe}from"./launch-console.js";import{a as g,i as _}from"./tool-provider.js";import{a as se}from"./helper.js";import{L as ce,R as v,_ as le,a as y,d as ue,g as de,h as fe,i as pe,l as me,m as he,n as b,o as x,r as S,t as C,v as ge,y as _e}from"./physical-device-control.js";import{a as w}from"./runner-client.js";import{St as ve,xt as ye}from"./internal/daemon.js";import{i as T,n as be,t as E}from"./simulator.js";import{a as D,c as xe,d as Se,i as Ce,l as we,n as Te,o as Ee,r as O,s as De,u as Oe}from"./app-resolution.js";import{n as ke}from"./install-artifact2.js";import{promises as k}from"node:fs";import Ae from"node:os";import A from"node:path";const je=[`--time`,`9:41`,`--dataNetwork`,`wifi`,`--wifiMode`,`active`,`--wifiBars`,`3`,`--batteryState`,`charged`,`--batteryLevel`,`100`],Me={0:`hide`,1:`wifi`,6:`3g`,7:`4g`,8:`lte`,9:`lte-a`,10:`lte+`,11:`5g`,12:`5g+`,13:`5g-uwb`,14:`5g-uc`},Ne={1:`searching`,2:`failed`,3:`active`},Pe={0:`notSupported`,1:`searching`,2:`failed`,3:`active`};function j(e,t,n){return v(e,t,n)}const M=new Set;async function Fe(e){let t=null,n=!1;if(Re(e))n=!0;else try{t=await ze(e),n=!0}catch(t){L(e,`snapshot_failed`,t)}try{(!n||t)&&await F(e),await I(e,je)}catch(t){L(e,`prepare_failed`,t)}return async()=>{await Ie(e,n?t:null)}}async function Ie(e,t){if(await F(e),!t){Le(e);return}await I(e,Ve(t)),N(e)}function N(e){M.delete(P(e))}function Le(e){M.add(P(e))}function Re(e){return M.has(P(e))}function P(e){return`${e.platform}:${e.kind}:${e.id}`}async function ze(e){return Be(n(await j(e,[`status_bar`,e.id,`list`],{allowFailure:!0}),`Failed to read simulator status bar overrides`).stdout)}async function F(e){await j(e,[`status_bar`,e.id,`clear`])}async function I(e,t){t.length!==0&&await j(e,[`status_bar`,e.id,`override`,...t])}function Be(e){let n={},r=e.split(`
2
+ `).map(e=>e.trim()).filter(e=>e.length>0&&e!==`Current Status Bar Overrides:`&&!/^=+$/.test(e));for(let e of r){let r=/^DataNetworkType:\s+(\d+)$/.exec(e);if(r){let e=Number(r[1]),i=Me[e];if(!i)throw new t(`COMMAND_FAILED`,`Unsupported simulator data network type: ${e}`);n.dataNetwork=i;continue}let i=/^WiFi Mode:\s+(\d+),\s+WiFi Bars:\s+(\d+)$/.exec(e);if(i){let e=Ne[Number(i[1])];e&&(n.wifiMode=e),n.wifiBars=i[2];continue}let a=/^Cell Mode:\s+(\d+),\s+Cell Bars:\s+(\d+)$/.exec(e);if(a){let e=Number(a[1]),r=Pe[e];if(!r)throw new t(`COMMAND_FAILED`,`Unsupported simulator cellular mode: ${e}`);n.cellularMode=r,n.cellularBars=a[2];continue}let o=/^Operator Name:\s*(.*)$/.exec(e);if(o){n.operatorName=o[1]??``;continue}}return Object.keys(n).length===0?null:n}function Ve(e){let t=[];return e.dataNetwork&&t.push(`--dataNetwork`,e.dataNetwork),e.wifiMode&&t.push(`--wifiMode`,e.wifiMode),e.wifiBars!==void 0&&(e.dataNetwork===`wifi`||e.wifiMode)&&t.push(`--wifiBars`,e.wifiBars),e.cellularMode&&t.push(`--cellularMode`,e.cellularMode),e.cellularBars!==void 0&&(e.cellularMode||He(e.dataNetwork)||e.operatorName!==void 0)&&t.push(`--cellularBars`,e.cellularBars),e.operatorName!==void 0&&t.push(`--operatorName`,e.operatorName),t}function He(e){return!!(e&&e!==`hide`&&e!==`wifi`)}function L(e,t,n){r({level:`warn`,phase:`ios_screenshot_status_bar_${t}`,data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,...S(n)}})}function R(e,t,n){return v(e,t,n)}const Ue={ensureBooted:E,prepareStatusBarForScreenshot:Fe,captureWithRetry:Ke,normalizeDensity:Qe,captureWithRunner:V,shouldFallbackToRunner:W},z=new Map,B=new Map;async function We(e,t,n={}){if(o(e)){await V(e,t,n.appBundleId,n.fullscreen,n.runnerOptions);return}if(e.kind===`simulator`){await Ge(e,t,n);return}await C(e).captureScreenshot(e,t,{appBundleId:n.appBundleId,fullscreen:n.fullscreen,runnerOptions:n.runnerOptions,runRunnerCommand:w})}async function Ge(e,n,r={}){if(e.kind!==`simulator`)throw new t(`UNSUPPORTED_OPERATION`,`Simulator screenshot fallback flow supports only iOS simulators`);let i={...Ue,...r.deps??{}};r.skipIosSimulatorBootCheck||await i.ensureBooted(e);let a=async()=>{};if(r.normalizeStatusBar===!0)try{a=await i.prepareStatusBarForScreenshot(e)}catch(t){U(e,`prepare_failed`,t)}try{try{await i.captureWithRetry(e,n),await i.normalizeDensity(e,n,r.pixelDensity);return}catch(t){let a=t;if(r.skipIosSimulatorBootCheck&&Ze(t)){await i.ensureBooted(e);try{await i.captureWithRetry(e,n),await i.normalizeDensity(e,n,r.pixelDensity);return}catch(e){a=e}}if(!i.shouldFallbackToRunner(a))throw a;Ye(e,`simctl_screenshot`,a)}await i.captureWithRunner(e,n,r.appBundleId,r.fullscreen,r.runnerOptions),await i.normalizeDensity(e,n,r.pixelDensity)}finally{await a().catch(t=>U(e,`restore_failed`,t))}}async function Ke(e,t){let n=p.fromTimeoutMs(le);await f(async({deadline:n})=>{await R(e,[`io`,e.id,`screenshot`,t],{timeoutMs:Math.max(1e3,n?.remainingMs()??2e4)})},{maxAttempts:5,baseDelayMs:he,maxDelayMs:fe,jitter:.2,shouldRetry:e=>W(e)},{deadline:n,phase:`ios_simulator_screenshot`})}async function V(e,n,r,i,a){if(e.kind===`device`&&!o(e)){await C(e).captureScreenshot(e,n,{appBundleId:r,fullscreen:i,runnerOptions:a,preferRunner:!0,runRunnerCommand:w});return}let s=(await w(e,{command:`screenshot`,appBundleId:r,fullscreen:i,inlineScreenshot:!1},a)).message;if(!s)throw new t(`COMMAND_FAILED`,`Failed to capture iOS screenshot: runner returned no file path`);if(o(e)){await k.copyFile(s,n);return}if(e.kind===`simulator`){await qe(e,s,n);return}throw new t(`COMMAND_FAILED`,`Unsupported Apple screenshot target`)}async function qe(e,n,r){let i=p.fromTimeoutMs(ue),a=`Unable to locate runner container for simulator screenshot`,o=B.get(e.id);if(o){let t=await H(o,n,r);if(t.copied)return;a=t.error,B.delete(e.id)}for(let t of _e){let o=await R(e,[`get_app_container`,e.id,t,`data`],{allowFailure:!0,timeoutMs:Je(i,ue,`runner screenshot container lookup`)});if(o.exitCode!==0){let e=o.stderr.trim();e&&(a=e);continue}let s=o.stdout.trim();if(!s){a=`simctl get_app_container returned empty output`;continue}let c=await H(s,n,r);if(c.copied){B.set(e.id,s);return}a=c.error}throw new t(`COMMAND_FAILED`,`Failed to capture iOS screenshot: ${a}`)}async function H(e,t,n){let r=`Runner screenshot was not found in the simulator container`;for(let i of Xe(e,t))try{return await k.copyFile(i,n),{copied:!0}}catch(e){r=e instanceof Error?e.message:String(e)}return{copied:!1,error:r}}function Je(e,n,r){let i=e.remainingMs();if(i>0)return i;throw new t(`COMMAND_FAILED`,`iOS ${r} timed out after ${n}ms`,{timeoutMs:n,step:r})}function Ye(e,t,n){let i=S(n);r({level:`warn`,phase:`ios_screenshot_fallback`,data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,from:t,to:`runner`,...i}})}function U(e,t,n){r({level:`warn`,phase:`ios_screenshot_status_bar_${t}`,data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,...S(n)}})}function Xe(e,t){let n=A.resolve(e),r=t.trim();if(!r)return[];let i=[],a=new Set,o=e=>{let t=A.normalize(e);a.has(t)||(a.add(t),i.push(t))},s=r.replace(/^\/+/,``),c=s.replace(/\\/g,`/`);if(s&&o(A.join(n,s)),A.isAbsolute(r)&&o(A.normalize(r)),c.startsWith(`tmp/`))o(A.join(n,c));else{let e=c.lastIndexOf(`/tmp/`);if(e>=0){let t=c.slice(e+1);o(A.join(n,t))}}let l=A.basename(r);return l&&o(A.join(n,`tmp`,l)),i}function W(e){if(!(e instanceof t)||e.code!==`COMMAND_FAILED`)return!1;let n=b(e);return n.includes(`timeout waiting for screen surfaces`)||n.includes(`nsposixerrordomain`)&&n.includes(`code=60`)&&n.includes(`screenshot`)||n.includes(`timed out`)&&n.includes(`screenshot`)}function Ze(e){if(!(e instanceof t)||e.code!==`COMMAND_FAILED`)return!1;let n=b(e);return n.includes(`not booted`)||n.includes(`current state: shutdown`)||n.includes(`current state is shutdown`)||n.includes(`current state=shutdown`)||n.includes(`state: shutdown`)||n.includes(`state=shutdown`)}async function Qe(e,t,n){let r=await $e(e),i=ye(await ve(t),r,n);i&&await l(t,i.width,i.height)}async function $e(e){let n=z.get(e.id);if(n!==void 0)return n;let r=await R(e,[`getenv`,e.id,`SIMULATOR_MAINSCREEN_SCALE`],{timeoutMs:de}),i=Number(r.stdout.trim());if(!Number.isFinite(i)||i<=0)throw new t(`COMMAND_FAILED`,`Failed to read iOS simulator screenshot scale from SIMULATOR_MAINSCREEN_SCALE`);return z.set(e.id,i),i}function G(e){if(!(e instanceof t)||e.code!==`COMMAND_FAILED`)return!1;let n=e.details??{};if(n.exitCode!==4)return!1;let r=String(n.stderr??``).toLowerCase();return r.includes(`fbsopenapplicationserviceerrordomain`)&&r.includes(`the request to open`)}async function et(e,t){let n=await g(ce(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 _(`plutil`,[`-extract`,`CFBundleExecutable`,`raw`,`-o`,`-`,`${r}/Info.plist`],{allowFailure:!0});if(i.exitCode!==0||!i.stdout.trim())return{installed:!0};let a=await _(`otool`,[`-l`,`${r}/${i.stdout.trim()}`],{allowFailure:!0});if(a.exitCode!==0)return{installed:!0};let o=a.stdout.toLowerCase();return{installed:!0,simulatorCompatible:o.includes(`iossimulator`)||o.includes(`platform 7`)}}function tt(e){return e.installed?e.simulatorCompatible===!1?`ARCH_MISMATCH`:`PERSISTENT_LAUNCH_FAIL`:`APP_NOT_INSTALLED`}function nt(e){switch(e){case`ARCH_MISMATCH`:return`The app binary was not built for the simulator platform. Rebuild with a simulator destination or use a physical device.`;case`APP_NOT_INSTALLED`:return`The app bundle is not installed on this simulator. Run install before open.`;case`PERSISTENT_LAUNCH_FAIL`:return`The simulator repeatedly refused to launch the app. Inspect crash logs in Console.app or ~/Library/Logs/DiagnosticReports/ and consider reinstalling the app.`;default:return`The simulator failed to launch the app. Retry with --debug and inspect diagnostics log for details.`}}const K=25e3;async function rt(e,n,r){let i=r?.launchConsole?.trim(),a=r?.launchArgs;if(i&&(!s(e)||e.kind!==`simulator`))throw new t(`UNSUPPORTED_OPERATION`,ae);if(o(e)){if(a&&a.length>0)throw new t(`UNSUPPORTED_OPERATION`,`--launch-args is not supported on macOS; launch arguments are currently iOS-only.`);await xe(e,n,r);return}let c=r?.url?.trim();if(c){if(i)throw new t(`INVALID_ARGS`,oe);if(!d(c))throw new t(`INVALID_ARGS`,`open <app> <url> requires a valid URL target`);if(e.kind===`simulator`){let t=!!a||ee(c);if(r?.terminateRunningApp||t){let i=r?.appBundleId??await D(e,n);t?await X(e,i,{...a?{launchArgs:a}:{},...r?.terminateRunningApp?{terminateRunningApp:!0}:{}}):await Y(e,i)}await q(e,c,void 0);return}let o=u(r?.appBundleId??await D(e,n),c);if(!o)throw new t(`INVALID_ARGS`,`Deep link open on iOS devices requires an active app bundle ID. Open the app first, then open the URL.`);await Q(e,o,{payloadUrl:c,launchArgs:a,runnerOptions:r?.runnerOptions});return}let l=n.trim();if(d(l)){if(i)throw new t(`INVALID_ARGS`,oe);if(e.kind===`simulator`){await q(e,l,a);return}let n=u(r?.appBundleId,l);if(!n)throw new t(`INVALID_ARGS`,`Deep link open on iOS devices requires an active app bundle ID. Open the app first, then open the URL.`);await Q(e,n,{payloadUrl:l,launchArgs:a,runnerOptions:r?.runnerOptions});return}let f=r?.appBundleId??await D(e,n);if(e.kind===`simulator`){await X(e,f,{...i?{launchConsole:i}:{},...a?{launchArgs:a}:{},...r?.terminateRunningApp?{terminateRunningApp:!0}:{}});return}await Q(e,f,{launchArgs:a,runnerOptions:r?.runnerOptions})}async function q(e,n,r){if(r&&r.length>0)throw new t(`INVALID_ARGS`,`--launch-args is not supported with iOS simulator URL opens (simctl openurl ignores launch args). Launch the app first with --launch-args, then issue the URL open in a separate call.`);await E(e),await y(e,[`openurl`,e.id,n])}async function it(e){o(e)||e.kind===`simulator`&&await be(e)!==`Booted`&&await E(e)}async function J(e,t,n){if(o(e)){await De(e,t);return}let r=await D(e,t);if(e.kind===`simulator`){await Y(e,r);return}await C(e).terminateApp(e,r,{runnerOptions:n,runRunnerCommand:w})}async function Y(e,n){await E(e);let r=x(e,[`terminate`,e.id,n]),a=await g(r,{allowFailure:!0,timeoutMs:ge});if(a.exitCode!==0&&!a.stderr.toLowerCase().includes(`found nothing to terminate`))throw new t(`COMMAND_FAILED`,`xcrun exited with code ${a.exitCode}`,i(a,{cmd:`xcrun`,args:r}))}async function X(e,n,r){await E(e);let a=0,o=p.fromTimeoutMs(me);try{await f(async({deadline:a})=>{if(a?.isExpired())throw new t(`COMMAND_FAILED`,`App launch deadline exceeded`,{timeoutMs:me});let o=x(e,at(e.id,n,r)),s=r?.launchConsole?await ot(o,r.launchConsole):await g(o,{allowFailure:!0});if(s.exitCode!==0)throw new t(`COMMAND_FAILED`,`xcrun exited with code ${s.exitCode}`,i(s,{cmd:`xcrun`,args:o}))},{maxAttempts:10,baseDelayMs:1e3,maxDelayMs:5e3,jitter:.2,shouldRetry(e){return G(e)?(a+=1,a<3):!1}},{deadline:o})}catch(t){if(G(t)){let r=t,i=tt(await et(e,n));r.details={...r.details,hint:nt(i)}}throw t}}function at(e,t,n){let r=[`launch`];return n?.launchConsole&&r.push(`--console-pty`),n?.terminateRunningApp&&r.push(`--terminate-running-process`),r.push(e,t),n?.launchArgs&&n.launchArgs.length>0&&r.push(...n.launchArgs),r}async function ot(e,n){await k.mkdir(A.dirname(n),{recursive:!0});try{let t=await g(e,{allowFailure:!0,timeoutMs:K});return await Z(n,t.stdout,t.stderr),t}catch(e){let i=(e instanceof t?e:void 0)?.details;if(i?.timeoutMs===K){let e=typeof i.stdout==`string`?i.stdout:``,t=typeof i.stderr==`string`?i.stderr:``;return await Z(n,e,t),r({level:`warn`,phase:`ios_simulator_launch_console_capture_timeout`,data:{timeoutMs:K,logPath:n,stdoutBytes:Buffer.byteLength(e),stderrBytes:Buffer.byteLength(t)}}),{stdout:e,stderr:t,exitCode:0}}throw e}}async function Z(e,t,n){await k.writeFile(e,st(t,n),`utf8`)}function st(e,t){return!e||!t||e.endsWith(`
3
+ `)||e.endsWith(`\r`)?`${e}${t}`:`${e}\n${t}`}async function Q(e,t,n){await C(e).launchApp(e,t,{...n,runRunnerCommand:w})}async function ct(e,n){return await O(e,async()=>{let r=await D(e,n);if(e.kind!==`simulator`)return await C(e).uninstallApp(e,r),{bundleId:r};await E(e);let a=await y(e,[`uninstall`,e.id,r],{allowFailure:!0});if(a.exitCode!==0&&!pe(`${a.stdout}\n${a.stderr}`.toLowerCase()))throw new t(`COMMAND_FAILED`,`simctl uninstall failed for ${r}`,i(a));return{bundleId:r}})}async function lt(e,t,n){e.kind!==`simulator`&&C(e).assertAppInstallationSupported(e);let r=await ke({kind:`path`,path:t},n);try{return await dt(e,r.installablePath),{archivePath:r.archivePath,installablePath:r.installablePath,bundleId:r.bundleId,appName:r.appName,launchTarget:r.bundleId}}finally{await r.cleanup()}}async function ut(e,t,n){return e.kind!==`simulator`&&C(e).assertAppInstallationSupported(e),await O(e,async()=>{let{bundleId:r}=await ct(e,t);return await lt(e,n,{appIdentifierHint:t}),{bundleId:r}})}async function dt(e,t){await O(e,async()=>{if(e.kind!==`simulator`){await C(e).installApp(e,t);return}await E(e),await y(e,[`install`,e.id,t])})}async function ft(e){return o(e)?await we():(T(e,`clipboard`),await E(e),n(await y(e,[`pbpaste`,e.id],{allowFailure:!0}),`Failed to read iOS simulator clipboard`).stdout.replace(/\r\n/g,`
4
+ `).replace(/\n$/,``))}async function pt(e,t){if(o(e)){await Se(t);return}T(e,`clipboard`),await E(e),n(await y(e,[`pbcopy`,e.id],{allowFailure:!0,stdin:t}),`Failed to write iOS simulator clipboard`)}async function mt(e,t,n){T(e,`push`),await E(e);let r=await k.mkdtemp(A.join(Ae.tmpdir(),`agent-device-ios-push-`)),i=A.join(r,`payload.apns`);try{await k.writeFile(i,`${JSON.stringify(n)}\n`,`utf8`),await y(e,[`push`,e.id,t,i])}finally{await k.rm(r,{recursive:!0,force:!0})}}let $=null,ht;async function gt(e,n,r,i,s){if(o(e)){let e=n.toLowerCase();if(e===`appearance`){await Oe(r);return}if(e===`permission`){let e=m(r);if(e===`deny`)throw new t(`INVALID_ARGS`,a(`permission`));return await se(e,vt(s?.permissionTarget))}throw new t(`INVALID_ARGS`,a(n))}T(e,`settings`),await E(e);let l=n.toLowerCase();switch(l){case`clear-app-state`:{if(r.toLowerCase()!==`clear`)throw new t(`INVALID_ARGS`,`settings clear-app-state only supports clear.`);if(!i)throw new t(`INVALID_ARGS`,`settings clear-app-state requires an app id or an active app session.`);let n=await _t(e,i);return{bundleId:n.bundleId,containerPath:n.containerPath,cleared:!0}}case`wifi`:{let t=h(r)?`active`:`failed`;await y(e,[`status_bar`,e.id,`override`,`--wifiMode`,t]),N(e);return}case`airplane`:h(r)?(await y(e,[`status_bar`,e.id,`override`,`--dataNetwork`,`hide`,`--wifiMode`,`failed`,`--wifiBars`,`0`,`--cellularMode`,`failed`,`--cellularBars`,`0`,`--operatorName`,``]),N(e)):(await y(e,[`status_bar`,e.id,`clear`]),Le(e));return;case`location`:{if(r.toLowerCase()===`set`){let{latitude:t,longitude:n}=c(s);return await y(e,[`location`,e.id,`set`,`${t},${n}`]),{latitude:t,longitude:n}}let n=h(r);if(!i)throw new t(`INVALID_ARGS`,`location setting requires an active app in session`);let a=n?`grant`:`revoke`;await y(e,[`privacy`,e.id,a,`location`,i]);return}case`faceid`:case`touchid`:{let t=l,n=xt[t];await kt(e,Ot(r,t),{settingName:t,label:n.label,modalityAliases:n.modalityAliases});return}case`appearance`:{let t=await yt(e,r);await y(e,[`ui`,e.id,`appearance`,t]);return}case`permission`:if(!i)throw new t(`INVALID_ARGS`,`permission setting requires an active app in session`);await Ct(e,St(m(r)),Dt(s?.permissionTarget,s?.permissionMode),i);return;default:throw new t(`INVALID_ARGS`,`Unsupported setting: ${n}`)}}async function _t(e,r){if(!s(e)||e.kind!==`simulator`)throw new t(`UNSUPPORTED_OPERATION`,`Clearing app state is currently supported only on iOS simulators.`);let i=await D(e,r);await E(e),await J(e,i);let a=n(await y(e,[`get_app_container`,e.id,i,`data`],{allowFailure:!0}),`simctl get_app_container failed for ${i}`).stdout.trim();if(!a)throw new t(`COMMAND_FAILED`,`simctl get_app_container returned an empty data container path for ${i}`);let o=await k.readdir(a);return await Promise.all(o.map(e=>k.rm(A.join(a,e),{recursive:!0,force:!0}))),{bundleId:i,containerPath:a}}function vt(e){let n=e?.trim().toLowerCase();if(n===`accessibility`||n===`screen-recording`||n===`input-monitoring`)return n;throw new t(`INVALID_ARGS`,`Unsupported macOS permission target. Use accessibility|screen-recording|input-monitoring.`)}async function yt(e,r){let i=ie(r);if(i!==`toggle`)return i;let a=n(await y(e,[`ui`,e.id,`appearance`],{allowFailure:!0}),`Failed to read current iOS appearance`),o=bt(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 bt(e,t){let n=/\b(light|dark|unsupported|unknown)\b/i.exec(`${e}\n${t}`);if(!n)return null;let r=n[1]?.toLowerCase();return r===`dark`?`dark`:r===`light`?`light`:null}const xt={faceid:{label:`Face ID`,modalityAliases:[`face`]},touchid:{label:`Touch ID`,modalityAliases:[`finger`,`touch`]}};function St(e){return e===`deny`?`revoke`:e}async function Ct(e,n,r,i){let a=await Tt(e);if(!a.has(r))throw new t(`UNSUPPORTED_OPERATION`,`iOS simctl privacy does not support service "${r}" on this runtime.`,{deviceId:e.id,appBundleId:i,hint:`Supported services: ${Array.from(a).sort().join(`, `)}`});let o=[`privacy`,e.id,n,r,i],s=r===`notifications`;if(!(n===`reset`&&s))try{await y(e,o);return}catch(n){throw s&&wt(n)?new t(`UNSUPPORTED_OPERATION`,`iOS simulator does not support setting notifications permission via simctl privacy on this runtime.`,{deviceId:e.id,appBundleId:i,hint:`Use reset notifications for reprompt behavior, or toggle notifications manually in Settings.`}):n}try{await y(e,o);return}catch(e){if(!wt(e))throw e}try{await y(e,[`privacy`,e.id,`reset`,`all`,i])}catch(n){throw new t(`COMMAND_FAILED`,`iOS simulator blocked direct notifications reset. Fallback reset-all also failed.`,{deviceId:e.id,appBundleId:i,hint:`Use reinstall to force a fresh notifications prompt, or reset simulator content and settings.`},n instanceof Error?n:void 0)}}function wt(e){if(!(e instanceof t)||e.code!==`COMMAND_FAILED`)return!1;let n=String(e.details?.stderr??``).toLowerCase();return(n.includes(`failed to grant access`)||n.includes(`failed to revoke access`)||n.includes(`failed to reset access`))&&n.includes(`operation not permitted`)}async function Tt(e){let n=te(e.simulatorSetPath),r=`${process.env.PATH??``}::${n??``}`;if($&&ht===r)return $;let i=await y(e,[`privacy`,`help`],{allowFailure:!0}),a=Et(`${i.stdout}\n${i.stderr}`);if(a.size===0)throw new t(`COMMAND_FAILED`,`Unable to determine supported simctl privacy services`,{stdout:i.stdout,stderr:i.stderr,exitCode:i.exitCode,hint:"Run `xcrun simctl privacy help` manually to verify available services for this runtime."});return $=a,ht=r,a}function Et(e){let t=new Set,n=!1;for(let r of e.split(`
5
+ `)){let e=r.trim();if(!e)continue;if(e===`service`){n=!0;continue}if(!n)continue;if(e.startsWith(`bundle identifier`))break;let i=/^([a-z-]+)\s+-\s+/.exec(e)?.[1];i!==void 0&&t.add(i)}return t}function Dt(e,n){let r=re(e);if(r!==`photos`&&n?.trim())throw new t(`INVALID_ARGS`,`Permission mode is only supported for photos. Received: ${n}.`);if(r===`camera`)return`camera`;if(r===`microphone`)return`microphone`;if(r===`contacts`)return`contacts`;if(r===`contacts-limited`)return`contacts-limited`;if(r===`notifications`)return`notifications`;if(r===`calendar`)return`calendar`;if(r===`location`)return`location`;if(r===`location-always`)return`location-always`;if(r===`media-library`)return`media-library`;if(r===`motion`)return`motion`;if(r===`reminders`)return`reminders`;if(r===`siri`)return`siri`;if(r===`photos`){let e=n?.trim().toLowerCase();if(!e||e===`full`)return`photos`;if(e===`limited`)return`photos-add`;throw new t(`INVALID_ARGS`,`Invalid photos mode: ${n}. Use full|limited.`)}throw new t(`INVALID_ARGS`,`Unsupported permission target: ${e}. Use camera|microphone|photos|contacts|contacts-limited|notifications|calendar|location|location-always|media-library|motion|reminders|siri.`)}function Ot(e,n){let r=e.trim().toLowerCase();if(r===`match`)return`match`;if(r===`nonmatch`)return`nonmatch`;if(r===`enroll`)return`enroll`;if(r===`unenroll`)return`unenroll`;throw new t(`INVALID_ARGS`,`Invalid ${n} state: ${e}. Use match|nonmatch|enroll|unenroll.`)}async function kt(e,n,r){let i=At(e.id,n,r.modalityAliases),a=[];for(let t of i){let n=x(e,t),r=await g(n,{allowFailure:!0});if(r.exitCode===0)return;a.push({args:n,stderr:r.stderr,stdout:r.stdout,exitCode:r.exitCode})}let o=ne(a);throw a.length>0&&a.every(e=>jt(e.stdout,e.stderr))?new t(`UNSUPPORTED_OPERATION`,`${r.label} simulation is not supported on this simulator runtime.`,{deviceId:e.id,action:n,setting:r.settingName,attempts:o}):new t(`COMMAND_FAILED`,`Failed to simulate ${r.settingName}.`,{deviceId:e.id,action:n,setting:r.settingName,attempts:o})}function At(e,t,n){let r=n.length>0?n:[`face`];switch(t){case`match`:return r.flatMap(t=>[[`biometric`,e,`match`,t],[`biometric`,`match`,e,t]]);case`nonmatch`:return r.flatMap(t=>[[`biometric`,e,`nonmatch`,t],[`biometric`,e,`nomatch`,t],[`biometric`,`nonmatch`,e,t],[`biometric`,`nomatch`,e,t]]);case`enroll`:return[[`biometric`,e,`enroll`,`yes`],[`biometric`,e,`enroll`,`1`],[`biometric`,`enroll`,e,`yes`],[`biometric`,`enroll`,e,`1`]];case`unenroll`:return[[`biometric`,e,`enroll`,`no`],[`biometric`,e,`enroll`,`0`],[`biometric`,`enroll`,e,`no`],[`biometric`,`enroll`,e,`0`]]}}function jt(e,t){let n=`${e}\n${t}`.toLowerCase();return n.includes(`unrecognized subcommand`)||n.includes(`unknown subcommand`)||n.includes(`not supported`)||n.includes(`unavailable`)||n.includes(`biometric`)&&n.includes(`invalid`)}var Mt=e({findIosSimulatorInstalledApp:()=>Te,installIosApp:()=>lt,installIosInstallablePath:()=>dt,listIosApps:()=>Ce,pushIosNotification:()=>mt,reinstallIosApp:()=>ut,resolveIosApp:()=>D,resolveIosSimulatorDeepLinkBundleId:()=>Ee});export{J as a,V as c,pt as i,We as l,gt as n,rt as o,ft as r,it as s,Mt as t};