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.
Files changed (88) hide show
  1. package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.19.3.apk +0 -0
  2. package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.19.3.apk.sha256 +1 -0
  3. 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
  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
  5. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.19.3.apk.sha256 +1 -0
  6. 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
  7. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h +7 -0
  8. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +99 -30
  9. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +9 -1
  10. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +21 -8
  11. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +10 -0
  12. package/dist/apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +1 -0
  13. package/dist/src/agent-browser-tool.js +1 -1
  14. package/dist/src/android-adb.js +1 -1
  15. package/dist/src/android-input-ownership.js +1 -1
  16. package/dist/src/android.js +1 -1
  17. package/dist/src/app-lifecycle.js +1 -1
  18. package/dist/src/apple-runner-options.js +1 -1
  19. package/dist/src/apps.js +5 -5
  20. package/dist/src/audio-probe.js +1 -1
  21. package/dist/src/cli-help.js +34 -19
  22. package/dist/src/cli-runner.js +1 -1
  23. package/dist/src/cli.js +11 -11
  24. package/dist/src/client.js +2 -2
  25. package/dist/src/contracts.js +1 -1
  26. package/dist/src/debug-symbols.js +1 -1
  27. package/dist/src/device-input-state.js +1 -1
  28. package/dist/src/devices2.js +1 -1
  29. package/dist/src/find.js +1 -1
  30. package/dist/src/generic.js +1 -1
  31. package/dist/src/helper.js +1 -1
  32. package/dist/src/index.d.ts +29 -0
  33. package/dist/src/index.js +1 -1
  34. package/dist/src/input-actions.js +1 -1
  35. package/dist/src/input-actions2.js +1 -1
  36. package/dist/src/install-source2.js +1 -1
  37. package/dist/src/interaction-snapshot.js +1 -1
  38. package/dist/src/interaction.js +1 -1
  39. package/dist/src/interactor.js +2 -2
  40. package/dist/src/internal/bin.js +1 -1
  41. package/dist/src/internal/daemon.js +20 -16
  42. package/dist/src/internal/png-worker.js +1 -1
  43. package/dist/src/lease.js +1 -1
  44. package/dist/src/linux.js +1 -1
  45. package/dist/src/notifications.js +1 -1
  46. package/dist/src/png-worker-contract.js +1 -1
  47. package/dist/src/points.js +3 -3
  48. package/dist/src/providers.js +1 -1
  49. package/dist/src/react-native.js +1 -1
  50. package/dist/src/record-trace-recording.js +2 -2
  51. package/dist/src/record-trace.js +1 -1
  52. package/dist/src/registry.js +24 -24
  53. package/dist/src/reporting.js +3 -3
  54. package/dist/src/runner-client.js +1 -1
  55. package/dist/src/runner-sequence.js +1 -1
  56. package/dist/src/runtime-transport.js +1 -1
  57. package/dist/src/runtime.js +2 -2
  58. package/dist/src/sdk-android-adb.js +3 -3
  59. package/dist/src/sdk-app-inventory.js +1 -1
  60. package/dist/src/sdk-batch-runner.js +2 -2
  61. package/dist/src/sdk-batch.js +2 -2
  62. package/dist/src/sdk-contracts.js +2 -2
  63. package/dist/src/sdk-device.js +1 -0
  64. package/dist/src/sdk-finders.js +1 -1
  65. package/dist/src/sdk-io.js +1 -1
  66. package/dist/src/sdk-remote-config.js +1 -1
  67. package/dist/src/sdk-selectors.js +1 -1
  68. package/dist/src/selector-runtime.js +1 -1
  69. package/dist/src/selectors.js +1 -1
  70. package/dist/src/server.js +2 -2
  71. package/dist/src/session.js +10 -10
  72. package/dist/src/setting-state.js +1 -1
  73. package/dist/src/simctl.js +1 -1
  74. package/dist/src/simulator.js +1 -1
  75. package/dist/src/snapshot-diagnostics.js +1 -1
  76. package/dist/src/snapshot.js +1 -1
  77. package/dist/src/snapshot2.js +2 -2
  78. package/dist/src/tool-provider.js +2 -2
  79. package/dist/src/tool-provider2.js +1 -1
  80. package/dist/src/tv-remote.js +9 -9
  81. package/dist/src/version.js +1 -1
  82. package/dist/src/web.js +1 -1
  83. package/package.json +11 -3
  84. package/server.json +2 -2
  85. package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.19.1.apk +0 -0
  86. package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.19.1.apk.sha256 +0 -1
  87. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.19.1.apk.sha256 +0 -1
  88. package/dist/src/snapshot-helper.js +0 -5
@@ -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.1",
4
- "assetName": "agent-device-android-multitouch-helper-0.19.1.apk",
5
- "sha256": "047938649fa5a1be4959384c3a6b64443cf4135567fb8f72cfb1107375b0eec7",
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": 19001,
7
+ "versionCode": 19003,
8
8
  "instrumentationRunner": "com.callstack.agentdevice.multitouchhelper/.MultiTouchInstrumentation",
9
9
  "statusProtocol": "android-multitouch-helper-v1"
10
10
  }
@@ -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.1",
4
- "releaseTag": "v0.19.1",
5
- "assetName": "agent-device-android-snapshot-helper-0.19.1.apk",
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": "e6e5119b4b2ef7d67b8e50acc745f0ffec78951be2094a9198cb491c5e078b22",
8
- "checksumName": "agent-device-android-snapshot-helper-0.19.1.apk.sha256",
7
+ "sha256": "a03f6bc81224ce0a16a141ef88b969f988ad7da52e0f4f1dfda34abaa81ac868",
8
+ "checksumName": "agent-device-android-snapshot-helper-0.19.3.apk.sha256",
9
9
  "packageName": "com.callstack.agentdevice.snapshothelper",
10
- "versionCode": 19001,
10
+ "versionCode": 19003,
11
11
  "instrumentationRunner": "com.callstack.agentdevice.snapshothelper/.SnapshotInstrumentation",
12
12
  "minSdk": 23,
13
13
  "targetSdk": 36,
@@ -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;
@@ -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 [self trySynthesizeSwipeWithApplication:application
110
- x:x
111
- y:y
112
- x2:x2
113
- y2:y2
114
- durationMs:durationMs];
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
- + (NSString * _Nullable)trySynthesizeSwipeWithApplication:(id)application
207
- x:(double)x
208
- y:(double)y
209
- x2:(double)x2
210
- y2:(double)y2
211
- durationMs:(double)durationMs {
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
- @"agent-device-swipe",
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 = RunnerSwipePointerPath(&bridge, CGPointMake(x, y), CGPointMake(x2, y2), durationMs);
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
- CGPoint point = RunnerInterpolatedPoint(start, end, RunnerSmoothStep(t));
429
- NSTimeInterval offset = durationSeconds * t;
430
- ((RunnerMsgSendPathMove)objc_msgSend)(path, bridge->moveSelector, point, offset);
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
  }
@@ -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
- if let message = RunnerSynthesizedGesture.synthesizeSwipe(
798
- withApplication: app,
799
- x: Double(start.x),
800
- y: Double(start.y),
801
- x2: Double(end.x),
802
- y2: Double(end.y),
803
- durationMs: durationMs
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."
@@ -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
@@ -234,6 +234,7 @@ extension RunnerTests {
234
234
  x2: dragPoints.x2,
235
235
  y2: dragPoints.y2,
236
236
  durationMs: durationMs,
237
+ semantics: step.dragSemantics,
237
238
  context: dragContext
238
239
  )
239
240
  }
@@ -1 +1 @@
1
- import{t as e}from"./rolldown-runtime.js";import{F as t,I 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};
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};
@@ -1 +1 @@
1
- import{F as e}from"./sdk-contracts.js";import{a as t,n}from"./sdk-android-adb.js";import{r}from"./sdk-app-inventory.js";import{a as i,g as a,h as o,m as s,o as c,u as l}from"./app-lifecycle.js";import{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
+ 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{F as t,I as n}from"./sdk-contracts.js";import{b as r,c as i,h as a,j as o,n as s,r as c,v as l,w as u}from"./sdk-android-adb.js";import{t as d}from"./timeouts.js";import{n as f,t as p}from"./manifest.js";import{n as m,t as h}from"./install-source2.js";import"node:fs";import g from"node:path";function ee(e){let t=e.trim();if(!t||/\s/.test(t))return!1;let n=/^([A-Za-z][A-Za-z0-9+.-]*):(.+)$/.exec(t);if(!n)return!1;let r=n[1]?.toLowerCase(),i=n[2]??``;return r===`http`||r===`https`||r===`ws`||r===`wss`||r===`ftp`||r===`ftps`?i.startsWith(`//`):!0}function _(e){let t=e.trim().split(`:`)[0]?.toLowerCase();return t===`http`||t===`https`}function te(e,t){let n=e?.trim();if(n)return n;if(_(t))return`com.apple.mobilesafari`}function ne(e={}){let t=e.ttlMs??3e4,n=e.nowMs??Date.now,r=new Map,i=e=>{let t=y(e);for(let e of r.keys())e.startsWith(t)&&r.delete(e)};return{get(e,t){let i=v(e,t),a=r.get(i);if(a){if(a.expiresAtMs<=n()){r.delete(i);return}return a.value}},set(e,i,a){return r.set(v(e,i),{value:a,expiresAtMs:n()+t}),a},clear(e){i(e)},async invalidateWhile(e,t){i(e);try{return await t()}finally{i(e)}}}}function v(e,t){return[e.platform,e.deviceId,e.variant??``,t.trim().toLowerCase()].join(`\0`)}function y(e){return[e.platform,e.deviceId,``].join(`\0`)}function b(e){return[`1`,`true`,`yes`,`on`].includes((e??``).trim().toLowerCase())}const x={attempts:3,baseDelayMs:200,maxDelayMs:2e3,jitter:.2};var S=class e{startedAtMs;expiresAtMs;constructor(e,t){this.startedAtMs=e,this.expiresAtMs=e+Math.max(0,t)}static fromTimeoutMs(t,n=Date.now()){return new e(n,t)}remainingMs(e=Date.now()){return Math.max(0,this.expiresAtMs-e)}elapsedMs(e=Date.now()){return Math.max(0,e-this.startedAtMs)}isExpired(e=Date.now()){return this.remainingMs(e)<=0}};async function C(e,n={},r={}){let i={maxAttempts:n.maxAttempts??x.attempts,baseDelayMs:n.baseDelayMs??x.baseDelayMs,maxDelayMs:n.maxDelayMs??x.maxDelayMs,jitter:n.jitter??x.jitter,shouldRetry:n.shouldRetry},a;for(let n=1;n<=i.maxAttempts;n+=1){if(r.signal?.aborted)throw new t(`COMMAND_FAILED`,`request canceled`,{reason:`request_canceled`});if(r.deadline?.isExpired()&&n>1)break;try{let t=await e({attempt:n,maxAttempts:i.maxAttempts,deadline:r.deadline});return r.onEvent?.({phase:r.phase,event:`succeeded`,attempt:n,maxAttempts:i.maxAttempts,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs()}),w({phase:r.phase,event:`succeeded`,attempt:n,maxAttempts:i.maxAttempts,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs()}),t}catch(e){a=e;let t=r.classifyReason?.(e),o={phase:r.phase,event:`attempt_failed`,attempt:n,maxAttempts:i.maxAttempts,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs(),reason:t};if(r.onEvent?.(o),w(o),n>=i.maxAttempts||i.shouldRetry&&!i.shouldRetry(e,n))break;let s=ie(i.baseDelayMs,i.maxDelayMs,i.jitter,n),c=r.deadline?Math.min(s,r.deadline.remainingMs()):s;if(c<=0)break;let l={phase:r.phase,event:`retry_scheduled`,attempt:n,maxAttempts:i.maxAttempts,delayMs:c,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs(),reason:t};r.onEvent?.(l),w(l),await ae(c,r.signal)}}let o={phase:r.phase,event:`exhausted`,attempt:i.maxAttempts,maxAttempts:i.maxAttempts,elapsedMs:r.deadline?.elapsedMs(),remainingMs:r.deadline?.remainingMs(),reason:r.classifyReason?.(a)};throw r.onEvent?.(o),w(o),a||new t(`COMMAND_FAILED`,`retry failed`)}async function re(e,t={}){return C(()=>e(),{maxAttempts:t.attempts,baseDelayMs:t.baseDelayMs,maxDelayMs:t.maxDelayMs,jitter:t.jitter,shouldRetry:t.shouldRetry})}function ie(e,t,n,r){let i=Math.min(t,e*2**(r-1)),a=i*n;return Math.max(0,i+(Math.random()*2-1)*a)}function ae(e,t){return new Promise(n=>{if(t?.aborted){n();return}let r=!1,i=()=>{r||(r=!0,t&&t.removeEventListener(`abort`,o),n())},a=setTimeout(i,e);function o(){clearTimeout(a),i()}t&&t.addEventListener(`abort`,o,{once:!0})})}function w(e){o({level:e.event===`attempt_failed`||e.event===`exhausted`?`warn`:`debug`,phase:`retry`,data:{...e}})}function T(e){return e?.trim()||void 0}function oe(e){return T(e)}function E(e){return new Set(e.split(/[\s,]+/).map(e=>e.trim()).filter(Boolean))}function D(e,t=process.env){let n=T(e)??T(t.AGENT_DEVICE_ANDROID_DEVICE_ALLOWLIST);if(n)return E(n)}const se=new Set([`IOS_BOOT_TIMEOUT`,`IOS_RUNNER_CONNECT_TIMEOUT`,`IOS_TOOL_MISSING`,`ANDROID_BOOT_TIMEOUT`,`ADB_TRANSPORT_UNAVAILABLE`,`CI_RESOURCE_STARVATION_SUSPECTED`]);function ce(e){return se.has(e.toUpperCase())}function O(e){let t=e.error?n(e.error):null,r=e.context?.platform,i=e.context?.phase;if(t?.code===`TOOL_MISSING`)return r===`android`?`ADB_TRANSPORT_UNAVAILABLE`:`IOS_TOOL_MISSING`;let a=t?.details??{},o=typeof a.message==`string`?a.message:void 0,s=typeof a.stdout==`string`?a.stdout:void 0,c=typeof a.stderr==`string`?a.stderr:void 0,l=a.boot&&typeof a.boot==`object`?a.boot:null,u=a.bootstatus&&typeof a.bootstatus==`object`?a.bootstatus:null,d=[e.message,t?.message,e.stdout,e.stderr,o,s,c,typeof l?.stdout==`string`?l.stdout:void 0,typeof l?.stderr==`string`?l.stderr:void 0,typeof u?.stdout==`string`?u.stdout:void 0,typeof u?.stderr==`string`?u.stderr:void 0].filter(Boolean).join(`
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(`
@@ -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{F as t}from"./sdk-contracts.js";import{_ as n,a as r,l as i,n as a,o,v as s,w as c}from"./sdk-android-adb.js";import{t as l}from"./timeouts.js";import{D as u,E as ee,h as d,p as f,u as p}from"./android-input-ownership.js";import{promises as m}from"node:fs";import te from"node:os";import h from"node:path";const ne=[`mCurrentFocus=Window{`,`mFocusedApp=AppWindowToken{`,`mResumedActivity:`,`ResumedActivity:`],re=/\bApplication Not Responding:\s*([A-Za-z0-9_.]+)/i,ie=/([^{}]*\bis(?:n't| not)\s+responding[^{}]*)/i,ae=/\b([A-Za-z][A-Za-z0-9_]*(?:\.[A-Za-z0-9_]+)+)\b/;function g(e){let t=new Set;for(let n of e.split(`
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{F as e,p as t}from"./sdk-contracts.js";import{p as n}from"./sdk-android-adb.js";import{D as r}from"./android-input-ownership.js";import{n as i}from"./runner-client.js";import{o as a}from"./tool-provider.js";import{t as o}from"./ttl-memo.js";import{H as s,W as c,gn as l,zt as u}from"./internal/daemon.js";import{promises as d}from"node:fs";import f from"node:os";import p from"node:path";const m=o({ttlMs:5e3});async function h(e,n={}){if(l(e))return;let r=_(e);if(m.get(r)===!0){if(!n.focusExisting)return;m.delete(r)}if(t(e)){if(e.kind===`simulator`){let{ensureBootedSimulator:t}=await import(`./simulator.js`).then(e=>e.o);await t(e,{deviceHub:n.deviceHub,focusExisting:n.focusExisting,onColdBootStart:n.onIosSimulatorColdBootStart}),g(r);return}if(e.kind===`device`){await v(e.id),g(r);return}}if(e.platform===`android`){let{waitForAndroidBoot:t}=await import(`./android-input-ownership.js`).then(e=>e.m);await t(e.id),g(r)}}function g(e){m.set(e,!0)}function _(e){let t=e.kind===`simulator`?e.simulatorSetPath??``:``;return JSON.stringify([e.platform,e.kind,e.id,e.target??``,t])}async function v(t){let r=p.join(f.tmpdir(),`agent-device-ready-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);try{let i=await a([`devicectl`,`device`,`info`,`details`,`--device`,t,`--json-output`,r,`--timeout`,`15`],{allowFailure:!0,timeoutMs:18e3}),{stdout:o,stderr:s}=i,c=await b(r);if(i.exitCode===0){if(!c.parsed)throw new e(`COMMAND_FAILED`,`iOS device readiness probe failed`,{kind:`probe_inconclusive`,deviceId:t,stdout:o,stderr:s,hint:`CoreDevice returned success but readiness JSON output was missing or invalid. Retry; if it persists restart Xcode and the iOS device.`});let n=c?.tunnelState?.toLowerCase();if(n===`connecting`)throw new e(`COMMAND_FAILED`,`iOS device is not ready for automation`,{kind:`not_ready`,deviceId:t,tunnelState:n,hint:`Device tunnel is still connecting. Keep the device unlocked and connected by cable until it is fully available in Xcode Devices, then retry.`});return}throw new e(`COMMAND_FAILED`,`iOS device is not ready for automation`,n(i,{kind:`not_ready`,deviceId:t,tunnelState:c?.tunnelState,hint:x(o,s)}))}catch(n){if(n instanceof e&&n.code===`COMMAND_FAILED`){if((typeof n.details?.kind==`string`?n.details.kind:``)===`not_ready`)throw n;let r=n.details??{},i=String(r.stdout??``),a=String(r.stderr??``),o=Number(r.timeoutMs??15e3),s=`CoreDevice did not respond within ${o}ms. Keep the device unlocked and trusted, then retry; if it persists restart Xcode and the iOS device.`;throw new e(`COMMAND_FAILED`,`iOS device readiness probe failed`,{deviceId:t,cause:n.message,timeoutMs:o,stdout:i,stderr:a,hint:i||a?x(i,a):s},n)}throw new e(`COMMAND_FAILED`,`iOS device readiness probe failed`,{deviceId:t,hint:`Reconnect the device, keep it unlocked, and retry.`},n instanceof Error?n:void 0)}finally{await d.rm(r,{force:!0}).catch(()=>{})}}function y(e){let t=e?.result;if(!t||typeof t!=`object`)return{};let n=t.connectionProperties?.tunnelState,r=t.device?.connectionProperties?.tunnelState,i=typeof n==`string`?n:typeof r==`string`?r:void 0;return i?{tunnelState:i}:{}}async function b(e){try{let t=await d.readFile(e,`utf8`);return{parsed:!0,tunnelState:y(JSON.parse(t)).tunnelState}}catch{return{parsed:!1}}}function x(e,t){return c(e,t)||(`${e}\n${t}`.toLowerCase().includes(`timed out waiting for all destinations`)?`Xcode destination did not become available in time. Keep device unlocked and retry.`:s)}function S(e){let{req:t,logPath:n,traceLogPath:r}=e;return{verbose:t.flags?.verbose,logPath:n,traceLogPath:r,requestId:t.meta?.requestId,iosXctestrunFile:t.flags?.iosXctestrunFile,iosXctestDerivedDataPath:t.flags?.iosXctestDerivedDataPath,iosXctestEnvDir:t.flags?.iosXctestEnvDir}}function C(e){let{req:t,logPath:n,appBundleId:r,traceLogPath:i}=e;return{...S({req:t,logPath:n,traceLogPath:i}),runnerLeaseContext:u(n,t.flags,r,i,t.meta?.requestId,t.meta).runnerLeaseContext}}function w(e){let{req:n,logPath:r,device:a,traceLogPath:o,enabled:s}=e;if(!(!s||!t(a)||a.kind!==`simulator`))return e=>i(e,S({req:n,logPath:r,traceLogPath:o}))}function T(e){let{req:t,logPath:n,device:i,surface:a,openTarget:o,traceLogPath:s}=e;return w({req:t,logPath:n,device:i,traceLogPath:s,enabled:a===`app`&&!!o&&!r(o??``)})}export{h as a,w as i,C as n,T as r,S as t};
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};