agent-device 0.19.3 → 0.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -70
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.1.apk +0 -0
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.1.apk.sha256 +1 -0
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.1.manifest.json +10 -0
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.1.apk +0 -0
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.1.apk.sha256 +1 -0
- package/{android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.19.3.manifest.json → android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.1.manifest.json} +6 -6
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.h +2 -0
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.m +5 -0
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h +5 -10
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +74 -174
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +113 -19
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+BlockingSystemModalResolution.swift +82 -0
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +325 -123
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +2 -2
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +149 -105
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +56 -2
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+LifecycleCacheTests.swift +73 -0
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +51 -26
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollGesture.swift +2 -2
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +2 -107
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +130 -13
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +51 -8
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotTraversalIdentityTests.swift +24 -0
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedGesturePolicy.swift +1 -1
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SystemModal.swift +43 -35
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +1 -1
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +16 -1
- package/dist/src/agent-browser-tool.js +1 -1
- package/dist/src/agent-device-client.js +3 -0
- package/dist/src/android-adb.d.ts +1 -1
- package/dist/src/android-adb.js +1 -1
- package/dist/src/android-input-method-overlays.js +1 -0
- package/dist/src/android-input-ownership.js +1 -6
- package/dist/src/android-system-chrome.js +1 -0
- package/dist/src/android.js +1 -1
- package/dist/src/app-lifecycle.js +5 -5
- package/dist/src/app-log-request-scope.js +1 -0
- package/dist/src/app-resolution.js +1 -0
- package/dist/src/apple-multitouch-support.js +1 -0
- package/dist/src/apps.js +5 -5
- package/dist/src/boot-diagnostics.js +2 -0
- package/dist/src/cli-config.js +1 -0
- package/dist/src/cli-help.js +131 -40
- package/dist/src/cli-runner.js +1 -1
- package/dist/src/cli.js +15 -11
- package/dist/src/command-metadata.js +1 -1
- package/dist/src/command-schema.js +3 -3
- package/dist/src/compatibility-policy.js +1 -0
- package/dist/src/context.js +1 -0
- package/dist/src/contracts.d.ts +2 -2
- package/dist/src/debug-symbols.js +1 -1
- package/dist/src/device-input-state.js +1 -1
- package/dist/src/devices2.js +1 -1
- package/dist/src/devices4.js +1 -0
- package/dist/src/find.js +1 -1
- package/dist/src/finders.js +1 -1
- package/dist/src/generic.js +1 -1
- package/dist/src/helper.js +1 -1
- package/dist/src/index.d.ts +533 -279
- package/dist/src/index.js +1 -1
- package/dist/src/input-actions.js +4 -3
- package/dist/src/install-artifact.js +1 -0
- package/dist/src/install-source.d.ts +5 -1
- package/dist/src/install-source.js +1 -1
- package/dist/src/install-source2.js +1 -1
- package/dist/src/interaction-snapshot.js +1 -1
- package/dist/src/interaction.d.ts +230 -0
- package/dist/src/interaction.js +1 -1
- package/dist/src/interactor.js +2 -2
- package/dist/src/interactor2.js +1 -0
- package/dist/src/internal/bin.js +1 -1
- package/dist/src/internal/daemon.js +42 -20
- package/dist/src/internal/png-worker.d.ts +22 -0
- package/dist/src/internal/png-worker.js +1 -1
- package/dist/src/launch-console.js +1 -0
- package/dist/src/lease-context.js +1 -0
- package/dist/src/lease.js +1 -1
- package/dist/src/linux.js +1 -1
- package/dist/src/logcat.js +5 -0
- package/dist/src/method-scope.js +1 -0
- package/dist/src/metro-types.d.ts +52 -0
- package/dist/src/metro.d.ts +3 -3
- package/dist/src/notifications.js +1 -1
- package/dist/src/png-worker-contract.js +1 -1
- package/dist/src/process-lock.js +1 -0
- package/dist/src/react-native.js +1 -1
- package/dist/src/record-trace.js +1 -1
- package/dist/src/recording-export-quality.js +15 -0
- package/dist/src/register-builtins.js +1 -0
- package/dist/src/registry.js +24 -24
- package/dist/src/remote-config.d.ts +1 -1
- package/dist/src/reporting.js +8 -7
- package/dist/src/runner-client.js +1 -1
- package/dist/src/runner-disposal.js +2 -0
- package/dist/src/runner-lease.js +1 -0
- package/dist/src/runner-provider.js +1 -1
- package/dist/src/runner-sequence.js +1 -1
- package/dist/src/runtime.js +3 -3
- package/dist/src/runtime2.js +1 -0
- package/dist/src/sdk-android-adb.d.ts +97 -33
- package/dist/src/sdk-android-adb.js +4 -4
- package/dist/src/sdk-batch-runner.d.ts +2 -1
- package/dist/src/sdk-batch-runner.js +3 -2
- package/dist/src/sdk-contracts.d.ts +70 -149
- package/dist/src/sdk-contracts.js +1 -1
- package/dist/src/sdk-device.js +1 -1
- package/dist/src/sdk-finders.d.ts +5 -4
- package/dist/src/sdk-finders.js +1 -1
- package/dist/src/sdk-io.d.ts +1 -1
- package/dist/src/sdk-remote-config.d.ts +19 -96
- package/dist/src/sdk-remote-config.js +1 -1
- package/dist/src/sdk-selectors.js +1 -1
- package/dist/src/selector-runtime.js +1 -1
- package/dist/src/selector-vocabulary.js +1 -0
- package/dist/src/selectors.d.ts +12 -3
- package/dist/src/selectors.js +1 -1
- package/dist/src/server.js +3 -3
- package/dist/src/session-target-evidence.js +1 -0
- package/dist/src/session.js +8 -10
- package/dist/src/simctl.js +1 -1
- package/dist/src/simulator.js +1 -1
- package/dist/src/snapshot-diagnostics.js +5 -2
- package/dist/src/snapshot-quality.js +1 -1
- package/dist/src/snapshot.js +1 -1
- package/dist/src/snapshot2.js +1 -2
- package/dist/src/tool-provider3.js +1 -0
- package/dist/src/unsupported-interactor.js +1 -0
- package/dist/src/url.js +1 -1
- package/dist/src/web.js +1 -1
- package/package.json +34 -27
- package/server.json +2 -2
- package/skills/agent-device/SKILL.md +5 -3
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.19.3.apk +0 -0
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.19.3.apk.sha256 +0 -1
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.19.3.manifest.json +0 -10
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.19.3.apk +0 -0
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.19.3.apk.sha256 +0 -1
- package/dist/src/apple-runner-options.js +0 -1
- package/dist/src/cli-output.js +0 -1
- package/dist/src/client.js +0 -3
- package/dist/src/handler-utils.js +0 -1
- package/dist/src/output-path.js +0 -1
- package/dist/src/points.js +0 -4
- package/dist/src/provider.js +0 -1
- package/dist/src/providers.js +0 -1
- package/dist/src/record-trace-recording.js +0 -23
- package/dist/src/recording-provider.js +0 -1
- package/dist/src/sdk-batch.js +0 -2
- package/dist/src/selectors2.js +0 -1
- package/dist/src/ttl-memo.js +0 -1
- package/dist/src/tv-remote.js +0 -14
- package/dist/src/xml.js +0 -2
- /package/{macos-helper → apple/macos-helper}/Package.swift +0 -0
- /package/{macos-helper → apple/macos-helper}/Sources/AgentDeviceMacOSHelper/AudioProbe.swift +0 -0
- /package/{macos-helper → apple/macos-helper}/Sources/AgentDeviceMacOSHelper/SnapshotTraversal.swift +0 -0
- /package/{macos-helper → apple/macos-helper}/Sources/AgentDeviceMacOSHelper/main.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.pbxproj +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/xcshareddata/xcschemes/AgentDeviceRunner.xcscheme +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/AgentDeviceRunnerUITests-Bridging-Header.h +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/AgentDeviceRunnerUITests.entitlements +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerObjCExceptionCatcher.h +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerObjCExceptionCatcher.m +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTapPointPolicy.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AXSnapshotFallback.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Environment.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Exceptions.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+FlatSnapshotFiltering.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScreenRecorder.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TvRemote.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests.xctestplan +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/RecordingScripts/recording-overlay.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/RecordingScripts/recording-resize.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/RecordingScripts/recording-trim.swift +0 -0
- /package/{src/platforms/linux → linux}/atspi-dump.py +0 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import XCTest
|
|
2
|
+
|
|
3
|
+
extension RunnerTests {
|
|
4
|
+
func testSnapshotTraversalIdentityPreservesSameOriginNodesWithDifferentBounds() {
|
|
5
|
+
let wrapper = Self.snapshotTraversalIdentity(
|
|
6
|
+
elementType: .other,
|
|
7
|
+
label: "Article",
|
|
8
|
+
identifier: "",
|
|
9
|
+
frame: CGRect(x: 0, y: 97, width: 393, height: 48)
|
|
10
|
+
)
|
|
11
|
+
let leaf = Self.snapshotTraversalIdentity(
|
|
12
|
+
elementType: .other,
|
|
13
|
+
label: "Article",
|
|
14
|
+
identifier: "",
|
|
15
|
+
frame: CGRect(x: 0, y: 97, width: 131, height: 48)
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
#if os(iOS)
|
|
19
|
+
XCTAssertNotEqual(wrapper, leaf)
|
|
20
|
+
#else
|
|
21
|
+
XCTAssertEqual(wrapper, leaf)
|
|
22
|
+
#endif
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -103,7 +103,7 @@ func synthesizedGesturePolicy(_ kind: SynthesizedGesturePolicyKind) -> Synthesiz
|
|
|
103
103
|
|
|
104
104
|
func sequenceHasSynthesizedCoordinateStep(_ steps: [SequenceStep]) -> Bool {
|
|
105
105
|
steps.contains { step in
|
|
106
|
-
step.synthesized == true &&
|
|
106
|
+
step.synthesized == true && step.kind == "tap"
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
@@ -3,17 +3,15 @@ import XCTest
|
|
|
3
3
|
extension RunnerTests {
|
|
4
4
|
// MARK: - Blocking System Modal Snapshot
|
|
5
5
|
|
|
6
|
-
func blockingSystemAlertSnapshot() -> DataPayload? {
|
|
6
|
+
func blockingSystemAlertSnapshot(deadline: Date = .distantFuture) -> DataPayload? {
|
|
7
7
|
#if os(macOS)
|
|
8
8
|
return nil
|
|
9
9
|
#else
|
|
10
|
-
guard let
|
|
11
|
-
return nil
|
|
12
|
-
}
|
|
13
|
-
let actions = actionableElements(in: modal)
|
|
14
|
-
guard !actions.isEmpty else {
|
|
10
|
+
guard case .resolved(let resolvedModal) = resolveBlockingSystemModal(deadline: deadline) else {
|
|
15
11
|
return nil
|
|
16
12
|
}
|
|
13
|
+
let modal = resolvedModal.root
|
|
14
|
+
let actions = resolvedModal.actions
|
|
17
15
|
|
|
18
16
|
let title = preferredSystemModalTitle(modal)
|
|
19
17
|
guard let modalNode = safeMakeSnapshotNode(
|
|
@@ -29,20 +27,6 @@ extension RunnerTests {
|
|
|
29
27
|
}
|
|
30
28
|
var nodes: [SnapshotNode] = [modalNode]
|
|
31
29
|
|
|
32
|
-
for content in informativeElements(in: modal, excluding: actions) {
|
|
33
|
-
guard let contentNode = safeMakeSnapshotNode(
|
|
34
|
-
element: content,
|
|
35
|
-
index: nodes.count,
|
|
36
|
-
type: elementTypeName(content.elementType),
|
|
37
|
-
depth: 1,
|
|
38
|
-
parentIndex: 0,
|
|
39
|
-
hittableOverride: false
|
|
40
|
-
) else {
|
|
41
|
-
continue
|
|
42
|
-
}
|
|
43
|
-
nodes.append(contentNode)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
30
|
for action in actions {
|
|
47
31
|
guard let actionNode = safeMakeSnapshotNode(
|
|
48
32
|
element: action,
|
|
@@ -57,11 +41,33 @@ extension RunnerTests {
|
|
|
57
41
|
nodes.append(actionNode)
|
|
58
42
|
}
|
|
59
43
|
|
|
44
|
+
// Action controls are the interaction contract. Informative text is useful context, but
|
|
45
|
+
// must not make a usable modal miss the bounded system-modal probe deadline.
|
|
46
|
+
let contentDeadline = deadline.addingTimeInterval(-0.5)
|
|
47
|
+
for content in informativeElements(in: modal, deadline: contentDeadline) {
|
|
48
|
+
guard Date() < contentDeadline,
|
|
49
|
+
let contentNode = safeMakeSnapshotNode(
|
|
50
|
+
element: content,
|
|
51
|
+
index: nodes.count,
|
|
52
|
+
type: elementTypeName(content.elementType),
|
|
53
|
+
depth: 1,
|
|
54
|
+
parentIndex: 0,
|
|
55
|
+
hittableOverride: false
|
|
56
|
+
)
|
|
57
|
+
else {
|
|
58
|
+
break
|
|
59
|
+
}
|
|
60
|
+
nodes.append(contentNode)
|
|
61
|
+
}
|
|
62
|
+
|
|
60
63
|
return DataPayload(nodes: nodes, truncated: false)
|
|
61
64
|
#endif
|
|
62
65
|
}
|
|
63
66
|
|
|
64
|
-
func firstBlockingSystemModal(
|
|
67
|
+
func firstBlockingSystemModal(
|
|
68
|
+
in springboard: XCUIApplication,
|
|
69
|
+
deadline: Date = .distantFuture
|
|
70
|
+
) -> XCUIElement? {
|
|
65
71
|
let disableSafeProbe = RunnerEnv.isTruthy("AGENT_DEVICE_RUNNER_DISABLE_SAFE_MODAL_PROBE")
|
|
66
72
|
let queryElements: (() -> [XCUIElement]) -> [XCUIElement] = { fetch in
|
|
67
73
|
if disableSafeProbe {
|
|
@@ -79,6 +85,12 @@ extension RunnerTests {
|
|
|
79
85
|
}
|
|
80
86
|
}
|
|
81
87
|
|
|
88
|
+
// Don't start the second (sheet) enumeration once the shared probe deadline is spent (#1244).
|
|
89
|
+
if Date() >= deadline {
|
|
90
|
+
NSLog("AGENT_DEVICE_RUNNER_SYSTEM_MODAL_PROBE_DEADLINE stage=sheets")
|
|
91
|
+
return nil
|
|
92
|
+
}
|
|
93
|
+
|
|
82
94
|
let sheets = queryElements {
|
|
83
95
|
springboard.sheets.allElementsBoundByIndex
|
|
84
96
|
}
|
|
@@ -137,20 +149,18 @@ extension RunnerTests {
|
|
|
137
149
|
}
|
|
138
150
|
}
|
|
139
151
|
|
|
140
|
-
private func informativeElements(in element: XCUIElement,
|
|
141
|
-
let actionKeys = Set(actions.map(systemModalElementKey))
|
|
152
|
+
private func informativeElements(in element: XCUIElement, deadline: Date) -> [XCUIElement] {
|
|
142
153
|
var seen = Set<String>()
|
|
143
154
|
var contents: [XCUIElement] = []
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
continue
|
|
155
|
+
for type in readableSystemModalTypes {
|
|
156
|
+
guard Date() < deadline else { break }
|
|
157
|
+
for candidate in modalDescendants(in: element, matching: type, limit: 2) {
|
|
158
|
+
guard Date() < deadline else { return contents }
|
|
159
|
+
guard let key = safeInformativeElementKey(candidate) else { continue }
|
|
160
|
+
if seen.contains(key) { continue }
|
|
161
|
+
seen.insert(key)
|
|
162
|
+
contents.append(candidate)
|
|
150
163
|
}
|
|
151
|
-
if seen.contains(key) { continue }
|
|
152
|
-
seen.insert(key)
|
|
153
|
-
contents.append(candidate)
|
|
154
164
|
}
|
|
155
165
|
return contents
|
|
156
166
|
}
|
|
@@ -173,11 +183,9 @@ extension RunnerTests {
|
|
|
173
183
|
return Array(elements.prefix(limit))
|
|
174
184
|
}
|
|
175
185
|
|
|
176
|
-
private func safeInformativeElementKey(_ candidate: XCUIElement
|
|
186
|
+
private func safeInformativeElementKey(_ candidate: XCUIElement) -> String? {
|
|
177
187
|
safely("MODAL_CONTENT") { () -> String? in
|
|
178
188
|
let key = systemModalElementKey(candidate)
|
|
179
|
-
if actionKeys.contains(key) { return nil }
|
|
180
|
-
if actionableTypes.contains(candidate.elementType) { return nil }
|
|
181
189
|
if !candidate.exists { return nil }
|
|
182
190
|
let frame = candidate.frame
|
|
183
191
|
if frame.isNull || frame.isEmpty { return nil }
|
|
@@ -29,6 +29,15 @@ final class RunnerTests: XCTestCase {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
static let springboardBundleId = "com.apple.springboard"
|
|
32
|
+
// SpringBoard hosts blocking system modals on iOS/visionOS; tvOS (PineBoard/HeadBoard)
|
|
33
|
+
// and macOS have no such host, so there is nothing to probe there.
|
|
34
|
+
static let hasSpringBoardSystemModalHost: Bool = {
|
|
35
|
+
#if os(tvOS) || os(macOS)
|
|
36
|
+
return false
|
|
37
|
+
#else
|
|
38
|
+
return true
|
|
39
|
+
#endif
|
|
40
|
+
}()
|
|
32
41
|
static let defaultRecordingFps: Int32 = 15
|
|
33
42
|
var listener: NWListener?
|
|
34
43
|
var doneExpectation: XCTestExpectation?
|
|
@@ -38,6 +47,7 @@ final class RunnerTests: XCTestCase {
|
|
|
38
47
|
lazy var springboard = XCUIApplication(bundleIdentifier: Self.springboardBundleId)
|
|
39
48
|
var currentApp: XCUIApplication?
|
|
40
49
|
var currentBundleId: String?
|
|
50
|
+
var currentAppProcessIdentifier: Int?
|
|
41
51
|
let maxRequestBytes = 2 * 1024 * 1024
|
|
42
52
|
let mainThreadExecutionTimeout: TimeInterval = 30
|
|
43
53
|
let appExistenceTimeout: TimeInterval = 30
|
|
@@ -76,6 +86,7 @@ final class RunnerTests: XCTestCase {
|
|
|
76
86
|
var snapshotXCTestChannelPenaltyBundleId: String?
|
|
77
87
|
var snapshotXCTestChannelPenaltyUntil = Date.distantPast
|
|
78
88
|
let snapshotXCTestChannelPenaltyDuration: TimeInterval = 120
|
|
89
|
+
var snapshotXCTestPenaltyWarmupExemptionPending = false
|
|
79
90
|
// Bluesky-class screens can grind ~4-8s before an XCTest-backed snapshot tier fails; anything
|
|
80
91
|
// past this threshold marks the screen hostile so the next capture uses non-XCTest recovery.
|
|
81
92
|
let snapshotXCTestSlowCaptureThreshold: TimeInterval = 3
|
|
@@ -87,6 +98,9 @@ final class RunnerTests: XCTestCase {
|
|
|
87
98
|
let treeCaptureLock = NSLock()
|
|
88
99
|
var abandonedTreeCaptureCount = 0
|
|
89
100
|
let treeCaptureSliceBudget: TimeInterval = 8
|
|
101
|
+
// Bounds the pre-plan SpringBoard system-modal probe, which can otherwise grind for tens of
|
|
102
|
+
// seconds on remote-hosted consent dialogs and bypass the plan budget (#1244).
|
|
103
|
+
let systemModalProbeBudget: TimeInterval = 4
|
|
90
104
|
// Observability for the record(_:) suppression below: how many AX-broken-screen snapshot
|
|
91
105
|
// issues this session muted, so wedge investigations see the volume without grepping logs.
|
|
92
106
|
let suppressedIssueLock = NSLock()
|
|
@@ -107,7 +121,8 @@ final class RunnerTests: XCTestCase {
|
|
|
107
121
|
.tabBar,
|
|
108
122
|
.textField,
|
|
109
123
|
.secureTextField,
|
|
110
|
-
.textView
|
|
124
|
+
.textView,
|
|
125
|
+
.webView
|
|
111
126
|
]
|
|
112
127
|
// Keep blocker actions narrow to avoid false positives from generic hittable containers.
|
|
113
128
|
let actionableTypes: Set<XCUIElement.ElementType> = [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./rolldown-runtime.js";import{_ as t,v as n}from"./sdk-contracts.js";import{T as r,j as i,v as a}from"./sdk-android-adb.js";import{
|
|
1
|
+
import{t as e}from"./rolldown-runtime.js";import{_ as t,v as n}from"./sdk-contracts.js";import{T as r,j as i,v as a}from"./sdk-android-adb.js";import{c as o,i as s,o as c,t as l}from"./host-process.js";import{t as u}from"./process-lock.js";import d from"node:crypto";import f from"node:fs";import p from"node:os";import m from"node:path";const h=new Map;function g(e){let t=d.createHash(`sha256`);return t.update(m.resolve(e.stateDir)),t.update(`\0`),t.update(m.resolve(e.installDir)),`--agent-device-managed-web=${t.digest(`hex`).slice(0,16)}`}function _(e,t){let n=g(t),r=e?.trim();return r?T(r).includes(n)?r:`${r},${n}`:n}function v(e){return E(e.AGENT_BROWSER_IDLE_TIMEOUT_MS)??E(e.AGENT_DEVICE_WEB_IDLE_TIMEOUT_MS)??3e5}function y(e,t){if(e.pid!==process.pid&&D(e.command)){if(e.command.includes(g(t)))return{process:e,reason:`launch-marker`};if(O(t).some(t=>j(e,t)))return{process:e,reason:`managed-browser-home`}}}function b(e,t){let n=e.flatMap(e=>{let n=y(e,t);return n?[n]:[]});return{count:n.length,pids:n.map(e=>e.process.pid),processes:n}}async function x(e){return b(await w(),e)}async function S(e,t,n={}){let r=k([...n.openWebSessionNames??[]]);if(r.length>0)return A(`open-web-session`,{openWebSessionNames:r});let a=v(process.env),s=N(e);if(s!==void 0&&Date.now()-s<a)return A(`recent-browser-activity`,{idleTimeoutMs:a,latestActivityMs:s});let c=await w(),u=b(c,e),d=l(u.processes.map(e=>e.process.pid),c).map(e=>e.pid),f={...u,signalPids:d};return u.count===0?f:(i({level:`warn`,phase:`web_agent_browser_orphan_cleanup`,data:{reason:t,count:u.count,pids:u.pids,signalPids:d,stateDir:e.stateDir,installDir:e.installDir,matchReasons:u.processes.map(e=>e.reason)}}),await o({pids:d,termTimeoutMs:1500,killTimeoutMs:1e3}),f)}async function C(e,t={}){let n=Date.now(),r=m.resolve(e.stateDir),i=h.get(r);if(!(i!==void 0&&n-i<3e4))return h.set(r,n),await S(e,`provider-startup`,t)}async function w(){return await r(async()=>await s({timeoutMs:1500}))}function T(e){return e.split(/[,\n]/).map(e=>e.trim()).filter(Boolean)}function E(e){if(e===void 0||e.trim()===``)return;let t=Number.parseInt(e,10);return Number.isInteger(t)&&t>0?t:void 0}function D(e){return/\b(?:Google Chrome for Testing|Chrome for Testing|Chromium|chrome|chrome\.exe|chromium|chromium-browser|headless_shell)\b/i.test(e)}function O(e){return k([m.join(e.homeDir,`.agent-browser`,`browsers`),m.join(e.runtimeHomeDir,`.agent-browser`,`browsers`)])}function k(e){return[...new Set(e.filter(e=>e.length>0))]}function A(e,t){return{count:0,pids:[],processes:[],signalPids:[],skipped:{reason:e,...t}}}function j(e,t){let n=M(e.command),r=`${M(m.resolve(t)).replace(/\/+$/,``)}/`;return n.includes(r)}function M(e){return e.replace(/\\/g,`/`)}function N(e){let t=ee(e.socketDir).map(e=>P(e)).filter(e=>e!==void 0);return t.length>0?Math.max(...t):void 0}function ee(e){try{return f.readdirSync(e).map(t=>m.join(e,t))}catch{return[]}}function P(e){try{return f.statSync(e).mtimeMs}catch{return}}var F=e({doctorManagedAgentBrowser:()=>H,getManagedAgentBrowserStatus:()=>U,mapManagedAgentBrowserError:()=>$,resolveAgentBrowserTool:()=>B,setupManagedAgentBrowser:()=>V});const I=`0.27.1`,L=`agent-browser`,R=5*6e4,z=6e4;async function B(e){let t=U(e);if(t.installed)return W(t);throw Q(t)}async function V(e){let t=U(e);te(t.nodeMajor);let n=await u({lockDirPath:m.join(t.installDir,`..`,`.agent-browser-install.lock`),owner:{pid:process.pid,startTime:c(process.pid),acquiredAtMs:Date.now()},timeoutMs:R,description:`managed agent-browser setup`});try{let t=U(e);return t.installed?t:(f.mkdirSync(t.installDir,{recursive:!0}),await G(t),await K(t,[`install`],{timeoutMs:R}),await K(t,[`doctor`,`--offline`,`--quick`],{timeoutMs:z}),Y(t),U(e))}finally{await n()}}async function H(e){let t=U(e);if(!t.installed)throw Q(t);return{status:t,...await K(t,[`doctor`,`--offline`,`--quick`],{timeoutMs:z,allowFailure:!0})}}function U(e){let t=e.stateDir??process.env.AGENT_DEVICE_STATE_DIR??oe(),n=m.join(t,`tools`,`agent-browser`,I),r=Z(n),i=m.join(n,`home`),a=ne(n),o=re(n),s=ae(r)&&X(n),c=Number.parseInt(process.versions.node.split(`.`)[0]??`0`,10);return{version:I,stateDir:t,installDir:n,binaryPath:r,homeDir:i,runtimeHomeDir:a,socketDir:o,installed:s,nodeMajor:c,nodeSupported:c>=24}}function W(e){if(!e.installed)throw Q(e);return{command:e.binaryPath,env:q(e,process.env)}}async function G(e){let t=m.join(e.installDir,`package`);f.mkdirSync(t,{recursive:!0}),await a(`npm`,[`install`,`--prefix`,t,`--no-audit`,`--no-fund`,`--no-save`,`${L}@${I}`],{env:process.env,timeoutMs:R})}async function K(e,t,n){return await a(e.binaryPath,t,{allowFailure:n.allowFailure,env:q(e,process.env),timeoutMs:n.timeoutMs})}function q(e,t){return f.mkdirSync(e.homeDir,{recursive:!0}),J(e),f.mkdirSync(e.socketDir,{recursive:!0}),{...t,HOME:e.runtimeHomeDir,AGENT_BROWSER_SOCKET_DIR:e.socketDir,AGENT_BROWSER_IDLE_TIMEOUT_MS:String(v(t)),AGENT_BROWSER_ARGS:_(t.AGENT_BROWSER_ARGS,e)}}function J(e){if(e.runtimeHomeDir!==e.homeDir){f.mkdirSync(m.dirname(e.runtimeHomeDir),{recursive:!0});try{let t=f.lstatSync(e.runtimeHomeDir);if(t.isSymbolicLink()&&f.readlinkSync(e.runtimeHomeDir)===e.homeDir||t.isDirectory())return;t.isSymbolicLink()&&f.unlinkSync(e.runtimeHomeDir)}catch(e){if(!ie(e))throw e}try{f.symlinkSync(e.homeDir,e.runtimeHomeDir,`dir`)}catch{f.mkdirSync(e.runtimeHomeDir,{recursive:!0})}}}function Y(e){f.writeFileSync(m.join(e.installDir,`manifest.json`),JSON.stringify({package:L,version:I,node:process.version,installedAt:new Date().toISOString()},null,2),`utf8`)}function X(e){return f.existsSync(m.join(e,`manifest.json`))}function Z(e){let t=m.join(e,`package`);return m.join(t,`node_modules`,`.bin`,process.platform===`win32`?`agent-browser.cmd`:`agent-browser`)}function Q(e){return new t(`TOOL_MISSING`,`Managed web browser backend is not installed.`,{version:I,installDir:e.installDir,hint:e.nodeSupported===!1?`Web automation requires Node 24+; current Node is ${process.version}.`:"Run `agent-device web setup` to install the managed web backend."})}function te(e){if(!(e>=24))throw new t(`UNSUPPORTED_OPERATION`,`Web automation requires Node 24 or newer.`,{currentNode:process.version,requiredNodeMajor:24,hint:`Run agent-device with Node 24+ for web setup and web automation.`})}function ne(e){if(process.platform===`win32`)return m.join(e,`home`);let t=d.createHash(`sha1`).update(e).digest(`hex`).slice(0,12);return m.join(p.tmpdir(),`agent-device-web`,t)}function re(e){let t=d.createHash(`sha1`).update(e).digest(`hex`).slice(0,12);return m.join(p.tmpdir(),`adw`,t)}function ie(e){return typeof e==`object`&&!!e&&`code`in e&&e.code===`ENOENT`}function ae(e){try{return f.accessSync(e,f.constants.X_OK),!0}catch{return!1}}function oe(){return m.join(process.env.HOME??process.cwd(),`.agent-device`)}function $(e){let r=n(e);return r.code===`TOOL_MISSING`?new t(r.code,r.message,{...r.details??{},hint:typeof r.details?.hint==`string`?r.details.hint:"Run `agent-device web setup` to install the managed web backend."}):r}export{S as a,B as i,U as n,C as o,$ as r,x as s,F as t};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{t as e}from"./rolldown-runtime.js";import{S as t,_ as n,a as r,v as i,w as a}from"./sdk-contracts.js";import{A as o,C as s,I as c,b as l,j as u,v as d,x as f}from"./sdk-android-adb.js";import{C as p,F as ee,S as te,T as ne,_ as re,b as ie,m,p as ae,v as oe,x as se,y as h}from"./sdk-batch-runner.js";import{Xt as ce,Yt as le,bt as ue,cr as g,ct as de,lt as fe,rn as pe,sr as _,tn as me}from"./recording-export-quality.js";import{E as he,T as ge,_ as _e,_t as v,b as ve,bt as ye,ct as be,ft as xe,g as y,h as Se,k as Ce,l as we,m as Te,mt as Ee,n as De,s as Oe,u as ke,ut as Ae,w as b,x as je,y as Me,yt as Ne}from"./snapshot-diagnostics.js";import{n as x,t as Pe}from"./version.js";import{n as Fe}from"./path-resolution.js";import{a as Ie,d as Le,o as Re,u as ze}from"./sdk-device.js";import{t as S}from"./timeouts.js";import{u as Be}from"./host-process.js";import{S as Ve,c as He,g as Ue,h as We,i as Ge,m as Ke}from"./registry.js";import{n as qe,t as Je}from"./url.js";import{t as Ye}from"./client-metro-companion.js";import{n as Xe,r as Ze}from"./project-runtime.js";import{t as Qe}from"./runtime-transport.js";import{t as $e}from"./command-metadata.js";import{createHash as et,randomUUID as tt}from"node:crypto";import C from"node:fs";import nt from"node:os";import w from"node:path";import T from"node:http";import E from"node:https";import{pipeline as D}from"node:stream/promises";import{Transform as rt,Writable as it}from"node:stream";import O from"node:net";async function at(e,t){let n=C.statSync(e),r=w.basename(e),i=n.isDirectory(),a=ct(t)??st(e,n),o=[];try{let t=i?await ot(e,o):e,n=C.statSync(t);return{payloadPath:t,fileName:r,artifactType:i?`app-bundle`:`file`,platform:a,contentType:i?`application/gzip`:`application/octet-stream`,sha256:await ut(t),sizeBytes:n.size,cleanup:()=>lt(o)}}catch(e){throw lt(o),e}}async function ot(e,t){let n=C.mkdtempSync(w.join(nt.tmpdir(),`agent-device-upload-${tt()}-`));t.push(n);let r=w.join(n,`${w.basename(e)}.tar.gz`);return await d(`tar`,[`czf`,r,`-C`,w.dirname(e),w.basename(e)],{env:{...process.env,COPYFILE_DISABLE:`1`}}),r}function st(e,t){let n=e.toLowerCase();if(t.isDirectory()&&n.endsWith(`.app`)||n.endsWith(`.ipa`))return`ios`;if(n.endsWith(`.apk`)||n.endsWith(`.aab`))return`android`}function ct(e){return e===`ios`||e===`android`?e:void 0}function lt(e){for(let t of e)C.rmSync(t,{recursive:!0,force:!0})}async function ut(e){let t=et(`sha256`),r=new it({write(e,n,r){t.update(e),r()}});return await D(C.createReadStream(e),r).catch(e=>{throw new n(`COMMAND_FAILED`,`Failed to read local artifact`,{},e instanceof Error?e:void 0)}),t.digest(`hex`)}function dt(e){let{stage:t,fileName:n,startOffset:r,totalBytes:i,onProgress:a}=e,o=r,s=mt(i),c=Math.min(i,r+s);return new rt({transform(e,r,l){o=Math.min(i,o+e.byteLength),o>=c&&(a?.({type:`progress`,stage:t,fileName:n,transferredBytes:o,totalBytes:i}),c=Math.min(i,o+s)),l(null,e)}})}function ft(){if(!(process.stderr.isTTY!==!0||process.env.CI))return e=>{process.stderr.write(`${pt(e)}\n`)}}function pt(e){switch(e.type){case`start`:return`Uploading ${e.fileName} (${k(e.totalBytes)}) via ${ht(e.stage)} upload`;case`resume`:return`Resuming ${e.fileName} at ${k(e.transferredBytes)} of ${k(e.totalBytes)}`;case`progress`:return`Uploaded ${k(e.transferredBytes)} of ${k(e.totalBytes)} (${gt(e.transferredBytes,e.totalBytes)})`;case`fallback`:return`Direct upload did not complete; retrying ${e.fileName} with ${ht(e.to)} upload`}}function mt(e){return Math.max(8388608,Math.ceil(e*.05))}function ht(e){return e===`direct`?`direct`:`legacy`}function gt(e,t){return t<=0?`100%`:`${Math.min(100,Math.floor(e/t*100))}%`}function k(e){if(e<1024)return`${e} B`;let t=e/1024;if(t<1024)return`${t.toFixed(1)} KiB`;let n=t/1024;return n<1024?`${n.toFixed(1)} MiB`:`${(n/1024).toFixed(1)} GiB`}const _t=300*1e3,vt=`uploadStreamRetryable`;async function yt(e){return await A({...e,url:e.url,redirectCount:0,startOffset:0})}function bt(e){return e instanceof n&&e.details?.[vt]===!0}async function A(e){let t=e.url.protocol===`https:`?E:T,r=C.statSync(e.payloadPath).size,i=Ct(e.headers,e.startOffset,r);return St(e.progress,e.startOffset,r),await new Promise((a,o)=>{let s=!1,c=t.request({protocol:e.url.protocol,host:e.url.hostname,port:e.url.port,method:e.method,path:e.url.pathname+e.url.search,headers:i},t=>{s=!0,Ae(t).then(i=>{clearTimeout(l);let s=t.statusCode??500,c=t.headers.location;if(c&&wt(s)){if(e.redirectCount>=5){o(new n(`COMMAND_FAILED`,`Artifact upload exceeded redirect limit`,{maxRedirects:5,url:e.url.toString()}));return}let t=new URL(c,e.url);A({...e,url:t,redirectCount:e.redirectCount+1}).then(a,o);return}let u=Tt(s)?Et(t.headers,r):void 0;if(u!==void 0){if(u>=r){a({statusCode:200,statusMessage:`Upload already complete`,body:``});return}if(u<=e.startOffset){o(new n(`COMMAND_FAILED`,`Artifact upload resume did not advance`,{offset:u,previousOffset:e.startOffset,url:e.url.toString()}));return}A({...e,startOffset:u}).then(a,o);return}a({statusCode:s,statusMessage:t.statusMessage,body:i})}).catch(o)}),l=setTimeout(()=>{c.destroy(),o(new n(`COMMAND_FAILED`,e.timeoutMessage,{timeoutMs:_t,...e.timeoutHint?{hint:e.timeoutHint}:{},...xt(e)}))},_t);c.on(`error`,t=>{s||(clearTimeout(l),o(new n(`COMMAND_FAILED`,e.errorMessage,{...e.errorHint?{hint:e.errorHint}:{},...xt(e)},t)))}),c.on(`close`,()=>clearTimeout(l)),D(C.createReadStream(e.payloadPath,{start:e.startOffset}),dt({stage:e.progress?.stage??`legacy`,fileName:e.progress?.fileName??w.basename(e.payloadPath),startOffset:e.startOffset,totalBytes:r,onProgress:e.progress?.onProgress}),c).catch(e=>{s||(c.destroy(),o(new n(`COMMAND_FAILED`,`Failed to read local artifact`,{},e instanceof Error?e:Error(String(e)))))})})}function xt(e){return e.retryable===!0?{[vt]:!0}:{}}function St(e,t,n){e?.onProgress?.({type:t>0?`resume`:`start`,stage:e.stage,fileName:e.fileName,transferredBytes:t,totalBytes:n})}function Ct(e,t,n){return t<=0?e:{...e,"content-length":Math.max(n-t,0),"content-range":`bytes ${t}-${n-1}/${n}`}}function wt(e){return[301,302,303,307,308].includes(e)}function Tt(e){return e===308}function Et(e,t){let n=Dt(e[`x-upload-offset`]??e[`upload-offset`]);if(n!==void 0)return Math.min(n,t);let r=Ot(e.range)?.match(/^bytes=0-(\d+)$/);if(!r)return;let i=Number(r[1]);if(!(!Number.isSafeInteger(i)||i<0))return Math.min(i+1,t)}function Dt(e){let t=Ot(e);if(t===void 0)return;let n=Number(t);return Number.isSafeInteger(n)&&n>=0?n:void 0}function Ot(e){return Array.isArray(e)?e[0]:e}const kt=30*1e3;async function At(e){let t=await at(e.localPath,e.platform),n=e.baseUrl.endsWith(`/`)?e.baseUrl:`${e.baseUrl}/`,r=tt();try{let i=await Pt({normalizedBase:n,token:e.token,artifact:t,uploadAttemptId:r});return i?.kind===`cache-hit`?i.uploadId:i?.kind===`direct-upload`?await jt({normalizedBase:n,token:e.token,artifact:t,preflight:i,uploadAttemptId:r,onProgress:e.onProgress})||(e.onProgress?.({type:`fallback`,from:`direct`,to:`legacy`,fileName:t.fileName}),await Nt({normalizedBase:n,token:e.token,artifact:t,onProgress:e.onProgress})):await Nt({normalizedBase:n,token:e.token,artifact:t,onProgress:e.onProgress})}finally{t.cleanup()}}async function jt(e){let t=async t=>(await Lt(e.artifact,t,e.onProgress),await Rt({normalizedBase:e.normalizedBase,token:e.token,uploadId:t.uploadId}));try{return await t(e.preflight)}catch(n){if(!Mt(n))return;let r=await Pt({normalizedBase:e.normalizedBase,token:e.token,artifact:e.artifact,uploadAttemptId:e.uploadAttemptId});if(r?.kind===`cache-hit`)return r.uploadId;if(r?.kind===`direct-upload`)try{return await t(r)}catch{return}return}}function Mt(e){return e instanceof n?bt(e):!0}async function Nt(e){let{normalizedBase:t,token:r,artifact:i}=e,a=new URL(`upload`,t),o={"content-type":i.contentType,"x-artifact-type":i.artifactType,"x-artifact-filename":i.fileName,"x-artifact-hash":i.sha256,"x-artifact-hash-algorithm":`sha256`,"transfer-encoding":`chunked`};Object.assign(o,v(r));let s=await yt({url:a,method:`POST`,headers:o,payloadPath:i.payloadPath,timeoutMessage:`Artifact upload timed out`,timeoutHint:`The upload to the remote daemon exceeded the 5-minute timeout.`,errorMessage:`Failed to upload artifact to remote daemon`,errorHint:`Verify the remote daemon is reachable and supports artifact uploads.`,progress:{stage:`legacy`,fileName:i.fileName,onProgress:e.onProgress}});try{let e=JSON.parse(s.body);if(!e.ok||!e.uploadId)throw new n(`COMMAND_FAILED`,`Upload failed: ${s.body}`);return e.uploadId}catch(e){throw e instanceof n?e:new n(`COMMAND_FAILED`,`Invalid upload response: ${s.body}`)}}async function Pt(e){let t=new URL(`upload/preflight`,e.normalizedBase),n={"content-type":`application/json`};Object.assign(n,v(e.token));let r=await fetch(t,{method:`POST`,headers:n,signal:AbortSignal.timeout(kt),body:JSON.stringify({uploadAttemptId:e.uploadAttemptId,sha256:e.artifact.sha256,fileName:e.artifact.fileName,sizeBytes:e.artifact.sizeBytes,artifactType:e.artifact.artifactType,...e.artifact.platform?{platform:e.artifact.platform}:{},contentType:e.artifact.contentType})}).catch(()=>void 0);if(r?.ok)return Ft(await r.json().catch(()=>void 0))}function Ft(e){if(!e||typeof e!=`object`)return;let t=e;if(t.ok!==!0||typeof t.uploadId!=`string`)return;if(t.cacheHit===!0)return{kind:`cache-hit`,uploadId:t.uploadId};let n=t.upload;if(!n||typeof n.url!=`string`)return;let r=n.headers??{};if(It(r))return{kind:`direct-upload`,uploadId:t.uploadId,url:n.url,headers:r}}function It(e){return!e||typeof e!=`object`||Array.isArray(e)?!1:Object.values(e).every(e=>typeof e==`string`)}async function Lt(e,t,r){let i=await yt({url:new URL(t.url),method:`PUT`,headers:t.headers,payloadPath:e.payloadPath,timeoutMessage:`Direct artifact upload timed out`,timeoutHint:`The direct upload ticket did not accept the artifact within the timeout.`,errorMessage:`Failed to upload artifact with direct upload ticket`,retryable:!0,progress:{stage:`direct`,fileName:e.fileName,onProgress:r}});if(i.statusCode<200||i.statusCode>=300)throw new n(`COMMAND_FAILED`,`Direct artifact upload failed`,{statusCode:i.statusCode,statusMessage:i.statusMessage})}async function Rt(e){let t=new URL(`upload/finalize`,e.normalizedBase),r={"content-type":`application/json`};Object.assign(r,v(e.token));let i=await fetch(t,{method:`POST`,headers:r,signal:AbortSignal.timeout(kt),body:JSON.stringify({uploadId:e.uploadId})}).catch(e=>{throw new n(`COMMAND_FAILED`,`Failed to finalize direct artifact upload`,{},e)});if(!i.ok)throw new n(`COMMAND_FAILED`,`Direct artifact upload finalize failed`,{status:i.status,statusText:i.statusText});let a=await i.json().catch(()=>void 0);if(!a?.ok||!a.uploadId)throw new n(`COMMAND_FAILED`,`Invalid upload finalize response`);return a.uploadId}async function zt(e,t){let n=[...e.positionals??[]],r=e.flags?{...e.flags}:void 0,i=e.meta?.installSource,a={},o,s=ft();if(!tn(t))return Wt({positionals:n,flags:r,installSource:i,uploadedArtifactId:o,clientArtifactPaths:a});r=Vt(e,n,r,a);let c=await Gt(e,t,s);return c&&(i=c.installSource,o=c.uploadedArtifactId??o),e.command!==`install`&&e.command!==`reinstall`||(o=await Bt(e,t,n,s)??o),Wt({positionals:n,flags:r,installSource:i,uploadedArtifactId:o,clientArtifactPaths:a})}async function Bt(e,t,n,r){let i=n.length===1?0:1,a=n[i];if(a===void 0)return;if(a.startsWith(`remote:`)){n[i]=a.slice(7);return}let o=Ut(a,e.meta?.cwd);if(o)return await At({localPath:o,baseUrl:t.baseUrl,token:t.token,platform:e.flags?.platform,onProgress:r})}function Vt(e,t,n,r){let i=Kt(e,t);if(!i)return n;i.positionalPath!==void 0&&(t[i.positionalIndex]=i.positionalPath);let a=Ht(n,i.flagPath);return r[i.field]=i.localPath,a}function Ht(e,t){return t===void 0?e:{...e??{},out:t}}function Ut(e,t){let n=w.isAbsolute(e)?e:w.resolve(t??process.cwd(),e);return C.existsSync(n)?n:void 0}function Wt(e){return{positionals:e.positionals,flags:e.flags,installSource:e.installSource,uploadedArtifactId:e.uploadedArtifactId,...Object.keys(e.clientArtifactPaths).length>0?{clientArtifactPaths:e.clientArtifactPaths}:{}}}async function Gt(e,t,n){let r=e.meta?.installSource;if(e.command!==`install_source`||!r||r.kind!==`path`)return null;let i=r.path.trim();if(!i)return{installSource:r};if(i.startsWith(`remote:`))return{installSource:{...r,path:i.slice(7)}};let a=w.isAbsolute(i)?i:w.resolve(e.meta?.cwd??process.cwd(),i);if(!C.existsSync(a))return{installSource:{...r,path:a}};let o=await At({localPath:a,baseUrl:t.baseUrl,token:t.token,platform:e.flags?.platform,onProgress:n});return{installSource:{...r,path:a},uploadedArtifactId:o}}function Kt(e,t){if(e.command===`screenshot`){let n=Xt(e,`path`,`.png`);return t[0]?{field:`path`,localPath:n,positionalIndex:0,positionalPath:M(`screenshot`,`.png`)}:{field:`path`,localPath:n,positionalIndex:0,flagPath:M(`screenshot`,`.png`)}}if(e.command===`record`&&(t[0]??``).toLowerCase()===`start`){if(!Jt(e)&&e.flags?.platform===void 0)return null;let t=qt(e),n=Yt(Xt(e,`outPath`,t,1),e);return{field:`outPath`,localPath:n,positionalIndex:1,positionalPath:M(`recording`,w.extname(n)||t)}}return null}function qt(e){return Ee(e.flags?.platform)}function Jt(e){return j(e.positionals?.[1])||j(e.flags?.out)}function Yt(e,t){return t.flags?.platform===`web`?xe(e,qt(t)):e}function Xt(e,t,n,r=0){let i=e.positionals?.[r]??e.flags?.out,a=`${t===`path`?`screenshot`:`recording`}-${Date.now()}${n}`,o=j(i)?i:a;return w.isAbsolute(o)?o:w.resolve(e.meta?.cwd??process.cwd(),o)}function j(e){return typeof e==`string`&&e.trim().length>0}function M(e,t){let n=t.startsWith(`.`)?t:`.${t}`;return w.posix.join(`/tmp`,`agent-device-${e}-${Date.now()}-${Math.random().toString(36).slice(2,8)}${n}`)}async function Zt(e,t,n){let r=Array.isArray(n.data?.artifacts)?n.data.artifacts:[];if(r.length===0||!e.baseUrl)return n;let i=n.data?{...n.data}:{},a=[];for(let n of r){if(!n||typeof n!=`object`||typeof n.artifactId!=`string`){a.push(n);continue}let r=Qt(n,t);await $t({baseUrl:e.baseUrl,token:e.token,artifactId:n.artifactId,destinationPath:r,requestScope:t.meta}),i[n.field]=r,a.push({...n,localPath:r})}return i.artifacts=a,{ok:!0,data:i}}function Qt(e,t){if(e.localPath&&e.localPath.trim().length>0)return e.localPath;let n=t.meta?.clientArtifactPaths?.[e.field];if(n&&n.trim().length>0)return n;let r=e.fileName?.trim()||`${e.field}-${Date.now()}`;return w.resolve(t.meta?.cwd??process.cwd(),r)}async function $t(e){let t=new URL(en(e.baseUrl,e.artifactId)),r=t.protocol===`https:`?E:T;await C.promises.mkdir(w.dirname(e.destinationPath),{recursive:!0}),await new Promise((i,a)=>{let o=!1,s=e.timeoutMs??9e4,c=t=>{if(!o){if(o=!0,clearTimeout(u),t){C.promises.rm(e.destinationPath,{force:!0}).finally(()=>a(t));return}i()}},l=r.request({protocol:t.protocol,host:t.hostname,port:t.port,method:`GET`,path:t.pathname+t.search,headers:{...v(e.token),...Ne(e.requestScope?.tenantId)}},t=>{if((t.statusCode??500)>=400){let r=``;t.setEncoding(`utf8`),t.on(`data`,e=>{r+=e}),t.on(`end`,()=>{c(new n(`COMMAND_FAILED`,`Failed to download remote artifact`,{artifactId:e.artifactId,statusCode:t.statusCode,requestId:e.requestScope?.requestId,body:r}))});return}t.on(`aborted`,()=>{c(new n(`COMMAND_FAILED`,`Remote artifact download was interrupted`,{artifactId:e.artifactId,requestId:e.requestScope?.requestId}))}),D(t,C.createWriteStream(e.destinationPath)).then(()=>c(),e=>c(e instanceof Error?e:Error(String(e))))}),u=setTimeout(()=>{let t=new n(`COMMAND_FAILED`,`Remote artifact download timed out`,{artifactId:e.artifactId,requestId:e.requestScope?.requestId,timeoutMs:s});c(t),l.destroy(t)},s);l.on(`error`,t=>{if(t instanceof n){c(t);return}c(new n(`COMMAND_FAILED`,`Failed to download remote artifact`,{artifactId:e.artifactId,requestId:e.requestScope?.requestId,timeoutMs:s},t instanceof Error?t:void 0))}),l.end()})}function en(e,t){let n=e.endsWith(`/`)?e:`${e}/`;return new URL(`artifacts/${encodeURIComponent(t)}`,n).toString()}function tn(e){return typeof e.baseUrl==`string`&&e.baseUrl.length>0}const nn=3e3,rn=1e3;function N(e){let t=pn(e);if(!t||typeof t!=`object`)return null;let n=t,r=an(n);if(!r)return null;let i=on(n);return i?{token:r,...i,transport:sn(n.transport),pid:F(n.pid)??0,version:P(n.version),codeSignature:P(n.codeSignature),processStartTime:P(n.processStartTime)}:null}function an(e){return typeof e.token==`string`&&e.token.length>0?e.token:null}function on(e){let t=F(e.port),n=F(e.httpPort);return t===void 0&&n===void 0?null:{port:t,httpPort:n}}function sn(e){return e===`socket`||e===`http`||e===`dual`?e:void 0}function P(e){return typeof e==`string`?e:void 0}function F(e){return Number.isInteger(e)&&Number(e)>0?Number(e):void 0}function I(e){let t=pn(e);if(!t||typeof t!=`object`)return null;let n=t;return Number.isInteger(n.pid)&&Number(n.pid)>0?{pid:Number(n.pid),processStartTime:typeof n.processStartTime==`string`?n.processStartTime:void 0,startedAt:typeof n.startedAt==`number`?n.startedAt:void 0}:null}function L(e){mn(e)}function R(e){mn(e)}function cn(e){let t=B(e);if(!t.hasLock||t.hasInfo)return;let n=I(e.lockPath);if(!n){R(e.lockPath);return}y(n.pid,n.processStartTime)||R(e.lockPath)}async function z(e,t,n={}){let r=n.stopLiveProcesses??!0,i={reason:t,removedInfo:!1,removedLock:!1,stoppedInfoProcess:!1,stoppedLockProcess:!1};try{let t=C.existsSync(e.infoPath),n=N(e.infoPath);if(n){let t=y(n.pid,n.processStartTime);t&&!r?i.retainedInfoProcess=!0:(t&&(await V(n),i.stoppedInfoProcess=!0),L(e.infoPath),i.removedInfo=!0)}else t&&(L(e.infoPath),i.removedInfo=!0);let a=C.existsSync(e.lockPath),o=I(e.lockPath);if(o){let t=y(o.pid,o.processStartTime);t&&!r?i.retainedLockProcess=!0:(t&&(await _e(o.pid,{termTimeoutMs:nn,killTimeoutMs:rn,expectedStartTime:o.processStartTime}),i.stoppedLockProcess=!0),R(e.lockPath),i.removedLock=!0)}else a&&(R(e.lockPath),i.removedLock=!0)}catch(e){i.error=e instanceof Error?e.message:String(e)}return u({level:i.error?`warn`:`info`,phase:`daemon_startup_metadata_cleanup`,data:i}),i}function B(e){return{hasInfo:C.existsSync(e.infoPath),hasLock:C.existsSync(e.lockPath)}}async function ln(e){let t=B(e);if(!t.hasLock||t.hasInfo)return!1;let n=I(e.lockPath);return!n||!y(n.pid,n.processStartTime)?(R(e.lockPath),!0):!1}async function V(e){await _e(e.pid,{termTimeoutMs:nn,killTimeoutMs:rn,expectedStartTime:e.processStartTime})}function un(e){return typeof e.baseUrl==`string`&&e.baseUrl.length>0}function dn(e,t=b(process.env.AGENT_DEVICE_STATE_DIR)){let n=fn(t);return e.hasLock&&!e.hasInfo?`agent-device attempted to clean stale daemon metadata automatically, but ${t.lockPath} still exists without ${t.infoPath}. Retry with --debug; if this persists after confirming no agent-device daemon process is running, run: ${n}`:e.hasLock&&e.hasInfo?`agent-device attempted to clean stale daemon metadata automatically, but ${t.infoPath} and ${t.lockPath} still remain. Retry with --debug; if this persists after confirming no agent-device daemon process is running, run: ${n}`:e.hasInfo?`agent-device did not observe reachable daemon metadata after retrying, and ${t.infoPath} still remains. Stale metadata was cleaned automatically when safe; retry with --debug. If this persists after confirming no agent-device daemon process is running, run: ${n}`:`agent-device did not observe reachable daemon metadata after retrying. Stale metadata was cleaned automatically when safe; retry with --debug and check daemon diagnostics logs. If stale metadata returns after confirming no agent-device daemon process is running, run: ${n}`}function fn(e){return`rm -f ${Me(e.infoPath)} ${Me(e.lockPath)}`}function pn(e){if(!C.existsSync(e))return null;try{return JSON.parse(C.readFileSync(e,`utf8`))}catch{return null}}function mn(e){try{C.existsSync(e)&&C.unlinkSync(e)}catch{}}function hn(e,t){if(t.onProgress){t.onProgress(e);return}e.type===`command`&&(t.req.command===`doctor`&&Ve(),process.stderr.write(`${e.message}\n`))}function H(e,t,r){return new n(`COMMAND_FAILED`,`Invalid daemon response`,{requestId:e.meta?.requestId,line:t},r instanceof Error?r:void 0)}function gn(e){let t=t=>(e.onError(t),!0);return{handleLine(n){let r;try{r=JSON.parse(n)}catch(r){return t(H(e.req,n,r))}if(we(r))try{return hn(r.event,{req:e.req,onProgress:e.onProgress}),!1}catch(e){return t(e)}return ke(r)?(e.onResponse(r.response),!0):e.responseFormat===`socket-legacy`?(e.onResponse(r),!0):t(H(e.req,n,Error(`Missing daemon progress response envelope`)))}}}function _n(e,t){return Te(e)&&String(Array.isArray(t)?t.join(`,`):t??``).includes(`application/x-ndjson`)}function vn(e,t){let{req:n,isSettled:r,resolve:i,reject:a,clearTimeout:o}=t,s=``,c=gn({req:n,onProgress:t.onProgress,responseFormat:`socket-legacy`,onResponse(t){o(),i(t),e.end()},onError(e){o(),a(e)}});e.setEncoding(`utf8`),e.on(`data`,e=>{if(r())return;let t=Se(s,e);s=t.buffer;for(let e of t.lines)if(c.handleLine(e))return})}function yn(e,t){let{req:r,handleResponseBody:i,reject:a,clearTimeout:o}=t,s=``,c=!1,l=gn({req:r,onProgress:t.onProgress,responseFormat:`ndjson-envelope`,onResponse(e){c=!0,o(),i(JSON.stringify(e))},onError(e){c=!0,o(),a(e)}});e.setEncoding(`utf8`),e.on(`data`,e=>{if(c)return;let t=Se(s,e);s=t.buffer;for(let e of t.lines)if(e&&l.handleLine(e))return}),e.on(`end`,()=>{if(c)return;let e=s.trim();e&&l.handleLine(e)||(c=!0,o(),a(H(r,e)))}),e.on(`error`,e=>{c||(c=!0,o(),a(new n(`COMMAND_FAILED`,`Failed to read daemon response`,{requestId:r.meta?.requestId},e instanceof Error?e:void 0)))})}function bn(e,t){let{info:r,req:i,resolve:a,reject:o}=t;try{let t=xn(e);if(t.error){o(Sn(t.error,i.meta?.requestId));return}if(!t.result||typeof t.result!=`object`){o(new n(`COMMAND_FAILED`,`Invalid daemon RPC response`,{requestId:i.meta?.requestId}));return}Cn(r,i,t.result,a,o)}catch(t){o(new n(`COMMAND_FAILED`,`Invalid daemon response`,{requestId:i.meta?.requestId,line:e},t instanceof Error?t:void 0))}}function xn(e){return JSON.parse(e)}function Sn(e,t){let r=e.data??{};return new n(a(r.code==null?void 0:String(r.code),`COMMAND_FAILED`),String(r.message??e.message??`Daemon RPC request failed`),{...typeof r.details==`object`&&r.details?r.details:{},hint:typeof r.hint==`string`?r.hint:void 0,diagnosticId:typeof r.diagnosticId==`string`?r.diagnosticId:void 0,logPath:typeof r.logPath==`string`?r.logPath:void 0,retriable:typeof r.retriable==`boolean`?r.retriable:void 0,supportedOn:typeof r.supportedOn==`string`?r.supportedOn:void 0,requestId:t})}async function Cn(e,t,n,r,i){try{r(e.baseUrl&&n.ok?await Zt(e,t,n):n)}catch(e){i(e)}}function wn(e,t){let n=e.meta?.requestId??o();return Tn(e.command)?{jsonrpc:`2.0`,id:n,method:En(e.command),params:Dn(e,e.command,t)}:{jsonrpc:`2.0`,id:n,method:`agent_device.command`,params:e}}function Tn(e){return e===`lease_allocate`||e===`lease_heartbeat`||e===`lease_release`}function En(e){switch(e){case`lease_allocate`:return`agent_device.lease.allocate`;case`lease_heartbeat`:return`agent_device.lease.heartbeat`;case`lease_release`:return`agent_device.lease.release`}}function Dn(e,t,n){return me(ce(e),t,{includeTokenParam:n.includeTokenParam,token:e.token,session:e.session})}const On=[`xcodebuild .*AgentDeviceRunnerUITests/RunnerTests/testCommand`,`xcodebuild .*AgentDeviceRunner\\.env\\.session-`,`xcodebuild build-for-testing .*apple/runner/AgentDeviceRunner/AgentDeviceRunner\\.xcodeproj`];function kn(e){let t=ee(e.command);if(t.envelopeMs===`unbounded`)return;let n={...t,envelopeMs:t.envelopeMs};return An(n,e.positionals??[])??jn(n,e.flags)??n.envelopeMs}function An(e,t){if(e.budget.source!==`positional-parser`)return;let n=e.budget.parser(t);return n===null?void 0:Nn(e,n)}function jn(e,t){if(e.budget.source===`flag`)return e.budget.envelope===`widen`?Mn(e,e.budget,t):typeof t?.timeoutMs==`number`?t.timeoutMs:e.envelopeMs}function Mn(e,t,n){if(n?.settle!==!0)return e.envelopeMs;let r=typeof n.timeoutMs==`number`?n.timeoutMs:t.defaultBudgetMs;return typeof r==`number`?Pn(e,r):e.envelopeMs}function Nn(e,t){return Math.max(e.envelopeMs,t+Bn)}function Pn(e,t){return Math.max(e.envelopeMs,e.envelopeMs+t+Bn)}function Fn(e,t,r,i,a,o){let s=a?{terminated:0}:Rn(),c=!a&&In(i),l=c?zn(e,t):{forcedKill:!1};return u({level:`error`,phase:`daemon_request_timeout`,data:{timeoutMs:o,requestId:r,command:i,timedOutRunnerPidsTerminated:s.terminated,timedOutRunnerCleanupError:s.error,daemonPidReset:c?e.pid:void 0,daemonPidForceKilled:c?l.forcedKill:void 0,daemonPreservedAfterTimeout:!a&&!c,daemonBaseUrl:e.baseUrl}}),new n(`COMMAND_FAILED`,`Daemon request timed out`,{timeoutMs:o,requestId:r,hint:Ln({remote:a,resetDaemon:c,command:i})})}function In(e){return ee(e).onTimeout===`reset-daemon`}function Ln(e){let{remote:t,resetDaemon:n,command:r}=e;if(t)return`Retry with --debug and verify the remote daemon URL, auth token, and remote host logs.`;if(!n){let e=r===g.snapshot?` If this was the first Apple-platform snapshot on the device, run agent-device prepare ios-runner with the same --platform before snapshot/test so runner startup is handled explicitly.`:``;return`Retry with --debug and check daemon diagnostics logs. The timed-out ${r??`request`} request was canceled and Apple runner work was aborted when detected; the daemon was kept alive so the session can still be closed or inspected.${e}`}return`Retry with --debug and check daemon diagnostics logs. Timed-out Apple runner xcodebuild processes were terminated when detected.`}function Rn(){let e=0;try{for(let t of On)s(`pkill`,[`-f`,t],{allowFailure:!0}).exitCode===0&&(e+=1);return{terminated:e}}catch(t){return{terminated:e,error:t instanceof Error?t.message:String(t)}}}function zn(e,t){let n=!1;try{y(e.pid,e.processStartTime)&&(process.kill(e.pid,`SIGKILL`),n=!0)}catch{V(e)}finally{L(t.infoPath),R(t.lockPath)}return{forcedKill:n}}const Bn=3e4,Vn=`Daemon HTTP endpoint is unavailable`,Hn=`Daemon socket endpoint is unavailable`;async function Un(e,t){let n=Qn(e,t);if(await Wn(e,n))return!0;let r=$n(e,t,n);return r?await Wn(e,r):!1}async function Wn(e,t){return t===`http`?await Kn(e):await Gn(e.port)}function Gn(e){return e?new Promise(t=>{let n=!1,r=O.createConnection({host:`127.0.0.1`,port:e},()=>{i(!0)}),i=e=>{n||(n=!0,r.destroy(),t(e))};r.setTimeout(500),r.on(`timeout`,()=>{i(!1)}),r.on(`error`,()=>{i(!1)})}):Promise.resolve(!1)}function Kn(e){return Jn(e).then(e=>e.reachable)}async function qn(e){let t=await Jn(e);if(!e.baseUrl||!t.reachable)return t;if(typeof t.rpcProtocolVersion==`number`&&t.rpcProtocolVersion!==2)throw new n(`COMMAND_FAILED`,`Remote daemon RPC protocol is incompatible`,{daemonBaseUrl:e.baseUrl,clientVersion:x(),remoteVersion:t.version,remoteService:t.service,supportedRpcProtocolVersion:2,remoteRpcProtocolVersion:t.rpcProtocolVersion,hint:`Upgrade agent-device on the client or remote host so both support the same daemon RPC protocol.`});return t}function Jn(e){let t=e.baseUrl?ye(e.baseUrl,`health`):e.httpPort?`http://127.0.0.1:${e.httpPort}/health`:null;if(!t)return Promise.resolve({reachable:!1});let n=new URL(t),r=n.protocol===`https:`?E:T,i=e.baseUrl?3e3:500;return new Promise(t=>{let a=e.baseUrl?v(e.token):{},o=r.request({protocol:n.protocol,host:n.hostname,port:n.port,path:n.pathname+n.search,method:`GET`,timeout:i,headers:a},e=>{let n=``;e.setEncoding(`utf8`),e.on(`data`,e=>{n+=e}),e.on(`end`,()=>{let r=e.statusCode??500;t({reachable:r<500,statusCode:r,...Yn(n)})})});o.on(`timeout`,()=>{o.destroy(),t({reachable:!1})}),o.on(`error`,()=>{t({reachable:!1})}),o.end()})}function Yn(e){try{let t=JSON.parse(e);return{service:typeof t.service==`string`?t.service:void 0,version:typeof t.version==`string`?t.version:void 0,rpcProtocolVersion:typeof t.rpcProtocolVersion==`number`?t.rpcProtocolVersion:void 0}}catch{return{}}}async function Xn(e,t,n,r,i,a={}){let o=Qn(e,n);try{return await Zn(e,t,r,i,o,a)}catch(s){let c=$n(e,n,o);if(!c||!er(s,o))throw s;return await Zn(e,t,r,i,c,a)}}async function Zn(e,t,n,r,i,a){return i===`http`?await ir(e,t,n,r,a):await rr(e,t,n,r,a)}function Qn(e,t){if(e.baseUrl){if(t===`socket`)throw new n(`COMMAND_FAILED`,`Remote daemon endpoint only supports HTTP transport`,{daemonBaseUrl:e.baseUrl});return`http`}if(t===`http`||t===`socket`)return tr(e,t);let r=(e.transport===`socket`||e.transport===`dual`?[`socket`,`http`]:[`http`,`socket`]).find(t=>U(e,t));if(r)return r;throw new n(`COMMAND_FAILED`,`Daemon metadata has no reachable transport`)}function U(e,t){return t===`http`?!!e.httpPort:!!e.port}function $n(e,t,n){if(t!==`auto`||e.baseUrl)return null;let r=n===`socket`?`http`:`socket`;return U(e,r)?r:null}function er(e,t){return t===`socket`&&e instanceof n&&e.code===`COMMAND_FAILED`&&e.message===`Failed to communicate with daemon`&&e.details?.daemonSocketRequestWritten===!1}function tr(e,t){if(U(e,t))return t;throw new n(`COMMAND_FAILED`,t===`http`?Vn:Hn)}function nr(e,t,r,i={}){return u({level:`error`,phase:`daemon_request_socket_error`,data:{requestId:t,message:e instanceof Error?e.message:String(e)}}),new n(`COMMAND_FAILED`,`Failed to communicate with daemon`,{...i,requestId:t,hint:r?`Retry command. If this persists, verify the remote daemon URL, auth token, and remote host reachability.`:`Retry command. If this persists, clean stale daemon metadata and start a fresh session.`},e instanceof Error?e:void 0)}async function rr(e,t,r,i,a){let o=e.port;if(!o)throw new n(`COMMAND_FAILED`,Hn);return new Promise((n,s)=>{let c=!1,l=O.createConnection({host:`127.0.0.1`,port:o},()=>{c=!0,l.write(`${JSON.stringify(t)}\n`)}),u=!1,d=typeof i==`number`?setTimeout(()=>{u=!0,l.destroy(),s(Fn(e,r,t.meta?.requestId,t.command,!1,i))},i):void 0;vn(l,{req:t,onProgress:a.onProgress,isSettled:()=>u,clearTimeout:()=>{d&&clearTimeout(d)},resolve:e=>{u=!0,n(e)},reject:e=>{u=!0,s(e)}}),l.on(`error`,e=>{u||(u=!0,d&&clearTimeout(d),s(nr(e,t.meta?.requestId,!1,{daemonSocketRequestWritten:c})))})})}async function ir(e,t,r,i,a){let o=e.baseUrl?new URL(ye(e.baseUrl,`rpc`)):e.httpPort?new URL(`http://127.0.0.1:${e.httpPort}/rpc`):null;if(!o)throw new n(`COMMAND_FAILED`,Vn);let s=JSON.stringify(wn(t,{includeTokenParam:!e.baseUrl})),c={"content-type":`application/json`,"content-length":Buffer.byteLength(s)};return e.baseUrl&&Object.assign(c,v(e.token)),await new Promise((l,u)=>{let d=(o.protocol===`https:`?E:T).request({protocol:o.protocol,host:o.hostname,port:o.port,method:`POST`,path:o.pathname+o.search,headers:c},r=>{if(_n(t,r.headers?.[`content-type`])){yn(r,{req:t,onProgress:a.onProgress,reject:u,clearTimeout:()=>{p&&clearTimeout(p)},handleResponseBody:n=>bn(n,{info:e,req:t,resolve:l,reject:u})});return}Ae(r).then(n=>{p&&clearTimeout(p),bn(n,{info:e,req:t,resolve:l,reject:u})}).catch(e=>{p&&clearTimeout(p),u(new n(`COMMAND_FAILED`,`Failed to read daemon response`,{requestId:t.meta?.requestId},e instanceof Error?e:void 0))})}),f=un(e),p=typeof i==`number`?setTimeout(()=>{d.destroy(),u(Fn(e,r,t.meta?.requestId,t.command,f,i))},i):void 0;d.on(`error`,e=>{p&&clearTimeout(p),u(nr(e,t.meta?.requestId,f))}),d.write(s),d.end()})}const W=15e3,G=new O.BlockList;G.addSubnet(`127.0.0.0`,8,`ipv4`),G.addAddress(`::1`,`ipv6`),G.addSubnet(`::ffff:127.0.0.0`,104,`ipv6`);function ar(e){let t=or(e),n=sr(e),r=cr(e,n.remoteBaseUrl),i=lr(e,t,n.rawBaseUrl);return{paths:b(i?ur():t),transportPreference:r.preference,serverMode:r.serverMode,ownedStateDir:i,remoteBaseUrl:n.remoteBaseUrl,remoteAuthToken:n.authToken}}function or(e){return e.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR}function sr(e){let t=e.flags?.daemonBaseUrl??process.env.AGENT_DEVICE_DAEMON_BASE_URL,n=Nr(t),r=e.flags?.daemonAuthToken??process.env.AGENT_DEVICE_DAEMON_AUTH_TOKEN;return Pr(n,r),{rawBaseUrl:t,remoteBaseUrl:n,authToken:r}}function cr(e,t){let r=e.flags?.daemonTransport??process.env.AGENT_DEVICE_DAEMON_TRANSPORT,i=he(r);if(t&&i===`socket`)throw new n(`INVALID_ARGS`,`Remote daemon base URL only supports HTTP transport. Remove --daemon-transport socket.`,{daemonBaseUrl:t});return{preference:i,serverMode:ge(e.flags?.daemonServerMode??process.env.AGENT_DEVICE_DAEMON_SERVER_MODE??(r===`dual`?`dual`:void 0))}}function lr(e,t,n){return wr(e.command)&&!t&&!n}function ur(){return C.mkdtempSync(w.join(nt.tmpdir(),`agent-device-replay-daemon-`))}async function dr(e){if(e.remoteBaseUrl)return await fr(e);let t=await pr(e);return t?{info:t,startedByClient:!1}:(cn(e.paths),await yr(e))}async function fr(e){let t={transport:`http`,token:e.remoteAuthToken??``,pid:0,baseUrl:e.remoteBaseUrl};if((await qn(t)).reachable)return{info:t,startedByClient:!1};throw new n(`COMMAND_FAILED`,`Remote daemon is unavailable`,{daemonBaseUrl:e.remoteBaseUrl,hint:`Verify AGENT_DEVICE_DAEMON_BASE_URL points to a reachable daemon with GET /health and POST /rpc. If this CLI was connected with connect proxy, run agent-device disconnect to return to the local daemon.`})}async function pr(e){let t=N(e.paths.infoPath);if(!t)return null;let n=await mr(t,e.transportPreference);return gr(t,n)?t:(_r(t,n,e.paths.baseDir),await V(t),L(e.paths.infoPath),null)}async function mr(e,t){try{return await Un(e,t)}catch(e){if(hr(e))return!1;throw e}}function hr(e){return e instanceof n&&e.code===`COMMAND_FAILED`&&(e.message===`Daemon HTTP endpoint is unavailable`||e.message===`Daemon socket endpoint is unavailable`)}function gr(e,t){return e.version===x()&&e.codeSignature===Ar()&&t}function _r(e,t,n){try{let r=e.version?`pid ${e.pid}, v${e.version}`:`pid ${e.pid}`,i=vr(e,t);process.stderr.write(`Replacing daemon (${r}) in ${n}: ${i}\n`)}catch{}}function vr(e,t){return e.version===x()?e.codeSignature===Ar()?t?`not reusable`:`unreachable`:`code-signature mismatch`:`version mismatch (client v${x()})`}async function yr(e){let t=0,r=[],i,a;for(let n=1;n<=2;n+=1){let o;try{o=Or(e),a={pid:o.pid}}catch(t){if(i=t instanceof Error?t.message:String(t),r.push(await z(e.paths,`start_error`)),n<2){await S(150);continue}break}let s=await Dr(W,e,o);if(s.kind===`ready`)return{info:s.info,startedByClient:!0};if(s.kind===`early_exit`){if(a=s.exit,i=jr(s.exit),r.push(await z(e.paths,`start_error`)),n<2){await S(150);continue}break}if(await ln(e.paths)){t+=1;continue}let c=B(e.paths),l=n<2,u=await z(e.paths,`startup_timeout`,{stopLiveProcesses:!1});if(r.push(u),u.retainedInfoProcess||u.retainedLockProcess){let t=await Dr(W,e,o);if(t.kind===`ready`)return{info:t.info,startedByClient:!0};t.kind===`early_exit`&&(a=t.exit,i=jr(t.exit));break}if(!l)break;!c.hasInfo&&!c.hasLock&&await S(150)}let o=B(e.paths),s=Mr(e.paths.logPath);throw new n(`COMMAND_FAILED`,`Failed to start daemon`,{kind:`daemon_startup_failed`,stateDir:e.paths.baseDir,infoPath:e.paths.infoPath,lockPath:e.paths.lockPath,logPath:e.paths.logPath,startupTimeoutMs:W,startupAttempts:2,lockRecoveryCount:t,cleanupResults:r,startError:i,daemonProcess:a,...s?{daemonLogTail:s}:{},metadataState:o,hint:dn(o,e.paths)})}async function br(e,t,n,r){if(!wr(e.command)||!t.startedByClient&&!n.ownedStateDir||un(t.info)||Sr(r)||Tr(e,r))return r;let i={pid:t.info.pid,removedInfo:!1,removedLock:!1,removedStateDir:!1,error:void 0},a=r;try{await V(t.info)}catch(e){i.error=e instanceof Error?e.message:String(e)}finally{let e=C.existsSync(n.paths.infoPath);L(n.paths.infoPath),i.removedInfo=e&&!C.existsSync(n.paths.infoPath);let t=C.existsSync(n.paths.lockPath);if(R(n.paths.lockPath),i.removedLock=t&&!C.existsSync(n.paths.lockPath),n.ownedStateDir){let e=Ce(n.paths.sessionsDir);e?a=xr(r,e):(C.rmSync(n.paths.baseDir,{recursive:!0,force:!0}),i.removedStateDir=!C.existsSync(n.paths.baseDir))}}return u({level:i.error?`warn`:`info`,phase:`daemon_replay_cleanup`,data:i}),a}function xr(e,r){if(e&&!e.ok)return e;let{sessionName:i,tombstone:a}=r,o=a.sourcePath?`re-run: replay ${a.sourcePath} --save-script`:`re-run your replay <script> --save-script from the start`;return{ok:!1,error:t(new n(`REPAIR_COMMIT_FAILED`,`The repair transaction for session "${i}" completed, but committing its healed script failed at teardown: ${a.commitFailure.message}. ${o}.`))}}function Sr(e){if(!e||e.ok||e.error.code!==`REPLAY_DIVERGENCE`)return!1;let t=e.error.details?.divergence;if(!t||typeof t!=`object`)return!1;let n=t.resume;return!n||typeof n!=`object`?!1:n.repairSessionHeld===!0}function Cr(e,t){let n=`This repair session's daemon was kept alive to continue the repair; pass --state-dir ${t} on your next command (press, replay --from, or close --save-script) to reach it.`,r=e.error.hint;return{...e,error:{...e.error,hint:r?`${r} ${n}`:n}}}function wr(e){return e===g.replay||e===g.test}function Tr(e,t){return e.command!==g.replay||!t||!t.ok?!1:t.data?.sessionActive===!0}function Er(e,t){let n=e.data??{},r=typeof n.session==`string`?n.session:void 0,i=[...t?[`--state-dir ${ve(t)}`]:[],...r?[`--session ${ve(r)}`]:[]];if(i.length===0)return e;let a=`This session's daemon was kept alive because its script left the session active; pass ${i.join(` `)} on your next command to reach it.`,o=typeof n.message==`string`?n.message:void 0;return{...e,data:{...n,hint:a,message:o?`${o} ${a}`:a}}}async function Dr(e,t,n){let r=Date.now(),i;for(n.exited.then(e=>{i=e});Date.now()-r<e;){if(i)return{kind:`early_exit`,exit:i};let e=N(t.paths.infoPath);if(e&&await Un(e,t.transportPreference))return{kind:`ready`,info:e};if(i)return{kind:`early_exit`,exit:i};await S(100)}return{kind:`timeout`}}function Or(e){let t=kr(),n=t.useSrc?[`--experimental-strip-types`,t.srcPath]:[t.distPath],r={...process.env,AGENT_DEVICE_STATE_DIR:e.paths.baseDir,AGENT_DEVICE_DAEMON_SERVER_MODE:e.serverMode};C.mkdirSync(e.paths.baseDir,{recursive:!0});let i=C.openSync(e.paths.logPath,`a`),a=C.openSync(e.paths.logPath,`a`);try{return f(process.execPath,n,{env:r,stdio:[`ignore`,i,a]})}finally{C.closeSync(i),C.closeSync(a)}}function kr(){let e=Pe(),t=[w.join(e,`dist`,`src`,`internal`,`daemon.js`),w.join(e,`dist`,`src`,`daemon.js`)],r=t[0];if(r===void 0)throw new n(`COMMAND_FAILED`,`Daemon dist path list is empty`);let i=t.find(e=>C.existsSync(e))??r,a=w.join(e,`src`,`daemon.ts`),o=t.some(e=>C.existsSync(e)),s=C.existsSync(a);if(!o&&!s)throw new n(`COMMAND_FAILED`,`Daemon entry not found`,{distPaths:t,srcPath:a});return{root:e,distPath:i,distPaths:t,srcPath:a,useSrc:(process.execArgv.includes(`--experimental-strip-types`)||!o)&&s}}function Ar(){let e=kr();return je(e.useSrc?e.srcPath:e.distPath,e.root)}function jr(e){return e.error?`daemon process ${e.pid} failed to start: ${e.error}`:e.signal?`daemon process ${e.pid} exited before readiness with signal ${e.signal}`:`daemon process ${e.pid} exited before readiness with code ${e.exitCode??0}`}function Mr(e){try{if(!C.existsSync(e))return;let t=C.statSync(e);if(t.size<=0)return;let n=Math.min(t.size,64e3),r=C.openSync(e,`r`);try{let e=Buffer.alloc(n);C.readSync(r,e,0,n,t.size-n);let i=e.toString(`utf8`).trim();return i.length>0?i:void 0}finally{C.closeSync(r)}}catch{return}}function Nr(e){if(!e)return;let t;try{t=new URL(e)}catch(t){throw new n(`INVALID_ARGS`,`Invalid daemon base URL`,{daemonBaseUrl:e},t instanceof Error?t:void 0)}if(t.protocol!==`http:`&&t.protocol!==`https:`)throw new n(`INVALID_ARGS`,`Daemon base URL must use http or https`,{daemonBaseUrl:e});return t.toString().replace(/\/+$/,``)}function Pr(e,t){if(!e)return;let r=new URL(e).hostname;if(!Fr(r)&&!(typeof t==`string`&&t.trim().length>0))throw new n(`INVALID_ARGS`,`Remote daemon base URL for non-loopback hosts requires daemon authentication`,{daemonBaseUrl:e,hint:`Provide --daemon-auth-token or AGENT_DEVICE_DAEMON_AUTH_TOKEN when using a non-loopback remote daemon URL.`})}function Fr(e){let t=e.trim().toLowerCase().replace(/^\[(.*)\]$/,`$1`);return t===`localhost`?!0:O.isIPv4(t)?G.check(t,`ipv4`):O.isIPv6(t)?G.check(t,`ipv6`):!1}async function Ir(e,t={}){let n=e.meta?.requestId??o(),r=!!(e.meta?.debug||e.flags?.verbose),i=ar(e),a=kn(e),s=await c(`daemon_startup`,async()=>await dr(i),{requestId:n,session:e.session}),l=s.info,d=await zt(e,l);Br(e.command);let f={...e,positionals:d.positionals,flags:d.flags,token:l.token,meta:{...e.meta??{},requestId:n,debug:r,includeCost:e.meta?.includeCost,cwd:e.meta?.cwd,sessionExplicit:e.meta?.sessionExplicit,tenantId:e.meta?.tenantId??e.flags?.tenant,runId:e.meta?.runId??e.flags?.runId,leaseId:e.meta?.leaseId??e.flags?.leaseId,sessionIsolation:e.meta?.sessionIsolation??e.flags?.sessionIsolation,lockPolicy:e.meta?.lockPolicy,lockPlatform:e.meta?.lockPlatform,...d.uploadedArtifactId?{uploadedArtifactId:d.uploadedArtifactId}:{},...d.clientArtifactPaths?{clientArtifactPaths:d.clientArtifactPaths}:{},...d.installSource?{installSource:d.installSource}:{}}};return u({level:`info`,phase:`daemon_request_prepare`,data:{requestId:n,command:e.command,session:e.session}}),await Lr(e,s,i,async()=>zr(Rr(await c(`daemon_request`,async()=>await Xn(l,f,i.transportPreference,i.paths,a,t),{requestId:n,command:e.command}),i),e,i))}async function Lr(e,t,r,i){let a,o=!1,s;try{a=await i()}catch(e){o=!0,s=e}let c=await br(e,t,r,a);if(o)throw s;if(!c)throw new n(`COMMAND_FAILED`,`Daemon request produced no response after cleanup`);return c}function Rr(e,t){return e.ok||!t.ownedStateDir||!Sr(e)?e:Cr(e,t.paths.baseDir)}function zr(e,t,n){return!e.ok||!Tr(t,e)?e:Er(e,n.ownedStateDir?n.paths.baseDir:void 0)}function Br(e){!Vr(e)||process.stderr.isTTY!==!0||process.env.CI||process.stderr.write(e===g.reinstall?`Reinstalling...
|
|
2
|
+
`:`Installing...
|
|
3
|
+
`)}function Vr(e){return e===g.install||e===g.reinstall||e===_.installSource}const Hr=8081,Ur=`.expo/.virtual-metro-entry.bundle`;function Wr(e,t){if(e==null||e===``)return t;let r=Number.parseInt(String(e),10);if(!Number.isInteger(r)||r<1||r>65535)throw new n(`INVALID_ARGS`,`Invalid Metro port: ${String(e)}. Use 1-65535.`);return r}function Gr(e){let t=K(e.bundleUrl),r=t??e.runtime?.bundleUrl,i=!!t,a=!!K(r),o=Qe({metroHost:Kr(e,i,a),metroPort:qr(e,i,a),bundleUrl:r});if(!o)throw new n(`INVALID_ARGS`,`Unable to resolve Metro host and port for reload.`);let s=Jr(r);return{reloadUrl:Yr(o,o.scheme,`${s}/reload`),messageSocketUrl:Yr(o,o.scheme===`https`?`wss`:`ws`,`${s}/message`)}}function Kr(e,t,n){return K(e.metroHost)??(t?void 0:K(e.runtime?.metroHost))??(n?void 0:`localhost`)}function qr(e,t,n){if(e.metroPort!==void 0)return Wr(e.metroPort,Hr);if(!t)return e.runtime?.metroPort??(n?void 0:Hr)}function Jr(e){let t=K(e);if(!t)return``;let n;try{n=new URL(t)}catch{return``}let r=n.pathname.replace(/\/+$/,``),i=`/${Ur}`;if(r.endsWith(i))return r.slice(0,-i.length);let a=r.lastIndexOf(`/`);return(a>=0?r.slice(a+1):r).endsWith(`.bundle`)&&a>0?r.slice(0,a):``}function Yr(e,t,n){let r=new URL(`${t}://localhost`);return r.hostname=e.host,r.port=String(e.port),r.pathname=n,r.toString()}function K(e){return typeof e==`string`&&e.trim()?e.trim():void 0}function Xr(e){return typeof e==`string`&&e.trim()?qe(e.trim()):``}function q(e){return typeof e==`string`&&e.trim()?e.trim():void 0}function Zr(e,t,n){return Fe(e,{env:t,cwd:n})}function Qr(e){try{return C.accessSync(e,C.constants.F_OK),!0}catch{return!1}}function $r(e){try{return C.statSync(e).isDirectory()}catch{return!1}}function ei(e){let t=w.join(e,`package.json`),r=Ze(e);if(!r)throw new n(`INVALID_ARGS`,`package.json not found at ${t}`);return r}const ti=[{file:`pnpm-lock.yaml`,command:`pnpm`},{file:`yarn.lock`,command:`yarn`},{file:`bun.lock`,command:`bun`},{file:`bun.lockb`,command:`bun`},{file:`package-lock.json`,command:`npm`}];function ni(e){let t=e;for(;;){for(let{file:e,command:n}of ti)if(Qr(w.join(t,e)))return{command:n,installArgs:[`install`]};if(Qr(w.join(t,`.git`)))return null;let e=w.dirname(t);if(e===t)return null;t=e}}function ri(e){return ni(e)??{command:`npm`,installArgs:[`install`]}}function ii(e,t){if(t!==`auto`)return t;let n=Xe(e);return n===`expo`||n===`repack`?n:`react-native`}function ai(e,t){return typeof{...e.dependencies??{},...e.devDependencies??{}}[t]==`string`}function oi(e,t){return[`js`,`mjs`,`cjs`,`ts`,`mts`,`cts`].some(n=>Qr(w.join(e,`${t}.config.${n}`)))}function si(e,t){return oi(e,`rspack`)?`rspack`:oi(e,`webpack`)?`webpack`:ai(t,`@rspack/core`)?`rspack`:ai(t,`webpack`)?`webpack`:`rspack`}function ci(e,t,n){if(e==null||e===``)return t;let r=Number.parseInt(String(e),10);return Number.isInteger(r)?Math.max(r,n):t}function li(e){return e===`expo`?Ur:`index.bundle`}function ui(e,t,n){return{platform:t,bundleUrl:Je(e,t,li(n))}}function di(e,t){return{platform:t,metroHost:q(e?.metro_host),metroPort:e?.metro_port,bundleUrl:q(e?.metro_bundle_url),launchUrl:q(e?.launch_url)}}function fi(e,t){if($r(w.join(e,`node_modules`)))return{installed:!1};let n=ri(e);try{s(n.command,n.installArgs,{cwd:e,env:t})}catch(e){throw pi(e,n)}return{installed:!0,packageManager:n.command}}function pi(e,t){let r=i(e),a=`Dependency install failed using detected package manager "${t.command}". If dependencies are already installed (for example via a monorepo root install), pass --no-install-deps to skip this step, or install manually with "${t.command} ${t.installArgs.join(` `)}" from the project root.`;return new n(r.code,r.message,{...r.details??{},hint:a,packageManager:t.command},r)}async function mi(e){await S(e)}async function hi(e,t,r={}){try{let n=await fetch(e,{headers:r,signal:AbortSignal.timeout(t)});return{ok:n.ok,status:n.status,body:await n.text()}}catch(r){throw r instanceof Error&&r.name===`TimeoutError`?new n(`COMMAND_FAILED`,`Timed out fetching ${e} after ${t}ms`):r}}async function gi(e,t){try{let n=await hi(e,t);return n.ok&&n.body.includes(`packager-status:running`)}catch{return!1}}function _i(e,t,n,r){return e===`expo`?{command:`npx`,installArgs:[`expo`,`start`,`--host`,`lan`,`--port`,String(n)]}:e===`repack`?{command:`npx`,installArgs:[`react-native`,t===`webpack`?`webpack-start`:`rspack-start`,`--host`,r,`--port`,String(n)]}:{command:`npx`,installArgs:[`react-native`,`start`,`--host`,r,`--port`,String(n)]}}function vi(e,t,r,i,a,o,s){let c=_i(t,r,i,a);C.mkdirSync(w.dirname(o),{recursive:!0});let u=C.openSync(o,`a`),d=0;try{d=l(c.command,c.installArgs,{cwd:e,env:s,stdio:[`ignore`,u,u]})}finally{C.closeSync(u)}if(!Number.isInteger(d)||d<=0)throw new n(`COMMAND_FAILED`,`Failed to start React Native dev server. Expected a detached child PID.`);return{pid:d}}async function yi(e){if(!(!Number.isInteger(e)||e<=0)){try{process.kill(e,`SIGTERM`)}catch(e){let t=e.code;if(t===`ESRCH`||t===`EPERM`)return;throw e}if(!await Be(e,1e3)){try{process.kill(e,`SIGKILL`)}catch(e){let t=e.code;if(t===`ESRCH`||t===`EPERM`)return;throw e}await Be(e,1e3)}}}function bi(e,t){return{Authorization:`Bearer ${t}`,"Content-Type":`application/json`,...e.includes(`ngrok`)?{"ngrok-skip-browser-warning":`1`}:{}}}function J(e,t){let r=new n(`COMMAND_FAILED`,e);return r.retryable=t,r}function xi(e,t){return!!(e>=500||e===408||e===425||e===429||JSON.stringify(t).includes(`Metro companion is not connected`))}function Si(e){return!!(e&&typeof e==`object`&&`retryable`in e&&e.retryable===!0)}async function Ci(e){let t;try{t=await fetch(`${e.baseUrl}/api/metro/bridge`,{method:`POST`,headers:bi(e.baseUrl,e.bearerToken),body:JSON.stringify({...e.scope,...e.runtime?{ios_runtime:e.runtime}:{},timeout_ms:e.timeoutMs}),signal:AbortSignal.timeout(e.timeoutMs)})}catch(t){throw t instanceof Error&&t.name===`TimeoutError`?J(`/api/metro/bridge timed out after ${e.timeoutMs}ms calling ${e.baseUrl}/api/metro/bridge`,!0):J(t instanceof Error?t.message:String(t),!0)}let n=wi(await t.text(),t.status,e.baseUrl);if(!t.ok)throw J(`/api/metro/bridge failed (${t.status}): ${JSON.stringify(n)}`,xi(t.status,n));return Ei(n)}function wi(e,t,n){if(!e)return{};try{let t=JSON.parse(e);if(!t||typeof t!=`object`||Array.isArray(t))throw Error(`Expected a JSON object`);return t}catch(r){let i=e.slice(0,200);throw J(`/api/metro/bridge returned invalid JSON (${t}) from ${n}: ${r instanceof Error?r.message:String(r)}. body=${JSON.stringify(i)}`,xi(t,e))}}function Ti(e){return{enabled:e.enabled,baseUrl:e.base_url,statusUrl:e.status_url??``,bundleUrl:e.bundle_url??``,iosRuntime:di(e.ios_runtime,`ios`),androidRuntime:di(e.android_runtime,`android`),upstream:{bundleUrl:e.upstream.bundle_url??``,host:e.upstream.host??``,port:e.upstream.port??0,statusUrl:e.upstream.status_url??``},probe:{reachable:e.probe.reachable,statusCode:e.probe.status_code,latencyMs:e.probe.latency_ms,detail:e.probe.detail}}}function Ei(e){let t=e.data??e;if(!t||typeof t!=`object`||Array.isArray(t))throw J(`/api/metro/bridge returned malformed descriptor: Expected a JSON object.`,!1);try{return Ti(t)}catch(e){throw J(`/api/metro/bridge returned malformed descriptor: ${e instanceof Error?e.message:String(e)}`,!1)}}function Y(e,t,n,r,i){let a=[`Metro bridge is required for this run but could not be configured via ${e}/api/metro/bridge.`];return t&&a.push(`bridgeError=${t}`),n?.probe.reachable===!1&&a.push(`bridgeProbe=${n.probe.detail||`unreachable (status ${n.probe.statusCode||0})`}`),r&&r!==t&&a.push(`initialBridgeError=${r}`),i&&a.push(`metroCompanionLog=${i}`),a.join(` `)}function Di(e,t){if(!t?.iosRuntime.bundleUrl)throw new n(`COMMAND_FAILED`,Y(e,`bridge descriptor is missing ios_runtime.metro_bundle_url`,t))}function Oi(e,t,r){let i=t||q(r.AGENT_DEVICE_METRO_BEARER_TOKEN)||``,a=i||e&&q(r.AGENT_DEVICE_DAEMON_AUTH_TOKEN)||``;if(e&&!a)throw new n(`INVALID_ARGS`,`metro prepare requires proxy auth when --proxy-base-url is provided. Pass --bearer-token or set AGENT_DEVICE_METRO_BEARER_TOKEN or AGENT_DEVICE_DAEMON_AUTH_TOKEN.`);if(!e&&i)throw new n(`INVALID_ARGS`,`metro prepare requires --proxy-base-url when proxy auth is provided.`);return{proxyEnabled:!!(e&&a),proxyBaseUrl:e,proxyBearerToken:a}}function ki(e){if(!e?.tenantId||!e.runId||!e.leaseId)throw new n(`INVALID_ARGS`,`metro prepare with proxy requires tenantId, runId, and leaseId bridge scope.`);return e}function Ai(e,t){if(!(e||t))throw new n(`INVALID_ARGS`,`metro prepare requires --public-base-url <url> or --proxy-base-url <url>.`)}function ji(e,t,n){return e.runtimeFilePath?Zr(e.runtimeFilePath,t,n):null}function Mi(e,t,n,r){return Zr(e.logPath??w.join(r,`.agent-device`,`metro.log`),t,n)}function Ni(e){let t=e.env??process.env,n=process.cwd(),r=Zr(e.projectRoot??n,t,n),i=e.kind??`auto`,a=null,o;i===`auto`?(a=ei(r),o=ii(a,i)):o=i;let s=null;o===`repack`&&(a??=ei(r),s=si(r,a));let c=Xr(e.publicBaseUrl),l=Xr(e.proxyBaseUrl);Ai(c,l);let{proxyEnabled:u,proxyBaseUrl:d,proxyBearerToken:f}=Oi(l,q(e.proxyBearerToken)??``,t);return{env:t,projectRoot:r,kind:o,repackBundler:s,metroPort:Wr(e.metroPort??8081,8081),listenHost:q(e.listenHost)??`0.0.0.0`,statusHost:q(e.statusHost)??`127.0.0.1`,publicBaseUrl:c,proxyBaseUrl:d,proxyBearerToken:f,bridgeScope:u?ki(e.bridgeScope):null,startupTimeoutMs:ci(e.startupTimeoutMs,18e4,3e4),probeTimeoutMs:ci(e.probeTimeoutMs,1e4,1e3),reuseExisting:e.reuseExisting??!0,installProjectDeps:e.installDependenciesIfNeeded??!0,runtimeFilePath:ji(e,t,n),logPath:Mi(e,t,n,r)}}async function Pi(e,t,n){let r=Date.now()+t;for(;Date.now()<r;){let t=r-Date.now();if(await gi(e,Math.min(n,Math.max(t,1))))return!0;let i=Math.min(500,Math.max(r-Date.now(),0));i>0&&await mi(i)}return!1}async function Fi(e){let t=Date.now()+e.startupTimeoutMs,r=null,i=null;for(;Date.now()<t;){try{let t=await Ci({baseUrl:e.baseUrl,bearerToken:e.bearerToken,scope:e.scope,runtime:e.runtime,timeoutMs:e.probeTimeoutMs});if(t.probe.reachable!==!1)return t;r=t,i=null}catch(t){if(i=t instanceof Error?t.message:String(t),!Si(t))throw new n(`COMMAND_FAILED`,Y(e.baseUrl,i,r,e.initialBridgeError,e.companionLogPath),void 0,t)}let a=Math.min(1e3,Math.max(t-Date.now(),0));a>0&&await mi(a)}throw new n(`COMMAND_FAILED`,Y(e.baseUrl,i,r,e.initialBridgeError,e.companionLogPath))}async function Ii(e){let t=`http://${e.statusHost}:${e.metroPort}/status`;if(e.reuseExisting&&await gi(t,e.probeTimeoutMs))return{started:!1,reused:!0,pid:0,statusUrl:t};let r=vi(e.projectRoot,e.kind,e.repackBundler,e.metroPort,e.listenHost,e.logPath,e.env);if(await Pi(t,e.startupTimeoutMs,e.probeTimeoutMs))return{started:!0,reused:!1,pid:r.pid,statusUrl:t};throw await yi(r.pid).catch(()=>{}),new n(`COMMAND_FAILED`,`React Native dev server did not become ready at ${t} within ${e.startupTimeoutMs}ms. Check ${e.logPath}.`,{logPath:e.logPath})}async function Li(e,t){let n=t.bridgeScope;if(!n)return null;let r=null,i=null;try{r=await Ci({baseUrl:t.proxyBaseUrl,bearerToken:t.proxyBearerToken,scope:n,timeoutMs:t.probeTimeoutMs})}catch(e){if(!Si(e))throw e;i=e instanceof Error?e.message:String(e)}return(!r||r.probe.reachable===!1)&&(r=await Ri(e,t,n,r,i)),Di(t.proxyBaseUrl,r),r}async function Ri(e,t,r,i,a){let o;try{o=(await Ye({projectRoot:t.projectRoot,serverBaseUrl:t.proxyBaseUrl,bearerToken:t.proxyBearerToken,bridgeScope:r,localBaseUrl:`http://${t.statusHost}:${t.metroPort}`,launchUrl:q(e.launchUrl),profileKey:q(e.companionProfileKey),consumerKey:q(e.companionConsumerKey),env:t.env})).logPath}catch(e){throw new n(`COMMAND_FAILED`,Y(t.proxyBaseUrl,e instanceof Error?e.message:String(e),i,a),void 0,e)}try{return await Fi({baseUrl:t.proxyBaseUrl,bearerToken:t.proxyBearerToken,scope:r,probeTimeoutMs:t.probeTimeoutMs,startupTimeoutMs:t.startupTimeoutMs,initialBridgeError:a,companionLogPath:o})}catch(e){throw e instanceof Error?e:Error(String(e))}}function zi(e,t){return{baseIosRuntime:e?ui(e,`ios`,t):{platform:`ios`},baseAndroidRuntime:e?ui(e,`android`,t):{platform:`android`}}}function Bi(e,t){e&&(C.mkdirSync(w.dirname(e),{recursive:!0}),C.writeFileSync(e,JSON.stringify(t,null,2)))}async function Vi(e={}){let t=Ni(e),n=t.installProjectDeps?fi(t.projectRoot,t.env):{installed:!1},r=await Ii(t),{baseIosRuntime:i,baseAndroidRuntime:a}=zi(t.publicBaseUrl,t.kind),o=await Li(e,t),s=o?.iosRuntime??i,c=o?.androidRuntime??a,l={projectRoot:t.projectRoot,kind:t.kind,dependenciesInstalled:n.installed,packageManager:n.packageManager??null,started:r.started,reused:r.reused,pid:r.pid,logPath:t.logPath,statusUrl:r.statusUrl,runtimeFilePath:t.runtimeFilePath,iosRuntime:s,androidRuntime:c,bridge:o};return Bi(t.runtimeFilePath,l),l}function Hi(e){let t=e.trimStart().slice(0,15).toLowerCase();return t.startsWith(`<!doctype`)||t.startsWith(`<html`)}async function Ui(e,t){await new Promise((r,i)=>{let a=new WebSocket(e),o=e=>{clearTimeout(s);try{a.close()}catch{}i(new n(`COMMAND_FAILED`,e))},s=setTimeout(()=>{o(`Timed out broadcasting reload to ${e} after ${t}ms`)},t);a.addEventListener(`open`,()=>{a.send(JSON.stringify({version:2,method:`reload`}));let e=()=>{if(a.bufferedAmount===0){clearTimeout(s),a.close(),r();return}setTimeout(e,20)};e()}),a.addEventListener(`error`,()=>{o(`Failed to reach the dev server message socket at ${e}`)})})}async function Wi(e={}){let t=ci(e.timeoutMs,1e4,1e3),{reloadUrl:r,messageSocketUrl:i}=Gr(e),a=await hi(r,t);if(a.ok&&!Hi(a.body))return{reloaded:!0,reloadUrl:r,status:a.status,body:a.body,transport:`http`};try{await Ui(i,t)}catch(e){throw new n(`COMMAND_FAILED`,`React Native dev server reload failed (${a.status}).`,{reloadUrl:r,status:a.status,body:a.body.slice(0,500),messageSocketUrl:i,hint:`Verify Metro or Re.Pack is running and the target React Native app is connected to this dev server instance.`},e instanceof Error?e:void 0)}return{reloaded:!0,reloadUrl:i,status:a.status,body:``,transport:`message-socket`}}function Gi(e,t){return w.join(e,`metro-sessions`,`${be(t)}.json`)}function Ki(e){let t=Gi(e.stateDir,e.session);C.mkdirSync(w.dirname(t),{recursive:!0}),C.writeFileSync(t,`${JSON.stringify(e.hints,null,2)}\n`,`utf8`)}function qi(e){let t=Gi(e.stateDir,e.session),n;try{n=C.readFileSync(t,`utf8`)}catch{return}let r;try{r=JSON.parse(n)}catch{return}if(!r||typeof r!=`object`||Array.isArray(r))return;let i=r,a={};return typeof i.metroHost==`string`&&i.metroHost&&(a.metroHost=i.metroHost),typeof i.metroPort==`number`&&Number.isInteger(i.metroPort)&&(a.metroPort=i.metroPort),typeof i.bundleUrl==`string`&&i.bundleUrl&&(a.bundleUrl=i.bundleUrl),Object.keys(a).length>0?a:void 0}function Ji(e){C.rmSync(Gi(e.stateDir,e.session),{force:!0})}const Yi={...Ge(),batch:Ke(Xi)};function Xi(e,t,r){let i=Yi[e];if(!i)throw Error(`Missing daemon writer for batch command: ${e}`);let a=$e(e);if(!a)throw Error(`Missing command metadata for batch command: ${e}`);try{return Qi(i,a,a.readInput(t))}catch(t){throw new n(`INVALID_ARGS`,`Batch step ${r} ${e} input is invalid: ${t instanceof Error?t.message:String(t)}`,void 0,t)}}function Zi(e,t){let n=Yi[e];if(!n)throw Error(`Missing daemon writer for command: ${e}`);return Qi(n,$e(e),t)}function Qi(e,t,n){let r=e(n);return{...r,...t?{metadataFlags:Ue(t,r.options)}:{}}}function $i(e){throw new n(a(e.code),e.message,{...e.details??{},hint:e.hint,diagnosticId:e.diagnosticId,logPath:e.logPath,retriable:e.retriable,supportedOn:e.supportedOn})}function ea(e,t){let n=h(e,`bundleId`),r=h(e,`package`);return{app:p(e,`app`),appPath:p(e,`appPath`),platform:te(e,`platform`),appId:n??r,bundleId:n,package:r,identifiers:de({session:t,bundleId:n,packageName:r})}}function ta(e,t){let r=h(e,`bundleId`),i=h(e,`packageName`),a=r??i??h(e,`appId`),o=h(e,`launchTarget`)??i??r??a;if(!o)throw new n(`COMMAND_FAILED`,`Daemon response is missing "launchTarget".`,{response:e});return{appName:h(e,`appName`),appId:a,bundleId:r,packageName:i,launchTarget:o,installablePath:h(e,`installablePath`),archivePath:h(e,`archivePath`),materializationId:h(e,`materializationId`),materializationExpiresAt:h(e,`materializationExpiresAt`),identifiers:de({session:t,bundleId:r,packageName:i,appId:a})}}function na(e){return{released:e.released===!0,materializationId:p(e,`materializationId`),identifiers:{}}}function ra(e){let{record:t,platform:n,id:r,name:i,target:a}=oa(e,`name`),o=aa(t);return{platform:n,target:a,kind:ie(t,`kind`),id:r,name:i,booted:typeof t.booted==`boolean`?t.booted:void 0,...Re(n)&&o?{appleOs:o}:{},identifiers:fe(n,r,i),...sa(n,r)}}function ia(e){let{record:t,platform:n,id:r,name:i,target:a}=oa(e,`name`),o=p(t,`device`),s=aa(t),c={session:i,...fe(n,r,o)};return{name:i,createdAt:se(t,`createdAt`),sessionStateDir:h(t,`sessionStateDir`),runnerLogPath:h(t,`runnerLogPath`),device:{platform:n,target:a,id:r,name:o,...s?{appleOs:s}:{},identifiers:c,...sa(n,r,{simulatorSetPath:oe(t,`ios_simulator_device_set`)})},identifiers:c}}function aa(e){let t=e.appleOs;return Ie(t)?t:void 0}function oa(e,t){let n=ae(e);return{record:n,platform:te(n,`platform`),id:p(n,`id`),name:p(n,t),target:re(n,`target`)}}function sa(e,t,n={}){if(e===`ios`)return{ios:{udid:t,...n.simulatorSetPath===void 0?{}:{simulatorSetPath:n.simulatorSetPath}}};if(!Le(e))return{};let r=n.serial??t;return e===`android`?{android:{serial:r}}:{vega:{serial:r}}}function ca(e){if(!m(e))return;let t=e.platform,n=h(e,`metroHost`),r=typeof e.metroPort==`number`?e.metroPort:void 0,i=h(e,`bundleUrl`),a=h(e,`launchUrl`);return{platform:t===`ios`||t===`android`?t:void 0,metroHost:n,metroPort:r,bundleUrl:i,launchUrl:a}}function la(e){let t=e.platform,n=h(e,`id`),r=h(e,`device`);if(!ze(t)||!n||!r)return;let i=re(e,`target`),a=Le(t)?h(e,`serial`)??n:void 0;return{platform:t,target:i,id:n,name:r,identifiers:{...fe(t,n,r),...a?{serial:a}:{}},...sa(t,t===`ios`?h(e,`device_udid`)??n:n,{simulatorSetPath:oe(e,`ios_simulator_device_set`),serial:a})}}function ua(e){if(m(e)&&!(typeof e.durationMs!=`number`||typeof e.measuredAt!=`string`||typeof e.method!=`string`))return{durationMs:e.durationMs,measuredAt:e.measuredAt,method:e.method,appTarget:h(e,`appTarget`),appBundleId:h(e,`appBundleId`)}}function da(e){if(!m(e)||typeof e.success!=`boolean`||typeof e.exitCode!=`number`||typeof e.stdout!=`string`||typeof e.stderr!=`string`)return;let t=fa(e.error);return{success:e.success,exitCode:e.exitCode,stdout:e.stdout,stderr:e.stderr,...t?{error:t}:{}}}function fa(e){if(m(e)&&!(typeof e.code!=`string`||typeof e.message!=`string`))return{code:e.code,message:e.message,...typeof e.hint==`string`?{hint:e.hint}:{},...typeof e.diagnosticId==`string`?{diagnosticId:e.diagnosticId}:{},...typeof e.logPath==`string`?{logPath:e.logPath}:{},...m(e.details)?{details:e.details}:{}}}function pa(e){return Array.isArray(e)?e:[]}function ma(e){let t=le(e);return ne({requestId:e.requestId,cwd:e.cwd,sessionExplicit:e.session!==void 0,debug:e.debug,includeCost:e.cost,responseLevel:e.responseLevel,lockPolicy:e.lockPolicy,lockPlatform:e.lockPlatform,...pe(t),sessionIsolation:e.sessionIsolation,installSource:e.installSource,retainMaterializedPaths:e.retainMaterializedPaths,materializedPathRetentionMs:e.materializedPathRetentionMs,materializationId:e.materializationId})}function X(e){return e??`default`}var ha=e({createAgentDeviceClient:()=>ga});function ga(e={},t={}){let i=t.transport??Ir,a=t=>r(t.responseLevel??e.responseLevel),o=async(t,n=[],r={},a,o)=>{let s=Q(e,r),c=await i({session:X(s.session),command:t,positionals:n,...o?{input:o}:{},flags:We(s,a),runtime:s.runtime,meta:ma(s)});return c.ok||$i(c.error),c.data??{}},s=async(e={})=>{let t=await o(_.sessionList,[],e);return(Array.isArray(t.sessions)?t.sessions:[]).map(ia)},c=async(e,t={})=>{let n=Zi(e,t);return await o(n.command,n.positionals,n.options,n.metadataFlags,n.input)},l=(t={})=>X(Q(e,t).session);return{command:{wait:async e=>await c(`wait`,e),alert:async(e={})=>await c(`alert`,e),...Ea(c),reactNative:async e=>await c(`react-native`,e),doctor:async(e={})=>await c(`doctor`,e),prepare:async e=>await c(`prepare`,e),viewport:async e=>await c(`viewport`,e)},devices:{list:async(e={})=>{let t=await c(`devices`,e);return(Array.isArray(t.devices)?t.devices:[]).map(ra)},capabilities:async(e={})=>{let t=await c(`capabilities`,e),n=Array.isArray(t.availableCommands)?t.availableCommands.filter(e=>typeof e==`string`):[];return{device:ra(t.device),availableCommands:n}},boot:async(e={})=>await c(`boot`,e),shutdown:async(e={})=>await c(`shutdown`,e)},sessions:{list:async(e={})=>await s(e),stateDir:async(t={})=>b(Q(e,t).stateDir??process.env.AGENT_DEVICE_STATE_DIR).baseDir,close:async(t={})=>{let n=l(t);try{let e=await c(`close`,t);return{session:n,shutdown:da(e.shutdown),provider:Z(e.provider),savedScript:h(e,`savedScript`),identifiers:{session:n}}}finally{ja(e,t)}},saveScript:async(e={})=>{let t=await o(_.sessionSaveScript,e.path===void 0?[]:[e.path],e,{force:e.force}),r=t.actionCount;if(typeof r!=`number`||!Number.isInteger(r)||r<0)throw new n(`COMMAND_FAILED`,`Daemon returned an invalid saved-script action count.`);let i=p(t,`session`);return{session:i,savedScript:p(t,`savedScript`),actionCount:r,identifiers:{session:i}}},artifacts:async(e={})=>await c(`artifacts`,e)},apps:{install:async e=>ea(await c(`install`,e),l(e)),reinstall:async e=>ea(await c(`reinstall`,e),l(e)),installFromSource:async e=>ta(await c(`install-from-source`,e),l(e)),list:async(e={})=>{let t=await c(`apps`,e);return Array.isArray(t.apps)?t.apps.filter(e=>typeof e==`string`):[]},open:async t=>{let n=l(t),r=await c(`open`,t);Aa({config:e,options:t,runtime:Q(e,t).runtime,sessionReused:r.sessionReused===!0});let i=la(r),a=h(r,`appBundleId`),o=a,s=Array.isArray(r.warnings)?r.warnings.filter(e=>typeof e==`string`):[];return{session:n,...s.length>0?{warnings:s}:{},sessionStateDir:h(r,`sessionStateDir`),eventLogPath:h(r,`eventLogPath`),appName:h(r,`appName`),appBundleId:a,appId:o,startup:ua(r.startup),runtime:ca(r.runtime),device:i,identifiers:{session:n,deviceId:i?.id,deviceName:i?.name,udid:i?.ios?.udid,serial:i?.android?.serial,appId:o,appBundleId:a}}},close:async(e={})=>{let t=l(e),n=await c(`close`,e);return{session:t,closedApp:e.app,shutdown:da(n.shutdown),savedScript:h(n,`savedScript`),identifiers:{session:t}}},push:async e=>await c(`push`,e),triggerEvent:async e=>await c(`trigger-app-event`,e)},materializations:{release:async e=>na(await o(_.releaseMaterializedPaths,[],{...e,materializationId:e.materializationId}))},leases:{allocate:async e=>Ma(await o(_.leaseAllocate,[],{...e,leaseId:void 0})),heartbeat:async e=>Ma(await o(_.leaseHeartbeat,[],e)),release:async e=>{let t=await o(_.leaseRelease,[],e);return{released:t.released===!0,provider:Z(t.provider)}}},metro:{prepare:async t=>{let n=await Vi({projectRoot:t.projectRoot??e.cwd,kind:t.kind,publicBaseUrl:t.publicBaseUrl,proxyBaseUrl:t.proxyBaseUrl,proxyBearerToken:t.bearerToken,bridgeScope:t.bridgeScope,launchUrl:t.launchUrl,companionProfileKey:t.companionProfileKey,companionConsumerKey:t.companionConsumerKey,metroPort:t.port,listenHost:t.listenHost,statusHost:t.statusHost,startupTimeoutMs:t.startupTimeoutMs,probeTimeoutMs:t.probeTimeoutMs,reuseExisting:t.reuseExisting,installDependenciesIfNeeded:t.installDependenciesIfNeeded,runtimeFilePath:t.runtimeFilePath,logPath:t.logPath});return Da(e,n),n},reload:async(t={})=>await Wi({metroHost:t.metroHost,metroPort:t.metroPort,bundleUrl:t.bundleUrl,runtime:e.runtime??Oa(e),timeoutMs:t.timeoutMs})},capture:{snapshot:async(e={})=>{let t=l(e),n=await c(`snapshot`,e);return a(e)?n:wa(n,t)},screenshot:async(e={})=>{let t=l(e),n=await c(`screenshot`,e);if(a(e))return n;let r=Oe(n);return{path:p(n,`path`),width:r?.width,height:r?.height,logicalWidth:r?.logicalWidth,logicalHeight:r?.logicalHeight,pixelDensity:r?.pixelDensity,overlayRefs:r?.overlayRefs,identifiers:{session:t}}},diff:async e=>await c(`diff`,e)},interactions:{click:async e=>await c(`click`,e),press:async e=>await c(`press`,e),longPress:async e=>await c(`longpress`,e),swipe:async e=>await c(`swipe`,e),pan:async e=>await c(`gesture`,_a(e)),fling:async e=>await c(`gesture`,va(e)),swipeGesture:async e=>await c(`gesture`,ya(e)),focus:async e=>await c(`focus`,e),type:async e=>await c(`type`,e),fill:async e=>await c(`fill`,e),scroll:async e=>await c(`scroll`,e),pinch:async e=>await c(`gesture`,ba(e)),rotateGesture:async e=>await c(`gesture`,xa(e)),transformGesture:async e=>await c(`gesture`,Sa(e)),get:async e=>await c(`get`,e),is:async e=>await c(`is`,e),find:async e=>await c(`find`,e)},replay:{run:async e=>await c(`replay`,e),test:async e=>await c(`test`,e)},batch:{run:async e=>await c(`batch`,e)},observability:{perf:async(e={})=>await c(`perf`,e),logs:async(e={})=>await c(`logs`,e),events:async(e={})=>await c(`events`,e),network:async(e={})=>await c(`network`,e),audio:async(e={})=>await c(`audio`,e)},debug:{symbols:async t=>{let{symbolicateCrashArtifact:n}=await import(`./debug-symbols.js`);return n({cwd:t.cwd??e.cwd,...t})}},recording:{record:async e=>await c(`record`,e),trace:async e=>await c(`trace`,e)},settings:{update:async e=>await c(`settings`,e)}}}function _a(e){let{x:t,y:n,dx:r,dy:i,...a}=e;return{...a,kind:`pan`,origin:{x:t,y:n},delta:{x:r,y:i}}}function va(e){let{x:t,y:n,...r}=e;return{...r,kind:`fling`,origin:{x:t,y:n}}}function ya(e){return{...e,kind:`swipe`}}function ba(e){let{x:t,y:n,...r}=e;return Ca(t,n,`pinch`),{...r,kind:`pinch`,...t===void 0&&n===void 0?{}:{origin:{x:t,y:n}}}}function xa(e){let{x:t,y:n,...r}=e;return Ca(t,n,`rotate`),{...r,kind:`rotate`,...t===void 0&&n===void 0?{}:{origin:{x:t,y:n}}}}function Sa(e){let{x:t,y:n,dx:r,dy:i,...a}=e;return{...a,kind:`transform`,origin:{x:t,y:n},delta:{x:r,y:i}}}function Ca(e,t,r){if(e===void 0!=(t===void 0))throw new n(`INVALID_ARGS`,`gesture ${r} center requires both x and y`)}function wa(e,t){let n=h(e,`appBundleId`);return{nodes:pa(e.nodes),truncated:e.truncated===!0,appName:h(e,`appName`),appBundleId:n,...Ta(e),identifiers:{session:t,appId:n,appBundleId:n}}}function Ta(e){let t=Z(e.visibility),n=Z(e.unchanged),r=De(e.snapshotDiagnostics);return{...t?{visibility:t}:{},...ue(e),...n?{unchanged:n}:{},...r?{snapshotDiagnostics:r}:{},...typeof e.refsGeneration==`number`?{refsGeneration:e.refsGeneration}:{}}}function Ea(e){let t={};for(let[n,r]of Object.entries(He.clientCommandMethods??{}))t[n]=async(t={})=>await e(r,t);let n=t.orientation;if(!n)throw Error(`orientation client method missing from the system command family`);return t.rotate=async(e={})=>({...await n(e),action:`rotate`}),t}function Z(e){return m(e)?e:void 0}function Q(e,t){return{...e,...t}}function $(e,t={}){let n=Q(e,t);return{stateDir:b(n.stateDir??process.env.AGENT_DEVICE_STATE_DIR).baseDir,session:X(n.session)}}function Da(e,t){try{let n=new URL(t.statusUrl),r=Number.parseInt(n.port,10);if(!n.hostname||!Number.isInteger(r))return;let i=t.bridge?void 0:t.iosRuntime.bundleUrl??t.androidRuntime.bundleUrl;Ki({...$(e),hints:{metroHost:n.hostname,metroPort:r,bundleUrl:i}})}catch{}}function Oa(e){try{return qi($(e))}catch{return}}function ka(e){if(!e)return;let{metroHost:t,metroPort:n,bundleUrl:r}=e;if(!(t===void 0&&n===void 0&&r===void 0))return{metroHost:t,metroPort:n,bundleUrl:r}}function Aa(e){try{let t=$(e.config,e.options),n=ka(e.runtime);if(n){Ki({...t,hints:n});return}e.sessionReused||Ji(t)}catch{}}function ja(e,t){try{Ji($(e,t))}catch{}}function Ma(e){let t=e.lease;if(!m(t))throw Error(`Invalid lease response from daemon`);return{leaseId:p(t,`leaseId`),tenantId:p(t,`tenantId`),runId:p(t,`runId`),backend:p(t,`backend`),leaseProvider:h(t,`leaseProvider`),clientId:h(t,`clientId`),deviceKey:h(t,`deviceKey`),createdAt:typeof t.createdAt==`number`?t.createdAt:void 0,heartbeatAt:typeof t.heartbeatAt==`number`?t.heartbeatAt:void 0,expiresAt:typeof t.expiresAt==`number`?t.expiresAt:void 0}}export{dr as a,Ir as i,ga as n,ar as o,$i as r,ha as t};
|
|
@@ -36,7 +36,7 @@ type AndroidLogcatCaptureOptions = {
|
|
|
36
36
|
};
|
|
37
37
|
declare function captureAndroidLogcatWithAdb(adb: AndroidAdbExecutor, options?: AndroidLogcatCaptureOptions): Promise<string>;
|
|
38
38
|
//#endregion
|
|
39
|
-
//#region src/
|
|
39
|
+
//#region src/contracts/android-input-ownership.d.ts
|
|
40
40
|
type AndroidInputOwner = 'app' | 'ime' | 'unknown';
|
|
41
41
|
//#endregion
|
|
42
42
|
//#region src/platforms/android/device-input-state.d.ts
|
package/dist/src/android-adb.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{_ as e}from"./sdk-contracts.js";import{
|
|
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};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{u as e}from"./sdk-contracts.js";import{Z as t,gt as n,ht as r,rt as i,s as a}from"./sdk-selectors.js";import{n as o}from"./android-input-ownership.js";const s=[`x`,`y`,`width`,`height`];function c(t){let n=l(t);if(!n)return null;let r=e(n);return!Number.isFinite(r.x)||!Number.isFinite(r.y)?null:r}function l(e){if(!e)return null;let t=Number(e.x),n=Number(e.y),r=Number(e.width),i=Number(e.height);return!Number.isFinite(t)||!Number.isFinite(n)||!Number.isFinite(r)||!Number.isFinite(i)||r<0||i<0?null:{x:t,y:n,width:r,height:i}}function u(e,t){return!e||!t?!1:s.every(n=>Math.abs(e[n]-t[n])<=.5)}function d(e){return{x:f(e.x,e.width),y:f(e.y,e.height)}}function f(e,t){if(t<=1)return Math.floor(e);let n=Math.ceil(e),r=Math.floor(e+t-1);return Math.round(Math.min(r,Math.max(n,e+t/2)))}function p(e){let t=new Map;for(let n of e)t.set(n.index,n);return t}function m(e,t,n){let r=[],i=new Set([e.index]),o=e;for(;r.length<n;){if(typeof o.parentIndex!=`number`)return{chain:r,broken:!1};let e=t.get(o.parentIndex);if(!e||i.has(e.index))return{chain:r,broken:!0};i.add(e.index);let n=a(e);r.push({role:n.role,...n.label===void 0?{}:{label:n.label}}),o=e}return{chain:r,broken:!1}}function h(e,t,i,o){return e.filter(e=>{if(!n(a(e),i))return!1;let s=m(e,t,Math.max(o.length,1));return!s.broken&&r(s.chain,o)})}const g=[`tabbar`,`toolbar`,`navigationbar`,`bottomnavigation`,`bottomnavigationview`,`sheet`,`dialog`,`alert`,`popover`,`menu`],_=[`button`,`link`,`menuitem`,`tabitem`,`textfield`,`searchfield`,`edittext`,`checkbox`,`radio`,`switch`,`cell`];function v(e,t={}){if(e.length<2)return e;let n=[...e],r=new Map(n.map(e=>[e.index,e])),i={nodes:n,byIndex:r,overlayPositions:n.flatMap((e,n)=>w(e,r,t)?[n]:[])},a=!1;for(let[e,r]of n.entries()){if(!C(r)||!b(i,e,r,t))continue;a=!0;let o={...r,hittable:!1,interactionBlocked:`covered`,presentationHints:F(r.presentationHints)};n[e]=o,i.byIndex.set(o.index,o)}return a?n:e}function y(e){return e.interactionBlocked!==void 0}function b(t,n,r,i){let a=P(r.rect);if(!a)return null;let o=e(a);for(let e of t.overlayPositions){if(e<=n)continue;let s=t.nodes[e];if(s&&x(t,e,r,a,o,i))return s}return null}function x(e,n,r,i,a,o){if(!e.nodes[n])return!1;let s=S(e,n,r,i,o);return!!(s&&t(s,a.x,a.y))}function S(e,t,n,r,i){let a=e.nodes[t];if(!a||!w(a,e.byIndex,i)||M(n,a,e.byIndex))return null;let o=P(a.rect);return!o||u(r,o)||b(e,t,a,i)?null:o}function C(e){return P(e.rect)?e.hittable===!0||O(e)?!0:!!(e.label?.trim()||e.value?.trim()||e.identifier?.trim()):!1}function w(e,t,n){return!P(e.rect)||j(e)?!1:k(e,g)||T(e,t,n)}function T(e,t,n){return n.isAdditionalOverlayNode?.(e)===!0?!E(e,t,n):!1}function E(e,t,n){let r=typeof e.parentIndex==`number`?t.get(e.parentIndex):void 0,i=new Set;for(;r&&!i.has(r.index);){if(D(r,n))return!0;i.add(r.index),r=typeof r.parentIndex==`number`?t.get(r.parentIndex):void 0}return!1}function D(e,t){return t.isAdditionalOverlayNode?.(e)===!0&&P(e.rect)!==null&&!j(e)}function O(e){return k(e,_)}function k(e,t){let n=A(e);return t.some(e=>n.includes(e))}function A(e){return[e.type,e.role,e.subrole].map(e=>i(e??``)).join(` `)}function j(e){let t=A(e);return t.includes(`application`)||t.includes(`window`)}function M(e,t,n){return N(e,t,n)||N(t,e,n)}function N(e,t,n){let r=typeof t.parentIndex==`number`?n.get(t.parentIndex):void 0,i=new Set;for(;r&&!i.has(r.index);){if(r.index===e.index)return!0;i.add(r.index),r=typeof r.parentIndex==`number`?n.get(r.parentIndex):void 0}return!1}function P(e){let t=l(e);return t&&t.width>0&&t.height>0?t:null}function F(e){return Array.from(new Set([...e??[],`covered`]))}function I(e){return e?o({packageName:e.bundleId,resourceId:e.identifier}).inputMethodOwned:!1}export{p as a,l as c,m as i,d as l,v as n,h as o,y as r,u as s,I as t,c as u};
|
|
@@ -1,6 +1 @@
|
|
|
1
|
-
|
|
2
|
-
`).toLowerCase();return r===`ios`&&(d.includes(`runner did not accept connection`)||i===`connect`&&(d.includes(`timed out`)||d.includes(`timeout`)||d.includes(`econnrefused`)||d.includes(`connection refused`)||d.includes(`fetch failed`)||d.includes(`socket hang up`)))?`IOS_RUNNER_CONNECT_TIMEOUT`:r===`ios`&&i===`boot`&&(d.includes(`timed out`)||d.includes(`timeout`))?`IOS_BOOT_TIMEOUT`:r===`android`&&i===`boot`&&(d.includes(`timed out`)||d.includes(`timeout`))?`ANDROID_BOOT_TIMEOUT`:d.includes(`resource temporarily unavailable`)||d.includes(`killed: 9`)||d.includes(`cannot allocate memory`)||d.includes(`system is low on memory`)?`CI_RESOURCE_STARVATION_SUSPECTED`:r===`android`&&(d.includes(`device not found`)||d.includes(`no devices`)||d.includes(`device offline`)||d.includes(`offline`)||d.includes(`unauthorized`)||d.includes(`not authorized`)||d.includes(`unable to locate device`)||d.includes(`invalid device`))?`ADB_TRANSPORT_UNAVAILABLE`:t?.code===`COMMAND_FAILED`||d.length>0?`BOOT_COMMAND_FAILED`:`UNKNOWN`}function k(e){switch(e){case`IOS_BOOT_TIMEOUT`:return`Retry simulator boot and inspect simctl bootstatus logs; in CI reduce parallel jobs or use a larger runner.`;case`IOS_RUNNER_CONNECT_TIMEOUT`:return`Retry runner startup, inspect xcodebuild logs, and verify simulator responsiveness before command execution.`;case`ANDROID_BOOT_TIMEOUT`:return`Retry emulator startup and verify sys.boot_completed reaches 1; consider increasing startup budget in CI.`;case`ADB_TRANSPORT_UNAVAILABLE`:return`Check adb server/device transport (adb devices -l), restart adb, and ensure the target device is online and authorized.`;case`CI_RESOURCE_STARVATION_SUSPECTED`:return`CI machine may be resource constrained; reduce parallel jobs or use a larger runner.`;case`IOS_TOOL_MISSING`:return`Xcode command-line tools are missing or not in PATH; run xcode-select --install and verify xcrun works.`;case`BOOT_COMMAND_FAILED`:return`Inspect command stderr/stdout for the failing boot phase and retry after environment validation.`;default:return`Retry once and inspect verbose logs for the failing phase.`}}var le=e({ensureAndroidEmulatorBooted:()=>Se,listAndroidDevices:()=>U,parseAndroidAvdList:()=>G,parseAndroidEmulatorAvdNameOutput:()=>I,parseAndroidFeatureListForTv:()=>H,parseAndroidTargetFromCharacteristics:()=>V,resolveAndroidAvdName:()=>K,resolveAndroidEmulatorAvdName:()=>B,waitForAndroidBoot:()=>J});const A=1e4,ue=[`android.software.leanback`,`android.software.leanback_only`,`android.hardware.type.television`];async function j(e,t,n){let r=Array(e.length),i=0,a=Math.min(t,e.length);return await Promise.all(Array.from({length:a},async()=>{for(;i<e.length;){let t=i;i+=1,r[t]=await n(e[t])}})),r}function M(e){return`${e.stdout}\n${e.stderr}`}function N(e,t){return[`-s`,e,...t]}function P(e){return e.startsWith(`emulator-`)}function F(e){return e.toLowerCase().replace(/_/g,` `).replace(/\s+/g,` `).trim()}function I(e){let t=e.split(`
|
|
3
|
-
`).map(e=>e.trim()).filter(e=>e.length>0);if(t.length!==0)return t.at(-1)===`OK`&&t.pop(),t.join(`
|
|
4
|
-
`).trim()||void 0}async function L(e,t=A){return l(`adb`,N(e,[`shell`,`getprop`,`sys.boot_completed`]),{allowFailure:!0,timeoutMs:t})}async function R(e,t){let n=t.replace(/_/g,` `).trim();if(!P(e))return n||e;let r=await B(e);return r?r.replace(/_/g,` `):n||e}async function z(e,t,r){try{return await r(`adb`,N(e,t),{allowFailure:!0,timeoutMs:1e4})}catch(e){if(de(n(e)))return;throw e}}function de(e){return e.code===`COMMAND_FAILED`&&typeof e.details?.timeoutMs==`number`}async function B(e,t=l){for(let n of[`ro.boot.qemu.avd_name`,`persist.sys.avd_name`]){let r=await z(e,[`shell`,`getprop`,n],t);if(!r)continue;let i=r.stdout.trim();if(r.exitCode===0&&i.length>0)return i}let n=await z(e,[`emu`,`avd`,`name`],t);if(!n)return;let r=I(n.stdout);if(n.exitCode===0&&r)return r}function V(e){let t=e.toLowerCase();return t.includes(`tv`)||t.includes(`leanback`)?`tv`:null}function H(e){return/feature:android\.(software\.leanback(_only)?|hardware\.type\.television)\b/i.test(e)}async function fe(e,t){let n=M(await l(`adb`,N(e,[`shell`,`cmd`,`package`,`has-feature`,t]),{allowFailure:!0,timeoutMs:A})).toLowerCase();return n.includes(`true`)?!0:n.includes(`false`)?!1:null}async function pe(e){return(await j(ue,2,async t=>await fe(e,t))).some(e=>e===!0)}async function me(e){return V(M(await l(`adb`,N(e,[`shell`,`getprop`,`ro.build.characteristics`]),{allowFailure:!0,timeoutMs:A})))===`tv`||await pe(e)||H(M(await l(`adb`,N(e,[`shell`,`pm`,`list`,`features`]),{allowFailure:!0,timeoutMs:A})))?`tv`:`mobile`}async function U(e={}){if(await f(),!await u(`adb`))throw new t(`TOOL_MISSING`,`adb not found in PATH`);let n=e.serialAllowlist??D(void 0),r=await j((await W()).filter(e=>!n||n.has(e.serial)),3,async({serial:e,rawModel:t})=>{let[n,r,i]=await Promise.all([R(e,t),xe(e),me(e)]);return{platform:`android`,id:e,name:n,kind:P(e)?`emulator`:`device`,target:i,booted:r}});return[...r,...await ge(r)]}function he(e){return e.split(`
|
|
5
|
-
`).map(e=>e.trim()).filter(e=>e.length>0&&!e.startsWith(`List of devices`)).map(e=>e.split(/\s+/)).flatMap(e=>{let t=e[0];return t===void 0||e[1]!==`device`?[]:[{serial:t,rawModel:(e.find(e=>e.startsWith(`model:`))??``).replace(`model:`,``)}]})}async function W(){try{return he((await l(`adb`,[`devices`,`-l`],{timeoutMs:A})).stdout)}catch(e){throw c(e)}}function G(e){return e.split(`
|
|
6
|
-
`).map(e=>e.trim()).filter(e=>e.length>0)}function K(e,t){let n=e.find(e=>e===t);if(n)return n;let r=F(t);return e.find(e=>F(e)===r)}async function q(){return G(a(await l(`emulator`,[`-list-avds`],{allowFailure:!0,timeoutMs:A}),`Failed to list Android emulator AVDs`,{hint:`Verify Android emulator tooling is installed and available in PATH.`}).stdout)}async function ge(e){let t=await q().catch(()=>[]),n=new Set(e.filter(e=>e.kind===`emulator`).map(e=>F(e.name)));return t.filter(e=>!n.has(F(e))).map(e=>({platform:`android`,id:e,name:e,kind:`emulator`,target:_e(e),booted:!1}))}function _e(e){return/\b(tv|television)\b/i.test(F(e))?`tv`:`mobile`}function ve(e,t,n){let r=F(t);return e.find(e=>e.platform!==`android`||e.kind!==`emulator`||n&&e.id!==n?!1:F(e.name)===r)}async function ye(e){let n=Date.now(),r=Math.min(1e3,Math.max(50,Math.floor(e.timeoutMs/20)));for(;Date.now()-n<e.timeoutMs;){try{let t=await be(e.avdName,e.serial);if(t)return{platform:`android`,id:t,name:e.avdName,kind:`emulator`,target:`mobile`,booted:!1}}catch{}await d(r)}throw new t(`COMMAND_FAILED`,`Android emulator did not appear in time`,{avdName:e.avdName,serial:e.serial,timeoutMs:e.timeoutMs,hint:`Check emulator logs and verify the AVD can start from command line.`})}async function be(e,t){let n=F(e),r=(await W()).filter(e=>t&&e.serial!==t?!1:P(e.serial));for(let e of r)if(F(e.rawModel)===n||F(await R(e.serial,e.rawModel))===n)return e.serial}async function xe(e){try{return(await L(e)).stdout.trim()===`1`}catch{return!1}}async function Se(e){await f();let n=e.avdName.trim();if(!n)throw new t(`INVALID_ARGS`,`Android emulator boot requires a non-empty AVD name.`);let i=e.timeoutMs??12e4;if(!await u(`adb`))throw new t(`TOOL_MISSING`,`adb not found in PATH`);if(!await u(`emulator`))throw new t(`TOOL_MISSING`,`emulator not found in PATH`);let a=await q(),o=K(a,n);if(!o)throw new t(`DEVICE_NOT_FOUND`,`No Android emulator AVD named ${e.avdName}`,{requestedAvdName:n,availableAvds:a,hint:"Run `emulator -list-avds` and pass an existing AVD name to --device."});let s=Date.now(),c=ve(await U(),o,e.serial),l=c&&P(c.id)?c:void 0;if(!l){let t=[`-avd`,o];e.headless&&t.push(`-no-window`,`-no-audio`),r(`emulator`,t)}let d=l??await ye({avdName:o,serial:e.serial,timeoutMs:i}),p=Date.now()-s,m=Math.max(1e3,i-p);await J(d.id,m);let h=(await U()).find(e=>e.id===d.id);return h?{...h,name:o,booted:!0}:{...d,name:o,booted:!0}}async function J(e,r=6e4){let i=r,a=S.fromTimeoutMs(i),o=Math.min(1e3,Math.max(50,Math.floor(i/20))),s=Math.max(1,Math.ceil(i/o)),c,l=!1;try{await C(async({deadline:n})=>{if(n?.isExpired())throw l=!0,new t(`COMMAND_FAILED`,`Android boot deadline exceeded`,{serial:e,timeoutMs:r,elapsedMs:a.elapsedMs(),message:`timeout`});let o=Math.max(1e3,n?.remainingMs()??i),s=await L(e,Math.min(o,A));if(c=s,s.stdout.trim()!==`1`)throw new t(`COMMAND_FAILED`,`Android device is still booting`,{serial:e,stdout:s.stdout,stderr:s.stderr,exitCode:s.exitCode})},{maxAttempts:s,baseDelayMs:o,maxDelayMs:o,jitter:0,shouldRetry:e=>{let t=O({error:e,stdout:c?.stdout,stderr:c?.stderr,context:{platform:`android`,phase:`boot`}});return t!==`ADB_TRANSPORT_UNAVAILABLE`&&t!==`ANDROID_BOOT_TIMEOUT`}},{deadline:a,phase:`boot`,classifyReason:e=>O({error:e,stdout:c?.stdout,stderr:c?.stderr,context:{platform:`android`,phase:`boot`}})})}catch(r){let o=n(r),s=c?.stdout,u=c?.stderr,d=c?.exitCode,f=O({error:r,stdout:s,stderr:u,context:{platform:`android`,phase:`boot`}});f===`BOOT_COMMAND_FAILED`&&o.message===`Android device is still booting`&&(f=`ANDROID_BOOT_TIMEOUT`);let p={serial:e,timeoutMs:i,elapsedMs:a.elapsedMs(),reason:f,hint:k(f),stdout:s,stderr:u,exitCode:d};throw l||f===`ANDROID_BOOT_TIMEOUT`?new t(`COMMAND_FAILED`,`Android device did not finish booting in time`,p):o.code===`TOOL_MISSING`?new t(`TOOL_MISSING`,o.message,{...p,...o.details??{}}):f===`ADB_TRANSPORT_UNAVAILABLE`?new t(`COMMAND_FAILED`,o.message,{...p,...o.details??{}}):new t(o.code,o.message,{...p,...o.details??{}},o.cause)}}async function Ce(e,t,n){return await i(e)(t,n)}function we(e){return{platform:`android`,id:e,name:e,kind:e.startsWith(`emulator-`)?`emulator`:`device`,booted:!0}}function Te(e,t){let n=`${e}\n${t}`.toLowerCase();return n.includes(`no shell command implementation`)||n.includes(`unknown command`)}var Ee=e({prepareAndroidInstallArtifact:()=>Y});async function Y(e,t){let n=e.kind===`url`&&h(e.url),r=await m({source:e,isInstallablePath:(e,t)=>t.isFile()&&De(e),installableLabel:`Android installable (.apk or .aab)`,allowArchiveExtraction:e.kind!==`url`||n,signal:t?.signal});try{let e=t?.resolveIdentity===!1?{}:await Oe(r.installablePath);return{archivePath:r.archivePath,installablePath:r.installablePath,packageName:e.packageName,cleanup:r.cleanup}}catch(e){throw await r.cleanup(),e}}function De(e){let t=g.extname(e).toLowerCase();return t===`.apk`||t===`.aab`}async function Oe(e){let t=g.extname(e).toLowerCase();return t!==`.apk`&&t!==`.aab`?{}:{packageName:await p(e)}}async function ke(e,t={}){let n=[`logcat`,`-d`,`-v`,`time`];t.lines!==void 0&&n.push(`-t`,String(Math.max(1,Math.floor(t.lines))));let r=await e(n,{allowFailure:!0,timeoutMs:t.timeoutMs,signal:t.signal});if(r.exitCode!==0)throw s(`Failed to capture Android logcat`,r);return r.stdout}function X(e,n={}){if(!e.spawn)throw new t(`UNSUPPORTED_OPERATION`,`Android ADB provider does not support streams`,{capability:`adb.spawn`});let r=[`logcat`,`-v`,`time`];n.pid&&r.push(`--pid`,n.pid);let i=e.spawn(r,{stdio:[`ignore`,`pipe`,`pipe`],signal:n.signal});return n.output&&i.stdout&&i.stdout.pipe(n.output,{end:!1}),i}const Z=new Set([`com.google.android.inputmethod.latin`,`com.samsung.android.honeyboard`,`com.touchtype.swiftkey`,`com.microsoft.swiftkey`]);function Q(e){let t=$(e.packageName),n=(e.resourceId??``).toLowerCase(),r=$(e.activeInputMethodPackage);if(t&&r&&t===r)return{inputMethodOwned:!0,source:`active-input-method`};if(r&&n.startsWith(`${r}:id/`))return{inputMethodOwned:!0,source:`active-input-method-resource`};if(t&&Z.has(t))return{inputMethodOwned:!0,source:`known-ime-package`};for(let e of Z)if(n.startsWith(`${e}:id/`))return{inputMethodOwned:!0,source:`known-ime-resource`};return{inputMethodOwned:!1,source:`app`}}function Ae(e,t,n){return Q({packageName:e,resourceId:t,activeInputMethodPackage:n}).inputMethodOwned}function je(e){return Q(e).inputMethodOwned}function Me(e){let t=$(e);return!!(t&&Z.has(t))}function Ne(e){let t=(e??``).toLowerCase();for(let e of Z)if(t.startsWith(`${e}:id/`))return!0;return!1}function Pe(e,t,n){return!e&&!t?`unknown`:Ae(e,t,n)?`ime`:`app`}function Fe(e){for(let t of[/\bmCurMethodId=([^\s]+)/i,/\bmCurId=([^\s]+)/i,/\bmCurrentInputMethodId=([^\s]+)/i,/\bcurMethodId=([^\s]+)/i]){let n=Ie(e.match(t)?.[1]);if(n)return n}}function Ie(e){let t=(e??``).trim();if(t)return $((t.split(`/`)[0]??``).match(/[a-zA-Z0-9_.]+/)?.[0])}function $(e){return(e??``).trim().toLowerCase()||void 0}export{b as C,ee as D,ne as E,_ as O,S,re as T,O as _,Ne as a,D as b,X as c,we as d,Te as f,k as g,J as h,Me as i,te as k,Ee as l,le as m,Q as n,Fe as o,Ce as p,je as r,ke as s,Pe as t,Y as u,ce as v,C as w,oe as x,E as y};
|
|
1
|
+
const e=new Set([`com.google.android.inputmethod.latin`,`com.samsung.android.honeyboard`,`com.touchtype.swiftkey`,`com.microsoft.swiftkey`]);function t(t){let n=l(t.packageName),r=(t.resourceId??``).toLowerCase(),i=l(t.activeInputMethodPackage);if(n&&i&&n===i)return{inputMethodOwned:!0,source:`active-input-method`};if(i&&r.startsWith(`${i}:id/`))return{inputMethodOwned:!0,source:`active-input-method-resource`};if(n&&e.has(n))return{inputMethodOwned:!0,source:`known-ime-package`};for(let t of e)if(r.startsWith(`${t}:id/`))return{inputMethodOwned:!0,source:`known-ime-resource`};return{inputMethodOwned:!1,source:`app`}}function n(e,n,r){return t({packageName:e,resourceId:n,activeInputMethodPackage:r}).inputMethodOwned}function r(e){return t(e).inputMethodOwned}function i(t){let n=l(t);return!!(n&&e.has(n))}function a(t){let n=(t??``).toLowerCase();for(let t of e)if(n.startsWith(`${t}:id/`))return!0;return!1}function o(e,t,r){return!e&&!t?`unknown`:n(e,t,r)?`ime`:`app`}function s(e){for(let t of[/\bmCurMethodId=([^\s]+)/i,/\bmCurId=([^\s]+)/i,/\bmCurrentInputMethodId=([^\s]+)/i,/\bcurMethodId=([^\s]+)/i]){let n=c(e.match(t)?.[1]);if(n)return n}}function c(e){let t=(e??``).trim();if(t)return l((t.split(`/`)[0]??``).match(/[a-zA-Z0-9_.]+/)?.[0])}function l(e){return(e??``).trim().toLowerCase()||void 0}export{a,i,t as n,s as o,r,o as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=`com.android.systemui`;function t(t){let n=t??``;if(!n.startsWith(`com.android.systemui:id/`))return!1;let r=n.slice(`${e}:id/`.length);return/(^|_)(status_bar|navigation_bar)(_|$)/.test(r)}function n(e){return`systemChrome`in e&&e.systemChrome===!0}function r(e){if(!n(e))return e;let{systemChrome:t,...r}=e;return r}function i(e){return e.some(n)?e.map(r):e}export{r as a,i,n,t as r,e as t};
|
package/dist/src/android.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{S as e,_ as t}from"./sdk-contracts.js";import{F as n,c as r,d as i,j as a,l as o,n as s,o as c,p as l}from"./sdk-android-adb.js";import{Ct as u,pn as d}from"./tv-remote.js";import{n as ee,t as te}from"./version.js";import{t as ne}from"./timeouts.js";import{c as f,d as re,l as ie,n as p}from"./app-lifecycle.js";import{p as m}from"./android-input-ownership.js";import{a as h,s as g}from"./device-input-state.js";import{At as _,Dt as v,Ot as ae,jt as y,kt as oe}from"./internal/daemon.js";import{a as se,c as ce,d as le,f as ue,i as de,l as b,m as x,n as S,o as C,p as w,r as T,t as E,u as D}from"./input-actions.js";import{a as O,i as k,n as A,r as j,t as M}from"./setting-state.js";import N from"node:crypto";import{promises as P}from"node:fs";import F from"node:path";import I from"node:fs/promises";const L=`android-multitouch-helper-v1`,R=`ANDROID_MULTITOUCH_HELPER_NO_FINAL_RESULT`,z=`ANDROID_MULTITOUCH_HELPER_REPORTED_FAILURE`;async function B(t,n){let r=await G(t,{kind:`swipe`,...n});if(r)return r;try{return await K(t,{kind:`swipe`,...n})}catch(r){return a({level:`warn`,phase:`android_swipe_helper_fallback`,data:{error:e(r).message}}),await w(t,n.x1,n.y1,n.x2,n.y2,n.durationMs),{backend:`adb-input-swipe-fallback`}}}async function V(e,n){if(!Number.isFinite(n.scale)||n.scale<=0)throw new t(`INVALID_ARGS`,`gesture pinch requires scale > 0`);let r=await U(e,n.x,n.y);return await W(e,{kind:`pinch`,x:r.x,y:r.y,scale:n.scale,durationMs:n.durationMs})}async function H(e,n){if(!Number.isFinite(n.degrees))throw new t(`INVALID_ARGS`,`gesture rotate requires finite degrees`);if(n.velocity!==void 0&&(!Number.isFinite(n.velocity)||n.velocity===0))throw new t(`INVALID_ARGS`,`gesture rotate velocity must be a non-zero number`);let r=await U(e,n.x,n.y),i=n.degrees;return await W(e,{kind:`rotate`,x:r.x,y:r.y,degrees:i,durationMs:n.durationMs})}async function fe(e,n){if(!Number.isFinite(n.scale)||n.scale<=0)throw new t(`INVALID_ARGS`,`gesture transform requires scale > 0`);if(!Number.isFinite(n.degrees))throw new t(`INVALID_ARGS`,`gesture transform requires finite degrees`);if(![n.x,n.y,n.dx,n.dy].every(Number.isFinite))throw new t(`INVALID_ARGS`,`gesture transform requires finite x y dx dy`);return await W(e,{kind:`transform`,x:n.x,y:n.y,dx:n.dx,dy:n.dy,scale:n.scale,degrees:n.degrees,durationMs:n.durationMs})}async function U(e,t,n){if(t!==void 0&&n!==void 0)return{x:t,y:n};let r=await se(e);return{x:Math.round(r.width/2),y:Math.round(r.height/2)}}async function W(e,t){return await G(e,t)||await K(e,t)}async function G(e,t){let n=i(e);if(n)return{backend:`provider-native-touch`,...await n(t)??{}}}async function K(e,t){let i=r(e),s=await _e(),c=o(e),l=await n(`android_multitouch_helper_install`,async()=>await ye({adb:i,adbProvider:c,artifact:s,deviceKey:Ce(e)}),{packageName:s.manifest.packageName,versionCode:s.manifest.versionCode});a({phase:`android_multitouch_helper_install_decision`,data:l});let u=await n(`android_multitouch_helper_gesture`,async()=>await me({adb:i,request:q(t),packageName:s.manifest.packageName,instrumentationRunner:s.manifest.instrumentationRunner}),{packageName:s.manifest.packageName,version:s.manifest.version});return{backend:`android-multitouch-helper`,helperVersion:s.manifest.version,installReason:l.reason,...u}}function q(e){let t=Math.round(pe(e));switch(e.kind){case`swipe`:return{kind:`swipe`,x1:Math.round(e.x1),y1:Math.round(e.y1),x2:Math.round(e.x2),y2:Math.round(e.y2),durationMs:t};case`pinch`:return{kind:`pinch`,x:Math.round(e.x),y:Math.round(e.y),scale:e.scale,radius:160,durationMs:t};case`rotate`:return{kind:`rotate`,x:Math.round(e.x),y:Math.round(e.y),degrees:e.degrees,radius:160,durationMs:t};case`transform`:return{kind:`transform`,x:Math.round(e.x),y:Math.round(e.y),dx:Math.round(e.dx),dy:Math.round(e.dy),scale:e.scale,degrees:e.degrees,durationMs:t}}}function pe(e){if(e.durationMs!==void 0)return e.durationMs;if(e.kind===`swipe`||e.kind===`pinch`)return 300;let t=Math.ceil(Math.abs(e.degrees)/3)*16;return Math.min(Math.max(300,t),2400)}async function me(e){let n=Buffer.from(JSON.stringify({protocol:L,...e.request})).toString(`base64`),r=await e.adb([`shell`,`am`,`instrument`,`-w`,`-e`,`payloadBase64`,n,e.instrumentationRunner],{allowFailure:!0,timeoutMs:45e3}),i;try{i=he(`${r.stdout}\n${r.stderr}`)}catch(e){if(e instanceof t){if(e.code===z)throw new t(`COMMAND_FAILED`,e.message,e.details,e);if(e.code!==R)throw e}throw new t(`COMMAND_FAILED`,r.exitCode===0?`Android multi-touch helper output could not be parsed`:`Android multi-touch helper failed before returning parseable output`,{stdout:r.stdout,stderr:r.stderr,exitCode:r.exitCode},e)}if(r.exitCode!==0)throw new t(`COMMAND_FAILED`,`Android multi-touch helper failed`,l(r,{helper:i}));return i}function he(e){let n=ae(e).results.find(e=>e.agentDeviceProtocol===L);if(!n)throw new t(R,`Android multi-touch helper did not return a final result`);if(n.ok!==`true`)throw new t(z,ge(n),{errorType:n.errorType,helper:n});return{kind:n.kind,helperApiVersion:n.helperApiVersion,injectedEvents:y(n.injectedEvents),elapsedMs:y(n.elapsedMs)}}function ge(e){return e.message&&e.message!==`null`?e.message:e.errorType||`Android multi-touch helper returned an error`}async function _e(){let n=ee(),r=F.join(te(),`android-multitouch-helper`,`dist`),i=F.join(r,`agent-device-android-multitouch-helper-${n}.manifest.json`);try{let e=ve(JSON.parse(await I.readFile(i,`utf8`))),t=F.join(r,e.assetName);return await I.access(t),{apkPath:t,manifest:e}}catch(n){throw new t(`UNSUPPORTED_OPERATION`,`Android touch gestures require the bundled Android touch helper artifact, but it was not found or could not be read`,{manifestPath:i,error:e(n).message},n)}}function ve(e){if(!e||typeof e!=`object`||Array.isArray(e))throw new t(`INVALID_ARGS`,`Android multi-touch helper manifest must be an object.`);let n=e;return{name:X(n.name,`name`,`android-multitouch-helper`),version:Y(n.version,`version`),assetName:Y(n.assetName,`assetName`),sha256:Te(n.sha256),packageName:X(n.packageName,`packageName`,`com.callstack.agentdevice.multitouchhelper`),versionCode:we(n.versionCode,`versionCode`),instrumentationRunner:X(n.instrumentationRunner,`instrumentationRunner`,`com.callstack.agentdevice.multitouchhelper/.MultiTouchInstrumentation`),statusProtocol:X(n.statusProtocol,`statusProtocol`,L)}}async function ye(e){let{adb:t,artifact:n}=e,r=n.manifest.packageName,i=n.manifest.versionCode,a=`${e.deviceKey}\0${r}\0${i}`;if(J.has(a))return{packageName:r,versionCode:i,installed:!1,reason:`current`};let o=await be(t,r);if(o!==void 0&&o>=i)return J.add(a),{packageName:r,versionCode:i,installedVersionCode:o,installed:!1,reason:`current`};await xe(n);let l=await c(n.apkPath,{provider:e.adbProvider,replace:!0,allowTestPackages:!0,allowFailure:!0,timeoutMs:3e4});if(l.exitCode!==0)throw s(`Failed to install Android multi-touch helper`,l,{packageName:r,versionCode:i});return J.add(a),{packageName:r,versionCode:i,installedVersionCode:o,installed:!0,reason:o===void 0?`missing`:`outdated`}}const J=new Set;async function be(e,t){let n=await e([`shell`,`cmd`,`package`,`list`,`packages`,`--show-versioncode`,t],{allowFailure:!0,timeoutMs:5e3});if(n.exitCode!==0)return;let r=RegExp(`package:${Ee(t)}(?:\\s|$).*versionCode:(\\d+)`).exec(`${n.stdout}\n${n.stderr}`);return r?Number(r[1]):void 0}async function xe(e){let n=await Se(e.apkPath);if(n!==e.manifest.sha256)throw new t(`COMMAND_FAILED`,`Android multi-touch helper APK checksum mismatch`,{apkPath:e.apkPath,expectedSha256:e.manifest.sha256,actualSha256:n})}async function Se(e){let t=N.createHash(`sha256`);return t.update(await I.readFile(e)),t.digest(`hex`)}function Ce(e){return`${e.platform}:${e.id}`}function Y(e,n){if(typeof e!=`string`||e.length===0)throw new t(`INVALID_ARGS`,`Android multi-touch helper manifest ${n} is required.`);return e}function we(e,t){return oe(e,t,`multi-touch helper`)}function X(e,t,n){return _(e,t,n,`multi-touch helper`)}function Te(e){let n=Y(e,`sha256`).trim().toLowerCase();if(n.length!==64||!/^[0-9a-f]+$/.test(n))throw new t(`INVALID_ARGS`,`Android multi-touch helper manifest sha256 must be a 64-character hex string.`);return n}function Ee(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}const Z=[`window_animation_scale`,`transition_animation_scale`,`animator_duration_scale`];async function De(e,n,r,i,a){switch(n.toLowerCase()){case`wifi`:await m(e,[`shell`,`svc`,`wifi`,M(r)?`enable`:`disable`]);return;case`airplane`:{let t=M(r),n=t?`1`:`0`,i=t?`true`:`false`;await m(e,[`shell`,`settings`,`put`,`global`,`airplane_mode_on`,n]),await m(e,[`shell`,`am`,`broadcast`,`-a`,`android.intent.action.AIRPLANE_MODE`,`--ez`,`state`,i]);return}case`location`:if(r.toLowerCase()===`set`){if(e.kind!==`emulator`)throw new t(`UNSUPPORTED_OPERATION`,`Android precise location coordinates are supported only on emulators.`,{deviceId:e.id,hint:`Use an Android emulator for adb emu geo fix, or configure location through device/provider tooling.`});let{latitude:n,longitude:r}=u(a);return await m(e,[`emu`,`geo`,`fix`,String(r),String(n)]),{latitude:n,longitude:r}}await m(e,[`shell`,`settings`,`put`,`secure`,`location_mode`,M(r)?`3`:`0`]);return;case`animations`:{let t=M(r)?`1`:`0`;for(let n of Z)await m(e,[`shell`,`settings`,`put`,`global`,n,t]);return{scale:t,keys:[...Z]}}case`appearance`:await m(e,[`shell`,`cmd`,`uimode`,`night`,await Me(e,r)===`dark`?`yes`:`no`]);return;case`clear-app-state`:{if(r.toLowerCase()!==`clear`)throw new t(`INVALID_ARGS`,`settings clear-app-state only supports clear.`);if(!i)throw new t(`INVALID_ARGS`,`settings clear-app-state requires an app id or an active app session.`);let n=await re(e,i);if(n.type===`intent`)throw new t(`INVALID_ARGS`,`settings clear-app-state requires a package name, not an intent.`);await m(e,[`shell`,`am`,`force-stop`,n.value],{allowFailure:!0});let a=await m(e,[`shell`,`pm`,`clear`,n.value],{allowFailure:!0});if(a.exitCode!==0||!/\bSuccess\b/i.test(a.stdout))throw new t(`COMMAND_FAILED`,`Failed to clear Android app data for ${n.value}`,{package:n.value,stdout:a.stdout,stderr:a.stderr,exitCode:a.exitCode});return{package:n.value,cleared:!0}}case`fingerprint`:await ke(e,Oe(r));return;case`permission`:{if(!i)throw new t(`INVALID_ARGS`,`permission setting requires an active app in session`);let n=j(r),o=Pe(a?.permissionTarget,a?.permissionMode);if(o.kind===`notifications`){await Ie(e,i,n,o);return}let s=n===`grant`?`grant`:`revoke`;if(o.type===`photos`){await Fe(e,i,s);return}await m(e,[`shell`,`pm`,s,i,o.value]);return}default:throw new t(`INVALID_ARGS`,`Unsupported setting: ${n}`)}}function Oe(e){let n=e.trim().toLowerCase();if(n===`match`)return`match`;if(n===`nonmatch`)return`nonmatch`;throw new t(`INVALID_ARGS`,`Invalid fingerprint state: ${e}. Use match|nonmatch.`)}async function ke(e,n){let r=Ae(e,n),i=[];for(let t of r){let n=await m(e,t,{allowFailure:!0});if(n.exitCode===0)return;i.push({args:t,stdout:n.stdout,stderr:n.stderr,exitCode:n.exitCode})}let a=O(i);throw i.length>0&&i.every(e=>je(e.stdout,e.stderr))?new t(`UNSUPPORTED_OPERATION`,`Android fingerprint simulation is not supported on this target/runtime.`,{deviceId:e.id,action:n,hint:`Use an Android emulator with biometric support, or a device/runtime that exposes cmd fingerprint.`,attempts:a}):new t(`COMMAND_FAILED`,`Failed to simulate Android fingerprint.`,{deviceId:e.id,action:n,attempts:a})}function Ae(e,t){let n=t===`match`?`1`:`9999`,r=[[`shell`,`cmd`,`fingerprint`,`touch`,n],[`shell`,`cmd`,`fingerprint`,`finger`,n]];return e.kind===`emulator`&&r.push([`emu`,`finger`,`touch`,n]),r}function je(e,t){let n=`${e}\n${t}`.toLowerCase();return n.includes(`unknown command`)||n.includes(`can't find service: fingerprint`)||n.includes(`service fingerprint was not found`)||n.includes(`fingerprint cmd unavailable`)||n.includes(`emu command is not supported`)||n.includes(`emulator console is not running`)||n.includes(`fingerprint`)&&n.includes(`not found`)}async function Me(e,n){let r=A(n);if(r!==`toggle`)return r;let i=await m(e,[`shell`,`cmd`,`uimode`,`night`],{allowFailure:!0});if(i.exitCode!==0)throw s(`Failed to read current Android appearance`,i);let a=Ne(i.stdout,i.stderr);if(!a)throw new t(`COMMAND_FAILED`,`Unable to determine current Android appearance for toggle`,{stdout:i.stdout,stderr:i.stderr});return a===`auto`?`dark`:a===`dark`?`light`:`dark`}function Ne(e,t){let n=/night mode:\s*(yes|no|auto)\b/i.exec(`${e}\n${t}`);if(!n)return null;let r=n[1]?.toLowerCase();return r===`yes`?`dark`:r===`no`?`light`:r===`auto`?`auto`:null}function Pe(e,n){let r=k(e);if(n?.trim())throw new t(`INVALID_ARGS`,`Permission mode is only supported for photos. Received: ${n}.`);if(r===`camera`)return{kind:`pm`,value:`android.permission.CAMERA`,type:`camera`};if(r===`microphone`)return{kind:`pm`,value:`android.permission.RECORD_AUDIO`,type:`microphone`};if(r===`photos`)return{kind:`pm`,value:`android.permission.READ_MEDIA_IMAGES`,type:`photos`};if(r===`contacts`)return{kind:`pm`,value:`android.permission.READ_CONTACTS`,type:`contacts`};if(r===`notifications`)return{kind:`notifications`,appOps:`POST_NOTIFICATION`,permission:`android.permission.POST_NOTIFICATIONS`};throw new t(`INVALID_ARGS`,`Unsupported permission target on Android: ${e}. Use camera|microphone|photos|contacts|notifications.`)}async function Fe(e,n,r){let i=await Le(e),a=i!==null&&i>=33?[`android.permission.READ_MEDIA_IMAGES`,`android.permission.READ_EXTERNAL_STORAGE`]:[`android.permission.READ_EXTERNAL_STORAGE`,`android.permission.READ_MEDIA_IMAGES`],o=[];for(let t of a){let i=await m(e,[`shell`,`pm`,r,n,t],{allowFailure:!0});if(i.exitCode===0)return;o.push({permission:t,stderr:i.stderr,exitCode:i.exitCode})}throw new t(`COMMAND_FAILED`,`Failed to ${r} Android photos permission`,{appPackage:n,sdkInt:i,attempts:o})}async function Ie(e,t,n,r){let i=n===`grant`?`allow`:n===`deny`?`deny`:`default`;n===`grant`?await m(e,[`shell`,`pm`,`grant`,t,r.permission],{allowFailure:!0}):(await m(e,[`shell`,`pm`,`revoke`,t,r.permission],{allowFailure:!0}),n===`reset`&&(await m(e,[`shell`,`pm`,`clear-permission-flags`,t,r.permission,`user-set`],{allowFailure:!0}),await m(e,[`shell`,`pm`,`clear-permission-flags`,t,r.permission,`user-fixed`],{allowFailure:!0}))),await m(e,[`shell`,`appops`,`set`,t,r.appOps,i])}async function Le(e){let t=await m(e,[`shell`,`getprop`,`ro.build.version.sdk`],{allowFailure:!0});if(t.exitCode!==0)return null;let n=Number.parseInt(t.stdout.trim(),10);return!Number.isFinite(n)||n<=0?null:n}const Q=Buffer.from([137,80,78,71,13,10,26,10]);async function Re(e,t,n={}){if(n.stabilize===!1){await $(e,t);return}await ze(e);try{await ne(1e3),await $(e,t)}finally{await Be(e).catch(()=>{})}}async function ze(e){let t=t=>m(e,[`shell`,t],{allowFailure:!0});await t(`settings put global sysui_demo_allowed 1`);let n=e=>t(`am broadcast -a com.android.systemui.demo -e command ${e}`);await n(`clock -e hhmm 0941`),await n(`notifications -e visible false`)}async function Be(e){await m(e,[`shell`,`am broadcast -a com.android.systemui.demo -e command exit`],{allowFailure:!0})}async function $(e,n){let r=await m(e,[`exec-out`,`screencap`,`-p`],{binaryStdout:!0});if(!r.stdoutBuffer)throw new t(`COMMAND_FAILED`,`Failed to capture screenshot`);let i=r.stdoutBuffer.indexOf(Q);if(i<0)throw new t(`COMMAND_FAILED`,`Screenshot data does not contain a valid PNG header`);let a=Ve(r.stdoutBuffer,i);if(!a)throw new t(`COMMAND_FAILED`,`Screenshot data does not contain a complete PNG payload`);await P.writeFile(n,r.stdoutBuffer.subarray(i,a))}function Ve(e,t){let n=t+Q.length;for(;n+8<=e.length;){let t=e.readUInt32BE(n),r=n+4,i=e.toString(`ascii`,r,r+4),a=n+12+t;if(a>e.length)return null;if(i===`IEND`)return a;n=a}return null}function He(e){return{open:(t,n)=>f(e,t,{activity:n?.activity,appBundleId:n?.appBundleId,launchArgs:n?.launchArgs,url:n?.url}),openDevice:()=>ie(e),close:t=>p(e,t),tap:(t,n)=>b(e,t,n),doubleTap:async(t,n)=>{await b(e,t,n),await b(e,t,n)},swipe:(t,n,r,i,a)=>B(e,{x1:t,y1:n,x2:r,y2:i,durationMs:a}),pan:(t,n,r,i,a)=>B(e,{x1:t,y1:n,x2:r,y2:i,durationMs:a}),fling:(t,n,r,i,a)=>B(e,{x1:t,y1:n,x2:r,y2:i,durationMs:a}),longPress:(t,n,r)=>ce(e,t,n,r),focus:(t,n)=>de(e,t,n),type:(t,n)=>x(e,t,n),fill:(t,n,r,i)=>T(e,t,n,r,i),scroll:(t,n)=>ue(e,t,n),pinch:(t,n,r)=>V(e,{scale:t,x:n,y:r}),screenshot:(t,n)=>Re(e,t,n),snapshot:async t=>{let r=await n(`snapshot_capture`,async()=>await v(e,{appBundleId:t?.appBundleId,interactiveOnly:t?.interactiveOnly,depth:t?.depth,scope:t?.scope,raw:t?.raw,helperSessionScope:t?.appBundleId?`daemon-session`:`command`}),{backend:`android`});return{nodes:r.nodes??[],truncated:r.truncated??!1,backend:`android`,...d(r)}},back:t=>S(e),home:()=>C(e),rotate:t=>le(e,t),rotateGesture:(t,n,r,i)=>H(e,{degrees:t,x:n,y:r,velocity:i}),transformGesture:t=>fe(e,t),appSwitcher:()=>E(e),tvRemote:(t,n)=>D(e,t,n),readClipboard:()=>h(e),writeClipboard:t=>g(e,t),setSetting:(t,n,r,i)=>De(e,t,n,r,i)}}export{He as createAndroidInteractor};
|
|
1
|
+
import{t as e}from"./rolldown-runtime.js";import{_ as t}from"./sdk-contracts.js";import{I as n,d as r,n as i}from"./sdk-android-adb.js";import{Ct as a,xt as o}from"./recording-export-quality.js";import{t as s}from"./timeouts.js";import{c,d as l,l as u,n as ee}from"./app-lifecycle.js";import{s as d}from"./logcat.js";import{a as f,s as p}from"./device-input-state.js";import{Lt as te}from"./internal/daemon.js";import{c as m,d as h,f as g,h as _,i as v,l as y,m as b,n as x,o as S,p as C,r as w,t as T,u as E}from"./input-actions.js";import{a as D,i as O,n as k,r as A,t as j}from"./setting-state.js";import{t as M}from"./method-scope.js";import{promises as N}from"node:fs";const P=[`window_animation_scale`,`transition_animation_scale`,`animator_duration_scale`];async function F(e,n,r,i,o){switch(n.toLowerCase()){case`wifi`:await d(e,[`shell`,`svc`,`wifi`,j(r)?`enable`:`disable`]);return;case`airplane`:{let t=j(r),n=t?`1`:`0`,i=t?`true`:`false`;await d(e,[`shell`,`settings`,`put`,`global`,`airplane_mode_on`,n]),await d(e,[`shell`,`am`,`broadcast`,`-a`,`android.intent.action.AIRPLANE_MODE`,`--ez`,`state`,i]);return}case`location`:if(r.toLowerCase()===`set`){if(e.kind!==`emulator`)throw new t(`UNSUPPORTED_OPERATION`,`Android precise location coordinates are supported only on emulators.`,{deviceId:e.id,hint:`Use an Android emulator for adb emu geo fix, or configure location through device/provider tooling.`});let{latitude:n,longitude:r}=a(o);return await d(e,[`emu`,`geo`,`fix`,String(r),String(n)]),{latitude:n,longitude:r}}await d(e,[`shell`,`settings`,`put`,`secure`,`location_mode`,j(r)?`3`:`0`]);return;case`animations`:{let t=j(r)?`1`:`0`;for(let n of P)await d(e,[`shell`,`settings`,`put`,`global`,n,t]);return{scale:t,keys:[...P]}}case`appearance`:await d(e,[`shell`,`cmd`,`uimode`,`night`,await B(e,r)===`dark`?`yes`:`no`]);return;case`clear-app-state`:{if(r.toLowerCase()!==`clear`)throw new t(`INVALID_ARGS`,`settings clear-app-state only supports clear.`);if(!i)throw new t(`INVALID_ARGS`,`settings clear-app-state requires an app id or an active app session.`);let n=await l(e,i);if(n.type===`intent`)throw new t(`INVALID_ARGS`,`settings clear-app-state requires a package name, not an intent.`);await d(e,[`shell`,`am`,`force-stop`,n.value],{allowFailure:!0});let a=await d(e,[`shell`,`pm`,`clear`,n.value],{allowFailure:!0});if(a.exitCode!==0||!/\bSuccess\b/i.test(a.stdout))throw new t(`COMMAND_FAILED`,`Failed to clear Android app data for ${n.value}`,{package:n.value,stdout:a.stdout,stderr:a.stderr,exitCode:a.exitCode});return{package:n.value,cleared:!0}}case`fingerprint`:await L(e,I(r));return;case`permission`:{if(!i)throw new t(`INVALID_ARGS`,`permission setting requires an active app in session`);let n=A(r),a=H(o?.permissionTarget,o?.permissionMode);if(a.kind===`notifications`){await W(e,i,n,a);return}let s=n===`grant`?`grant`:`revoke`;if(a.type===`photos`){await U(e,i,s);return}await d(e,[`shell`,`pm`,s,i,a.value]);return}default:throw new t(`INVALID_ARGS`,`Unsupported setting: ${n}`)}}function I(e){let n=e.trim().toLowerCase();if(n===`match`)return`match`;if(n===`nonmatch`)return`nonmatch`;throw new t(`INVALID_ARGS`,`Invalid fingerprint state: ${e}. Use match|nonmatch.`)}async function L(e,n){let r=R(e,n),i=[];for(let t of r){let n=await d(e,t,{allowFailure:!0});if(n.exitCode===0)return;i.push({args:t,stdout:n.stdout,stderr:n.stderr,exitCode:n.exitCode})}let a=D(i);throw i.length>0&&i.every(e=>z(e.stdout,e.stderr))?new t(`UNSUPPORTED_OPERATION`,`Android fingerprint simulation is not supported on this target/runtime.`,{deviceId:e.id,action:n,hint:`Use an Android emulator with biometric support, or a device/runtime that exposes cmd fingerprint.`,attempts:a}):new t(`COMMAND_FAILED`,`Failed to simulate Android fingerprint.`,{deviceId:e.id,action:n,attempts:a})}function R(e,t){let n=t===`match`?`1`:`9999`,r=[[`shell`,`cmd`,`fingerprint`,`touch`,n],[`shell`,`cmd`,`fingerprint`,`finger`,n]];return e.kind===`emulator`&&r.push([`emu`,`finger`,`touch`,n]),r}function z(e,t){let n=`${e}\n${t}`.toLowerCase();return n.includes(`unknown command`)||n.includes(`can't find service: fingerprint`)||n.includes(`service fingerprint was not found`)||n.includes(`fingerprint cmd unavailable`)||n.includes(`emu command is not supported`)||n.includes(`emulator console is not running`)||n.includes(`fingerprint`)&&n.includes(`not found`)}async function B(e,n){let r=k(n);if(r!==`toggle`)return r;let a=await d(e,[`shell`,`cmd`,`uimode`,`night`],{allowFailure:!0});if(a.exitCode!==0)throw i(`Failed to read current Android appearance`,a);let o=V(a.stdout,a.stderr);if(!o)throw new t(`COMMAND_FAILED`,`Unable to determine current Android appearance for toggle`,{stdout:a.stdout,stderr:a.stderr});return o===`auto`?`dark`:o===`dark`?`light`:`dark`}function V(e,t){let n=/night mode:\s*(yes|no|auto)\b/i.exec(`${e}\n${t}`);if(!n)return null;let r=n[1]?.toLowerCase();return r===`yes`?`dark`:r===`no`?`light`:r===`auto`?`auto`:null}function H(e,n){let r=O(e);if(n?.trim())throw new t(`INVALID_ARGS`,`Permission mode is only supported for photos. Received: ${n}.`);if(r===`camera`)return{kind:`pm`,value:`android.permission.CAMERA`,type:`camera`};if(r===`microphone`)return{kind:`pm`,value:`android.permission.RECORD_AUDIO`,type:`microphone`};if(r===`photos`)return{kind:`pm`,value:`android.permission.READ_MEDIA_IMAGES`,type:`photos`};if(r===`contacts`)return{kind:`pm`,value:`android.permission.READ_CONTACTS`,type:`contacts`};if(r===`notifications`)return{kind:`notifications`,appOps:`POST_NOTIFICATION`,permission:`android.permission.POST_NOTIFICATIONS`};throw new t(`INVALID_ARGS`,`Unsupported permission target on Android: ${e}. Use camera|microphone|photos|contacts|notifications.`)}async function U(e,n,r){let i=await G(e),a=i!==null&&i>=33?[`android.permission.READ_MEDIA_IMAGES`,`android.permission.READ_EXTERNAL_STORAGE`]:[`android.permission.READ_EXTERNAL_STORAGE`,`android.permission.READ_MEDIA_IMAGES`],o=[];for(let t of a){let i=await d(e,[`shell`,`pm`,r,n,t],{allowFailure:!0});if(i.exitCode===0)return;o.push({permission:t,stderr:i.stderr,exitCode:i.exitCode})}throw new t(`COMMAND_FAILED`,`Failed to ${r} Android photos permission`,{appPackage:n,sdkInt:i,attempts:o})}async function W(e,t,n,r){let i=n===`grant`?`allow`:n===`deny`?`deny`:`default`;n===`grant`?await d(e,[`shell`,`pm`,`grant`,t,r.permission],{allowFailure:!0}):(await d(e,[`shell`,`pm`,`revoke`,t,r.permission],{allowFailure:!0}),n===`reset`&&(await d(e,[`shell`,`pm`,`clear-permission-flags`,t,r.permission,`user-set`],{allowFailure:!0}),await d(e,[`shell`,`pm`,`clear-permission-flags`,t,r.permission,`user-fixed`],{allowFailure:!0}))),await d(e,[`shell`,`appops`,`set`,t,r.appOps,i])}async function G(e){let t=await d(e,[`shell`,`getprop`,`ro.build.version.sdk`],{allowFailure:!0});if(t.exitCode!==0)return null;let n=Number.parseInt(t.stdout.trim(),10);return!Number.isFinite(n)||n<=0?null:n}const K=Buffer.from([137,80,78,71,13,10,26,10]);async function q(e,t,n={}){if(n.stabilize===!1){await X(e,t);return}await J(e);try{await s(1e3),await X(e,t)}finally{await Y(e).catch(()=>{})}}async function J(e){let t=t=>d(e,[`shell`,t],{allowFailure:!0});await t(`settings put global sysui_demo_allowed 1`);let n=e=>t(`am broadcast -a com.android.systemui.demo -e command ${e}`);await n(`clock -e hhmm 0941`),await n(`notifications -e visible false`)}async function Y(e){await d(e,[`shell`,`am broadcast -a com.android.systemui.demo -e command exit`],{allowFailure:!0})}async function X(e,n){let r=await d(e,[`exec-out`,`screencap`,`-p`],{binaryStdout:!0});if(!r.stdoutBuffer)throw new t(`COMMAND_FAILED`,`Failed to capture screenshot`);let i=r.stdoutBuffer.indexOf(K);if(i<0)throw new t(`COMMAND_FAILED`,`Screenshot data does not contain a valid PNG header`);let a=Z(r.stdoutBuffer,i);if(!a)throw new t(`COMMAND_FAILED`,`Screenshot data does not contain a complete PNG payload`);await N.writeFile(n,r.stdoutBuffer.subarray(i,a))}function Z(e,t){let n=t+K.length;for(;n+8<=e.length;){let t=e.readUInt32BE(n),r=n+4,i=e.toString(`ascii`,r,r+4),a=n+12+t;if(a>e.length)return null;if(i===`IEND`)return a;n=a}return null}var Q=e({createAndroidInteractor:()=>$});function $(e,t){let i={open:(t,n)=>c(e,t,{activity:n?.activity,appBundleId:n?.appBundleId,launchArgs:n?.launchArgs,url:n?.url}),openDevice:()=>u(e),close:t=>ee(e,t),tap:(t,n)=>y(e,t,n),doubleTap:async(t,n)=>{await y(e,t,n),await y(e,t,n)},longPress:(t,n,r)=>m(e,t,n,r),focus:(t,n)=>v(e,t,n),type:(t,n)=>C(e,t,n),fill:(t,n,r,i)=>w(e,t,n,r,i),scroll:(t,n)=>h(e,t,n),performGesture:t=>b(e,t),gestureViewport:()=>_(e),screenshot:(t,n)=>q(e,t,n),snapshot:async t=>{let r=await n(`snapshot_capture`,async()=>await te(e,{appBundleId:t?.appBundleId,interactiveOnly:t?.interactiveOnly,depth:t?.depth,scope:t?.scope,raw:t?.raw,includeHiddenContentHints:t?.includeHiddenContentHints,helperSessionScope:t?.appBundleId?`daemon-session`:`command`}),{backend:`android`});return{nodes:r.nodes??[],truncated:r.truncated??!1,backend:`android`,...o(r)}},back:t=>x(e),home:()=>S(e),setOrientation:t=>g(e,t),appSwitcher:()=>T(e),tvRemote:(t,n)=>E(e,t,n),readClipboard:()=>f(e),writeClipboard:t=>p(e,t),setSetting:(t,n,r,i)=>F(e,t,n,r,i)};return t?M(i,n=>r(t,{serial:e.id},n)):i}export{$ as n,Q as t};
|