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.
- 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
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.3.apk.sha256 +1 -0
- 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
- 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
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.3.apk.sha256 +1 -0
- 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
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +0 -176
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Keyboard.swift +184 -0
- package/dist/src/android-adb.d.ts +3 -26
- package/dist/src/android-adb.js +1 -1
- package/dist/src/app-helpers.js +1 -0
- package/dist/src/apps.js +5 -5
- package/dist/src/context.js +1 -1
- package/dist/src/device-input-state.d.ts +26 -0
- package/dist/src/find.js +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/interaction-snapshot.js +1 -1
- package/dist/src/interaction.js +1 -1
- package/dist/src/internal/bin.js +1 -1
- package/dist/src/internal/daemon.js +14 -14
- package/dist/src/lease.js +1 -1
- package/dist/src/limrun.d.ts +78 -2
- package/dist/src/logcat.js +1 -1
- package/dist/src/recording-export-quality.js +2 -3
- package/dist/src/runtime2.js +1 -1
- package/dist/src/sdk-android-adb.d.ts +1 -1
- package/dist/src/selector-runtime.js +1 -1
- package/dist/src/session-target-evidence.js +1 -1
- package/dist/src/session.js +8 -8
- package/dist/src/snapshot2.js +1 -1
- package/dist/src/tv-remote.d.ts +1 -1
- package/package.json +14 -15
- package/server.json +2 -2
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.2.apk.sha256 +0 -1
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.2.apk.sha256 +0 -1
|
Binary file
|
|
@@ -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.
|
|
4
|
-
"assetName": "agent-device-android-ime-helper-0.20.
|
|
5
|
-
"sha256": "
|
|
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":
|
|
7
|
+
"versionCode": 20003,
|
|
8
8
|
"serviceComponent": "com.callstack.agentdevice.imehelper/.TestInputMethodService",
|
|
9
9
|
"broadcastProtocol": "android-ime-helper-v1"
|
|
10
10
|
}
|
|
Binary file
|
|
@@ -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.
|
|
4
|
-
"releaseTag": "v0.20.
|
|
5
|
-
"assetName": "agent-device-android-snapshot-helper-0.20.
|
|
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": "
|
|
8
|
-
"checksumName": "agent-device-android-snapshot-helper-0.20.
|
|
7
|
+
"sha256": "de7ecffc80d54dd1c173d3bf445ff8c9a02de2d124bcf39abeafeb5a6a08c95e",
|
|
8
|
+
"checksumName": "agent-device-android-snapshot-helper-0.20.3.apk.sha256",
|
|
9
9
|
"packageName": "com.callstack.agentdevice.snapshothelper",
|
|
10
|
-
"versionCode":
|
|
10
|
+
"versionCode": 20003,
|
|
11
11
|
"instrumentationRunner": "com.callstack.agentdevice.snapshothelper/.SnapshotInstrumentation",
|
|
12
12
|
"minSdk": 23,
|
|
13
13
|
"targetSdk": 36,
|
package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift
CHANGED
|
@@ -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,
|
package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Keyboard.swift
ADDED
|
@@ -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 {
|
|
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
|
-
|
|
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 };
|
package/dist/src/android-adb.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{_ as e}from"./sdk-contracts.js";import{i as t
|
|
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
|
|
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
|
|
4
|
-
`).replace(/\n$/,``))}async function
|
|
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
|
|
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};
|