zig-mobile-runner 0.1.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/CHANGELOG.md +484 -0
- package/CONTRIBUTING.md +42 -0
- package/FEATURES.md +112 -0
- package/LICENSE +21 -0
- package/README.md +255 -0
- package/SECURITY.md +34 -0
- package/build.zig +38 -0
- package/build.zig.zon +7 -0
- package/clients/README.md +144 -0
- package/clients/go/README.md +24 -0
- package/clients/go/examples/fake-session/main.go +93 -0
- package/clients/go/go.mod +3 -0
- package/clients/go/zmr/client.go +432 -0
- package/clients/kotlin/README.md +35 -0
- package/clients/kotlin/build.gradle.kts +35 -0
- package/clients/kotlin/settings.gradle.kts +15 -0
- package/clients/kotlin/src/main/kotlin/dev/zmr/FakeSession.kt +86 -0
- package/clients/kotlin/src/main/kotlin/dev/zmr/ZmrClient.kt +67 -0
- package/clients/python/README.md +29 -0
- package/clients/python/examples/fake_session.py +48 -0
- package/clients/python/pyproject.toml +13 -0
- package/clients/python/zmr_client.py +202 -0
- package/clients/rust/Cargo.lock +107 -0
- package/clients/rust/Cargo.toml +10 -0
- package/clients/rust/README.md +19 -0
- package/clients/rust/examples/fake_session.rs +70 -0
- package/clients/rust/src/lib.rs +461 -0
- package/clients/swift/Package.swift +16 -0
- package/clients/swift/README.md +36 -0
- package/clients/swift/Sources/ZMRClient/ZMRClient.swift +114 -0
- package/clients/swift/Sources/ZMRFakeSession/main.swift +86 -0
- package/clients/typescript/README.md +34 -0
- package/clients/typescript/examples/fake-session.mjs +36 -0
- package/clients/typescript/index.d.ts +144 -0
- package/clients/typescript/index.mjs +192 -0
- package/clients/typescript/package.json +8 -0
- package/docs/adr/0001-agent-native-runner-boundary.md +31 -0
- package/docs/adr/0002-app-local-zmr-contract.md +39 -0
- package/docs/adr/0003-ios-simulator-xctest-shim.md +41 -0
- package/docs/adr/0004-benchmark-claims-and-baseline-collection.md +37 -0
- package/docs/adr/README.md +12 -0
- package/docs/ai-agents.md +156 -0
- package/docs/app-integration.md +316 -0
- package/docs/benchmarking.md +275 -0
- package/docs/client-installation.md +141 -0
- package/docs/clients.md +98 -0
- package/docs/config.md +175 -0
- package/docs/demo.md +259 -0
- package/docs/dsl.md +57 -0
- package/docs/install.md +233 -0
- package/docs/market-positioning.md +70 -0
- package/docs/npm.md +359 -0
- package/docs/protocol-fixtures/README.md +8 -0
- package/docs/protocol-fixtures/core-session.requests.jsonl +8 -0
- package/docs/protocol-fixtures/core-session.responses.jsonl +8 -0
- package/docs/protocol-versioning.md +65 -0
- package/docs/protocol.md +560 -0
- package/docs/publication.md +77 -0
- package/docs/release-audit.md +99 -0
- package/docs/release-candidate.md +111 -0
- package/docs/release-evidence.md +188 -0
- package/docs/release-notes-template.md +58 -0
- package/docs/roadmap.md +334 -0
- package/docs/scenario-authoring.md +88 -0
- package/docs/shipping.md +170 -0
- package/docs/trace-privacy.md +88 -0
- package/docs/troubleshooting.md +256 -0
- package/examples/android-app-auth-probe.json +89 -0
- package/examples/android-app-error-state.json +13 -0
- package/examples/android-app-login-smoke.json +192 -0
- package/examples/android-app-onboarding.json +12 -0
- package/examples/android-app-referral-deep-link.json +12 -0
- package/examples/android-shim-smoke.json +19 -0
- package/examples/demo-failure.json +12 -0
- package/examples/demo-fake.json +14 -0
- package/examples/ios-dev-client-open-link.json +26 -0
- package/examples/ios-dev-client-route-snapshot.json +24 -0
- package/examples/ios-shim-smoke.json +23 -0
- package/examples/ios-smoke.json +9 -0
- package/go.work +3 -0
- package/npm/agents.mjs +183 -0
- package/npm/app-config.mjs +95 -0
- package/npm/build-zmr.mjs +21 -0
- package/npm/commands.mjs +104 -0
- package/npm/generated-files.mjs +50 -0
- package/npm/index.mjs +75 -0
- package/npm/init-app.mjs +80 -0
- package/npm/package-scripts.mjs +72 -0
- package/npm/postinstall.mjs +21 -0
- package/npm/scaffold.mjs +179 -0
- package/npm/scenarios.mjs +93 -0
- package/npm/setup.mjs +69 -0
- package/npm/wizard.mjs +117 -0
- package/npm/zmr.mjs +23 -0
- package/package.json +114 -0
- package/prebuilds/darwin-arm64/zmr +0 -0
- package/prebuilds/darwin-x64/zmr +0 -0
- package/prebuilds/linux-arm64/zmr +0 -0
- package/prebuilds/linux-x64/zmr +0 -0
- package/schemas/README.md +26 -0
- package/schemas/action-result.schema.json +27 -0
- package/schemas/capabilities-output.schema.json +98 -0
- package/schemas/devices-output.schema.json +25 -0
- package/schemas/doctor-output.schema.json +51 -0
- package/schemas/explain-output.schema.json +51 -0
- package/schemas/import-output.schema.json +23 -0
- package/schemas/init-output.schema.json +71 -0
- package/schemas/json-rpc.schema.json +55 -0
- package/schemas/release-manifest.schema.json +43 -0
- package/schemas/release-readiness-output.schema.json +127 -0
- package/schemas/run-output.schema.json +43 -0
- package/schemas/scenario.schema.json +128 -0
- package/schemas/schemas-output.schema.json +26 -0
- package/schemas/semantic-snapshot.schema.json +116 -0
- package/schemas/snapshot.schema.json +60 -0
- package/schemas/trace-event.schema.json +14 -0
- package/schemas/trace-manifest.schema.json +59 -0
- package/schemas/validate-output.schema.json +42 -0
- package/schemas/version-output.schema.json +23 -0
- package/schemas/zmr-config.schema.json +75 -0
- package/scripts/android-emulator.sh +126 -0
- package/scripts/assert-ios-physical-ready.sh +213 -0
- package/scripts/benchmark-command.sh +307 -0
- package/scripts/benchmark.sh +359 -0
- package/scripts/benchmark_gate.py +117 -0
- package/scripts/benchmark_result_row.py +88 -0
- package/scripts/compare-benchmarks.py +288 -0
- package/scripts/create-android-demo-app.sh +342 -0
- package/scripts/create-ios-demo-app.sh +261 -0
- package/scripts/demo-android-real.sh +232 -0
- package/scripts/demo-ios-real.sh +270 -0
- package/scripts/demo.sh +464 -0
- package/scripts/device-matrix.sh +338 -0
- package/scripts/ensure-ios-shim-target.rb +237 -0
- package/scripts/install-android-shim.sh +281 -0
- package/scripts/install-ios-shim.sh +589 -0
- package/scripts/pilot-gate.sh +560 -0
- package/scripts/release-readiness.py +838 -0
- package/scripts/release-readiness.sh +91 -0
- package/scripts/run-android-pilot.sh +561 -0
- package/scripts/run-ios-pilot.sh +509 -0
- package/shims/android/README.md +21 -0
- package/shims/android/ZMRShimInstrumentedTest.java +152 -0
- package/shims/android/protocol.md +18 -0
- package/shims/ios/README.md +50 -0
- package/shims/ios/ZMRShim.swift +110 -0
- package/shims/ios/ZMRShimUITestCase.swift +475 -0
- package/shims/ios/protocol.md +74 -0
- package/skills/zmr-mobile-testing/SKILL.md +127 -0
- package/src/android.zig +344 -0
- package/src/android_device_info.zig +99 -0
- package/src/android_emulator.zig +154 -0
- package/src/android_screen_recording.zig +112 -0
- package/src/android_shell.zig +112 -0
- package/src/bundle.zig +124 -0
- package/src/bundle_redaction.zig +272 -0
- package/src/bundle_tar.zig +123 -0
- package/src/cli_devices.zig +97 -0
- package/src/cli_doctor.zig +114 -0
- package/src/cli_import.zig +70 -0
- package/src/cli_info.zig +39 -0
- package/src/cli_init.zig +72 -0
- package/src/cli_output.zig +467 -0
- package/src/cli_run.zig +259 -0
- package/src/cli_serve.zig +287 -0
- package/src/cli_trace.zig +111 -0
- package/src/cli_validate.zig +41 -0
- package/src/command.zig +211 -0
- package/src/config.zig +305 -0
- package/src/config_diagnostics.zig +212 -0
- package/src/config_paths.zig +49 -0
- package/src/device_registry.zig +37 -0
- package/src/doctor.zig +412 -0
- package/src/doctor_hints.zig +52 -0
- package/src/errors.zig +55 -0
- package/src/fake_device.zig +163 -0
- package/src/health.zig +28 -0
- package/src/importer.zig +343 -0
- package/src/importer_json.zig +100 -0
- package/src/importer_model.zig +103 -0
- package/src/ios.zig +399 -0
- package/src/ios_devices.zig +219 -0
- package/src/ios_lifecycle.zig +72 -0
- package/src/ios_shim.zig +242 -0
- package/src/ios_snapshot.zig +20 -0
- package/src/json_fields.zig +80 -0
- package/src/json_rpc.zig +150 -0
- package/src/json_rpc_methods.zig +318 -0
- package/src/json_rpc_observation.zig +31 -0
- package/src/json_rpc_params.zig +52 -0
- package/src/json_rpc_protocol.zig +110 -0
- package/src/json_rpc_trace.zig +73 -0
- package/src/main.zig +135 -0
- package/src/mcp.zig +234 -0
- package/src/mcp_protocol.zig +64 -0
- package/src/mcp_trace.zig +83 -0
- package/src/report.zig +346 -0
- package/src/report_html.zig +63 -0
- package/src/report_values.zig +27 -0
- package/src/run_options.zig +152 -0
- package/src/runner.zig +280 -0
- package/src/runner_actions.zig +109 -0
- package/src/runner_config.zig +6 -0
- package/src/runner_diagnostics.zig +268 -0
- package/src/runner_events.zig +170 -0
- package/src/runner_native.zig +88 -0
- package/src/runner_waits.zig +300 -0
- package/src/scaffold.zig +472 -0
- package/src/scenario.zig +346 -0
- package/src/scenario_fields.zig +50 -0
- package/src/schema_registry.zig +53 -0
- package/src/selector.zig +84 -0
- package/src/semantic.zig +171 -0
- package/src/trace.zig +315 -0
- package/src/trace_json.zig +340 -0
- package/src/trace_summary.zig +218 -0
- package/src/trace_summary_diagnostic.zig +202 -0
- package/src/types.zig +120 -0
- package/src/uiautomator.zig +164 -0
- package/src/validation.zig +187 -0
- package/src/version.zig +22 -0
- package/viewer/app.js +373 -0
- package/viewer/index.html +126 -0
- package/viewer/parser.js +233 -0
- package/viewer/styles.css +585 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# Benchmarking
|
|
2
|
+
|
|
3
|
+
ZMR benchmark output is intentionally simple: each run appends one JSON object to `results.jsonl`, and `zmr report` turns that directory into a local HTML report.
|
|
4
|
+
|
|
5
|
+
## Single Tool Benchmark
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
scripts/benchmark.sh \
|
|
9
|
+
--zmr examples/android-app-login-smoke.json \
|
|
10
|
+
--device emulator-5554 \
|
|
11
|
+
--runs 10 \
|
|
12
|
+
--trace-root traces/zmr-login \
|
|
13
|
+
--results traces/bench-comparison/results.jsonl \
|
|
14
|
+
--replace \
|
|
15
|
+
--min-pass-rate 100 \
|
|
16
|
+
--max-failures 0 \
|
|
17
|
+
--max-p95-ms 30000
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The command writes trace artifacts under `--trace-root` and appends normalized
|
|
21
|
+
rows to `--results`. Omit `--results` to use `<trace-root>/results.jsonl`.
|
|
22
|
+
Omitting `--trace-root` writes under `traces/` in the current app directory,
|
|
23
|
+
not inside the installed ZMR package.
|
|
24
|
+
Use `--replace` when starting a fresh shared comparison file.
|
|
25
|
+
When any gate option is present, `scripts/benchmark_gate.py` reads
|
|
26
|
+
`results.jsonl` and exits non-zero if pass rate, failure count, mean duration,
|
|
27
|
+
or p95 duration misses the configured threshold.
|
|
28
|
+
|
|
29
|
+
Generate a report:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
zmr report traces/bench-<timestamp> --out traces/bench-<timestamp>/report.html
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Pilot Wrapper
|
|
36
|
+
|
|
37
|
+
The configurable Android pilot script can run both sample scenarios repeatedly:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
./scripts/run-android-pilot.sh \
|
|
41
|
+
--app-root /path/to/mobile-app \
|
|
42
|
+
--device emulator-5554 \
|
|
43
|
+
--runs 20 \
|
|
44
|
+
--min-pass-rate 100 \
|
|
45
|
+
--max-failures 0 \
|
|
46
|
+
--max-p95-ms 30000
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
For lower variance, restore a clean emulator snapshot at the start of the pilot.
|
|
50
|
+
Use `--screen-record` when investigating visual flakes:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
./scripts/run-android-pilot.sh \
|
|
54
|
+
--app-root /path/to/mobile-app \
|
|
55
|
+
--device emulator-5554 \
|
|
56
|
+
--avd Small_Phone \
|
|
57
|
+
--reset-emulator \
|
|
58
|
+
--restore-snapshot zmr-clean \
|
|
59
|
+
--screen-record \
|
|
60
|
+
--runs 20 \
|
|
61
|
+
--min-pass-rate 100 \
|
|
62
|
+
--max-failures 0
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
For `--runs 1`, the script exports normal and redacted `.zmrtrace` bundles. For `--runs > 1`, it writes benchmark directories and HTML reports.
|
|
66
|
+
|
|
67
|
+
The iOS pilot wrapper supports the same repeated-run gates:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
./scripts/run-ios-pilot.sh \
|
|
71
|
+
--app-root /path/to/mobile-app \
|
|
72
|
+
--app-path /path/to/mobile-app/build/Debug-iphonesimulator/Sample.app \
|
|
73
|
+
--device booted \
|
|
74
|
+
--ios-shim /path/to/mobile-app/.zmr/ios-shim \
|
|
75
|
+
--runs 20 \
|
|
76
|
+
--min-pass-rate 100 \
|
|
77
|
+
--max-failures 0 \
|
|
78
|
+
--max-p95-ms 45000
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
For a paired physical iOS device, pass the target type, a concrete device
|
|
82
|
+
identifier from `zmr devices`, and a signed device artifact:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
./scripts/run-ios-pilot.sh \
|
|
86
|
+
--app-root /path/to/mobile-app \
|
|
87
|
+
--app-path /path/to/mobile-app/build/Release-iphoneos/Sample.ipa \
|
|
88
|
+
--ios-device-type physical \
|
|
89
|
+
--device <physical-device-id> \
|
|
90
|
+
--ios-shim /path/to/mobile-app/.zmr/ios-shim \
|
|
91
|
+
--runs 20 \
|
|
92
|
+
--min-pass-rate 100 \
|
|
93
|
+
--max-failures 0 \
|
|
94
|
+
--max-p95-ms 45000
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
When `--ios-shim` is set, the iOS pilot prewarms the app-local XCTest shim with
|
|
98
|
+
an `appState` command before timing scenarios. That moves cold
|
|
99
|
+
`xcodebuild build-for-testing` work out of the measured run and fails early when
|
|
100
|
+
the UI test target is miswired. Use `--skip-shim-prewarm` only when measuring
|
|
101
|
+
first-command cold-start behavior.
|
|
102
|
+
|
|
103
|
+
For release validation on a machine that has both platform builds and targets
|
|
104
|
+
ready, `zmr-pilot-gate` runs the Android and iOS pilot wrappers with one
|
|
105
|
+
external gate command:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
zmr-pilot-gate \
|
|
109
|
+
--android --ios \
|
|
110
|
+
--android-app-root /path/to/mobile-app \
|
|
111
|
+
--ios-app-root /path/to/mobile-app --ios-app-path /path/to/mobile-app/build/Debug-iphonesimulator/Sample.app \
|
|
112
|
+
--ios-device-type simulator \
|
|
113
|
+
--ios-shim /path/to/mobile-app/.zmr/ios-shim \
|
|
114
|
+
--runs 20 \
|
|
115
|
+
--min-pass-rate 100 \
|
|
116
|
+
--max-failures 0
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Reading Results
|
|
120
|
+
|
|
121
|
+
Benchmark reports include:
|
|
122
|
+
|
|
123
|
+
- pass rate
|
|
124
|
+
- failure count
|
|
125
|
+
- mean duration
|
|
126
|
+
- p95 duration
|
|
127
|
+
- per-run status
|
|
128
|
+
- terminal trace status
|
|
129
|
+
- failed step index and error when available
|
|
130
|
+
- links to each run's `events.jsonl`
|
|
131
|
+
|
|
132
|
+
Before making public performance claims, run the same scenario repeatedly on a clean emulator image and include the raw `results.jsonl` plus the redacted trace bundle for any failure.
|
|
133
|
+
|
|
134
|
+
## Compare Against A Baseline
|
|
135
|
+
|
|
136
|
+
Use `zmr-compare-benchmarks` when a private app repo has benchmark rows from
|
|
137
|
+
ZMR and another local runner. The public ZMR repo keeps this generic: rows are
|
|
138
|
+
grouped by the `tool` field and no external runner is hardcoded.
|
|
139
|
+
|
|
140
|
+
Collect ZMR rows into the shared comparison file first:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
zmr-benchmark \
|
|
144
|
+
--zmr .zmr/android-smoke.json \
|
|
145
|
+
--platform android \
|
|
146
|
+
--device emulator-5554 \
|
|
147
|
+
--app-id com.example.mobiletest \
|
|
148
|
+
--app-build <build-id-or-artifact> \
|
|
149
|
+
--runs 20 \
|
|
150
|
+
--trace-root traces/zmr-login \
|
|
151
|
+
--results traces/bench-comparison/results.jsonl \
|
|
152
|
+
--replace \
|
|
153
|
+
--min-pass-rate 100 \
|
|
154
|
+
--max-failures 0
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Then collect rows from an existing command-line runner by wrapping it with
|
|
158
|
+
`zmr-benchmark-command`. This keeps benchmark collection tool-agnostic while
|
|
159
|
+
still capturing per-run stdout/stderr logs and appending to the same results
|
|
160
|
+
file:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
zmr-benchmark-command \
|
|
164
|
+
--tool baseline \
|
|
165
|
+
--platform android \
|
|
166
|
+
--device emulator-5554 \
|
|
167
|
+
--app-id com.example.mobiletest \
|
|
168
|
+
--scenario .zmr/android-smoke.json \
|
|
169
|
+
--app-build <build-id-or-artifact> \
|
|
170
|
+
--runs 20 \
|
|
171
|
+
--trace-root traces/baseline-login \
|
|
172
|
+
--results traces/bench-comparison/results.jsonl \
|
|
173
|
+
-- baseline-runner test .baseline/login.yaml
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
For another runner or command, only change `--tool` and the command after
|
|
177
|
+
`--`:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
zmr-benchmark-command \
|
|
181
|
+
--tool runner-b \
|
|
182
|
+
--platform ios \
|
|
183
|
+
--device booted \
|
|
184
|
+
--app-id com.example.mobiletest \
|
|
185
|
+
--scenario .zmr/ios-smoke.json \
|
|
186
|
+
--app-build <build-id-or-artifact> \
|
|
187
|
+
--runs 20 \
|
|
188
|
+
--trace-root traces/runner-b-login \
|
|
189
|
+
--results traces/bench-comparison/results.jsonl \
|
|
190
|
+
-- npm run e2e:ios
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
zmr-compare-benchmarks \
|
|
195
|
+
--results traces/bench-comparison/results.jsonl \
|
|
196
|
+
--candidate zmr \
|
|
197
|
+
--baseline baseline \
|
|
198
|
+
--min-candidate-pass-rate 100 \
|
|
199
|
+
--max-candidate-failures 0 \
|
|
200
|
+
--min-mean-speedup 1.25 \
|
|
201
|
+
--min-p95-speedup 1.25 \
|
|
202
|
+
--format markdown \
|
|
203
|
+
--out traces/bench-comparison/comparison.md \
|
|
204
|
+
--evidence-out traces/bench-comparison/evidence.jsonl
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
The report includes pass rate, failure count, mean duration, p95 duration, mean
|
|
208
|
+
speedup, p95 speedup, candidate/baseline run counts, and whether the rows have
|
|
209
|
+
the same benchmark context. The optional gates make CI fail when ZMR is not
|
|
210
|
+
reliable enough or not faster than the baseline by the required margin. Only
|
|
211
|
+
compare runs collected on the same host, device state, app build, and scenario.
|
|
212
|
+
`--evidence-out` requires `--min-candidate-pass-rate`,
|
|
213
|
+
`--max-candidate-failures`, `--min-mean-speedup`, and `--min-p95-speedup`, so
|
|
214
|
+
market-claim evidence records explicit reliability and speedup thresholds. When
|
|
215
|
+
`--evidence-out` is set, a successful comparison also requires at least 20 candidate rows,
|
|
216
|
+
at least 20 baseline rows, and matching `platform`, `device`,
|
|
217
|
+
`appId`, `scenario`, and `appBuild` metadata across candidate and baseline
|
|
218
|
+
rows, then appends a `competitive benchmark comparison` row that
|
|
219
|
+
`zmr-release-readiness --target market-claim` can consume directly.
|
|
220
|
+
|
|
221
|
+
## Device Matrix
|
|
222
|
+
|
|
223
|
+
Use `zmr-device-matrix` when CI needs to run one or more scenarios across
|
|
224
|
+
multiple local emulators, simulators, or attached devices:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
zmr-device-matrix \
|
|
228
|
+
--matrix .zmr/device-matrix.json \
|
|
229
|
+
--trace-root traces/zmr-matrix \
|
|
230
|
+
--min-pass-rate 100 \
|
|
231
|
+
--max-failures 0
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Example matrix:
|
|
235
|
+
|
|
236
|
+
```json
|
|
237
|
+
{
|
|
238
|
+
"runs": 2,
|
|
239
|
+
"appId": "com.example.mobiletest",
|
|
240
|
+
"devices": [
|
|
241
|
+
{
|
|
242
|
+
"name": "android-api-35",
|
|
243
|
+
"platform": "android",
|
|
244
|
+
"serial": "emulator-5554",
|
|
245
|
+
"scenario": ".zmr/android-smoke.json",
|
|
246
|
+
"adb": "adb",
|
|
247
|
+
"androidShim": ".zmr/android-shim"
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"name": "ios-18",
|
|
251
|
+
"platform": "ios",
|
|
252
|
+
"iosDeviceType": "simulator",
|
|
253
|
+
"serial": "booted",
|
|
254
|
+
"scenario": ".zmr/ios-smoke.json",
|
|
255
|
+
"xcrun": "xcrun",
|
|
256
|
+
"iosShim": ".zmr/ios-shim"
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
"name": "ios-physical",
|
|
260
|
+
"platform": "ios",
|
|
261
|
+
"iosDeviceType": "physical",
|
|
262
|
+
"serial": "<physical-device-id>",
|
|
263
|
+
"scenario": ".zmr/ios-smoke.json",
|
|
264
|
+
"xcrun": "xcrun",
|
|
265
|
+
"iosShim": ".zmr/ios-shim"
|
|
266
|
+
}
|
|
267
|
+
]
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
The command writes `matrix.jsonl` and `summary.json` under the trace root.
|
|
272
|
+
Each device/run pair has a normal trace directory, so failures can be inspected
|
|
273
|
+
with `zmr explain`, `zmr report`, or the trace viewer.
|
|
274
|
+
For iOS rows, omit `iosDeviceType` for the default simulator path or set it to
|
|
275
|
+
`physical` to pass `--ios-device-type physical` through to `zmr run`.
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Client Installation
|
|
2
|
+
|
|
3
|
+
ZMR has two layers:
|
|
4
|
+
|
|
5
|
+
1. The `zmr` binary controls devices, runs scenarios, serves JSON-RPC, and writes traces.
|
|
6
|
+
2. Language clients are optional wrappers around `zmr serve --transport stdio`.
|
|
7
|
+
|
|
8
|
+
For fastest adoption, install the binary once with npm, a release tarball, or
|
|
9
|
+
Homebrew. Then use a language client only when you want tests or agents written
|
|
10
|
+
in that language.
|
|
11
|
+
|
|
12
|
+
## Binary First
|
|
13
|
+
|
|
14
|
+
Today, install the GitHub release tarball:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install --save-dev https://github.com/johnmikel/zig-mobile-runner/releases/download/v0.1.0/zig-mobile-runner-0.1.0.tgz
|
|
18
|
+
npx zmr version
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
After the npm registry package is published:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install --save-dev zig-mobile-runner
|
|
25
|
+
npx zmr-wizard --app-id com.example.mobiletest --package-json
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Homebrew is the best install path for non-JavaScript teams because it gives any
|
|
29
|
+
language the same `zmr` executable:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Today, after downloading or building a release archive:
|
|
33
|
+
brew install --build-from-source ./dist/homebrew/zmr.rb
|
|
34
|
+
|
|
35
|
+
# Intended tap install after the tap is published:
|
|
36
|
+
brew tap johnmikel/zmr
|
|
37
|
+
brew install zmr
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## TypeScript
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install --save-dev zig-mobile-runner
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
import { createZmrClient } from "zig-mobile-runner/clients/typescript/index.mjs";
|
|
48
|
+
|
|
49
|
+
const zmr = createZmrClient({
|
|
50
|
+
command: "zmr",
|
|
51
|
+
args: ["serve", "--transport", "stdio", "--config", ".zmr/config.json"],
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Python
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
python3 -m pip install "git+https://github.com/johnmikel/zig-mobile-runner.git#subdirectory=clients/python"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from zmr_client import ZmrClient
|
|
63
|
+
|
|
64
|
+
with ZmrClient("zmr", ["serve", "--transport", "stdio", "--config", ".zmr/config.json"]) as zmr:
|
|
65
|
+
zmr.create_session()
|
|
66
|
+
snapshot = zmr.snapshot()
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Go
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
go get github.com/johnmikel/zig-mobile-runner/clients/go@main
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
```go
|
|
76
|
+
client, err := zmr.Start(ctx, "zmr", "serve", "--transport", "stdio", "--config", ".zmr/config.json")
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Rust
|
|
80
|
+
|
|
81
|
+
Until the Rust client is published as its own crate, add the repository as a
|
|
82
|
+
vendor checkout or submodule and depend on the client package by path:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
git submodule add https://github.com/johnmikel/zig-mobile-runner.git vendor/zig-mobile-runner
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
```toml
|
|
89
|
+
[dependencies]
|
|
90
|
+
zmr-client = { path = "vendor/zig-mobile-runner/clients/rust" }
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
```rust
|
|
94
|
+
let mut client = zmr_client::Client::start("zmr", ["serve", "--transport", "stdio"])?;
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Swift
|
|
98
|
+
|
|
99
|
+
Until the Swift client is split into a standalone SwiftPM repository or package
|
|
100
|
+
registry entry, add the repository as a vendor checkout or submodule and use a
|
|
101
|
+
local SwiftPM package path:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
git submodule add https://github.com/johnmikel/zig-mobile-runner.git vendor/zig-mobile-runner
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
```swift
|
|
108
|
+
.package(path: "vendor/zig-mobile-runner/clients/swift")
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The Swift client is for macOS agent/test tools. It is not embedded in the iOS
|
|
112
|
+
app under test.
|
|
113
|
+
|
|
114
|
+
## Kotlin
|
|
115
|
+
|
|
116
|
+
Use the Kotlin client as source or build a local jar:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
git submodule add https://github.com/johnmikel/zig-mobile-runner.git vendor/zig-mobile-runner
|
|
120
|
+
gradle -p vendor/zig-mobile-runner/clients/kotlin build
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
```kotlin
|
|
124
|
+
val zmr = ZmrClient(listOf("zmr", "serve", "--transport", "stdio", "--config", ".zmr/config.json"))
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The Kotlin client is useful for Android teams that prefer Kotlin for host-side
|
|
128
|
+
test orchestration. It does not replace the Android app shim or run inside the
|
|
129
|
+
app process.
|
|
130
|
+
|
|
131
|
+
## Which Client Should You Use?
|
|
132
|
+
|
|
133
|
+
- Use only the CLI for committed `.zmr/*.json` scenarios and CI.
|
|
134
|
+
- Use TypeScript or Python for most AI agents because they are easy to generate
|
|
135
|
+
and inspect.
|
|
136
|
+
- Use Go or Rust for long-running infrastructure services.
|
|
137
|
+
- Use Swift or Kotlin when native mobile teams want host-side tooling in their
|
|
138
|
+
everyday language.
|
|
139
|
+
|
|
140
|
+
All clients call the same public JSON-RPC protocol, so feature parity should
|
|
141
|
+
come from the runner, not from language-specific behavior.
|
package/docs/clients.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Client Guide
|
|
2
|
+
|
|
3
|
+
ZMR clients are reference implementations for the JSON-RPC protocol used by
|
|
4
|
+
`zmr serve`. They are intentionally small and dependency-light.
|
|
5
|
+
|
|
6
|
+
## What Clients Mean
|
|
7
|
+
|
|
8
|
+
The runner is still the Zig binary. A client starts or connects to:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
zmr serve --transport stdio --config .zmr/config.json --trace-dir traces/zmr-agent
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Then it sends JSON-RPC methods such as:
|
|
15
|
+
|
|
16
|
+
- `runner.capabilities`
|
|
17
|
+
- `session.create`
|
|
18
|
+
- `observe.snapshot`
|
|
19
|
+
- `observe.semanticSnapshot`
|
|
20
|
+
- `ui.tap`
|
|
21
|
+
- `wait.until`
|
|
22
|
+
- `assert.visible`
|
|
23
|
+
- `assert.healthy`
|
|
24
|
+
- `trace.events`
|
|
25
|
+
- `trace.export`
|
|
26
|
+
|
|
27
|
+
Use clients when an AI agent, service, or test harness wants to drive ZMR
|
|
28
|
+
programmatically instead of shelling out for each scenario. For package-manager
|
|
29
|
+
install commands, see [client-installation.md](client-installation.md).
|
|
30
|
+
Prefer the semantic snapshot helper for agent planning; it normalizes native
|
|
31
|
+
Android/iOS classes into roles, selectors, bounds, and recommended actions.
|
|
32
|
+
Use `assert.healthy` after launches, deep links, and high-risk transitions so
|
|
33
|
+
agent-written tests fail on crash overlays and development-client load errors
|
|
34
|
+
even when normal page text is also present.
|
|
35
|
+
|
|
36
|
+
## Language Layouts
|
|
37
|
+
|
|
38
|
+
| Language | Files | Why it looks this way |
|
|
39
|
+
| --- | --- | --- |
|
|
40
|
+
| TypeScript | `clients/typescript/index.mjs`, `index.d.ts` | ESM runtime plus type declarations, no build step required |
|
|
41
|
+
| Python | `clients/python/zmr_client.py`, `pyproject.toml` | Standard-library importable module that can be vendored or pip-installed from source |
|
|
42
|
+
| Go | `clients/go/zmr/client.go` | Normal Go package inside a module |
|
|
43
|
+
| Rust | `clients/rust/src/lib.rs` | Cargo library crate convention |
|
|
44
|
+
| Swift | `clients/swift/Sources/ZMRClient/ZMRClient.swift` | SwiftPM package for macOS host-side tools |
|
|
45
|
+
| Kotlin | `clients/kotlin/src/main/kotlin/dev/zmr/ZmrClient.kt` | Gradle/Kotlin source package for JVM host-side tools |
|
|
46
|
+
|
|
47
|
+
Rust has `src/lib.rs` because Cargo expects a library crate there. The other
|
|
48
|
+
clients do have equivalent entry points; they are just idiomatic for their
|
|
49
|
+
languages rather than named `lib.rs`. Swift and Kotlin are useful for native
|
|
50
|
+
mobile teams, but they still run on the development machine and drive the
|
|
51
|
+
external `zmr` binary. They are not app-runtime SDKs.
|
|
52
|
+
|
|
53
|
+
## Quick Starts
|
|
54
|
+
|
|
55
|
+
TypeScript:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
node clients/typescript/examples/fake-session.mjs
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Python:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
python3 clients/python/examples/fake_session.py
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Go:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
go run ./clients/go/examples/fake-session \
|
|
71
|
+
--zmr ./zig-out/bin/zmr \
|
|
72
|
+
--adb ./tests/fake-adb.sh \
|
|
73
|
+
--trace-dir traces/demo-go-client
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Rust:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
cargo run --manifest-path clients/rust/Cargo.toml --example fake_session -- \
|
|
80
|
+
--zmr ./zig-out/bin/zmr \
|
|
81
|
+
--adb ./tests/fake-adb.sh \
|
|
82
|
+
--trace-dir traces/demo-rust-client
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Swift:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
swift build --package-path clients/swift
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Kotlin:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
gradle -p clients/kotlin build
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
For real app usage, replace the fake server with `zmr serve --transport stdio`
|
|
98
|
+
and pass `.zmr/config.json`.
|
package/docs/config.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# App-Local Config
|
|
2
|
+
|
|
3
|
+
ZMR uses `.zmr/config.json` as the app-local source of truth for default app ids,
|
|
4
|
+
devices, scenario paths, and trace directories.
|
|
5
|
+
|
|
6
|
+
The schema is published at `schemas/zmr-config.schema.json`.
|
|
7
|
+
Runtime parsing follows the schema for primitive field types. For example,
|
|
8
|
+
boolean fields such as `artifacts.screenRecording`, `artifacts.screenshots`,
|
|
9
|
+
`android.resetBeforeRun`, and `android.waitReady` must be JSON booleans, not
|
|
10
|
+
strings. Path, id, redaction-list, and script command string fields must be
|
|
11
|
+
non-empty. `zmr doctor --json --config .zmr/config.json` reports those type/value mistakes as
|
|
12
|
+
structured `config` warnings. Unknown fields are rejected too, so typos in
|
|
13
|
+
app-local config do not silently fall back to defaults.
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"schemaVersion": 1,
|
|
20
|
+
"appId": "com.example.mobiletest",
|
|
21
|
+
"android": {
|
|
22
|
+
"enabled": true,
|
|
23
|
+
"defaultDevice": "emulator-5554",
|
|
24
|
+
"smokeScenario": ".zmr/android-smoke.json",
|
|
25
|
+
"traceDir": "traces/zmr-android",
|
|
26
|
+
"avdName": "Small_Phone",
|
|
27
|
+
"restoreSnapshot": "zmr-clean",
|
|
28
|
+
"createAvdIfMissing": false,
|
|
29
|
+
"avdSystemImage": "system-images;android-35;google_apis;arm64-v8a",
|
|
30
|
+
"avdDeviceProfile": "pixel_6",
|
|
31
|
+
"resetBeforeRun": false,
|
|
32
|
+
"waitReady": true
|
|
33
|
+
},
|
|
34
|
+
"ios": {
|
|
35
|
+
"enabled": true,
|
|
36
|
+
"defaultDevice": "booted",
|
|
37
|
+
"smokeScenario": ".zmr/ios-smoke.json",
|
|
38
|
+
"traceDir": "traces/zmr-ios"
|
|
39
|
+
},
|
|
40
|
+
"artifacts": {
|
|
41
|
+
"screenshots": true,
|
|
42
|
+
"hierarchy": true,
|
|
43
|
+
"logs": true,
|
|
44
|
+
"screenRecording": false
|
|
45
|
+
},
|
|
46
|
+
"redaction": {
|
|
47
|
+
"denylistText": ["customer dob", "internal token"],
|
|
48
|
+
"allowlistText": ["public token label"],
|
|
49
|
+
"denylistResourceIds": ["password-field", "ssn"],
|
|
50
|
+
"allowlistResourceIds": ["public-token-label"]
|
|
51
|
+
},
|
|
52
|
+
"tools": {
|
|
53
|
+
"androidShimPath": "./.zmr/android-shim",
|
|
54
|
+
"iosShimPath": "./.zmr/ios-shim"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Precedence
|
|
60
|
+
|
|
61
|
+
ZMR auto-discovers `.zmr/config.json` from the current working directory.
|
|
62
|
+
Pass `--config <path>` to load a different file.
|
|
63
|
+
|
|
64
|
+
Relative scenario, trace, and shim paths from config resolve against the app
|
|
65
|
+
root. For the standard `.zmr/config.json` location, the app root is the parent
|
|
66
|
+
directory of `.zmr/`, even when `--config` is an absolute path and ZMR is
|
|
67
|
+
invoked from another checkout. Relative optional tool commands such as
|
|
68
|
+
`tools.adbPath` are resolved the same way when they look like paths; bare
|
|
69
|
+
commands such as `adb`, `xcrun`, or `zig` stay as PATH lookups.
|
|
70
|
+
|
|
71
|
+
Explicit CLI flags always win:
|
|
72
|
+
|
|
73
|
+
- `--app-id` overrides `appId`
|
|
74
|
+
- `--device` overrides platform `defaultDevice`
|
|
75
|
+
- `--trace-dir` overrides platform `traceDir`
|
|
76
|
+
- `--android-avd`, `--create-avd-if-missing`, `--avd-system-image`, `--avd-device`, `--restore-snapshot`, `--reset-emulator`, and `--wait-emulator` override Android emulator lifecycle defaults
|
|
77
|
+
- `--screen-record` and `--no-screen-record` override `artifacts.screenRecording`
|
|
78
|
+
- a positional scenario path overrides platform `smokeScenario`
|
|
79
|
+
- `--adb`, `--emulator`, `--avdmanager`, `--android-shim`, `--xcrun`, `--ios-shim`, and `--zig` override optional tool paths
|
|
80
|
+
|
|
81
|
+
## Android Emulator Lifecycle
|
|
82
|
+
|
|
83
|
+
The Android platform config can boot and wait for an emulator before a traced
|
|
84
|
+
`zmr run` starts:
|
|
85
|
+
|
|
86
|
+
- `avdName`: AVD name passed to the Android emulator.
|
|
87
|
+
- `createAvdIfMissing`: check `emulator -list-avds` and create the AVD when absent.
|
|
88
|
+
- `avdSystemImage`: installed Android system image package for `avdmanager create avd`.
|
|
89
|
+
- `avdDeviceProfile`: optional device profile for the created AVD.
|
|
90
|
+
- `restoreSnapshot`: optional emulator snapshot name to load at boot.
|
|
91
|
+
- `resetBeforeRun`: best-effort `adb emu kill` before booting the configured AVD.
|
|
92
|
+
- `waitReady`: wait for `adb wait-for-device` and `sys.boot_completed=1`.
|
|
93
|
+
|
|
94
|
+
The equivalent CLI flags are `--android-avd <name>`,
|
|
95
|
+
`--create-avd-if-missing`, `--avd-system-image <package>`,
|
|
96
|
+
`--avd-device <profile>`, `--restore-snapshot <name>`, `--reset-emulator`,
|
|
97
|
+
and `--wait-emulator`. AVD creation, snapshot restore, and reset require an AVD
|
|
98
|
+
name. AVD creation also requires an installed system image package.
|
|
99
|
+
|
|
100
|
+
## Android Shim
|
|
101
|
+
|
|
102
|
+
Set `tools.androidShimPath` when an app repo has built or installed an Android
|
|
103
|
+
instrumentation shim command. ZMR sends one JSON command to the shim over stdin
|
|
104
|
+
and reads one JSON response from stdout. Existing ADB/UI Automator behavior
|
|
105
|
+
remains the fallback when no shim is configured.
|
|
106
|
+
|
|
107
|
+
CLI `--android-shim <path>` takes precedence over the config value for
|
|
108
|
+
`zmr run`, `zmr serve`, and `zmr doctor`.
|
|
109
|
+
|
|
110
|
+
## iOS Shim
|
|
111
|
+
|
|
112
|
+
Set `tools.iosShimPath` when an app repo has built or installed an
|
|
113
|
+
XCTest/XCUIAutomation shim command. ZMR sends one JSON command to the shim over
|
|
114
|
+
stdin and reads one JSON response from stdout. The public scenario and JSON-RPC
|
|
115
|
+
interfaces stay unchanged. The generated shim command may cache the XCTest
|
|
116
|
+
`build-for-testing` output and use `test-without-building` internally; ZMR still
|
|
117
|
+
treats it as a simple command transport.
|
|
118
|
+
|
|
119
|
+
With the shim configured, iOS waits and assertions use native XCTest selector
|
|
120
|
+
queries for single-field selectors before falling back to portable snapshot
|
|
121
|
+
matching. `observe.snapshot` uses a bounded XCTest snapshot so large native or
|
|
122
|
+
React Native trees remain fast enough for interactive agent loops.
|
|
123
|
+
|
|
124
|
+
CLI `--ios-shim <path>` takes precedence over the config value for `zmr run`,
|
|
125
|
+
`zmr serve`, and `zmr doctor`.
|
|
126
|
+
|
|
127
|
+
## Artifact Capture
|
|
128
|
+
|
|
129
|
+
The `artifacts` object controls what raw trace artifacts are persisted during
|
|
130
|
+
`zmr run`.
|
|
131
|
+
|
|
132
|
+
- `screenshots`: write PNG screenshot artifacts.
|
|
133
|
+
- `hierarchy`: write raw Android UI hierarchy XML artifacts.
|
|
134
|
+
- `logs`: include recent device log windows in snapshots.
|
|
135
|
+
- `screenRecording`: capture an Android MP4 for the whole traced `zmr run`.
|
|
136
|
+
|
|
137
|
+
Screenshots, hierarchy, and logs default to `true`; screen recording defaults to
|
|
138
|
+
`false` because it can be large and privacy-sensitive. Set raw artifacts to
|
|
139
|
+
`false` in app repos when traces may contain private visual state,
|
|
140
|
+
accessibility text, or log output. Selector matching can still use the live
|
|
141
|
+
hierarchy even when raw hierarchy persistence is disabled.
|
|
142
|
+
|
|
143
|
+
## Trace Redaction
|
|
144
|
+
|
|
145
|
+
The `redaction` object adds app-specific rules on top of ZMR's built-in email,
|
|
146
|
+
token, and sensitive-key scrubbing for persisted trace JSON.
|
|
147
|
+
|
|
148
|
+
- `denylistText`: redact trace strings containing any listed text.
|
|
149
|
+
- `allowlistText`: skip app-specific text denylist matches for strings
|
|
150
|
+
containing any listed text. Built-in email/token scrubbing still applies.
|
|
151
|
+
- `denylistResourceIds`: redact matching node `resourceId` values and force the
|
|
152
|
+
node's `text` and `contentDesc` fields to secret placeholders.
|
|
153
|
+
- `allowlistResourceIds`: skip resource-id based redaction for matching nodes.
|
|
154
|
+
Built-in value-level email/token scrubbing still applies.
|
|
155
|
+
|
|
156
|
+
All matches are case-insensitive substrings. These rules apply to local
|
|
157
|
+
persisted snapshot JSON and trace events for both `zmr run` and
|
|
158
|
+
`zmr serve --trace-dir`. They do not edit raw screenshot pixels or raw hierarchy
|
|
159
|
+
XML; disable those artifacts or share `zmr export --redact` bundles when traces
|
|
160
|
+
leave a trusted machine.
|
|
161
|
+
|
|
162
|
+
## Commands
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
zmr init --app --json --dir . --app-id com.example.mobiletest
|
|
166
|
+
zmr run --config .zmr/config.json
|
|
167
|
+
zmr run .zmr/android-smoke.json --device emulator-5554
|
|
168
|
+
zmr serve --transport stdio --config .zmr/config.json
|
|
169
|
+
zmr mcp --config .zmr/config.json --trace-dir traces/zmr-agent
|
|
170
|
+
zmr doctor --strict --json --config .zmr/config.json
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
For `zmr serve`, the platform `traceDir` from `.zmr/config.json` is used as the
|
|
174
|
+
live JSON-RPC trace directory unless `--trace-dir` overrides it. Keep generated
|
|
175
|
+
trace output under `traces/` and ignore that directory in app repositories.
|