agent-device 0.16.10 → 0.16.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.16.10.apk → agent-device-android-multitouch-helper-0.16.11.apk} +0 -0
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.16.11.apk.sha256 +1 -0
- package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.16.10.manifest.json → agent-device-android-multitouch-helper-0.16.11.manifest.json} +4 -4
- package/android-snapshot-helper/README.md +6 -0
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.11.apk +0 -0
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.11.apk.sha256 +1 -0
- package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.16.10.manifest.json → agent-device-android-snapshot-helper-0.16.11.manifest.json} +6 -6
- package/dist/src/1352.js +1 -1
- package/dist/src/221.js +6 -6
- package/dist/src/2415.js +27 -27
- package/dist/src/2805.js +1 -1
- package/dist/src/4778.js +1 -0
- package/dist/src/5792.js +1 -1
- package/dist/src/6085.js +1 -1
- package/dist/src/6232.js +1 -1
- package/dist/src/8699.js +1 -1
- package/dist/src/9238.js +4 -0
- package/dist/src/9533.js +1 -1
- package/dist/src/9542.js +3 -3
- package/dist/src/apple.js +1 -1
- package/dist/src/args.js +54 -25
- package/dist/src/batch.d.ts +1 -0
- package/dist/src/cli.js +19 -19
- package/dist/src/command-metadata.js +1 -1
- package/dist/src/command-surface.js +1 -1
- package/dist/src/contracts.d.ts +1 -0
- package/dist/src/contracts.js +1 -1
- package/dist/src/generic.js +10 -10
- package/dist/src/index.d.ts +9 -0
- package/dist/src/record-trace.js +3 -3
- package/dist/src/session.js +9 -9
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h +7 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +109 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +123 -32
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +36 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +11 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/skills/dogfood/SKILL.md +1 -1
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.16.10.apk.sha256 +0 -1
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.10.apk +0 -0
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.10.apk.sha256 +0 -1
- package/dist/src/2842.js +0 -1
- package/dist/src/8114.js +0 -4
|
@@ -14,6 +14,13 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
14
14
|
radius:(double)radius
|
|
15
15
|
durationMs:(double)durationMs;
|
|
16
16
|
|
|
17
|
+
+ (NSString * _Nullable)synthesizeSwipeWithApplication:(id)application
|
|
18
|
+
x:(double)x
|
|
19
|
+
y:(double)y
|
|
20
|
+
x2:(double)x2
|
|
21
|
+
y2:(double)y2
|
|
22
|
+
durationMs:(double)durationMs;
|
|
23
|
+
|
|
17
24
|
@end
|
|
18
25
|
|
|
19
26
|
NS_ASSUME_NONNULL_END
|
|
@@ -47,6 +47,12 @@ static id RunnerPointerPath(
|
|
|
47
47
|
double durationMs,
|
|
48
48
|
double side
|
|
49
49
|
);
|
|
50
|
+
static id RunnerSwipePointerPath(
|
|
51
|
+
const RunnerXCTestEventBridge *bridge,
|
|
52
|
+
CGPoint start,
|
|
53
|
+
CGPoint end,
|
|
54
|
+
double durationMs
|
|
55
|
+
);
|
|
50
56
|
static CGPoint RunnerPointerPointAt(
|
|
51
57
|
double x,
|
|
52
58
|
double y,
|
|
@@ -58,6 +64,8 @@ static CGPoint RunnerPointerPointAt(
|
|
|
58
64
|
double t,
|
|
59
65
|
double side
|
|
60
66
|
);
|
|
67
|
+
static CGPoint RunnerInterpolatedPoint(CGPoint start, CGPoint end, double t);
|
|
68
|
+
static double RunnerSmoothStep(double t);
|
|
61
69
|
|
|
62
70
|
@implementation RunnerSynthesizedGesture
|
|
63
71
|
|
|
@@ -87,6 +95,26 @@ static CGPoint RunnerPointerPointAt(
|
|
|
87
95
|
}
|
|
88
96
|
}
|
|
89
97
|
|
|
98
|
+
+ (NSString * _Nullable)synthesizeSwipeWithApplication:(id)application
|
|
99
|
+
x:(double)x
|
|
100
|
+
y:(double)y
|
|
101
|
+
x2:(double)x2
|
|
102
|
+
y2:(double)y2
|
|
103
|
+
durationMs:(double)durationMs {
|
|
104
|
+
@try {
|
|
105
|
+
return [self trySynthesizeSwipeWithApplication:application
|
|
106
|
+
x:x
|
|
107
|
+
y:y
|
|
108
|
+
x2:x2
|
|
109
|
+
y2:y2
|
|
110
|
+
durationMs:durationMs];
|
|
111
|
+
} @catch (NSException *exception) {
|
|
112
|
+
NSString *name = exception.name ?: @"NSException";
|
|
113
|
+
NSString *reason = exception.reason ?: @"private XCTest event synthesis failed";
|
|
114
|
+
return [NSString stringWithFormat:@"%@: %@", name, reason];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
90
118
|
+ (NSString * _Nullable)trySynthesizeTransformWithApplication:(id)application
|
|
91
119
|
x:(double)x
|
|
92
120
|
y:(double)y
|
|
@@ -151,6 +179,51 @@ static CGPoint RunnerPointerPointAt(
|
|
|
151
179
|
return nil;
|
|
152
180
|
}
|
|
153
181
|
|
|
182
|
+
+ (NSString * _Nullable)trySynthesizeSwipeWithApplication:(id)application
|
|
183
|
+
x:(double)x
|
|
184
|
+
y:(double)y
|
|
185
|
+
x2:(double)x2
|
|
186
|
+
y2:(double)y2
|
|
187
|
+
durationMs:(double)durationMs {
|
|
188
|
+
RunnerXCTestEventBridge bridge;
|
|
189
|
+
NSString *missing = RunnerResolveXCTestEventBridge(application, &bridge);
|
|
190
|
+
if (missing != nil) {
|
|
191
|
+
return missing;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
NSInteger interfaceOrientation =
|
|
195
|
+
((RunnerMsgSendInteger)objc_msgSend)(application, bridge.interfaceOrientationSelector);
|
|
196
|
+
NSInteger targetProcessID = ((RunnerMsgSendInteger)objc_msgSend)(application, bridge.processIDSelector);
|
|
197
|
+
if (targetProcessID <= 0) {
|
|
198
|
+
return @"private XCTest event synthesis unavailable: could not resolve target process ID";
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
id record = ((RunnerMsgSendInitRecord)objc_msgSend)(
|
|
202
|
+
[bridge.recordClass alloc],
|
|
203
|
+
bridge.initRecordSelector,
|
|
204
|
+
@"agent-device-swipe",
|
|
205
|
+
interfaceOrientation
|
|
206
|
+
);
|
|
207
|
+
if (record == nil) {
|
|
208
|
+
return @"private XCTest event synthesis failed: could not create event record";
|
|
209
|
+
}
|
|
210
|
+
((RunnerMsgSendSetInteger)objc_msgSend)(record, bridge.setTargetProcessIDSelector, targetProcessID);
|
|
211
|
+
|
|
212
|
+
id path = RunnerSwipePointerPath(&bridge, CGPointMake(x, y), CGPointMake(x2, y2), durationMs);
|
|
213
|
+
if (path == nil) {
|
|
214
|
+
return @"private XCTest event synthesis failed: could not create pointer path";
|
|
215
|
+
}
|
|
216
|
+
((RunnerMsgSendAddPath)objc_msgSend)(record, bridge.addPathSelector, path);
|
|
217
|
+
|
|
218
|
+
NSError *error = nil;
|
|
219
|
+
BOOL ok = ((RunnerMsgSendSynthesize)objc_msgSend)(record, bridge.synthesizeSelector, &error);
|
|
220
|
+
if (!ok) {
|
|
221
|
+
NSString *detail = error.localizedDescription ?: @"synthesizeWithError returned false";
|
|
222
|
+
return [NSString stringWithFormat:@"private XCTest event synthesis failed: %@", detail];
|
|
223
|
+
}
|
|
224
|
+
return nil;
|
|
225
|
+
}
|
|
226
|
+
|
|
154
227
|
static NSString * _Nullable RunnerResolveXCTestEventBridge(
|
|
155
228
|
id application,
|
|
156
229
|
RunnerXCTestEventBridge *bridge
|
|
@@ -270,6 +343,31 @@ static id RunnerPointerPath(
|
|
|
270
343
|
return path;
|
|
271
344
|
}
|
|
272
345
|
|
|
346
|
+
static id RunnerSwipePointerPath(
|
|
347
|
+
const RunnerXCTestEventBridge *bridge,
|
|
348
|
+
CGPoint start,
|
|
349
|
+
CGPoint end,
|
|
350
|
+
double durationMs
|
|
351
|
+
) {
|
|
352
|
+
id path =
|
|
353
|
+
((RunnerMsgSendInitPath)objc_msgSend)([bridge->pathClass alloc], bridge->initPathSelector, start, 0.0);
|
|
354
|
+
if (path == nil) {
|
|
355
|
+
return nil;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
int frameCount = MAX(3, (int)(durationMs / 16.0));
|
|
359
|
+
NSTimeInterval durationSeconds = durationMs / 1000.0;
|
|
360
|
+
for (int index = 1; index <= frameCount; index += 1) {
|
|
361
|
+
double t = (double)index / (double)frameCount;
|
|
362
|
+
CGPoint point = RunnerInterpolatedPoint(start, end, RunnerSmoothStep(t));
|
|
363
|
+
NSTimeInterval offset = durationSeconds * t;
|
|
364
|
+
((RunnerMsgSendPathMove)objc_msgSend)(path, bridge->moveSelector, point, offset);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
((RunnerMsgSendPathOffset)objc_msgSend)(path, bridge->liftSelector, durationSeconds);
|
|
368
|
+
return path;
|
|
369
|
+
}
|
|
370
|
+
|
|
273
371
|
static CGPoint RunnerPointerPointAt(
|
|
274
372
|
double x,
|
|
275
373
|
double y,
|
|
@@ -294,4 +392,15 @@ static CGPoint RunnerPointerPointAt(
|
|
|
294
392
|
return CGPointMake(centerX + cos(angle) * radius * side, centerY + sin(angle) * radius * side);
|
|
295
393
|
}
|
|
296
394
|
|
|
395
|
+
static CGPoint RunnerInterpolatedPoint(CGPoint start, CGPoint end, double t) {
|
|
396
|
+
return CGPointMake(
|
|
397
|
+
start.x + (end.x - start.x) * t,
|
|
398
|
+
start.y + (end.y - start.y) * t
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
static double RunnerSmoothStep(double t) {
|
|
403
|
+
return t * t * (3.0 - 2.0 * t);
|
|
404
|
+
}
|
|
405
|
+
|
|
297
406
|
@end
|
package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift
CHANGED
|
@@ -13,6 +13,10 @@ extension RunnerTests {
|
|
|
13
13
|
return (gestureStartUptimeMs, currentUptimeMs())
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
private func synthesizedSwipeFallbackHoldDuration(durationMs: Double) -> TimeInterval {
|
|
17
|
+
min(max((durationMs / 5.0) / 1000.0, 0.016), 0.120)
|
|
18
|
+
}
|
|
19
|
+
|
|
16
20
|
func unsupportedResponse(for outcome: RunnerInteractionOutcome) -> Response? {
|
|
17
21
|
switch outcome {
|
|
18
22
|
case .performed:
|
|
@@ -32,6 +36,43 @@ extension RunnerTests {
|
|
|
32
36
|
case drag(DragVisualizationFrame)
|
|
33
37
|
}
|
|
34
38
|
|
|
39
|
+
struct GestureFallback {
|
|
40
|
+
let strategy: String
|
|
41
|
+
let message: String
|
|
42
|
+
let hint: String?
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private func gestureFallback(strategy: String, from outcome: RunnerInteractionOutcome) -> GestureFallback? {
|
|
46
|
+
switch outcome {
|
|
47
|
+
case .performed:
|
|
48
|
+
return nil
|
|
49
|
+
case .unsupported(let message, let hint):
|
|
50
|
+
return GestureFallback(strategy: strategy, message: message, hint: hint)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private func performDragSeries(
|
|
55
|
+
count: Int,
|
|
56
|
+
pauseMs: Double,
|
|
57
|
+
pattern: String,
|
|
58
|
+
points: DragPoints,
|
|
59
|
+
_ drag: (_ x: Double, _ y: Double, _ x2: Double, _ y2: Double) -> RunnerInteractionOutcome
|
|
60
|
+
) -> RunnerInteractionOutcome {
|
|
61
|
+
var outcome = RunnerInteractionOutcome.performed
|
|
62
|
+
runSeries(count: count, pauseMs: pauseMs) { idx in
|
|
63
|
+
guard case .performed = outcome else {
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
let reverse = pattern == "ping-pong" && (idx % 2 == 1)
|
|
67
|
+
let startX = reverse ? points.x2 : points.x
|
|
68
|
+
let startY = reverse ? points.y2 : points.y
|
|
69
|
+
let endX = reverse ? points.x : points.x2
|
|
70
|
+
let endY = reverse ? points.y : points.y2
|
|
71
|
+
outcome = drag(startX, startY, endX, endY)
|
|
72
|
+
}
|
|
73
|
+
return outcome
|
|
74
|
+
}
|
|
75
|
+
|
|
35
76
|
/// Runs a gesture action with uniform timing capture. Touch gestures pass `idleTimeout: true`
|
|
36
77
|
/// (the default) to run inside the scroll idle-timeout + quiescence-skip wrapper; synthesis
|
|
37
78
|
/// gestures (pinch/rotate/transform) pass `false` because RunnerSynthesizedGesture governs its
|
|
@@ -60,7 +101,8 @@ extension RunnerTests {
|
|
|
60
101
|
private func gestureResponse(
|
|
61
102
|
message: String,
|
|
62
103
|
timing: (gestureStartUptimeMs: Double, gestureEndUptimeMs: Double),
|
|
63
|
-
frame: GestureFrame = .none
|
|
104
|
+
frame: GestureFrame = .none,
|
|
105
|
+
fallback: GestureFallback? = nil
|
|
64
106
|
) -> Response {
|
|
65
107
|
let data: DataPayload
|
|
66
108
|
switch frame {
|
|
@@ -68,7 +110,10 @@ extension RunnerTests {
|
|
|
68
110
|
data = DataPayload(
|
|
69
111
|
message: message,
|
|
70
112
|
gestureStartUptimeMs: timing.gestureStartUptimeMs,
|
|
71
|
-
gestureEndUptimeMs: timing.gestureEndUptimeMs
|
|
113
|
+
gestureEndUptimeMs: timing.gestureEndUptimeMs,
|
|
114
|
+
gestureFallback: fallback?.strategy,
|
|
115
|
+
gestureFallbackMessage: fallback?.message,
|
|
116
|
+
gestureFallbackHint: fallback?.hint
|
|
72
117
|
)
|
|
73
118
|
case .touch(let f):
|
|
74
119
|
data = DataPayload(
|
|
@@ -78,7 +123,10 @@ extension RunnerTests {
|
|
|
78
123
|
x: f?.x,
|
|
79
124
|
y: f?.y,
|
|
80
125
|
referenceWidth: f?.referenceWidth,
|
|
81
|
-
referenceHeight: f?.referenceHeight
|
|
126
|
+
referenceHeight: f?.referenceHeight,
|
|
127
|
+
gestureFallback: fallback?.strategy,
|
|
128
|
+
gestureFallbackMessage: fallback?.message,
|
|
129
|
+
gestureFallbackHint: fallback?.hint
|
|
82
130
|
)
|
|
83
131
|
case .drag(let f):
|
|
84
132
|
data = DataPayload(
|
|
@@ -90,7 +138,10 @@ extension RunnerTests {
|
|
|
90
138
|
x2: f.x2,
|
|
91
139
|
y2: f.y2,
|
|
92
140
|
referenceWidth: f.referenceWidth,
|
|
93
|
-
referenceHeight: f.referenceHeight
|
|
141
|
+
referenceHeight: f.referenceHeight,
|
|
142
|
+
gestureFallback: fallback?.strategy,
|
|
143
|
+
gestureFallbackMessage: fallback?.message,
|
|
144
|
+
gestureFallbackHint: fallback?.hint
|
|
94
145
|
)
|
|
95
146
|
}
|
|
96
147
|
return Response(ok: true, data: data)
|
|
@@ -495,7 +546,6 @@ extension RunnerTests {
|
|
|
495
546
|
guard let x = command.x, let y = command.y, let x2 = command.x2, let y2 = command.y2 else {
|
|
496
547
|
return Response(ok: false, error: ErrorPayload(message: "drag requires x, y, x2, and y2"))
|
|
497
548
|
}
|
|
498
|
-
let holdDuration = min(max((command.durationMs ?? 60) / 1000.0, 0.016), 10.0)
|
|
499
549
|
let dragPoints = keyboardAvoidingDragPoints(app: activeApp, x: x, y: y, x2: x2, y2: y2)
|
|
500
550
|
let dragFrame = resolvedDragVisualizationFrame(
|
|
501
551
|
app: activeApp,
|
|
@@ -504,6 +554,27 @@ extension RunnerTests {
|
|
|
504
554
|
x2: dragPoints.x2,
|
|
505
555
|
y2: dragPoints.y2
|
|
506
556
|
)
|
|
557
|
+
var fallback: GestureFallback?
|
|
558
|
+
if command.synthesized == true {
|
|
559
|
+
let durationMs = min(max(command.durationMs ?? 250, 16), 10000)
|
|
560
|
+
let (timing, outcome) = performGesture(activeApp, idleTimeout: false) {
|
|
561
|
+
synthesizedDragAt(
|
|
562
|
+
app: activeApp,
|
|
563
|
+
x: dragPoints.x,
|
|
564
|
+
y: dragPoints.y,
|
|
565
|
+
x2: dragPoints.x2,
|
|
566
|
+
y2: dragPoints.y2,
|
|
567
|
+
durationMs: durationMs
|
|
568
|
+
)
|
|
569
|
+
}
|
|
570
|
+
if case .performed = outcome {
|
|
571
|
+
return gestureResponse(message: "dragged", timing: timing, frame: .drag(dragFrame))
|
|
572
|
+
}
|
|
573
|
+
fallback = gestureFallback(strategy: "xctest-coordinate-drag", from: outcome)
|
|
574
|
+
}
|
|
575
|
+
let holdDuration = command.synthesized == true
|
|
576
|
+
? synthesizedSwipeFallbackHoldDuration(durationMs: command.durationMs ?? 250)
|
|
577
|
+
: min(max((command.durationMs ?? 60) / 1000.0, 0.016), 10.0)
|
|
507
578
|
let (timing, outcome) = performGesture(activeApp) {
|
|
508
579
|
dragAt(
|
|
509
580
|
app: activeApp,
|
|
@@ -517,7 +588,12 @@ extension RunnerTests {
|
|
|
517
588
|
if let response = unsupportedResponse(for: outcome) {
|
|
518
589
|
return response
|
|
519
590
|
}
|
|
520
|
-
return gestureResponse(
|
|
591
|
+
return gestureResponse(
|
|
592
|
+
message: "dragged",
|
|
593
|
+
timing: timing,
|
|
594
|
+
frame: .drag(dragFrame),
|
|
595
|
+
fallback: fallback
|
|
596
|
+
)
|
|
521
597
|
case .dragSeries:
|
|
522
598
|
guard let x = command.x, let y = command.y, let x2 = command.x2, let y2 = command.y2 else {
|
|
523
599
|
return Response(ok: false, error: ErrorPayload(message: "dragSeries requires x, y, x2, and y2"))
|
|
@@ -528,41 +604,56 @@ extension RunnerTests {
|
|
|
528
604
|
if pattern != "one-way" && pattern != "ping-pong" {
|
|
529
605
|
return Response(ok: false, error: ErrorPayload(message: "dragSeries pattern must be one-way or ping-pong"))
|
|
530
606
|
}
|
|
531
|
-
let holdDuration = min(max((command.durationMs ?? 60) / 1000.0, 0.016), 10.0)
|
|
532
607
|
let dragPoints = keyboardAvoidingDragPoints(app: activeApp, x: x, y: y, x2: x2, y2: y2)
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
608
|
+
var fallback: GestureFallback?
|
|
609
|
+
if command.synthesized == true {
|
|
610
|
+
let durationMs = min(max(command.durationMs ?? 250, 16), 10000)
|
|
611
|
+
let (timing, outcome) = performGesture(activeApp, idleTimeout: false) {
|
|
612
|
+
performDragSeries(
|
|
613
|
+
count: count,
|
|
614
|
+
pauseMs: pauseMs,
|
|
615
|
+
pattern: pattern,
|
|
616
|
+
points: dragPoints
|
|
617
|
+
) { startX, startY, endX, endY in
|
|
618
|
+
synthesizedDragAt(
|
|
542
619
|
app: activeApp,
|
|
543
|
-
x:
|
|
544
|
-
y:
|
|
545
|
-
x2:
|
|
546
|
-
y2:
|
|
547
|
-
|
|
548
|
-
)
|
|
549
|
-
} else {
|
|
550
|
-
outcome = dragAt(
|
|
551
|
-
app: activeApp,
|
|
552
|
-
x: dragPoints.x,
|
|
553
|
-
y: dragPoints.y,
|
|
554
|
-
x2: dragPoints.x2,
|
|
555
|
-
y2: dragPoints.y2,
|
|
556
|
-
holdDuration: holdDuration
|
|
620
|
+
x: startX,
|
|
621
|
+
y: startY,
|
|
622
|
+
x2: endX,
|
|
623
|
+
y2: endY,
|
|
624
|
+
durationMs: durationMs
|
|
557
625
|
)
|
|
558
626
|
}
|
|
559
627
|
}
|
|
560
|
-
|
|
628
|
+
if case .performed = outcome {
|
|
629
|
+
return gestureResponse(message: "drag series", timing: timing)
|
|
630
|
+
}
|
|
631
|
+
fallback = gestureFallback(strategy: "xctest-coordinate-drag-series", from: outcome)
|
|
632
|
+
}
|
|
633
|
+
let holdDuration = command.synthesized == true
|
|
634
|
+
? synthesizedSwipeFallbackHoldDuration(durationMs: command.durationMs ?? 250)
|
|
635
|
+
: min(max((command.durationMs ?? 60) / 1000.0, 0.016), 10.0)
|
|
636
|
+
let (timing, outcome) = performGesture(activeApp) {
|
|
637
|
+
performDragSeries(
|
|
638
|
+
count: count,
|
|
639
|
+
pauseMs: pauseMs,
|
|
640
|
+
pattern: pattern,
|
|
641
|
+
points: dragPoints
|
|
642
|
+
) { startX, startY, endX, endY in
|
|
643
|
+
dragAt(
|
|
644
|
+
app: activeApp,
|
|
645
|
+
x: startX,
|
|
646
|
+
y: startY,
|
|
647
|
+
x2: endX,
|
|
648
|
+
y2: endY,
|
|
649
|
+
holdDuration: holdDuration
|
|
650
|
+
)
|
|
651
|
+
}
|
|
561
652
|
}
|
|
562
653
|
if let response = unsupportedResponse(for: outcome) {
|
|
563
654
|
return response
|
|
564
655
|
}
|
|
565
|
-
return gestureResponse(message: "drag series", timing: timing)
|
|
656
|
+
return gestureResponse(message: "drag series", timing: timing, fallback: fallback)
|
|
566
657
|
case .remotePress:
|
|
567
658
|
guard let button = tvRemoteButton(from: command.remoteButton) else {
|
|
568
659
|
return Response(ok: false, error: ErrorPayload(message: "remotePress requires remoteButton"))
|
|
@@ -634,6 +634,42 @@ extension RunnerTests {
|
|
|
634
634
|
return performCoordinateDrag(app: app, x: x, y: y, x2: x2, y2: y2, holdDuration: holdDuration)
|
|
635
635
|
}
|
|
636
636
|
|
|
637
|
+
func synthesizedDragAt(
|
|
638
|
+
app: XCUIApplication,
|
|
639
|
+
x: Double,
|
|
640
|
+
y: Double,
|
|
641
|
+
x2: Double,
|
|
642
|
+
y2: Double,
|
|
643
|
+
durationMs: Double
|
|
644
|
+
) -> RunnerInteractionOutcome {
|
|
645
|
+
#if os(iOS)
|
|
646
|
+
if let message = RunnerSynthesizedGesture.synthesizeSwipe(
|
|
647
|
+
withApplication: app,
|
|
648
|
+
x: x,
|
|
649
|
+
y: y,
|
|
650
|
+
x2: x2,
|
|
651
|
+
y2: y2,
|
|
652
|
+
durationMs: durationMs
|
|
653
|
+
) {
|
|
654
|
+
return .unsupported(
|
|
655
|
+
message: message,
|
|
656
|
+
hint: "Falling back to XCTest coordinate drag may be slower; update Xcode if this persists."
|
|
657
|
+
)
|
|
658
|
+
}
|
|
659
|
+
return .performed
|
|
660
|
+
#elseif os(tvOS)
|
|
661
|
+
return .unsupported(
|
|
662
|
+
message: "coordinate drag is not supported on tvOS",
|
|
663
|
+
hint: "tvOS has no coordinate input; use remote-driven swipe/scroll to move focus instead."
|
|
664
|
+
)
|
|
665
|
+
#else
|
|
666
|
+
return .unsupported(
|
|
667
|
+
message: "coordinate drag is not supported on macOS",
|
|
668
|
+
hint: "macOS automation has no touchscreen; use mouse-driven interactions instead."
|
|
669
|
+
)
|
|
670
|
+
#endif
|
|
671
|
+
}
|
|
672
|
+
|
|
637
673
|
func keyboardAvoidingDragPoints(
|
|
638
674
|
app: XCUIApplication,
|
|
639
675
|
x: Double,
|
|
@@ -147,6 +147,7 @@ struct Command: Codable {
|
|
|
147
147
|
let scope: String?
|
|
148
148
|
let raw: Bool?
|
|
149
149
|
let fullscreen: Bool?
|
|
150
|
+
let synthesized: Bool?
|
|
150
151
|
}
|
|
151
152
|
|
|
152
153
|
struct Response: Codable {
|
|
@@ -189,6 +190,9 @@ struct DataPayload: Codable {
|
|
|
189
190
|
let wasVisible: Bool?
|
|
190
191
|
let dismissed: Bool?
|
|
191
192
|
let orientation: String?
|
|
193
|
+
let gestureFallback: String?
|
|
194
|
+
let gestureFallbackMessage: String?
|
|
195
|
+
let gestureFallbackHint: String?
|
|
192
196
|
|
|
193
197
|
init(
|
|
194
198
|
message: String? = nil,
|
|
@@ -217,7 +221,10 @@ struct DataPayload: Codable {
|
|
|
217
221
|
visible: Bool? = nil,
|
|
218
222
|
wasVisible: Bool? = nil,
|
|
219
223
|
dismissed: Bool? = nil,
|
|
220
|
-
orientation: String? = nil
|
|
224
|
+
orientation: String? = nil,
|
|
225
|
+
gestureFallback: String? = nil,
|
|
226
|
+
gestureFallbackMessage: String? = nil,
|
|
227
|
+
gestureFallbackHint: String? = nil
|
|
221
228
|
) {
|
|
222
229
|
self.message = message
|
|
223
230
|
self.text = text
|
|
@@ -246,6 +253,9 @@ struct DataPayload: Codable {
|
|
|
246
253
|
self.wasVisible = wasVisible
|
|
247
254
|
self.dismissed = dismissed
|
|
248
255
|
self.orientation = orientation
|
|
256
|
+
self.gestureFallback = gestureFallback
|
|
257
|
+
self.gestureFallbackMessage = gestureFallbackMessage
|
|
258
|
+
self.gestureFallbackHint = gestureFallbackHint
|
|
249
259
|
}
|
|
250
260
|
}
|
|
251
261
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-device",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.11",
|
|
4
4
|
"description": "Agent-native CLI for AI mobile testing and app automation across iOS, Android, tvOS, Android TV, macOS, and Linux.",
|
|
5
5
|
"mcpName": "io.github.callstackincubator/agent-device",
|
|
6
6
|
"license": "MIT",
|
package/server.json
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"url": "https://github.com/callstackincubator/agent-device",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "0.16.
|
|
10
|
+
"version": "0.16.11",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "agent-device",
|
|
15
|
-
"version": "0.16.
|
|
15
|
+
"version": "0.16.11",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
}
|
package/skills/dogfood/SKILL.md
CHANGED
|
@@ -22,6 +22,6 @@ Read current CLI guidance:
|
|
|
22
22
|
agent-device help dogfood
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
Loop: open
|
|
25
|
+
Loop: open app -> snapshot -i + screenshot -> explore flows -> capture evidence per issue -> close.
|
|
26
26
|
|
|
27
27
|
Target app is required; infer platform or ask. Findings must come from runtime behavior, not source reads. Let `help dogfood` provide exact report shape, evidence commands, and current workflow guidance.
|
package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.16.10.apk.sha256
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
c105a80a1cf81311718d2043e44a0176013e369b8a67ae16ad26c3c9085dd17a agent-device-android-multitouch-helper-0.16.10.apk
|
|
Binary file
|
package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.10.apk.sha256
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
ee874e75b05245f601bea045d906536f13a1268c945f1fb80baaf798e9f26a07 agent-device-android-snapshot-helper-0.16.10.apk
|
package/dist/src/2842.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
function e(e){let l=`${e??""}`.toLowerCase();return l.includes("scroll")||l.includes("recyclerview")||l.includes("listview")||l.includes("gridview")||l.includes("collectionview")||"table"===l}function l(l){return!!e(l.type)||`${l.role??""} ${l.subrole??""}`.toLowerCase().includes("scroll")}export{l as isScrollableNodeLike,e as isScrollableType};
|
package/dist/src/8114.js
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import e from"node:path";import t from"node:crypto";import n from"node:fs";import{resolveUserPath as r,expandUserHomePath as o}from"./3267.js";import{findProjectRoot as s}from"./9671.js";function i(e,t){let n=[],r=e+t.toString(),o=r.indexOf("\n");for(;-1!==o;){let e=r.slice(0,o).trim();r=r.slice(o+1),e&&n.push(e),o=r.indexOf("\n")}return{lines:n,buffer:r}}function a(t){let n,s=(n=(t??"").trim())?r(n):e.join(o("~"),".agent-device");return{baseDir:s,infoPath:e.join(s,"daemon.json"),lockPath:e.join(s,"daemon.lock"),logPath:e.join(s,"daemon.log"),sessionsDir:e.join(s,"sessions")}}function l(e){let t=(e??"").trim().toLowerCase();return"http"===t?"http":"dual"===t?"dual":"socket"}function u(e){let t=(e??"").trim().toLowerCase();return"auto"===t?"auto":"socket"===t?"socket":"http"===t?"http":"auto"}function p(e){return"tenant"===(e??"").trim().toLowerCase()?"tenant":"none"}function f(e){if(!e)return;let t=e.trim();if(t&&/^[a-zA-Z0-9._-]{1,128}$/.test(t))return t}let m=/(?:^|[^\w$.])(?:import|export)\s+(?:type\s+)?(?:[^'"`]*?\s+from\s+)?['"]([^'"]+)['"]/gm,c=/import\(\s*['"]([^'"]+)['"]\s*\)/gm,d=[".ts",".tsx",".js",".jsx",".mjs",".cjs"];function $(){let e=process.argv[1];return e?g(e):"unknown"}function g(r,o=s()){try{let s=e.resolve(o),i=[e.resolve(r)],a=new Set,l=[];for(;i.length>0;){let t=i.pop();if(!t||a.has(t))continue;a.add(t);let r=n.statSync(t);if(!r.isFile())continue;let o=e.relative(s,t)||t;l.push(`${o}:${r.size}:${Math.trunc(r.mtimeMs)}`);let u=n.readFileSync(t,"utf8");for(let n of function(e){let t=new Set;return h(e,m,t),h(e,c,t),[...t]}(u)){let r=function(t,n){let r=e.resolve(e.dirname(t),n),o=y(r);if(o)return o;for(let e of d){let t=y(`${r}${e}`);if(t)return t}for(let t of d){let n=y(e.join(r,`index${t}`));if(n)return n}return null}(t,n);r&&i.push(r)}}let u=l.sort().join("|"),p=t.createHash("sha1").update(u).digest("hex");return`graph:${l.length}:${p}`}catch{return"unknown"}}function h(e,t,n){t.lastIndex=0;let r=null;for(;null!==(r=t.exec(e));){let e=r[1]?.trim();e?.startsWith(".")&&n.add(e)}}function y(e){try{return n.statSync(e).isFile()?e:null}catch{return null}}function v(e){return e.meta?.requestProgress==="replay-test"}function S(e){return!!e&&"object"==typeof e&&"progress"===e.type&&!!e.event}function x(e){return!!e&&"object"==typeof e&&"response"===e.type&&!!e.response}function j(e){return`${JSON.stringify({type:"progress",event:e})}
|
|
2
|
-
`}function P(e){return`${JSON.stringify({type:"response",response:e})}
|
|
3
|
-
`}function D(e){return`${JSON.stringify({type:"response",response:e})}
|
|
4
|
-
`}function _(t){var n,r,o;let s,i,a;if("replay-test"!==t.type)return;let l=(n=t,(s=n.title?.trim())?JSON.stringify(s):e.basename(n.file)),u=void 0!==t.durationMs?` (${a=(i=Math.max(0,(r=t).durationMs??0)/1e3)>=10?`${i.toFixed(1)}s`:i>=1?`${i.toFixed(2)}s`:`${i.toFixed(3).replace(/0+$/,"").replace(/\.$/,"")}s`,r.attempt&&r.attempt>1&&!r.retrying?`total ${a}`:a})`:"",p=void 0===(o=t).attempt?"":"fail"===o.status&&o.retrying&&void 0!==o.maxAttempts?` attempt ${o.attempt}/${o.maxAttempts} retrying`:o.attempt>1?` after ${o.attempt} attempts`:"",f=t.message?.replace(/\s+/g," ").trim();return"pass"===t.status?`PASS ${l}${p}${u}`:"skip"===t.status?[`SKIP ${l}`,f?` ${f}`:""].filter(Boolean).join("\n"):[`FAIL ${l}${p}${u}`,f?` ${f}`:"",t.artifactsDir?` artifacts: ${t.artifactsDir}`:""].filter(Boolean).join("\n")}export{g as computeDaemonCodeSignature,i as consumeTextLines,_ as formatRequestProgressEvent,S as isDaemonProgressEnvelope,x as isDaemonResponseEnvelope,f as normalizeTenantId,$ as resolveDaemonCodeSignature,a as resolveDaemonPaths,l as resolveDaemonServerMode,u as resolveDaemonTransportPreference,p as resolveSessionIsolationMode,j as serializeDaemonProgressEnvelope,P as serializeDaemonResponseEnvelope,D as serializeDaemonRpcResponseEnvelope,v as shouldStreamRequestProgress};
|