agent-device 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -8
- package/dist/src/735.js +3 -0
- package/dist/src/bin.js +50 -41
- package/dist/src/daemon.js +29 -23
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.pbxproj +14 -6
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +104 -4
- package/package.json +4 -2
- package/skills/agent-device/SKILL.md +29 -2
- package/skills/agent-device/references/logs-and-debug.md +5 -0
- package/skills/agent-device/references/perf-metrics.md +53 -0
- package/skills/dogfood/SKILL.md +183 -0
- package/skills/dogfood/references/issue-taxonomy.md +83 -0
- package/skills/dogfood/templates/dogfood-report-template.md +52 -0
- package/dist/src/350.js +0 -3
|
@@ -258,7 +258,7 @@
|
|
|
258
258
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
|
259
259
|
MTL_FAST_MATH = YES;
|
|
260
260
|
ONLY_ACTIVE_ARCH = YES;
|
|
261
|
-
SDKROOT =
|
|
261
|
+
SDKROOT = auto;
|
|
262
262
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
|
263
263
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
|
264
264
|
};
|
|
@@ -315,7 +315,7 @@
|
|
|
315
315
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
|
316
316
|
MTL_ENABLE_DEBUG_INFO = NO;
|
|
317
317
|
MTL_FAST_MATH = YES;
|
|
318
|
-
SDKROOT =
|
|
318
|
+
SDKROOT = auto;
|
|
319
319
|
SWIFT_COMPILATION_MODE = wholemodule;
|
|
320
320
|
VALIDATE_PRODUCT = YES;
|
|
321
321
|
};
|
|
@@ -350,7 +350,9 @@
|
|
|
350
350
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
|
351
351
|
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
|
352
352
|
SWIFT_VERSION = 5.0;
|
|
353
|
-
|
|
353
|
+
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator";
|
|
354
|
+
TARGETED_DEVICE_FAMILY = "1,2,3";
|
|
355
|
+
TVOS_DEPLOYMENT_TARGET = 15.6;
|
|
354
356
|
};
|
|
355
357
|
name = Debug;
|
|
356
358
|
};
|
|
@@ -383,7 +385,9 @@
|
|
|
383
385
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
|
384
386
|
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
|
385
387
|
SWIFT_VERSION = 5.0;
|
|
386
|
-
|
|
388
|
+
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator";
|
|
389
|
+
TARGETED_DEVICE_FAMILY = "1,2,3";
|
|
390
|
+
TVOS_DEPLOYMENT_TARGET = 15.6;
|
|
387
391
|
};
|
|
388
392
|
name = Release;
|
|
389
393
|
};
|
|
@@ -404,7 +408,9 @@
|
|
|
404
408
|
SWIFT_OBJC_BRIDGING_HEADER = "AgentDeviceRunnerUITests/AgentDeviceRunnerUITests-Bridging-Header.h";
|
|
405
409
|
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
|
406
410
|
SWIFT_VERSION = 5.0;
|
|
407
|
-
|
|
411
|
+
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator";
|
|
412
|
+
TARGETED_DEVICE_FAMILY = "1,2,3";
|
|
413
|
+
TVOS_DEPLOYMENT_TARGET = 15.6;
|
|
408
414
|
TEST_TARGET_NAME = AgentDeviceRunner;
|
|
409
415
|
};
|
|
410
416
|
name = Debug;
|
|
@@ -426,7 +432,9 @@
|
|
|
426
432
|
SWIFT_OBJC_BRIDGING_HEADER = "AgentDeviceRunnerUITests/AgentDeviceRunnerUITests-Bridging-Header.h";
|
|
427
433
|
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
|
428
434
|
SWIFT_VERSION = 5.0;
|
|
429
|
-
|
|
435
|
+
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator";
|
|
436
|
+
TARGETED_DEVICE_FAMILY = "1,2,3";
|
|
437
|
+
TVOS_DEPLOYMENT_TARGET = 15.6;
|
|
430
438
|
TEST_TARGET_NAME = AgentDeviceRunner;
|
|
431
439
|
};
|
|
432
440
|
name = Release;
|
|
@@ -40,6 +40,7 @@ final class RunnerTests: XCTestCase {
|
|
|
40
40
|
private let postSnapshotInteractionDelay: TimeInterval = 0.2
|
|
41
41
|
private let firstInteractionAfterActivateDelay: TimeInterval = 0.25
|
|
42
42
|
private let scrollInteractionIdleTimeoutDefault: TimeInterval = 1.0
|
|
43
|
+
private let tvRemoteDoublePressDelayDefault: TimeInterval = 0.0
|
|
43
44
|
private let minRecordingFps = 1
|
|
44
45
|
private let maxRecordingFps = 120
|
|
45
46
|
private var needsPostSnapshotInteractionDelay = false
|
|
@@ -783,6 +784,28 @@ final class RunnerTests: XCTestCase {
|
|
|
783
784
|
}
|
|
784
785
|
needsPostSnapshotInteractionDelay = true
|
|
785
786
|
return Response(ok: true, data: snapshotFast(app: activeApp, options: options))
|
|
787
|
+
case .screenshot:
|
|
788
|
+
// If a target app bundle ID is provided, activate it first so the screenshot
|
|
789
|
+
// captures the target app rather than the AgentDeviceRunner itself.
|
|
790
|
+
if let bundleId = command.appBundleId, !bundleId.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
791
|
+
let targetApp = XCUIApplication(bundleIdentifier: bundleId)
|
|
792
|
+
targetApp.activate()
|
|
793
|
+
// Brief wait for the app transition animation to complete
|
|
794
|
+
Thread.sleep(forTimeInterval: 0.5)
|
|
795
|
+
}
|
|
796
|
+
let screenshot = XCUIScreen.main.screenshot()
|
|
797
|
+
guard let pngData = screenshot.image.pngData() else {
|
|
798
|
+
return Response(ok: false, error: ErrorPayload(message: "Failed to encode screenshot as PNG"))
|
|
799
|
+
}
|
|
800
|
+
let fileName = "screenshot-\(Int(Date().timeIntervalSince1970 * 1000)).png"
|
|
801
|
+
let filePath = (NSTemporaryDirectory() as NSString).appendingPathComponent(fileName)
|
|
802
|
+
do {
|
|
803
|
+
try pngData.write(to: URL(fileURLWithPath: filePath))
|
|
804
|
+
} catch {
|
|
805
|
+
return Response(ok: false, error: ErrorPayload(message: "Failed to write screenshot: \(error.localizedDescription)"))
|
|
806
|
+
}
|
|
807
|
+
// Return path relative to app container root (tmp/ maps to NSTemporaryDirectory)
|
|
808
|
+
return Response(ok: true, data: DataPayload(message: "tmp/\(fileName)"))
|
|
786
809
|
case .back:
|
|
787
810
|
if tapNavigationBack(app: activeApp) {
|
|
788
811
|
return Response(ok: true, data: DataPayload(message: "back"))
|
|
@@ -790,7 +813,7 @@ final class RunnerTests: XCTestCase {
|
|
|
790
813
|
performBackGesture(app: activeApp)
|
|
791
814
|
return Response(ok: true, data: DataPayload(message: "back"))
|
|
792
815
|
case .home:
|
|
793
|
-
|
|
816
|
+
pressHomeButton()
|
|
794
817
|
return Response(ok: true, data: DataPayload(message: "home"))
|
|
795
818
|
case .appSwitcher:
|
|
796
819
|
performAppSwitcherGesture(app: activeApp)
|
|
@@ -934,7 +957,7 @@ final class RunnerTests: XCTestCase {
|
|
|
934
957
|
|
|
935
958
|
private func isReadOnlyCommand(_ command: Command) -> Bool {
|
|
936
959
|
switch command.command {
|
|
937
|
-
case .findText, .snapshot:
|
|
960
|
+
case .findText, .snapshot, .screenshot:
|
|
938
961
|
return true
|
|
939
962
|
case .alert:
|
|
940
963
|
let action = (command.action ?? "get").lowercased()
|
|
@@ -961,7 +984,7 @@ final class RunnerTests: XCTestCase {
|
|
|
961
984
|
|
|
962
985
|
private func isRunnerLifecycleCommand(_ command: CommandType) -> Bool {
|
|
963
986
|
switch command {
|
|
964
|
-
case .shutdown, .recordStop:
|
|
987
|
+
case .shutdown, .recordStop, .screenshot:
|
|
965
988
|
return true
|
|
966
989
|
default:
|
|
967
990
|
return false
|
|
@@ -990,10 +1013,13 @@ final class RunnerTests: XCTestCase {
|
|
|
990
1013
|
back.tap()
|
|
991
1014
|
return true
|
|
992
1015
|
}
|
|
993
|
-
return
|
|
1016
|
+
return pressTvRemoteMenuIfAvailable()
|
|
994
1017
|
}
|
|
995
1018
|
|
|
996
1019
|
private func performBackGesture(app: XCUIApplication) {
|
|
1020
|
+
if pressTvRemoteMenuIfAvailable() {
|
|
1021
|
+
return
|
|
1022
|
+
}
|
|
997
1023
|
let target = app.windows.firstMatch.exists ? app.windows.firstMatch : app
|
|
998
1024
|
let start = target.coordinate(withNormalizedOffset: CGVector(dx: 0.05, dy: 0.5))
|
|
999
1025
|
let end = target.coordinate(withNormalizedOffset: CGVector(dx: 0.8, dy: 0.5))
|
|
@@ -1001,12 +1027,64 @@ final class RunnerTests: XCTestCase {
|
|
|
1001
1027
|
}
|
|
1002
1028
|
|
|
1003
1029
|
private func performAppSwitcherGesture(app: XCUIApplication) {
|
|
1030
|
+
if performTvRemoteAppSwitcherIfAvailable() {
|
|
1031
|
+
return
|
|
1032
|
+
}
|
|
1004
1033
|
let target = app.windows.firstMatch.exists ? app.windows.firstMatch : app
|
|
1005
1034
|
let start = target.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.99))
|
|
1006
1035
|
let end = target.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.7))
|
|
1007
1036
|
start.press(forDuration: 0.6, thenDragTo: end)
|
|
1008
1037
|
}
|
|
1009
1038
|
|
|
1039
|
+
private func pressHomeButton() {
|
|
1040
|
+
if pressTvRemoteHomeIfAvailable() {
|
|
1041
|
+
return
|
|
1042
|
+
}
|
|
1043
|
+
XCUIDevice.shared.press(.home)
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
private func pressTvRemoteMenuIfAvailable() -> Bool {
|
|
1047
|
+
#if os(tvOS)
|
|
1048
|
+
XCUIRemote.shared.press(.menu)
|
|
1049
|
+
return true
|
|
1050
|
+
#else
|
|
1051
|
+
return false
|
|
1052
|
+
#endif
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
private func pressTvRemoteHomeIfAvailable() -> Bool {
|
|
1056
|
+
#if os(tvOS)
|
|
1057
|
+
XCUIRemote.shared.press(.home)
|
|
1058
|
+
return true
|
|
1059
|
+
#else
|
|
1060
|
+
return false
|
|
1061
|
+
#endif
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
private func performTvRemoteAppSwitcherIfAvailable() -> Bool {
|
|
1065
|
+
#if os(tvOS)
|
|
1066
|
+
XCUIRemote.shared.press(.home)
|
|
1067
|
+
sleepFor(resolveTvRemoteDoublePressDelay())
|
|
1068
|
+
XCUIRemote.shared.press(.home)
|
|
1069
|
+
return true
|
|
1070
|
+
#else
|
|
1071
|
+
return false
|
|
1072
|
+
#endif
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
private func resolveTvRemoteDoublePressDelay() -> TimeInterval {
|
|
1076
|
+
guard
|
|
1077
|
+
let raw = ProcessInfo.processInfo.environment["AGENT_DEVICE_TV_REMOTE_DOUBLE_PRESS_DELAY_MS"],
|
|
1078
|
+
!raw.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
|
|
1079
|
+
else {
|
|
1080
|
+
return tvRemoteDoublePressDelayDefault
|
|
1081
|
+
}
|
|
1082
|
+
guard let parsedMs = Double(raw), parsedMs >= 0 else {
|
|
1083
|
+
return tvRemoteDoublePressDelayDefault
|
|
1084
|
+
}
|
|
1085
|
+
return min(parsedMs, 1000) / 1000.0
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1010
1088
|
private func findElement(app: XCUIApplication, text: String) -> XCUIElement? {
|
|
1011
1089
|
let predicate = NSPredicate(format: "label CONTAINS[c] %@ OR identifier CONTAINS[c] %@ OR value CONTAINS[c] %@", text, text, text)
|
|
1012
1090
|
let element = app.descendants(matching: .any).matching(predicate).firstMatch
|
|
@@ -1109,6 +1187,9 @@ final class RunnerTests: XCTestCase {
|
|
|
1109
1187
|
}
|
|
1110
1188
|
|
|
1111
1189
|
private func swipe(app: XCUIApplication, direction: SwipeDirection) {
|
|
1190
|
+
if performTvRemoteSwipeIfAvailable(direction: direction) {
|
|
1191
|
+
return
|
|
1192
|
+
}
|
|
1112
1193
|
let target = app.windows.firstMatch.exists ? app.windows.firstMatch : app
|
|
1113
1194
|
let start = target.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.2))
|
|
1114
1195
|
let end = target.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.8))
|
|
@@ -1127,6 +1208,24 @@ final class RunnerTests: XCTestCase {
|
|
|
1127
1208
|
}
|
|
1128
1209
|
}
|
|
1129
1210
|
|
|
1211
|
+
private func performTvRemoteSwipeIfAvailable(direction: SwipeDirection) -> Bool {
|
|
1212
|
+
#if os(tvOS)
|
|
1213
|
+
switch direction {
|
|
1214
|
+
case .up:
|
|
1215
|
+
XCUIRemote.shared.press(.up)
|
|
1216
|
+
case .down:
|
|
1217
|
+
XCUIRemote.shared.press(.down)
|
|
1218
|
+
case .left:
|
|
1219
|
+
XCUIRemote.shared.press(.left)
|
|
1220
|
+
case .right:
|
|
1221
|
+
XCUIRemote.shared.press(.right)
|
|
1222
|
+
}
|
|
1223
|
+
return true
|
|
1224
|
+
#else
|
|
1225
|
+
return false
|
|
1226
|
+
#endif
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1130
1229
|
private func pinch(app: XCUIApplication, scale: Double, x: Double?, y: Double?) {
|
|
1131
1230
|
let target = app.windows.firstMatch.exists ? app.windows.firstMatch : app
|
|
1132
1231
|
|
|
@@ -1741,6 +1840,7 @@ enum CommandType: String, Codable {
|
|
|
1741
1840
|
case swipe
|
|
1742
1841
|
case findText
|
|
1743
1842
|
case snapshot
|
|
1843
|
+
case screenshot
|
|
1744
1844
|
case back
|
|
1745
1845
|
case home
|
|
1746
1846
|
case appSwitcher
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-device",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Unified control plane for physical and virtual devices via an agent-driven CLI.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Callstack",
|
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
"build": "rslib build",
|
|
17
17
|
"clean:daemon": "rm -f ~/.agent-device/daemon.json && rm -f ~/.agent-device/daemon.lock",
|
|
18
18
|
"build:node": "pnpm build && pnpm clean:daemon",
|
|
19
|
-
"build:xcuitest": "
|
|
19
|
+
"build:xcuitest": "pnpm build:xcuitest:ios",
|
|
20
|
+
"build:xcuitest:ios": "rm -rf ~/.agent-device/ios-runner/derived/device && xcodebuild build-for-testing -project ios-runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj -scheme AgentDeviceRunner -destination \"generic/platform=iOS Simulator\" -derivedDataPath ~/.agent-device/ios-runner/derived",
|
|
21
|
+
"build:xcuitest:tvos": "rm -rf ~/.agent-device/ios-runner/derived/tvos && xcodebuild build-for-testing -project ios-runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj -scheme AgentDeviceRunner -destination \"generic/platform=tvOS Simulator\" -derivedDataPath ~/.agent-device/ios-runner/derived/tvos",
|
|
20
22
|
"build:all": "pnpm build:node && pnpm build:xcuitest",
|
|
21
23
|
"ad": "node bin/agent-device.mjs",
|
|
22
24
|
"format": "prettier --write .",
|
|
@@ -6,6 +6,7 @@ description: Automates interactions for iOS simulators/devices and Android emula
|
|
|
6
6
|
# Mobile Automation with agent-device
|
|
7
7
|
|
|
8
8
|
For exploration, use snapshot refs. For deterministic replay, use selectors.
|
|
9
|
+
For structured exploratory QA bug hunts and reporting, use [../dogfood/SKILL.md](../dogfood/SKILL.md).
|
|
9
10
|
|
|
10
11
|
## Start Here (Read This First)
|
|
11
12
|
|
|
@@ -22,7 +23,7 @@ Use this skill as a router, not a full manual.
|
|
|
22
23
|
|
|
23
24
|
- No target context yet: `devices` -> pick target -> `open`.
|
|
24
25
|
- Normal UI task: `open` -> `snapshot -i` -> `press/fill` -> `diff snapshot -i` -> `close`
|
|
25
|
-
- Debug/crash: `open <app>` -> `logs clear --restart` -> reproduce -> `logs path` -> targeted `grep`
|
|
26
|
+
- Debug/crash: `open <app>` -> `logs clear --restart` -> reproduce -> `network dump` -> `logs path` -> targeted `grep`
|
|
26
27
|
- Replay drift: `replay -u <path>` -> verify updated selectors
|
|
27
28
|
|
|
28
29
|
## Canonical Flows
|
|
@@ -43,6 +44,7 @@ agent-device close
|
|
|
43
44
|
```bash
|
|
44
45
|
agent-device open MyApp --platform ios
|
|
45
46
|
agent-device logs clear --restart
|
|
47
|
+
agent-device network dump 25
|
|
46
48
|
agent-device logs path
|
|
47
49
|
```
|
|
48
50
|
|
|
@@ -68,6 +70,14 @@ agent-device session list
|
|
|
68
70
|
```
|
|
69
71
|
|
|
70
72
|
Use `boot` only as fallback when `open` cannot find/connect to a ready target.
|
|
73
|
+
Use `--target mobile|tv` with `--platform` (required) to pick phone/tablet vs TV targets (AndroidTV/tvOS).
|
|
74
|
+
|
|
75
|
+
TV quick reference:
|
|
76
|
+
- AndroidTV: `open`/`apps` use TV launcher discovery automatically.
|
|
77
|
+
- TV target selection works on emulators/simulators and connected physical devices (AndroidTV + AppleTV).
|
|
78
|
+
- tvOS: runner-driven interactions and snapshots are supported (`snapshot`, `wait`, `press`, `fill`, `get`, `scroll`, `back`, `home`, `app-switcher`, `record` and related selector flows).
|
|
79
|
+
- tvOS `back`/`home`/`app-switcher` map to Siri Remote actions (`menu`, `home`, double-home) in the runner.
|
|
80
|
+
- tvOS follows iOS simulator-only command semantics for helpers like `pinch`, `settings`, and `push`.
|
|
71
81
|
|
|
72
82
|
### Snapshot and targeting
|
|
73
83
|
|
|
@@ -86,6 +96,11 @@ agent-device is visible 'id="anchor"'
|
|
|
86
96
|
|
|
87
97
|
```bash
|
|
88
98
|
agent-device appstate
|
|
99
|
+
agent-device clipboard read
|
|
100
|
+
agent-device clipboard write "token"
|
|
101
|
+
agent-device perf --json
|
|
102
|
+
agent-device network dump [limit] [summary|headers|body|all]
|
|
103
|
+
agent-device push <bundle|package> <payload.json|inline-json>
|
|
89
104
|
agent-device get text @e1
|
|
90
105
|
agent-device screenshot out.png
|
|
91
106
|
agent-device settings permission grant notifications
|
|
@@ -100,6 +115,11 @@ agent-device trace stop ./trace.log
|
|
|
100
115
|
agent-device batch --steps-file /tmp/batch-steps.json --json
|
|
101
116
|
```
|
|
102
117
|
|
|
118
|
+
### Performance Check
|
|
119
|
+
|
|
120
|
+
- Use `agent-device perf --json` (or `metrics --json`) after `open`.
|
|
121
|
+
- For detailed metric semantics, caveats, and interpretation guidance, see [references/perf-metrics.md](references/perf-metrics.md).
|
|
122
|
+
|
|
103
123
|
## Guardrails (High Value Only)
|
|
104
124
|
|
|
105
125
|
- Re-snapshot after UI mutations (navigation/modal/list changes).
|
|
@@ -107,7 +127,13 @@ agent-device batch --steps-file /tmp/batch-steps.json --json
|
|
|
107
127
|
- Use refs for discovery, selectors for replay/assertions.
|
|
108
128
|
- Use `fill` for clear-then-type semantics; use `type` for focused append typing.
|
|
109
129
|
- iOS `appstate` is session-scoped; Android `appstate` is live foreground state.
|
|
110
|
-
-
|
|
130
|
+
- Clipboard helpers: `clipboard read` / `clipboard write <text>` are supported on Android and iOS simulators; iOS physical devices are not supported yet.
|
|
131
|
+
- `network dump` is best-effort and parses HTTP(s) entries from the session app log file.
|
|
132
|
+
- iOS settings helpers are simulator-only; use `appearance light|dark|toggle` and faceid `match|nonmatch|enroll|unenroll`.
|
|
133
|
+
- For AndroidTV/tvOS selection, always pair `--target` with `--platform` (`ios`, `android`, or `apple` alias); target-only selection is invalid.
|
|
134
|
+
- `push` simulates notification delivery:
|
|
135
|
+
- iOS simulator uses APNs-style payload JSON.
|
|
136
|
+
- Android uses broadcast action + typed extras (string/boolean/number).
|
|
111
137
|
- Permission settings are app-scoped and require an active session app:
|
|
112
138
|
`settings permission <grant|deny|reset> <camera|microphone|photos|contacts|notifications> [full|limited]`
|
|
113
139
|
- `full|limited` mode applies only to iOS `photos`; other targets reject mode.
|
|
@@ -138,3 +164,4 @@ agent-device batch --steps-file /tmp/batch-steps.json --json
|
|
|
138
164
|
- [references/video-recording.md](references/video-recording.md)
|
|
139
165
|
- [references/coordinate-system.md](references/coordinate-system.md)
|
|
140
166
|
- [references/batching.md](references/batching.md)
|
|
167
|
+
- [references/perf-metrics.md](references/perf-metrics.md)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Logs (Token-Efficient Debugging)
|
|
2
2
|
|
|
3
3
|
Logging is off by default in normal flows. Enable it on demand for debugging windows. App output is written to a session-scoped file so agents can grep it instead of loading full logs into context.
|
|
4
|
+
`network dump` parses recent HTTP(s) entries from this same session app log file.
|
|
4
5
|
|
|
5
6
|
## Data Handling
|
|
6
7
|
|
|
@@ -23,6 +24,7 @@ Logging is off by default in normal flows. Enable it on demand for debugging win
|
|
|
23
24
|
```bash
|
|
24
25
|
agent-device open MyApp --platform ios # or --platform android
|
|
25
26
|
agent-device logs clear --restart # Preferred: stop stream, clear logs, and start streaming again
|
|
27
|
+
agent-device network dump 25 # Parse latest HTTP(s) requests (method/url/status) from app.log
|
|
26
28
|
agent-device logs path # Print path, e.g. ~/.agent-device/sessions/default/app.log
|
|
27
29
|
agent-device logs doctor # Check tool/runtime readiness for current session/device
|
|
28
30
|
agent-device logs mark "before tap" # Insert a timeline marker into app.log
|
|
@@ -41,10 +43,13 @@ Precondition: `logs clear --restart` requires an active app session (`open <app>
|
|
|
41
43
|
- `logs clear --restart`: convenience reset for repro loops (stop stream, clear files, restart stream).
|
|
42
44
|
- `logs doctor`: reports backend/tool checks and readiness notes for troubleshooting.
|
|
43
45
|
- `logs mark`: writes a timestamped marker line to the session log.
|
|
46
|
+
- `network dump [limit] [summary|headers|body|all]`: parses recent HTTP(s) lines from the session app log and returns request summaries.
|
|
47
|
+
- `network log ...`: alias for `network dump`.
|
|
44
48
|
|
|
45
49
|
## Behavior and Limits
|
|
46
50
|
|
|
47
51
|
- `logs start` appends to `app.log` and rotates to `app.log.1` when `app.log` exceeds 5 MB.
|
|
52
|
+
- `network dump` scans the last 4000 app-log lines, returns up to 200 entries, and truncates payload/header fields at 2048 characters.
|
|
48
53
|
- Android log streaming automatically rebinds to the app PID after process restarts.
|
|
49
54
|
- iOS log capture relies on Unified Logging signals (for example `os_log`); plain stdout/stderr output may be limited depending on app/runtime.
|
|
50
55
|
- Retention knobs:
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Performance Metrics (`perf` / `metrics`)
|
|
2
|
+
|
|
3
|
+
Use this reference when you need to measure launch performance in agent workflows.
|
|
4
|
+
|
|
5
|
+
## Quick flow
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
agent-device open Settings --platform ios
|
|
9
|
+
agent-device perf --json
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Alias:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
agent-device metrics --json
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## What is measured today
|
|
19
|
+
|
|
20
|
+
- Session-scoped `startup` timing only.
|
|
21
|
+
- Sampling method: `open-command-roundtrip`.
|
|
22
|
+
- Unit: milliseconds (`ms`).
|
|
23
|
+
- Source: elapsed wall-clock time around each session `open` command dispatch for the active app target.
|
|
24
|
+
|
|
25
|
+
## Output fields to use
|
|
26
|
+
|
|
27
|
+
- `metrics.startup.lastDurationMs`: most recent startup sample.
|
|
28
|
+
- `metrics.startup.lastMeasuredAt`: ISO timestamp of most recent sample.
|
|
29
|
+
- `metrics.startup.sampleCount`: number of retained samples.
|
|
30
|
+
- `metrics.startup.samples[]`: recent startup history for the current session.
|
|
31
|
+
- `sampling.startup.method`: current sampling method identifier.
|
|
32
|
+
|
|
33
|
+
## Platform support (current)
|
|
34
|
+
|
|
35
|
+
- iOS simulator: supported for startup sampling.
|
|
36
|
+
- iOS physical device: supported for startup sampling.
|
|
37
|
+
- Android emulator/device: supported for startup sampling.
|
|
38
|
+
- `fps`, `memory`, and `cpu`: currently placeholders (`available: false`).
|
|
39
|
+
|
|
40
|
+
## Interpretation guidance
|
|
41
|
+
|
|
42
|
+
- Treat startup values as command round-trip timing, not true app first-frame or first-interactive telemetry.
|
|
43
|
+
- Compare like-for-like runs:
|
|
44
|
+
- same device target
|
|
45
|
+
- same app build
|
|
46
|
+
- same workflow/session steps
|
|
47
|
+
- Use multiple runs and compare trend/median, not one-off samples.
|
|
48
|
+
|
|
49
|
+
## Common pitfalls
|
|
50
|
+
|
|
51
|
+
- Running `perf` before any `open` in the session yields no startup sample yet.
|
|
52
|
+
- Comparing values across different devices/runtimes introduces large noise.
|
|
53
|
+
- Interpreting current `startup` as CPU/FPS/memory would be incorrect.
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dogfood
|
|
3
|
+
description: Systematically explore and test a mobile app on iOS/Android with agent-device to find bugs, UX issues, and other problems. Use when asked to "dogfood", "QA", "exploratory test", "find issues", "bug hunt", or "test this app" on mobile. Produces a structured report with reproducible evidence: screenshots, optional repro videos, and detailed steps for every issue.
|
|
4
|
+
allowed-tools: Bash(agent-device:*), Bash(npx agent-device:*)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Dogfood (agent-device)
|
|
8
|
+
|
|
9
|
+
Systematically explore a mobile app, find issues, and produce a report with full reproduction evidence for every finding.
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
Only the **Target app** is required. Everything else has sensible defaults.
|
|
14
|
+
|
|
15
|
+
| Parameter | Default | Example override |
|
|
16
|
+
|-----------|---------|-----------------|
|
|
17
|
+
| **Target app** | _(required)_ | `Settings`, `com.example.app`, deep link URL |
|
|
18
|
+
| **Platform** | Infer from user context; otherwise ask (`ios` or `android`) | `--platform ios` |
|
|
19
|
+
| **Session name** | Slugified app/platform (for example `settings-ios`) | `--session my-session` |
|
|
20
|
+
| **Output directory** | `./dogfood-output/` | `Output directory: /tmp/mobile-qa` |
|
|
21
|
+
| **Scope** | Full app | `Focus on onboarding and profile` |
|
|
22
|
+
| **Authentication** | None | `Sign in to user@example.com` |
|
|
23
|
+
|
|
24
|
+
If the user gives enough context to start, begin immediately with defaults. Ask follow-up only when a required detail is missing (for example platform or credentials).
|
|
25
|
+
|
|
26
|
+
Prefer direct `agent-device` binary when available.
|
|
27
|
+
|
|
28
|
+
## Workflow
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
1. Initialize Set up session, output dirs, report file
|
|
32
|
+
2. Launch/Auth Open app and sign in if needed
|
|
33
|
+
3. Orient Capture initial snapshot and map navigation
|
|
34
|
+
4. Explore Systematically test flows and states
|
|
35
|
+
5. Document Record reproducible evidence per issue
|
|
36
|
+
6. Wrap up Reconcile summary, close session
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 1. Initialize
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
mkdir -p {OUTPUT_DIR}/screenshots {OUTPUT_DIR}/videos
|
|
43
|
+
cp {SKILL_DIR}/templates/dogfood-report-template.md {OUTPUT_DIR}/report.md
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 2. Launch/Auth
|
|
47
|
+
|
|
48
|
+
Start a named session and launch target app:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
agent-device --session {SESSION} open {TARGET_APP} --platform {PLATFORM}
|
|
52
|
+
agent-device --session {SESSION} snapshot -i
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
If login is required:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
agent-device --session {SESSION} snapshot -i
|
|
59
|
+
agent-device --session {SESSION} fill @e1 "{EMAIL}"
|
|
60
|
+
agent-device --session {SESSION} fill @e2 "{PASSWORD}"
|
|
61
|
+
agent-device --session {SESSION} press @e3
|
|
62
|
+
agent-device --session {SESSION} wait 1000
|
|
63
|
+
agent-device --session {SESSION} snapshot -i
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
For OTP/email codes: ask the user, wait for input, then continue.
|
|
67
|
+
|
|
68
|
+
### 3. Orient
|
|
69
|
+
|
|
70
|
+
Capture initial evidence and navigation anchors:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
agent-device --session {SESSION} screenshot {OUTPUT_DIR}/screenshots/initial.png
|
|
74
|
+
agent-device --session {SESSION} snapshot -i
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Map top-level navigation, tabs, and key workflows before deep testing.
|
|
78
|
+
|
|
79
|
+
### 4. Explore
|
|
80
|
+
|
|
81
|
+
Read [references/issue-taxonomy.md](references/issue-taxonomy.md) for severity/category calibration.
|
|
82
|
+
|
|
83
|
+
Strategy:
|
|
84
|
+
|
|
85
|
+
- Move through each major app area (tabs, drawers, settings pages).
|
|
86
|
+
- Test core journeys end-to-end (create, edit, delete, submit, recover).
|
|
87
|
+
- Validate edge states (empty/error/loading/offline/permissions denied).
|
|
88
|
+
- Use `diff snapshot -i` after UI transitions to avoid stale refs.
|
|
89
|
+
- Periodically capture `logs path` and inspect the app log when behavior looks suspicious.
|
|
90
|
+
|
|
91
|
+
Useful commands per screen:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
agent-device --session {SESSION} snapshot -i
|
|
95
|
+
agent-device --session {SESSION} screenshot {OUTPUT_DIR}/screenshots/{screen-name}.png
|
|
96
|
+
agent-device --session {SESSION} appstate
|
|
97
|
+
agent-device --session {SESSION} logs path
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 5. Document Issues (Repro-First)
|
|
101
|
+
|
|
102
|
+
Explore and document in one pass. When you find an issue, stop and fully capture evidence before continuing.
|
|
103
|
+
|
|
104
|
+
#### Interactive/behavioral issues
|
|
105
|
+
|
|
106
|
+
Use video + step screenshots:
|
|
107
|
+
|
|
108
|
+
1. Start recording:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
agent-device --session {SESSION} record start {OUTPUT_DIR}/videos/issue-{NNN}-repro.mp4
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
2. Reproduce with visible pacing. Capture each step:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
agent-device --session {SESSION} screenshot {OUTPUT_DIR}/screenshots/issue-{NNN}-step-1.png
|
|
118
|
+
sleep 1
|
|
119
|
+
# perform action
|
|
120
|
+
sleep 1
|
|
121
|
+
agent-device --session {SESSION} screenshot {OUTPUT_DIR}/screenshots/issue-{NNN}-step-2.png
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
3. Capture final broken state:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
sleep 2
|
|
128
|
+
agent-device --session {SESSION} screenshot {OUTPUT_DIR}/screenshots/issue-{NNN}-result.png
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
4. Stop recording:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
agent-device --session {SESSION} record stop
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
5. Append issue immediately to report with numbered steps and screenshot references.
|
|
138
|
+
|
|
139
|
+
#### Static/on-load issues
|
|
140
|
+
|
|
141
|
+
Single screenshot is sufficient; no video required:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
agent-device --session {SESSION} screenshot {OUTPUT_DIR}/screenshots/issue-{NNN}.png
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Set **Repro Video** to `N/A` in the report.
|
|
148
|
+
|
|
149
|
+
### 6. Wrap Up
|
|
150
|
+
|
|
151
|
+
Target 5-10 well-evidenced issues, then finish:
|
|
152
|
+
|
|
153
|
+
1. Reconcile summary severity counts in `report.md`.
|
|
154
|
+
2. Close session:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
agent-device --session {SESSION} close
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
3. Report total issues, severity breakdown, and highest-risk findings.
|
|
161
|
+
|
|
162
|
+
## Guidance
|
|
163
|
+
|
|
164
|
+
- Repro quality matters more than issue count.
|
|
165
|
+
- Use refs (`@eN`) for fast exploration, selectors for deterministic replay assertions when needed.
|
|
166
|
+
- Re-snapshot after any mutation (navigation, modal, list update, form submit).
|
|
167
|
+
- Use `fill` for clear-then-type semantics; use `type` for incremental typing behavior checks.
|
|
168
|
+
- Keep logs optional and targeted: enable/read app logs only when useful for diagnosis.
|
|
169
|
+
- Never read source code of the app under test; findings must come from observed runtime behavior.
|
|
170
|
+
- Write each issue immediately to avoid losing evidence.
|
|
171
|
+
- Never delete screenshots/videos/report artifacts during a session.
|
|
172
|
+
|
|
173
|
+
## References
|
|
174
|
+
|
|
175
|
+
| Reference | When to Read |
|
|
176
|
+
|-----------|--------------|
|
|
177
|
+
| [references/issue-taxonomy.md](references/issue-taxonomy.md) | Start of session; severity/categories/checklist |
|
|
178
|
+
|
|
179
|
+
## Templates
|
|
180
|
+
|
|
181
|
+
| Template | Purpose |
|
|
182
|
+
|----------|---------|
|
|
183
|
+
| [templates/dogfood-report-template.md](templates/dogfood-report-template.md) | Copy into output directory as the report file |
|