agent-device 0.19.1 → 0.19.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android-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 +1 -0
- package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.19.1.manifest.json → agent-device-android-multitouch-helper-0.19.3.manifest.json} +4 -4
- package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.19.1.apk → 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 +1 -0
- package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.19.1.manifest.json → agent-device-android-snapshot-helper-0.19.3.manifest.json} +6 -6
- package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h +7 -0
- package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +99 -30
- package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +9 -1
- package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +21 -8
- package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +10 -0
- package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +1 -0
- package/dist/src/agent-browser-tool.js +1 -1
- package/dist/src/android-adb.js +1 -1
- package/dist/src/android-input-ownership.js +1 -1
- package/dist/src/android.js +1 -1
- package/dist/src/app-lifecycle.js +1 -1
- package/dist/src/apple-runner-options.js +1 -1
- package/dist/src/apps.js +5 -5
- package/dist/src/audio-probe.js +1 -1
- package/dist/src/cli-help.js +34 -19
- package/dist/src/cli-runner.js +1 -1
- package/dist/src/cli.js +11 -11
- package/dist/src/client.js +2 -2
- package/dist/src/contracts.js +1 -1
- 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/find.js +1 -1
- package/dist/src/generic.js +1 -1
- package/dist/src/helper.js +1 -1
- package/dist/src/index.d.ts +29 -0
- package/dist/src/index.js +1 -1
- package/dist/src/input-actions.js +1 -1
- package/dist/src/input-actions2.js +1 -1
- package/dist/src/install-source2.js +1 -1
- package/dist/src/interaction-snapshot.js +1 -1
- package/dist/src/interaction.js +1 -1
- package/dist/src/interactor.js +2 -2
- package/dist/src/internal/bin.js +1 -1
- package/dist/src/internal/daemon.js +20 -16
- package/dist/src/internal/png-worker.js +1 -1
- package/dist/src/lease.js +1 -1
- package/dist/src/linux.js +1 -1
- package/dist/src/notifications.js +1 -1
- package/dist/src/png-worker-contract.js +1 -1
- package/dist/src/points.js +3 -3
- package/dist/src/providers.js +1 -1
- package/dist/src/react-native.js +1 -1
- package/dist/src/record-trace-recording.js +2 -2
- package/dist/src/record-trace.js +1 -1
- package/dist/src/registry.js +24 -24
- package/dist/src/reporting.js +3 -3
- package/dist/src/runner-client.js +1 -1
- package/dist/src/runner-sequence.js +1 -1
- package/dist/src/runtime-transport.js +1 -1
- package/dist/src/runtime.js +2 -2
- package/dist/src/sdk-android-adb.js +3 -3
- package/dist/src/sdk-app-inventory.js +1 -1
- package/dist/src/sdk-batch-runner.js +2 -2
- package/dist/src/sdk-batch.js +2 -2
- package/dist/src/sdk-contracts.js +2 -2
- package/dist/src/sdk-device.js +1 -0
- package/dist/src/sdk-finders.js +1 -1
- package/dist/src/sdk-io.js +1 -1
- 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/selectors.js +1 -1
- package/dist/src/server.js +2 -2
- package/dist/src/session.js +10 -10
- package/dist/src/setting-state.js +1 -1
- package/dist/src/simctl.js +1 -1
- package/dist/src/simulator.js +1 -1
- package/dist/src/snapshot-diagnostics.js +1 -1
- package/dist/src/snapshot.js +1 -1
- package/dist/src/snapshot2.js +2 -2
- package/dist/src/tool-provider.js +2 -2
- package/dist/src/tool-provider2.js +1 -1
- package/dist/src/tv-remote.js +9 -9
- package/dist/src/version.js +1 -1
- package/dist/src/web.js +1 -1
- package/package.json +11 -3
- package/server.json +2 -2
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.19.1.apk +0 -0
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.19.1.apk.sha256 +0 -1
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.19.1.apk.sha256 +0 -1
- package/dist/src/snapshot-helper.js +0 -5
|
Binary file
|
package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.19.3.apk.sha256
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
38e24a9ed16d41ac68b810c664e7bfc5c21d7a59be83d9789ea759e46c81eb7e agent-device-android-multitouch-helper-0.19.3.apk
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "android-multitouch-helper",
|
|
3
|
-
"version": "0.19.
|
|
4
|
-
"assetName": "agent-device-android-multitouch-helper-0.19.
|
|
5
|
-
"sha256": "
|
|
3
|
+
"version": "0.19.3",
|
|
4
|
+
"assetName": "agent-device-android-multitouch-helper-0.19.3.apk",
|
|
5
|
+
"sha256": "38e24a9ed16d41ac68b810c664e7bfc5c21d7a59be83d9789ea759e46c81eb7e",
|
|
6
6
|
"packageName": "com.callstack.agentdevice.multitouchhelper",
|
|
7
|
-
"versionCode":
|
|
7
|
+
"versionCode": 19003,
|
|
8
8
|
"instrumentationRunner": "com.callstack.agentdevice.multitouchhelper/.MultiTouchInstrumentation",
|
|
9
9
|
"statusProtocol": "android-multitouch-helper-v1"
|
|
10
10
|
}
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
a03f6bc81224ce0a16a141ef88b969f988ad7da52e0f4f1dfda34abaa81ac868 agent-device-android-snapshot-helper-0.19.3.apk
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "android-snapshot-helper",
|
|
3
|
-
"version": "0.19.
|
|
4
|
-
"releaseTag": "v0.19.
|
|
5
|
-
"assetName": "agent-device-android-snapshot-helper-0.19.
|
|
3
|
+
"version": "0.19.3",
|
|
4
|
+
"releaseTag": "v0.19.3",
|
|
5
|
+
"assetName": "agent-device-android-snapshot-helper-0.19.3.apk",
|
|
6
6
|
"apkUrl": null,
|
|
7
|
-
"sha256": "
|
|
8
|
-
"checksumName": "agent-device-android-snapshot-helper-0.19.
|
|
7
|
+
"sha256": "a03f6bc81224ce0a16a141ef88b969f988ad7da52e0f4f1dfda34abaa81ac868",
|
|
8
|
+
"checksumName": "agent-device-android-snapshot-helper-0.19.3.apk.sha256",
|
|
9
9
|
"packageName": "com.callstack.agentdevice.snapshothelper",
|
|
10
|
-
"versionCode":
|
|
10
|
+
"versionCode": 19003,
|
|
11
11
|
"instrumentationRunner": "com.callstack.agentdevice.snapshothelper/.SnapshotInstrumentation",
|
|
12
12
|
"minSdk": 23,
|
|
13
13
|
"targetSdk": 36,
|
package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h
CHANGED
|
@@ -21,6 +21,13 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
21
21
|
y2:(double)y2
|
|
22
22
|
durationMs:(double)durationMs;
|
|
23
23
|
|
|
24
|
+
+ (NSString * _Nullable)synthesizeContinuousDragWithApplication:(id)application
|
|
25
|
+
x:(double)x
|
|
26
|
+
y:(double)y
|
|
27
|
+
x2:(double)x2
|
|
28
|
+
y2:(double)y2
|
|
29
|
+
durationMs:(double)durationMs;
|
|
30
|
+
|
|
24
31
|
+ (NSString * _Nullable)synthesizeTapWithApplication:(id)application
|
|
25
32
|
x:(double)x
|
|
26
33
|
y:(double)y;
|
package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m
CHANGED
|
@@ -27,6 +27,13 @@ typedef struct {
|
|
|
27
27
|
SEL liftSelector;
|
|
28
28
|
} RunnerXCTestEventBridge;
|
|
29
29
|
|
|
30
|
+
typedef id (*RunnerDragPointerPathFactory)(
|
|
31
|
+
const RunnerXCTestEventBridge *,
|
|
32
|
+
CGPoint,
|
|
33
|
+
CGPoint,
|
|
34
|
+
double
|
|
35
|
+
);
|
|
36
|
+
|
|
30
37
|
static NSString * _Nullable RunnerResolveXCTestEventBridge(
|
|
31
38
|
id application,
|
|
32
39
|
RunnerXCTestEventBridge *bridge
|
|
@@ -53,6 +60,24 @@ static id RunnerSwipePointerPath(
|
|
|
53
60
|
CGPoint end,
|
|
54
61
|
double durationMs
|
|
55
62
|
);
|
|
63
|
+
static id RunnerContinuousDragPointerPath(
|
|
64
|
+
const RunnerXCTestEventBridge *bridge,
|
|
65
|
+
CGPoint start,
|
|
66
|
+
CGPoint end,
|
|
67
|
+
double durationMs
|
|
68
|
+
);
|
|
69
|
+
static NSString * _Nullable RunnerTrySynthesizeDrag(
|
|
70
|
+
id application,
|
|
71
|
+
CGPoint start,
|
|
72
|
+
CGPoint end,
|
|
73
|
+
double durationMs,
|
|
74
|
+
NSString *recordName,
|
|
75
|
+
RunnerDragPointerPathFactory pathFactory
|
|
76
|
+
);
|
|
77
|
+
// XCTest's synthesized swipe convention moves to the endpoint in 100 ms, then holds there for
|
|
78
|
+
// the requested duration. The fast movement is what distinguishes a swipe from a slow pan for
|
|
79
|
+
// nested UIKit gesture recognizers.
|
|
80
|
+
static const NSTimeInterval RunnerSwipeMovementDurationSeconds = 0.1;
|
|
56
81
|
static id RunnerTapPointerPath(
|
|
57
82
|
const RunnerXCTestEventBridge *bridge,
|
|
58
83
|
CGPoint point
|
|
@@ -68,8 +93,6 @@ static CGPoint RunnerPointerPointAt(
|
|
|
68
93
|
double t,
|
|
69
94
|
double side
|
|
70
95
|
);
|
|
71
|
-
static CGPoint RunnerInterpolatedPoint(CGPoint start, CGPoint end, double t);
|
|
72
|
-
static double RunnerSmoothStep(double t);
|
|
73
96
|
|
|
74
97
|
@implementation RunnerSynthesizedGesture
|
|
75
98
|
|
|
@@ -106,12 +129,36 @@ static double RunnerSmoothStep(double t);
|
|
|
106
129
|
y2:(double)y2
|
|
107
130
|
durationMs:(double)durationMs {
|
|
108
131
|
@try {
|
|
109
|
-
return
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
132
|
+
return RunnerTrySynthesizeDrag(
|
|
133
|
+
application,
|
|
134
|
+
CGPointMake(x, y),
|
|
135
|
+
CGPointMake(x2, y2),
|
|
136
|
+
durationMs,
|
|
137
|
+
@"agent-device-swipe",
|
|
138
|
+
RunnerSwipePointerPath
|
|
139
|
+
);
|
|
140
|
+
} @catch (NSException *exception) {
|
|
141
|
+
NSString *name = exception.name ?: @"NSException";
|
|
142
|
+
NSString *reason = exception.reason ?: @"private XCTest event synthesis failed";
|
|
143
|
+
return [NSString stringWithFormat:@"%@: %@", name, reason];
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
+ (NSString * _Nullable)synthesizeContinuousDragWithApplication:(id)application
|
|
148
|
+
x:(double)x
|
|
149
|
+
y:(double)y
|
|
150
|
+
x2:(double)x2
|
|
151
|
+
y2:(double)y2
|
|
152
|
+
durationMs:(double)durationMs {
|
|
153
|
+
@try {
|
|
154
|
+
return RunnerTrySynthesizeDrag(
|
|
155
|
+
application,
|
|
156
|
+
CGPointMake(x, y),
|
|
157
|
+
CGPointMake(x2, y2),
|
|
158
|
+
durationMs,
|
|
159
|
+
@"agent-device-continuous-drag",
|
|
160
|
+
RunnerContinuousDragPointerPath
|
|
161
|
+
);
|
|
115
162
|
} @catch (NSException *exception) {
|
|
116
163
|
NSString *name = exception.name ?: @"NSException";
|
|
117
164
|
NSString *reason = exception.reason ?: @"private XCTest event synthesis failed";
|
|
@@ -203,12 +250,14 @@ static double RunnerSmoothStep(double t);
|
|
|
203
250
|
return nil;
|
|
204
251
|
}
|
|
205
252
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
253
|
+
static NSString * _Nullable RunnerTrySynthesizeDrag(
|
|
254
|
+
id application,
|
|
255
|
+
CGPoint start,
|
|
256
|
+
CGPoint end,
|
|
257
|
+
double durationMs,
|
|
258
|
+
NSString *recordName,
|
|
259
|
+
RunnerDragPointerPathFactory pathFactory
|
|
260
|
+
) {
|
|
212
261
|
RunnerXCTestEventBridge bridge;
|
|
213
262
|
NSString *missing = RunnerResolveXCTestEventBridge(application, &bridge);
|
|
214
263
|
if (missing != nil) {
|
|
@@ -225,7 +274,7 @@ static double RunnerSmoothStep(double t);
|
|
|
225
274
|
id record = ((RunnerMsgSendInitRecord)objc_msgSend)(
|
|
226
275
|
[bridge.recordClass alloc],
|
|
227
276
|
bridge.initRecordSelector,
|
|
228
|
-
|
|
277
|
+
recordName,
|
|
229
278
|
interfaceOrientation
|
|
230
279
|
);
|
|
231
280
|
if (record == nil) {
|
|
@@ -233,7 +282,7 @@ static double RunnerSmoothStep(double t);
|
|
|
233
282
|
}
|
|
234
283
|
((RunnerMsgSendSetInteger)objc_msgSend)(record, bridge.setTargetProcessIDSelector, targetProcessID);
|
|
235
284
|
|
|
236
|
-
id path =
|
|
285
|
+
id path = pathFactory(&bridge, start, end, durationMs);
|
|
237
286
|
if (path == nil) {
|
|
238
287
|
return @"private XCTest event synthesis failed: could not create pointer path";
|
|
239
288
|
}
|
|
@@ -421,13 +470,44 @@ static id RunnerSwipePointerPath(
|
|
|
421
470
|
return nil;
|
|
422
471
|
}
|
|
423
472
|
|
|
473
|
+
NSTimeInterval durationSeconds = durationMs / 1000.0;
|
|
474
|
+
((RunnerMsgSendPathMove)objc_msgSend)(
|
|
475
|
+
path,
|
|
476
|
+
bridge->moveSelector,
|
|
477
|
+
end,
|
|
478
|
+
RunnerSwipeMovementDurationSeconds
|
|
479
|
+
);
|
|
480
|
+
|
|
481
|
+
((RunnerMsgSendPathOffset)objc_msgSend)(
|
|
482
|
+
path,
|
|
483
|
+
bridge->liftSelector,
|
|
484
|
+
RunnerSwipeMovementDurationSeconds + durationSeconds
|
|
485
|
+
);
|
|
486
|
+
return path;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
static id RunnerContinuousDragPointerPath(
|
|
490
|
+
const RunnerXCTestEventBridge *bridge,
|
|
491
|
+
CGPoint start,
|
|
492
|
+
CGPoint end,
|
|
493
|
+
double durationMs
|
|
494
|
+
) {
|
|
495
|
+
id path =
|
|
496
|
+
((RunnerMsgSendInitPath)objc_msgSend)([bridge->pathClass alloc], bridge->initPathSelector, start, 0.0);
|
|
497
|
+
if (path == nil) {
|
|
498
|
+
return nil;
|
|
499
|
+
}
|
|
500
|
+
|
|
424
501
|
int frameCount = MAX(3, (int)(durationMs / 16.0));
|
|
425
502
|
NSTimeInterval durationSeconds = durationMs / 1000.0;
|
|
426
503
|
for (int index = 1; index <= frameCount; index += 1) {
|
|
427
504
|
double t = (double)index / (double)frameCount;
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
505
|
+
double easedT = t * t * (3.0 - 2.0 * t);
|
|
506
|
+
CGPoint point = CGPointMake(
|
|
507
|
+
start.x + (end.x - start.x) * easedT,
|
|
508
|
+
start.y + (end.y - start.y) * easedT
|
|
509
|
+
);
|
|
510
|
+
((RunnerMsgSendPathMove)objc_msgSend)(path, bridge->moveSelector, point, durationSeconds * t);
|
|
431
511
|
}
|
|
432
512
|
|
|
433
513
|
((RunnerMsgSendPathOffset)objc_msgSend)(path, bridge->liftSelector, durationSeconds);
|
|
@@ -471,15 +551,4 @@ static CGPoint RunnerPointerPointAt(
|
|
|
471
551
|
return CGPointMake(centerX + cos(angle) * radius * side, centerY + sin(angle) * radius * side);
|
|
472
552
|
}
|
|
473
553
|
|
|
474
|
-
static CGPoint RunnerInterpolatedPoint(CGPoint start, CGPoint end, double t) {
|
|
475
|
-
return CGPointMake(
|
|
476
|
-
start.x + (end.x - start.x) * t,
|
|
477
|
-
start.y + (end.y - start.y) * t
|
|
478
|
-
);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
static double RunnerSmoothStep(double t) {
|
|
482
|
-
return t * t * (3.0 - 2.0 * t);
|
|
483
|
-
}
|
|
484
|
-
|
|
485
554
|
@end
|
|
@@ -867,6 +867,7 @@ extension RunnerTests {
|
|
|
867
867
|
durationMs: command.durationMs,
|
|
868
868
|
synthesized: command.synthesized == true,
|
|
869
869
|
message: "dragged",
|
|
870
|
+
dragSemantics: command.dragSemantics,
|
|
870
871
|
synthesizedPolicyKind: .synthesizedDrag
|
|
871
872
|
)
|
|
872
873
|
case .scroll:
|
|
@@ -1146,7 +1147,9 @@ extension RunnerTests {
|
|
|
1146
1147
|
ok: false,
|
|
1147
1148
|
error: ErrorPayload(
|
|
1148
1149
|
code: "UNSUPPORTED_OPERATION",
|
|
1149
|
-
message: "Unable to dismiss the iOS keyboard without a safe native dismiss control"
|
|
1150
|
+
message: "Unable to dismiss the iOS keyboard without a safe native dismiss control",
|
|
1151
|
+
hint:
|
|
1152
|
+
"The on-screen keyboard usually does not block agent-device interactions: press the next target directly instead of retrying dismiss. If that press fails or reports no visible effect, scroll the target into view, or use keyboard enter to press the return key when submission is wanted."
|
|
1150
1153
|
)
|
|
1151
1154
|
)
|
|
1152
1155
|
}
|
|
@@ -1272,6 +1275,7 @@ extension RunnerTests {
|
|
|
1272
1275
|
durationMs: Double?,
|
|
1273
1276
|
synthesized: Bool,
|
|
1274
1277
|
message: String,
|
|
1278
|
+
dragSemantics: SynthesizedDragSemantics? = nil,
|
|
1275
1279
|
synthesizedContext: SynthesizedCoordinateContext? = nil,
|
|
1276
1280
|
synthesizedPolicyKind: SynthesizedGesturePolicyKind
|
|
1277
1281
|
) -> Response {
|
|
@@ -1290,6 +1294,7 @@ extension RunnerTests {
|
|
|
1290
1294
|
y2: y2,
|
|
1291
1295
|
durationMs: durationMs,
|
|
1292
1296
|
message: message,
|
|
1297
|
+
dragSemantics: dragSemantics,
|
|
1293
1298
|
context: synthesizedContext,
|
|
1294
1299
|
policyKind: synthesizedPolicyKind
|
|
1295
1300
|
) {
|
|
@@ -1315,6 +1320,7 @@ extension RunnerTests {
|
|
|
1315
1320
|
x2: dragPoints.x2,
|
|
1316
1321
|
y2: dragPoints.y2,
|
|
1317
1322
|
durationMs: durationMs,
|
|
1323
|
+
semantics: dragSemantics,
|
|
1318
1324
|
context: context
|
|
1319
1325
|
)
|
|
1320
1326
|
}
|
|
@@ -1355,6 +1361,7 @@ extension RunnerTests {
|
|
|
1355
1361
|
y2: Double,
|
|
1356
1362
|
durationMs: Double?,
|
|
1357
1363
|
message: String,
|
|
1364
|
+
dragSemantics: SynthesizedDragSemantics?,
|
|
1358
1365
|
context: SynthesizedCoordinateContext?,
|
|
1359
1366
|
policyKind: SynthesizedGesturePolicyKind
|
|
1360
1367
|
) -> Response? {
|
|
@@ -1408,6 +1415,7 @@ extension RunnerTests {
|
|
|
1408
1415
|
x2: plan.points.x2,
|
|
1409
1416
|
y2: plan.points.y2,
|
|
1410
1417
|
durationMs: durationMs,
|
|
1418
|
+
semantics: dragSemantics,
|
|
1411
1419
|
context: plan.context
|
|
1412
1420
|
)
|
|
1413
1421
|
}
|
package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift
CHANGED
|
@@ -775,6 +775,7 @@ extension RunnerTests {
|
|
|
775
775
|
x2: Double,
|
|
776
776
|
y2: Double,
|
|
777
777
|
durationMs: Double,
|
|
778
|
+
semantics: SynthesizedDragSemantics?,
|
|
778
779
|
context: SynthesizedCoordinateContext? = nil
|
|
779
780
|
) -> RunnerInteractionOutcome {
|
|
780
781
|
#if os(iOS)
|
|
@@ -794,14 +795,26 @@ extension RunnerTests {
|
|
|
794
795
|
let frame = context.referenceFrame
|
|
795
796
|
let start = nativeSynthesizedPoint(orientedX: x, orientedY: y, in: frame, interfaceOrientation: orientation)
|
|
796
797
|
let end = nativeSynthesizedPoint(orientedX: x2, orientedY: y2, in: frame, interfaceOrientation: orientation)
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
798
|
+
let message = if semantics == .swipe {
|
|
799
|
+
RunnerSynthesizedGesture.synthesizeSwipe(
|
|
800
|
+
withApplication: app,
|
|
801
|
+
x: Double(start.x),
|
|
802
|
+
y: Double(start.y),
|
|
803
|
+
x2: Double(end.x),
|
|
804
|
+
y2: Double(end.y),
|
|
805
|
+
durationMs: durationMs
|
|
806
|
+
)
|
|
807
|
+
} else {
|
|
808
|
+
RunnerSynthesizedGesture.synthesizeContinuousDrag(
|
|
809
|
+
withApplication: app,
|
|
810
|
+
x: Double(start.x),
|
|
811
|
+
y: Double(start.y),
|
|
812
|
+
x2: Double(end.x),
|
|
813
|
+
y2: Double(end.y),
|
|
814
|
+
durationMs: durationMs
|
|
815
|
+
)
|
|
816
|
+
}
|
|
817
|
+
if let message {
|
|
805
818
|
return .unsupported(
|
|
806
819
|
message: message,
|
|
807
820
|
hint: "Private XCTest event synthesis is required for AX-free coordinate drag on iOS; update Xcode if this persists."
|
package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift
CHANGED
|
@@ -35,6 +35,12 @@ enum CommandType: String, Codable {
|
|
|
35
35
|
case shutdown
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
enum SynthesizedDragSemantics: String, Codable {
|
|
39
|
+
case swipe
|
|
40
|
+
case pan
|
|
41
|
+
case fling
|
|
42
|
+
}
|
|
43
|
+
|
|
38
44
|
/// Runner command traits — see CONTEXT.md ("Runner command traits").
|
|
39
45
|
///
|
|
40
46
|
/// Single source of truth for how the runner classifies a command across three
|
|
@@ -145,6 +151,8 @@ struct Command: Codable {
|
|
|
145
151
|
let raw: Bool?
|
|
146
152
|
let fullscreen: Bool?
|
|
147
153
|
let synthesized: Bool?
|
|
154
|
+
/// Preserves the public gesture's timing semantics after it is lowered to runner `drag`.
|
|
155
|
+
let dragSemantics: SynthesizedDragSemantics?
|
|
148
156
|
let steps: [SequenceStep]?
|
|
149
157
|
}
|
|
150
158
|
|
|
@@ -162,6 +170,8 @@ struct SequenceStep: Codable {
|
|
|
162
170
|
/// For `tap`/`drag` steps on iOS non-tv: use the synthesized HID fast path instead of the
|
|
163
171
|
/// drag-based XCUICoordinate path, matching the individual command behavior.
|
|
164
172
|
let synthesized: Bool?
|
|
173
|
+
/// Preserves one-shot swipe timing when repeated swipes are fused into a sequence.
|
|
174
|
+
let dragSemantics: SynthesizedDragSemantics?
|
|
165
175
|
}
|
|
166
176
|
|
|
167
177
|
/// Per-step result for a `sequence` response. `ok:false` carries the failing step's
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./rolldown-runtime.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{t as o}from"./timeouts.js";import{c as s,i as c,n as l,o as u,t as d}from"./host-process.js";import f from"node:crypto";import p from"node:fs";import m from"node:os";import h from"node:path";async function g(e){let{lockDirPath:n,owner:r}=e,i=h.join(n,`owner.json`),a=Date.now()+(e.timeoutMs??3e4),s=e.pollMs??100,c=e.ownerGraceMs??5e3,l=e.description??`process lock`;for(p.mkdirSync(h.dirname(n),{recursive:!0});Date.now()<a;)try{p.mkdirSync(n),_(i,r);let e=!1;return async()=>{e||(e=!0,p.rmSync(n,{recursive:!0,force:!0}))}}catch(e){let t=e;if(t.code!==`EEXIST`)throw t;if(ee(n,i,c))continue;await o(s)}throw new t(`COMMAND_FAILED`,`Timed out waiting for ${l}`,{lockDirPath:n,...y(n,i)})}function _(e,t){let n=`${e}.${process.pid}.${Date.now()}.tmp`;p.writeFileSync(n,JSON.stringify(t),`utf8`),p.renameSync(n,e)}function ee(e,t,n){let r=null;try{r=p.statSync(e)}catch{return!0}let i=v(t);return i?b(i)?!1:(p.rmSync(e,{recursive:!0,force:!0}),!0):Date.now()-r.mtimeMs<n?!1:(p.rmSync(e,{recursive:!0,force:!0}),!0)}function v(e){try{return JSON.parse(p.readFileSync(e,`utf8`))}catch{return null}}function y(e,t){let n=Date.now(),r=v(t),i;try{i=Math.max(0,Math.round(n-p.statSync(e).mtimeMs))}catch{}return{...i===void 0?{}:{lockAgeMs:i},...r?{ownerPid:r.pid,ownerStartTime:r.startTime,ownerAgeMs:Math.max(0,Math.round(n-r.acquiredAtMs))}:{}}}function b(e){return!Number.isInteger(e.pid)||e.pid<=0||!l(e.pid)?!1:e.startTime?u(e.pid)===e.startTime:!0}const x=new Map;function S(e){let t=f.createHash(`sha256`);return t.update(h.resolve(e.stateDir)),t.update(`\0`),t.update(h.resolve(e.installDir)),`--agent-device-managed-web=${t.digest(`hex`).slice(0,16)}`}function C(e,t){let n=S(t),r=e?.trim();return r?k(r).includes(n)?r:`${r},${n}`:n}function w(e){return A(e.AGENT_BROWSER_IDLE_TIMEOUT_MS)??A(e.AGENT_DEVICE_WEB_IDLE_TIMEOUT_MS)??3e5}function te(e,t){if(e.pid!==process.pid&&j(e.command)){if(e.command.includes(S(t)))return{process:e,reason:`launch-marker`};if(M(t).some(t=>re(e,t)))return{process:e,reason:`managed-browser-home`}}}function T(e,t){let n=e.flatMap(e=>{let n=te(e,t);return n?[n]:[]});return{count:n.length,pids:n.map(e=>e.process.pid),processes:n}}async function E(e){return T(await O(),e)}async function D(e,t,n={}){let r=N([...n.openWebSessionNames??[]]);if(r.length>0)return P(`open-web-session`,{openWebSessionNames:r});let a=w(process.env),o=I(e);if(o!==void 0&&Date.now()-o<a)return P(`recent-browser-activity`,{idleTimeoutMs:a,latestActivityMs:o});let c=await O(),l=T(c,e),u=d(l.processes.map(e=>e.process.pid),c).map(e=>e.pid),f={...l,signalPids:u};return l.count===0?f:(i({level:`warn`,phase:`web_agent_browser_orphan_cleanup`,data:{reason:t,count:l.count,pids:l.pids,signalPids:u,stateDir:e.stateDir,installDir:e.installDir,matchReasons:l.processes.map(e=>e.reason)}}),await s({pids:u,termTimeoutMs:1500,killTimeoutMs:1e3}),f)}async function ne(e,t={}){let n=Date.now(),r=h.resolve(e.stateDir),i=x.get(r);if(!(i!==void 0&&n-i<3e4))return x.set(r,n),await D(e,`provider-startup`,t)}async function O(){return await r(async()=>await c({timeoutMs:1500}))}function k(e){return e.split(/[,\n]/).map(e=>e.trim()).filter(Boolean)}function A(e){if(e===void 0||e.trim()===``)return;let t=Number.parseInt(e,10);return Number.isInteger(t)&&t>0?t:void 0}function j(e){return/\b(?:Google Chrome for Testing|Chrome for Testing|Chromium|chrome|chrome\.exe|chromium|chromium-browser|headless_shell)\b/i.test(e)}function M(e){return N([h.join(e.homeDir,`.agent-browser`,`browsers`),h.join(e.runtimeHomeDir,`.agent-browser`,`browsers`)])}function N(e){return[...new Set(e.filter(e=>e.length>0))]}function P(e,t){return{count:0,pids:[],processes:[],signalPids:[],skipped:{reason:e,...t}}}function re(e,t){let n=F(e.command),r=`${F(h.resolve(t)).replace(/\/+$/,``)}/`;return n.includes(r)}function F(e){return e.replace(/\\/g,`/`)}function I(e){let t=L(e.socketDir).map(e=>R(e)).filter(e=>e!==void 0);return t.length>0?Math.max(...t):void 0}function L(e){try{return p.readdirSync(e).map(t=>h.join(e,t))}catch{return[]}}function R(e){try{return p.statSync(e).mtimeMs}catch{return}}var z=e({doctorManagedAgentBrowser:()=>K,getManagedAgentBrowserStatus:()=>q,mapManagedAgentBrowserError:()=>$,resolveAgentBrowserTool:()=>W,setupManagedAgentBrowser:()=>G});const B=`0.27.1`,V=`agent-browser`,H=5*6e4,U=6e4;async function W(e){let t=q(e);if(t.installed)return J(t);throw Q(t)}async function G(e){let t=q(e);ce(t.nodeMajor);let n=await g({lockDirPath:h.join(t.installDir,`..`,`.agent-browser-install.lock`),owner:{pid:process.pid,startTime:u(process.pid),acquiredAtMs:Date.now()},timeoutMs:H,description:`managed agent-browser setup`});try{let t=q(e);return t.installed?t:(p.mkdirSync(t.installDir,{recursive:!0}),await Y(t),await X(t,[`install`],{timeoutMs:H}),await X(t,[`doctor`,`--offline`,`--quick`],{timeoutMs:U}),ae(t),q(e))}finally{await n()}}async function K(e){let t=q(e);if(!t.installed)throw Q(t);return{status:t,...await X(t,[`doctor`,`--offline`,`--quick`],{timeoutMs:U,allowFailure:!0})}}function q(e){let t=e.stateDir??process.env.AGENT_DEVICE_STATE_DIR??pe(),n=h.join(t,`tools`,`agent-browser`,B),r=se(n),i=h.join(n,`home`),a=le(n),o=ue(n),s=fe(r)&&oe(n),c=Number.parseInt(process.versions.node.split(`.`)[0]??`0`,10);return{version:B,stateDir:t,installDir:n,binaryPath:r,homeDir:i,runtimeHomeDir:a,socketDir:o,installed:s,nodeMajor:c,nodeSupported:c>=24}}function J(e){if(!e.installed)throw Q(e);return{command:e.binaryPath,env:Z(e,process.env)}}async function Y(e){let t=h.join(e.installDir,`package`);p.mkdirSync(t,{recursive:!0}),await a(`npm`,[`install`,`--prefix`,t,`--no-audit`,`--no-fund`,`--no-save`,`${V}@${B}`],{env:process.env,timeoutMs:H})}async function X(e,t,n){return await a(e.binaryPath,t,{allowFailure:n.allowFailure,env:Z(e,process.env),timeoutMs:n.timeoutMs})}function Z(e,t){return p.mkdirSync(e.homeDir,{recursive:!0}),ie(e),p.mkdirSync(e.socketDir,{recursive:!0}),{...t,HOME:e.runtimeHomeDir,AGENT_BROWSER_SOCKET_DIR:e.socketDir,AGENT_BROWSER_IDLE_TIMEOUT_MS:String(w(t)),AGENT_BROWSER_ARGS:C(t.AGENT_BROWSER_ARGS,e)}}function ie(e){if(e.runtimeHomeDir!==e.homeDir){p.mkdirSync(h.dirname(e.runtimeHomeDir),{recursive:!0});try{let t=p.lstatSync(e.runtimeHomeDir);if(t.isSymbolicLink()&&p.readlinkSync(e.runtimeHomeDir)===e.homeDir||t.isDirectory())return;t.isSymbolicLink()&&p.unlinkSync(e.runtimeHomeDir)}catch(e){if(!de(e))throw e}try{p.symlinkSync(e.homeDir,e.runtimeHomeDir,`dir`)}catch{p.mkdirSync(e.runtimeHomeDir,{recursive:!0})}}}function ae(e){p.writeFileSync(h.join(e.installDir,`manifest.json`),JSON.stringify({package:V,version:B,node:process.version,installedAt:new Date().toISOString()},null,2),`utf8`)}function oe(e){return p.existsSync(h.join(e,`manifest.json`))}function se(e){let t=h.join(e,`package`);return h.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:B,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 ce(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 le(e){if(process.platform===`win32`)return h.join(e,`home`);let t=f.createHash(`sha1`).update(e).digest(`hex`).slice(0,12);return h.join(m.tmpdir(),`agent-device-web`,t)}function ue(e){let t=f.createHash(`sha1`).update(e).digest(`hex`).slice(0,12);return h.join(m.tmpdir(),`adw`,t)}function de(e){return typeof e==`object`&&!!e&&`code`in e&&e.code===`ENOENT`}function fe(e){try{return p.accessSync(e,p.constants.X_OK),!0}catch{return!1}}function pe(){return h.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{D as a,g as c,W as i,q as n,ne as o,$ as r,E as s,z as t};
|
package/dist/src/android-adb.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{_ as e}from"./sdk-contracts.js";import{a as t,n}from"./sdk-android-adb.js";import{r}from"./sdk-app-inventory.js";import{a as i,g as a,h as o,m as s,o as c,u as l}from"./app-lifecycle.js";import{s as u}from"./android-input-ownership.js";import{c as d,i as f,n as p,o as m}from"./device-input-state.js";const h=`android.intent.category.LAUNCHER`,g=`android.intent.category.LEANBACK_LAUNCHER`;async function _(e,t={}){let n=await y(e,t.target??`auto`);return(r(t.filter)===`user-installed`?(await C(e)).filter(e=>n.has(e)):Array.from(n)).map(e=>({package:e,name:i(e)})).sort((e,t)=>e.package.localeCompare(t.package))}async function v(e){return await w(e,[[`shell`,`dumpsys`,`window`,`windows`],[`shell`,`dumpsys`,`window`]])||await w(e,[[`shell`,`dumpsys`,`activity`,`activities`],[`shell`,`dumpsys`,`activity`]])||{}}async function y(e,t){let n=(await Promise.all(b(t).map(async t=>{let n=await e(x(t),{allowFailure:!0});return n.exitCode===0?S(n.stdout):[]}))).flat();return new Set(n)}function b(e){switch(e){case`mobile`:return[h];case`tv`:return[g];default:return[h,g]}}function x(e){return[`shell`,`cmd`,`package`,`query-activities`,`--brief`,`-a`,`android.intent.action.MAIN`,`-c`,e]}function S(e){return e.trim().length===0?[]:o(e)}async function C(e){let t=await e([`shell`,`pm`,`list`,`packages`,`-3`],{allowFailure:!0});if(t.exitCode!==0)throw n(`Failed to list Android user-installed apps`,t);return a(t.stdout)}async function w(e,t){for(let n of t){let t=s((await e(n,{allowFailure:!0})).stdout??``);if(t)return t}return null}const T=`android.intent.category.LAUNCHER`,E=`android.intent.category.DEFAULT`;async function D(e,t){await e([`shell`,`am`,`force-stop`,t])}async function O(e,t,n=[T]){for(let r of n){let n=await e([`shell`,`cmd`,`package`,`resolve-activity`,`--brief`,`-a`,`android.intent.action.MAIN`,`-c`,r,t],{allowFailure:!0});if(n.exitCode!==0)continue;let i=l(n.stdout);if(i)return i}return null}async function k(t,n,r={}){let i=r.category??T;if(r.activity){await t([`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.MAIN`,`-c`,E,`-c`,i,`-n`,A(n,r.activity)]);return}let a=await t([`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.MAIN`,`-c`,E,`-c`,i,`-p`,n],{allowFailure:!0});if(a.exitCode===0&&!c(a.stdout,a.stderr))return;let o=await O(t,n,[i]);if(!o)throw new e(`COMMAND_FAILED`,`Failed to resolve Android launch component for ${n}`,{stdout:a.stdout,stderr:a.stderr,exitCode:a.exitCode});await t([`shell`,`am`,`start`,`-W`,`-a`,`android.intent.action.MAIN`,`-c`,E,`-c`,i,`-n`,o])}function A(e,t){return t.includes(`/`)?t:`${e}/${t.startsWith(`.`)?t:`.${t}`}`}export{u as captureAndroidLogcatWithAdb,t as createAndroidPortReverseManager,p as dismissAndroidKeyboardWithAdb,D as forceStopAndroidAppWithAdb,v as getAndroidAppStateWithAdb,f as getAndroidKeyboardStatusWithAdb,_ as listAndroidAppsWithAdb,k as openAndroidAppWithAdb,m as readAndroidClipboardWithAdb,d as writeAndroidClipboardWithAdb};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{t as e}from"./rolldown-runtime.js";import{
|
|
1
|
+
import{t as e}from"./rolldown-runtime.js";import{_ as t,v as n}from"./sdk-contracts.js";import{b as r,c as i,h as a,j as o,n as s,r as c,v as l,w as u}from"./sdk-android-adb.js";import{t as d}from"./timeouts.js";import{n as f,t as p}from"./manifest.js";import{n as m,t as h}from"./install-source2.js";import"node:fs";import g from"node:path";function ee(e){let t=e.trim();if(!t||/\s/.test(t))return!1;let n=/^([A-Za-z][A-Za-z0-9+.-]*):(.+)$/.exec(t);if(!n)return!1;let r=n[1]?.toLowerCase(),i=n[2]??``;return r===`http`||r===`https`||r===`ws`||r===`wss`||r===`ftp`||r===`ftps`?i.startsWith(`//`):!0}function _(e){let t=e.trim().split(`:`)[0]?.toLowerCase();return t===`http`||t===`https`}function te(e,t){let n=e?.trim();if(n)return n;if(_(t))return`com.apple.mobilesafari`}function ne(e={}){let t=e.ttlMs??3e4,n=e.nowMs??Date.now,r=new Map,i=e=>{let t=y(e);for(let e of r.keys())e.startsWith(t)&&r.delete(e)};return{get(e,t){let i=v(e,t),a=r.get(i);if(a){if(a.expiresAtMs<=n()){r.delete(i);return}return a.value}},set(e,i,a){return r.set(v(e,i),{value:a,expiresAtMs:n()+t}),a},clear(e){i(e)},async invalidateWhile(e,t){i(e);try{return await t()}finally{i(e)}}}}function v(e,t){return[e.platform,e.deviceId,e.variant??``,t.trim().toLowerCase()].join(`\0`)}function y(e){return[e.platform,e.deviceId,``].join(`\0`)}function b(e){return[`1`,`true`,`yes`,`on`].includes((e??``).trim().toLowerCase())}const x={attempts:3,baseDelayMs:200,maxDelayMs:2e3,jitter:.2};var S=class e{startedAtMs;expiresAtMs;constructor(e,t){this.startedAtMs=e,this.expiresAtMs=e+Math.max(0,t)}static fromTimeoutMs(t,n=Date.now()){return new e(n,t)}remainingMs(e=Date.now()){return Math.max(0,this.expiresAtMs-e)}elapsedMs(e=Date.now()){return Math.max(0,e-this.startedAtMs)}isExpired(e=Date.now()){return this.remainingMs(e)<=0}};async function C(e,n={},r={}){let i={maxAttempts:n.maxAttempts??x.attempts,baseDelayMs:n.baseDelayMs??x.baseDelayMs,maxDelayMs:n.maxDelayMs??x.maxDelayMs,jitter:n.jitter??x.jitter,shouldRetry:n.shouldRetry},a;for(let n=1;n<=i.maxAttempts;n+=1){if(r.signal?.aborted)throw new t(`COMMAND_FAILED`,`request canceled`,{reason:`request_canceled`});if(r.deadline?.isExpired()&&n>1)break;try{let t=await e({attempt:n,maxAttempts:i.maxAttempts,deadline:r.deadline});return r.onEvent?.({phase:r.phase,event:`succeeded`,attempt:n,maxAttempts:i.maxAttempts,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs()}),w({phase:r.phase,event:`succeeded`,attempt:n,maxAttempts:i.maxAttempts,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs()}),t}catch(e){a=e;let t=r.classifyReason?.(e),o={phase:r.phase,event:`attempt_failed`,attempt:n,maxAttempts:i.maxAttempts,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs(),reason:t};if(r.onEvent?.(o),w(o),n>=i.maxAttempts||i.shouldRetry&&!i.shouldRetry(e,n))break;let s=ie(i.baseDelayMs,i.maxDelayMs,i.jitter,n),c=r.deadline?Math.min(s,r.deadline.remainingMs()):s;if(c<=0)break;let l={phase:r.phase,event:`retry_scheduled`,attempt:n,maxAttempts:i.maxAttempts,delayMs:c,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs(),reason:t};r.onEvent?.(l),w(l),await ae(c,r.signal)}}let o={phase:r.phase,event:`exhausted`,attempt:i.maxAttempts,maxAttempts:i.maxAttempts,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs(),reason:r.classifyReason?.(a)};throw r.onEvent?.(o),w(o),a||new t(`COMMAND_FAILED`,`retry failed`)}async function re(e,t={}){return C(()=>e(),{maxAttempts:t.attempts,baseDelayMs:t.baseDelayMs,maxDelayMs:t.maxDelayMs,jitter:t.jitter,shouldRetry:t.shouldRetry})}function ie(e,t,n,r){let i=Math.min(t,e*2**(r-1)),a=i*n;return Math.max(0,i+(Math.random()*2-1)*a)}function ae(e,t){return new Promise(n=>{if(t?.aborted){n();return}let r=!1,i=()=>{r||(r=!0,t&&t.removeEventListener(`abort`,o),n())},a=setTimeout(i,e);function o(){clearTimeout(a),i()}t&&t.addEventListener(`abort`,o,{once:!0})})}function w(e){o({level:e.event===`attempt_failed`||e.event===`exhausted`?`warn`:`debug`,phase:`retry`,data:{...e}})}function T(e){return e?.trim()||void 0}function oe(e){return T(e)}function E(e){return new Set(e.split(/[\s,]+/).map(e=>e.trim()).filter(Boolean))}function D(e,t=process.env){let n=T(e)??T(t.AGENT_DEVICE_ANDROID_DEVICE_ALLOWLIST);if(n)return E(n)}const se=new Set([`IOS_BOOT_TIMEOUT`,`IOS_RUNNER_CONNECT_TIMEOUT`,`IOS_TOOL_MISSING`,`ANDROID_BOOT_TIMEOUT`,`ADB_TRANSPORT_UNAVAILABLE`,`CI_RESOURCE_STARVATION_SUSPECTED`]);function ce(e){return se.has(e.toUpperCase())}function O(e){let t=e.error?n(e.error):null,r=e.context?.platform,i=e.context?.phase;if(t?.code===`TOOL_MISSING`)return r===`android`?`ADB_TRANSPORT_UNAVAILABLE`:`IOS_TOOL_MISSING`;let a=t?.details??{},o=typeof a.message==`string`?a.message:void 0,s=typeof a.stdout==`string`?a.stdout:void 0,c=typeof a.stderr==`string`?a.stderr:void 0,l=a.boot&&typeof a.boot==`object`?a.boot:null,u=a.bootstatus&&typeof a.bootstatus==`object`?a.bootstatus:null,d=[e.message,t?.message,e.stdout,e.stderr,o,s,c,typeof l?.stdout==`string`?l.stdout:void 0,typeof l?.stderr==`string`?l.stderr:void 0,typeof u?.stdout==`string`?u.stdout:void 0,typeof u?.stderr==`string`?u.stderr:void 0].filter(Boolean).join(`
|
|
2
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
3
|
`).map(e=>e.trim()).filter(e=>e.length>0);if(t.length!==0)return t.at(-1)===`OK`&&t.pop(),t.join(`
|
|
4
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(`
|
package/dist/src/android.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{B as e,F as t}from"./sdk-contracts.js";import{F as n,c as r,d as i,j as a,l as o,n as s,o as c,p as l}from"./sdk-android-adb.js";import{Tt as u,hn as d}from"./tv-remote.js";import{n as ee,t as te}from"./version.js";import{t as ne}from"./timeouts.js";import{c as f,d as p,l as re,n as m}from"./app-lifecycle.js";import{p as h}from"./android-input-ownership.js";import{a as g,s as _}from"./device-input-state.js";import{d as v,f as y,l as ie,u as ae}from"./snapshot-helper.js";import{Dt as oe}from"./internal/daemon.js";import{a as se,c as ce,d as le,f as ue,i as de,l as b,m as x,n as S,o as C,p as w,r as T,t as E,u as D}from"./input-actions.js";import{a as O,i as k,n as A,r as j,t as M}from"./setting-state.js";import N from"node:crypto";import{promises as P}from"node:fs";import F from"node:path";import I from"node:fs/promises";const L=`android-multitouch-helper-v1`,R=`ANDROID_MULTITOUCH_HELPER_NO_FINAL_RESULT`,z=`ANDROID_MULTITOUCH_HELPER_REPORTED_FAILURE`;async function B(t,n){let r=await G(t,{kind:`swipe`,...n});if(r)return r;try{return await K(t,{kind:`swipe`,...n})}catch(r){return a({level:`warn`,phase:`android_swipe_helper_fallback`,data:{error:e(r).message}}),await w(t,n.x1,n.y1,n.x2,n.y2,n.durationMs),{backend:`adb-input-swipe-fallback`}}}async function V(e,n){if(!Number.isFinite(n.scale)||n.scale<=0)throw new t(`INVALID_ARGS`,`gesture pinch requires scale > 0`);let r=await U(e,n.x,n.y);return await W(e,{kind:`pinch`,x:r.x,y:r.y,scale:n.scale,durationMs:n.durationMs})}async function H(e,n){if(!Number.isFinite(n.degrees))throw new t(`INVALID_ARGS`,`gesture rotate requires finite degrees`);if(n.velocity!==void 0&&(!Number.isFinite(n.velocity)||n.velocity===0))throw new t(`INVALID_ARGS`,`gesture rotate velocity must be a non-zero number`);let r=await U(e,n.x,n.y),i=n.degrees;return await W(e,{kind:`rotate`,x:r.x,y:r.y,degrees:i,durationMs:n.durationMs})}async function fe(e,n){if(!Number.isFinite(n.scale)||n.scale<=0)throw new t(`INVALID_ARGS`,`gesture transform requires scale > 0`);if(!Number.isFinite(n.degrees))throw new t(`INVALID_ARGS`,`gesture transform requires finite degrees`);if(![n.x,n.y,n.dx,n.dy].every(Number.isFinite))throw new t(`INVALID_ARGS`,`gesture transform requires finite x y dx dy`);return await W(e,{kind:`transform`,x:n.x,y:n.y,dx:n.dx,dy:n.dy,scale:n.scale,degrees:n.degrees,durationMs:n.durationMs})}async function U(e,t,n){if(t!==void 0&&n!==void 0)return{x:t,y:n};let r=await se(e);return{x:Math.round(r.width/2),y:Math.round(r.height/2)}}async function W(e,t){return await G(e,t)||await K(e,t)}async function G(e,t){let n=i(e);if(n)return{backend:`provider-native-touch`,...await n(t)??{}}}async function K(e,t){let i=r(e),s=await _e(),c=o(e),l=await n(`android_multitouch_helper_install`,async()=>await ye({adb:i,adbProvider:c,artifact:s,deviceKey:Ce(e)}),{packageName:s.manifest.packageName,versionCode:s.manifest.versionCode});a({phase:`android_multitouch_helper_install_decision`,data:l});let u=await n(`android_multitouch_helper_gesture`,async()=>await me({adb:i,request:q(t),packageName:s.manifest.packageName,instrumentationRunner:s.manifest.instrumentationRunner}),{packageName:s.manifest.packageName,version:s.manifest.version});return{backend:`android-multitouch-helper`,helperVersion:s.manifest.version,installReason:l.reason,...u}}function q(e){let t=Math.round(pe(e));switch(e.kind){case`swipe`:return{kind:`swipe`,x1:Math.round(e.x1),y1:Math.round(e.y1),x2:Math.round(e.x2),y2:Math.round(e.y2),durationMs:t};case`pinch`:return{kind:`pinch`,x:Math.round(e.x),y:Math.round(e.y),scale:e.scale,radius:160,durationMs:t};case`rotate`:return{kind:`rotate`,x:Math.round(e.x),y:Math.round(e.y),degrees:e.degrees,radius:160,durationMs:t};case`transform`:return{kind:`transform`,x:Math.round(e.x),y:Math.round(e.y),dx:Math.round(e.dx),dy:Math.round(e.dy),scale:e.scale,degrees:e.degrees,durationMs:t}}}function pe(e){if(e.durationMs!==void 0)return e.durationMs;if(e.kind===`swipe`||e.kind===`pinch`)return 300;let t=Math.ceil(Math.abs(e.degrees)/3)*16;return Math.min(Math.max(300,t),2400)}async function me(e){let n=Buffer.from(JSON.stringify({protocol:L,...e.request})).toString(`base64`),r=await e.adb([`shell`,`am`,`instrument`,`-w`,`-e`,`payloadBase64`,n,e.instrumentationRunner],{allowFailure:!0,timeoutMs:45e3}),i;try{i=he(`${r.stdout}\n${r.stderr}`)}catch(e){if(e instanceof t){if(e.code===z)throw new t(`COMMAND_FAILED`,e.message,e.details,e);if(e.code!==R)throw e}throw new t(`COMMAND_FAILED`,r.exitCode===0?`Android multi-touch helper output could not be parsed`:`Android multi-touch helper failed before returning parseable output`,{stdout:r.stdout,stderr:r.stderr,exitCode:r.exitCode},e)}if(r.exitCode!==0)throw new t(`COMMAND_FAILED`,`Android multi-touch helper failed`,l(r,{helper:i}));return i}function he(e){let n=ie(e).results.find(e=>e.agentDeviceProtocol===L);if(!n)throw new t(R,`Android multi-touch helper did not return a final result`);if(n.ok!==`true`)throw new t(z,ge(n),{errorType:n.errorType,helper:n});return{kind:n.kind,helperApiVersion:n.helperApiVersion,injectedEvents:y(n.injectedEvents),elapsedMs:y(n.elapsedMs)}}function ge(e){return e.message&&e.message!==`null`?e.message:e.errorType||`Android multi-touch helper returned an error`}async function _e(){let n=ee(),r=F.join(te(),`android-multitouch-helper`,`dist`),i=F.join(r,`agent-device-android-multitouch-helper-${n}.manifest.json`);try{let e=ve(JSON.parse(await I.readFile(i,`utf8`))),t=F.join(r,e.assetName);return await I.access(t),{apkPath:t,manifest:e}}catch(n){throw new t(`UNSUPPORTED_OPERATION`,`Android touch gestures require the bundled Android touch helper artifact, but it was not found or could not be read`,{manifestPath:i,error:e(n).message},n)}}function ve(e){if(!e||typeof e!=`object`||Array.isArray(e))throw new t(`INVALID_ARGS`,`Android multi-touch helper manifest must be an object.`);let n=e;return{name:X(n.name,`name`,`android-multitouch-helper`),version:Y(n.version,`version`),assetName:Y(n.assetName,`assetName`),sha256:Te(n.sha256),packageName:X(n.packageName,`packageName`,`com.callstack.agentdevice.multitouchhelper`),versionCode:we(n.versionCode,`versionCode`),instrumentationRunner:X(n.instrumentationRunner,`instrumentationRunner`,`com.callstack.agentdevice.multitouchhelper/.MultiTouchInstrumentation`),statusProtocol:X(n.statusProtocol,`statusProtocol`,L)}}async function ye(e){let{adb:t,artifact:n}=e,r=n.manifest.packageName,i=n.manifest.versionCode,a=`${e.deviceKey}\0${r}\0${i}`;if(J.has(a))return{packageName:r,versionCode:i,installed:!1,reason:`current`};let o=await be(t,r);if(o!==void 0&&o>=i)return J.add(a),{packageName:r,versionCode:i,installedVersionCode:o,installed:!1,reason:`current`};await xe(n);let l=await c(n.apkPath,{provider:e.adbProvider,replace:!0,allowTestPackages:!0,allowFailure:!0,timeoutMs:3e4});if(l.exitCode!==0)throw s(`Failed to install Android multi-touch helper`,l,{packageName:r,versionCode:i});return J.add(a),{packageName:r,versionCode:i,installedVersionCode:o,installed:!0,reason:o===void 0?`missing`:`outdated`}}const J=new Set;async function be(e,t){let n=await e([`shell`,`cmd`,`package`,`list`,`packages`,`--show-versioncode`,t],{allowFailure:!0,timeoutMs:5e3});if(n.exitCode!==0)return;let r=RegExp(`package:${Ee(t)}(?:\\s|$).*versionCode:(\\d+)`).exec(`${n.stdout}\n${n.stderr}`);return r?Number(r[1]):void 0}async function xe(e){let n=await Se(e.apkPath);if(n!==e.manifest.sha256)throw new t(`COMMAND_FAILED`,`Android multi-touch helper APK checksum mismatch`,{apkPath:e.apkPath,expectedSha256:e.manifest.sha256,actualSha256:n})}async function Se(e){let t=N.createHash(`sha256`);return t.update(await I.readFile(e)),t.digest(`hex`)}function Ce(e){return`${e.platform}:${e.id}`}function Y(e,n){if(typeof e!=`string`||e.length===0)throw new t(`INVALID_ARGS`,`Android multi-touch helper manifest ${n} is required.`);return e}function we(e,t){return ae(e,t,`multi-touch helper`)}function X(e,t,n){return v(e,t,n,`multi-touch helper`)}function Te(e){let n=Y(e,`sha256`).trim().toLowerCase();if(n.length!==64||!/^[0-9a-f]+$/.test(n))throw new t(`INVALID_ARGS`,`Android multi-touch helper manifest sha256 must be a 64-character hex string.`);return n}function Ee(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}const Z=[`window_animation_scale`,`transition_animation_scale`,`animator_duration_scale`];async function De(e,n,r,i,a){switch(n.toLowerCase()){case`wifi`:await h(e,[`shell`,`svc`,`wifi`,M(r)?`enable`:`disable`]);return;case`airplane`:{let t=M(r),n=t?`1`:`0`,i=t?`true`:`false`;await h(e,[`shell`,`settings`,`put`,`global`,`airplane_mode_on`,n]),await h(e,[`shell`,`am`,`broadcast`,`-a`,`android.intent.action.AIRPLANE_MODE`,`--ez`,`state`,i]);return}case`location`:if(r.toLowerCase()===`set`){if(e.kind!==`emulator`)throw new t(`UNSUPPORTED_OPERATION`,`Android precise location coordinates are supported only on emulators.`,{deviceId:e.id,hint:`Use an Android emulator for adb emu geo fix, or configure location through device/provider tooling.`});let{latitude:n,longitude:r}=u(a);return await h(e,[`emu`,`geo`,`fix`,String(r),String(n)]),{latitude:n,longitude:r}}await h(e,[`shell`,`settings`,`put`,`secure`,`location_mode`,M(r)?`3`:`0`]);return;case`animations`:{let t=M(r)?`1`:`0`;for(let n of Z)await h(e,[`shell`,`settings`,`put`,`global`,n,t]);return{scale:t,keys:[...Z]}}case`appearance`:await h(e,[`shell`,`cmd`,`uimode`,`night`,await Me(e,r)===`dark`?`yes`:`no`]);return;case`clear-app-state`:{if(r.toLowerCase()!==`clear`)throw new t(`INVALID_ARGS`,`settings clear-app-state only supports clear.`);if(!i)throw new t(`INVALID_ARGS`,`settings clear-app-state requires an app id or an active app session.`);let n=await p(e,i);if(n.type===`intent`)throw new t(`INVALID_ARGS`,`settings clear-app-state requires a package name, not an intent.`);await h(e,[`shell`,`am`,`force-stop`,n.value],{allowFailure:!0});let a=await h(e,[`shell`,`pm`,`clear`,n.value],{allowFailure:!0});if(a.exitCode!==0||!/\bSuccess\b/i.test(a.stdout))throw new t(`COMMAND_FAILED`,`Failed to clear Android app data for ${n.value}`,{package:n.value,stdout:a.stdout,stderr:a.stderr,exitCode:a.exitCode});return{package:n.value,cleared:!0}}case`fingerprint`:await ke(e,Oe(r));return;case`permission`:{if(!i)throw new t(`INVALID_ARGS`,`permission setting requires an active app in session`);let n=j(r),o=Pe(a?.permissionTarget,a?.permissionMode);if(o.kind===`notifications`){await Ie(e,i,n,o);return}let s=n===`grant`?`grant`:`revoke`;if(o.type===`photos`){await Fe(e,i,s);return}await h(e,[`shell`,`pm`,s,i,o.value]);return}default:throw new t(`INVALID_ARGS`,`Unsupported setting: ${n}`)}}function Oe(e){let n=e.trim().toLowerCase();if(n===`match`)return`match`;if(n===`nonmatch`)return`nonmatch`;throw new t(`INVALID_ARGS`,`Invalid fingerprint state: ${e}. Use match|nonmatch.`)}async function ke(e,n){let r=Ae(e,n),i=[];for(let t of r){let n=await h(e,t,{allowFailure:!0});if(n.exitCode===0)return;i.push({args:t,stdout:n.stdout,stderr:n.stderr,exitCode:n.exitCode})}let a=O(i);throw i.length>0&&i.every(e=>je(e.stdout,e.stderr))?new t(`UNSUPPORTED_OPERATION`,`Android fingerprint simulation is not supported on this target/runtime.`,{deviceId:e.id,action:n,hint:`Use an Android emulator with biometric support, or a device/runtime that exposes cmd fingerprint.`,attempts:a}):new t(`COMMAND_FAILED`,`Failed to simulate Android fingerprint.`,{deviceId:e.id,action:n,attempts:a})}function Ae(e,t){let n=t===`match`?`1`:`9999`,r=[[`shell`,`cmd`,`fingerprint`,`touch`,n],[`shell`,`cmd`,`fingerprint`,`finger`,n]];return e.kind===`emulator`&&r.push([`emu`,`finger`,`touch`,n]),r}function je(e,t){let n=`${e}\n${t}`.toLowerCase();return n.includes(`unknown command`)||n.includes(`can't find service: fingerprint`)||n.includes(`service fingerprint was not found`)||n.includes(`fingerprint cmd unavailable`)||n.includes(`emu command is not supported`)||n.includes(`emulator console is not running`)||n.includes(`fingerprint`)&&n.includes(`not found`)}async function Me(e,n){let r=A(n);if(r!==`toggle`)return r;let i=await h(e,[`shell`,`cmd`,`uimode`,`night`],{allowFailure:!0});if(i.exitCode!==0)throw s(`Failed to read current Android appearance`,i);let a=Ne(i.stdout,i.stderr);if(!a)throw new t(`COMMAND_FAILED`,`Unable to determine current Android appearance for toggle`,{stdout:i.stdout,stderr:i.stderr});return a===`auto`?`dark`:a===`dark`?`light`:`dark`}function Ne(e,t){let n=/night mode:\s*(yes|no|auto)\b/i.exec(`${e}\n${t}`);if(!n)return null;let r=n[1]?.toLowerCase();return r===`yes`?`dark`:r===`no`?`light`:r===`auto`?`auto`:null}function Pe(e,n){let r=k(e);if(n?.trim())throw new t(`INVALID_ARGS`,`Permission mode is only supported for photos. Received: ${n}.`);if(r===`camera`)return{kind:`pm`,value:`android.permission.CAMERA`,type:`camera`};if(r===`microphone`)return{kind:`pm`,value:`android.permission.RECORD_AUDIO`,type:`microphone`};if(r===`photos`)return{kind:`pm`,value:`android.permission.READ_MEDIA_IMAGES`,type:`photos`};if(r===`contacts`)return{kind:`pm`,value:`android.permission.READ_CONTACTS`,type:`contacts`};if(r===`notifications`)return{kind:`notifications`,appOps:`POST_NOTIFICATION`,permission:`android.permission.POST_NOTIFICATIONS`};throw new t(`INVALID_ARGS`,`Unsupported permission target on Android: ${e}. Use camera|microphone|photos|contacts|notifications.`)}async function Fe(e,n,r){let i=await Le(e),a=i!==null&&i>=33?[`android.permission.READ_MEDIA_IMAGES`,`android.permission.READ_EXTERNAL_STORAGE`]:[`android.permission.READ_EXTERNAL_STORAGE`,`android.permission.READ_MEDIA_IMAGES`],o=[];for(let t of a){let i=await h(e,[`shell`,`pm`,r,n,t],{allowFailure:!0});if(i.exitCode===0)return;o.push({permission:t,stderr:i.stderr,exitCode:i.exitCode})}throw new t(`COMMAND_FAILED`,`Failed to ${r} Android photos permission`,{appPackage:n,sdkInt:i,attempts:o})}async function Ie(e,t,n,r){let i=n===`grant`?`allow`:n===`deny`?`deny`:`default`;n===`grant`?await h(e,[`shell`,`pm`,`grant`,t,r.permission],{allowFailure:!0}):(await h(e,[`shell`,`pm`,`revoke`,t,r.permission],{allowFailure:!0}),n===`reset`&&(await h(e,[`shell`,`pm`,`clear-permission-flags`,t,r.permission,`user-set`],{allowFailure:!0}),await h(e,[`shell`,`pm`,`clear-permission-flags`,t,r.permission,`user-fixed`],{allowFailure:!0}))),await h(e,[`shell`,`appops`,`set`,t,r.appOps,i])}async function Le(e){let t=await h(e,[`shell`,`getprop`,`ro.build.version.sdk`],{allowFailure:!0});if(t.exitCode!==0)return null;let n=Number.parseInt(t.stdout.trim(),10);return!Number.isFinite(n)||n<=0?null:n}const Q=Buffer.from([137,80,78,71,13,10,26,10]);async function Re(e,t,n={}){if(n.stabilize===!1){await $(e,t);return}await ze(e);try{await ne(1e3),await $(e,t)}finally{await Be(e).catch(()=>{})}}async function ze(e){let t=t=>h(e,[`shell`,t],{allowFailure:!0});await t(`settings put global sysui_demo_allowed 1`);let n=e=>t(`am broadcast -a com.android.systemui.demo -e command ${e}`);await n(`clock -e hhmm 0941`),await n(`notifications -e visible false`)}async function Be(e){await h(e,[`shell`,`am broadcast -a com.android.systemui.demo -e command exit`],{allowFailure:!0})}async function $(e,n){let r=await h(e,[`exec-out`,`screencap`,`-p`],{binaryStdout:!0});if(!r.stdoutBuffer)throw new t(`COMMAND_FAILED`,`Failed to capture screenshot`);let i=r.stdoutBuffer.indexOf(Q);if(i<0)throw new t(`COMMAND_FAILED`,`Screenshot data does not contain a valid PNG header`);let a=Ve(r.stdoutBuffer,i);if(!a)throw new t(`COMMAND_FAILED`,`Screenshot data does not contain a complete PNG payload`);await P.writeFile(n,r.stdoutBuffer.subarray(i,a))}function Ve(e,t){let n=t+Q.length;for(;n+8<=e.length;){let t=e.readUInt32BE(n),r=n+4,i=e.toString(`ascii`,r,r+4),a=n+12+t;if(a>e.length)return null;if(i===`IEND`)return a;n=a}return null}function He(e){return{open:(t,n)=>f(e,t,{activity:n?.activity,appBundleId:n?.appBundleId,launchArgs:n?.launchArgs,url:n?.url}),openDevice:()=>re(e),close:t=>m(e,t),tap:(t,n)=>b(e,t,n),doubleTap:async(t,n)=>{await b(e,t,n),await b(e,t,n)},swipe:(t,n,r,i,a)=>B(e,{x1:t,y1:n,x2:r,y2:i,durationMs:a}),pan:(t,n,r,i,a)=>B(e,{x1:t,y1:n,x2:r,y2:i,durationMs:a}),fling:(t,n,r,i,a)=>B(e,{x1:t,y1:n,x2:r,y2:i,durationMs:a}),longPress:(t,n,r)=>ce(e,t,n,r),focus:(t,n)=>de(e,t,n),type:(t,n)=>x(e,t,n),fill:(t,n,r,i)=>T(e,t,n,r,i),scroll:(t,n)=>ue(e,t,n),pinch:(t,n,r)=>V(e,{scale:t,x:n,y:r}),screenshot:(t,n)=>Re(e,t,n),snapshot:async t=>{let r=await n(`snapshot_capture`,async()=>await oe(e,{appBundleId:t?.appBundleId,interactiveOnly:t?.interactiveOnly,depth:t?.depth,scope:t?.scope,raw:t?.raw,helperSessionScope:t?.appBundleId?`daemon-session`:`command`}),{backend:`android`});return{nodes:r.nodes??[],truncated:r.truncated??!1,backend:`android`,...d(r)}},back:t=>S(e),home:()=>C(e),rotate:t=>le(e,t),rotateGesture:(t,n,r,i)=>H(e,{degrees:t,x:n,y:r,velocity:i}),transformGesture:t=>fe(e,t),appSwitcher:()=>E(e),tvRemote:(t,n)=>D(e,t,n),readClipboard:()=>g(e),writeClipboard:t=>_(e,t),setSetting:(t,n,r,i)=>De(e,t,n,r,i)}}export{He as createAndroidInteractor};
|
|
1
|
+
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,4 +1,4 @@
|
|
|
1
|
-
import{t as e}from"./rolldown-runtime.js";import{
|
|
1
|
+
import{t as e}from"./rolldown-runtime.js";import{_ as t}from"./sdk-contracts.js";import{_ as n,a as r,l as i,n as a,o,v as s,w as c}from"./sdk-android-adb.js";import{t as l}from"./timeouts.js";import{D as u,E as ee,h as d,p as f,u as p}from"./android-input-ownership.js";import{promises as m}from"node:fs";import te from"node:os";import h from"node:path";const ne=[`mCurrentFocus=Window{`,`mFocusedApp=AppWindowToken{`,`mResumedActivity:`,`ResumedActivity:`],re=/\bApplication Not Responding:\s*([A-Za-z0-9_.]+)/i,ie=/([^{}]*\bis(?:n't| not)\s+responding[^{}]*)/i,ae=/\b([A-Za-z][A-Za-z0-9_]*(?:\.[A-Za-z0-9_]+)+)\b/;function g(e){let t=new Set;for(let n of e.split(`
|
|
2
2
|
`)){let e=n.trim();if(!e)continue;let r=e.split(/\s+/)[0]??``;if(!r.includes(`/`))continue;let i=r.split(`/`)[0]??``;i.includes(`.`)&&i&&t.add(i)}return Array.from(t)}function _(e){return e.split(`
|
|
3
3
|
`).map(e=>{let t=e.trim();return t.startsWith(`package:`)?t.slice(8):t}).filter(Boolean)}function v(e){return y(e,e=>ce(e))}function oe(e){return y(e,(e,t)=>se(e,t))}function y(e,t){let n=e.split(`
|
|
4
4
|
`);for(let e of ne)for(let r of n){let n=r.indexOf(e);if(n===-1)continue;let i=r.trim(),a=t(r.slice(n+e.length),i);if(a)return a}return null}function se(e,t){let n=e.split(`}`)[0]?.trim()??e.trim(),r=re.exec(n);if(r){let e=r[1];return{package:e,focusedWindow:`Application Not Responding: ${e}`,raw:t}}let i=ie.exec(n);if(!i)return null;let a=i[1];if(a===void 0)return null;let o=a.trim().replace(/\s+/g,` `),s=ae.exec(o)?.[1];return{...s?{package:s}:{},focusedWindow:o,raw:t}}function ce(e){for(let t of e.trim().split(/\s+/)){let e=t.indexOf(`/`);if(e<=0)continue;let n=b(t.slice(0,e),!1),r=b(t.slice(e+1),!0);if(n&&r&&n.length===e)return{package:n,activity:r}}return null}function b(e,t){let n=0;for(;n<e.length&&le(e[n],t);)n+=1;return e.slice(0,n)}function le(e,t){if(!e)return!1;let n=e.charCodeAt(0);return n>=48&&n<=57||n>=65&&n<=90||n>=97&&n<=122||e===`_`||e===`.`||t&&e===`$`}const ue=/\.(?:apk|aab)$/i,de=/^[A-Za-z_][\w]*(\.[A-Za-z_][\w]*)+$/;function x(e){let t=e.trim();return t.length===0?`other`:ue.test(t)?t.includes(`/`)||t.includes(`\\`)||t.startsWith(`.`)||t.startsWith(`~`)||!S(t)?`binary`:`package`:S(t)?`package`:`other`}function S(e){return de.test(e)}function fe(e){return`Android runtime hints require an installed package name, not "${e}". Install or reinstall the app first, then relaunch by package.`}var pe=e({closeAndroidApp:()=>G,getAndroidAppState:()=>N,getAndroidBlockingDialogFocus:()=>P,inferAndroidAppName:()=>M,installAndroidApp:()=>He,installAndroidInstallablePath:()=>Q,installAndroidInstallablePathAndResolvePackageName:()=>$,isAmStartError:()=>H,listAndroidApps:()=>A,openAndroidApp:()=>L,openAndroidDevice:()=>W,parseAndroidLaunchComponent:()=>U,reinstallAndroidApp:()=>Ue,resolveAndroidApp:()=>k});const me={settings:{type:`intent`,value:`android.settings.SETTINGS`}},C=`android.intent.category.LAUNCHER`,w=`android.intent.category.LEANBACK_LAUNCHER`,T=`android.intent.category.DEFAULT`,E=`Run agent-device apps --platform android to discover the installed package name, then retry open with that exact package.`,he=new Set([`localhost`,`127.0.0.1`,`::1`,`[::1]`]),D=ee();function O(e){return{platform:`android`,deviceId:e.id,variant:e.target??``}}async function k(e,n){let r=n.trim();if(x(r)===`package`)return{type:`package`,value:r};let i=me[r.toLowerCase()];if(i)return i;let a=O(e),o=D.get(a,r);if(o)return o;let s=(await f(e,[`shell`,`pm`,`list`,`packages`])).stdout.split(`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{_ as e}from"./sdk-contracts.js";import{p as t}from"./sdk-android-adb.js";import{s as n}from"./sdk-device.js";import{D as r}from"./android-input-ownership.js";import{n as i}from"./runner-client.js";import{a}from"./tool-provider.js";import{t as o}from"./ttl-memo.js";import{H as s,W as c,Wt as l,xn as u}from"./internal/daemon.js";import{promises as d}from"node:fs";import f from"node:os";import p from"node:path";const m=o({ttlMs:5e3});async function h(e,t={}){if(u(e))return;let r=_(e);if(m.get(r)===!0){if(!t.focusExisting)return;m.delete(r)}if(n(e)){if(e.kind===`simulator`){let{ensureBootedSimulator:n}=await import(`./simulator.js`).then(e=>e.o);await n(e,{deviceHub:t.deviceHub,focusExisting:t.focusExisting,onColdBootStart:t.onIosSimulatorColdBootStart}),g(r);return}if(e.kind===`device`){await v(e.id),g(r);return}}if(e.platform===`android`){let{waitForAndroidBoot:t}=await import(`./android-input-ownership.js`).then(e=>e.m);await t(e.id),g(r)}}function g(e){m.set(e,!0)}function _(e){let t=e.kind===`simulator`?e.simulatorSetPath??``:``;return JSON.stringify([e.platform,e.kind,e.id,e.target??``,t])}async function v(n){let r=p.join(f.tmpdir(),`agent-device-ready-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);try{let i=await a([`devicectl`,`device`,`info`,`details`,`--device`,n,`--json-output`,r,`--timeout`,`15`],{allowFailure:!0,timeoutMs:18e3}),{stdout:o,stderr:s}=i,c=await b(r);if(i.exitCode===0){if(!c.parsed)throw new e(`COMMAND_FAILED`,`iOS device readiness probe failed`,{kind:`probe_inconclusive`,deviceId:n,stdout:o,stderr:s,hint:`CoreDevice returned success but readiness JSON output was missing or invalid. Retry; if it persists restart Xcode and the iOS device.`});let t=c?.tunnelState?.toLowerCase();if(t===`connecting`)throw new e(`COMMAND_FAILED`,`iOS device is not ready for automation`,{kind:`not_ready`,deviceId:n,tunnelState:t,hint:`Device tunnel is still connecting. Keep the device unlocked and connected by cable until it is fully available in Xcode Devices, then retry.`});return}throw new e(`COMMAND_FAILED`,`iOS device is not ready for automation`,t(i,{kind:`not_ready`,deviceId:n,tunnelState:c?.tunnelState,hint:x(o,s)}))}catch(t){if(t instanceof e&&t.code===`COMMAND_FAILED`){if((typeof t.details?.kind==`string`?t.details.kind:``)===`not_ready`)throw t;let r=t.details??{},i=String(r.stdout??``),a=String(r.stderr??``),o=Number(r.timeoutMs??15e3),s=`CoreDevice did not respond within ${o}ms. Keep the device unlocked and trusted, then retry; if it persists restart Xcode and the iOS device.`;throw new e(`COMMAND_FAILED`,`iOS device readiness probe failed`,{deviceId:n,cause:t.message,timeoutMs:o,stdout:i,stderr:a,hint:i||a?x(i,a):s},t)}throw new e(`COMMAND_FAILED`,`iOS device readiness probe failed`,{deviceId:n,hint:`Reconnect the device, keep it unlocked, and retry.`},t instanceof Error?t:void 0)}finally{await d.rm(r,{force:!0}).catch(()=>{})}}function y(e){let t=e?.result;if(!t||typeof t!=`object`)return{};let n=t.connectionProperties?.tunnelState,r=t.device?.connectionProperties?.tunnelState,i=typeof n==`string`?n:typeof r==`string`?r:void 0;return i?{tunnelState:i}:{}}async function b(e){try{let t=await d.readFile(e,`utf8`);return{parsed:!0,tunnelState:y(JSON.parse(t)).tunnelState}}catch{return{parsed:!1}}}function x(e,t){return c(e,t)||(`${e}\n${t}`.toLowerCase().includes(`timed out waiting for all destinations`)?`Xcode destination did not become available in time. Keep device unlocked and retry.`:s)}function S(e){let{req:t,logPath:n,traceLogPath:r}=e;return{verbose:t.flags?.verbose,logPath:n,traceLogPath:r,requestId:t.meta?.requestId,iosXctestrunFile:t.flags?.iosXctestrunFile,iosXctestDerivedDataPath:t.flags?.iosXctestDerivedDataPath,iosXctestEnvDir:t.flags?.iosXctestEnvDir}}function C(e){let{req:t,logPath:n,appBundleId:r,traceLogPath:i}=e;return{...S({req:t,logPath:n,traceLogPath:i}),runnerLeaseContext:l(n,t.flags,r,i,t.meta?.requestId,t.meta).runnerLeaseContext}}function w(e){let{req:t,logPath:r,device:a,traceLogPath:o,enabled:s}=e;if(!(!s||!n(a)||a.kind!==`simulator`))return e=>i(e,S({req:t,logPath:r,traceLogPath:o}))}function T(e){let{req:t,logPath:n,device:i,surface:a,openTarget:o,traceLogPath:s}=e;return w({req:t,logPath:n,device:i,traceLogPath:s,enabled:a===`app`&&!!o&&!r(o??``)})}export{h as a,w as i,C as n,T as r,S as t};
|