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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 John Mikel Regida
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# Zig Mobile Runner
|
|
2
|
+
|
|
3
|
+
> Agent-native mobile UI automation for Android, iOS simulators, and physical iOS devices.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/johnmikel/zig-mobile-runner/actions/workflows/ci.yml)
|
|
6
|
+
[](https://github.com/johnmikel/zig-mobile-runner/releases)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
ZMR gives AI agents and test harnesses a typed mobile control plane: observe the
|
|
10
|
+
app, choose an action, wait for the UI to settle, assert state, and export a
|
|
11
|
+
replayable trace. The runner does not embed an LLM. Agents stay outside and use
|
|
12
|
+
ZMR through JSON-RPC, CLI JSON, scenarios, and language clients.
|
|
13
|
+
|
|
14
|
+
## Why ZMR
|
|
15
|
+
|
|
16
|
+
- **AI-native protocol:** structured snapshots, semantic mobile trees, actions,
|
|
17
|
+
waits, assertions, live trace events, and redacted trace export over
|
|
18
|
+
JSON-RPC or MCP.
|
|
19
|
+
- **Trace-first debugging:** every run can produce screenshots, UI trees, logs,
|
|
20
|
+
timings, action inputs, assertion results, and an HTML report.
|
|
21
|
+
- **Fast local core:** Zig owns orchestration, subprocess control, selectors,
|
|
22
|
+
waits, retries, scenario execution, and release artifacts.
|
|
23
|
+
- **App-local setup:** `.zmr/config.json`, smoke scenarios, shim commands, and
|
|
24
|
+
traces live in the app repo.
|
|
25
|
+
- **Android and iOS:** Android uses ADB/UI Automator plus an optional native
|
|
26
|
+
shim. iOS simulators use `simctl`; physical iOS devices use `devicectl`;
|
|
27
|
+
selector-grade iOS automation uses the XCTest/XCUIAutomation shim.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
Inside a mobile app repo:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# Available after the npm registry package is published:
|
|
35
|
+
npm install --save-dev zig-mobile-runner
|
|
36
|
+
npx zmr-wizard --app-id com.example.mobiletest --package-json
|
|
37
|
+
npx zmr doctor --strict --json --config .zmr/config.json
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
For Expo development builds, add `--expo-dev-client-scheme <scheme>` to the
|
|
41
|
+
wizard command.
|
|
42
|
+
|
|
43
|
+
Today, install the release tarball from GitHub:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm install --save-dev https://github.com/johnmikel/zig-mobile-runner/releases/download/v0.1.0/zig-mobile-runner-0.1.0.tgz
|
|
47
|
+
npx zmr-wizard --app-id com.example.mobiletest --package-json
|
|
48
|
+
npx zmr doctor --strict --json --config .zmr/config.json
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
From source:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
git clone https://github.com/johnmikel/zig-mobile-runner.git
|
|
55
|
+
cd zig-mobile-runner
|
|
56
|
+
zig build-exe src/main.zig -target aarch64-macos.15.0 -O Debug -femit-bin=zig-out/bin/zmr
|
|
57
|
+
./zig-out/bin/zmr version
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Release archives and npm tarballs are attached to GitHub releases. The npm
|
|
61
|
+
registry package is pending publish.
|
|
62
|
+
|
|
63
|
+
Homebrew is the preferred binary install for teams that do not use JavaScript:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
brew install --build-from-source ./dist/homebrew/zmr.rb
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Try It
|
|
70
|
+
|
|
71
|
+
No device required:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
./scripts/demo.sh
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Real iOS simulator demo:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npx zmr-demo-ios --out /tmp/zmr-ios-demo --device booted
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The demo command boots an available simulator when none is already running.
|
|
84
|
+
|
|
85
|
+
Android app-local pilot:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
zmr-pilot-gate \
|
|
89
|
+
--android \
|
|
90
|
+
--android-app-root /path/to/mobile-app \
|
|
91
|
+
--android-app-id com.example.mobiletest \
|
|
92
|
+
--android-device emulator-5554 \
|
|
93
|
+
--runs 20 \
|
|
94
|
+
--min-pass-rate 100 \
|
|
95
|
+
--max-failures 0 \
|
|
96
|
+
--evidence-out /path/to/mobile-app/traces/zmr-pilots/evidence.jsonl
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
iOS app-local pilot:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
zmr-pilot-gate \
|
|
103
|
+
--ios \
|
|
104
|
+
--ios-app-root /path/to/mobile-app \
|
|
105
|
+
--ios-app-path /path/to/mobile-app/build/Debug-iphonesimulator/Sample.app \
|
|
106
|
+
--ios-app-id com.example.mobiletest \
|
|
107
|
+
--ios-device booted \
|
|
108
|
+
--ios-shim /path/to/mobile-app/.zmr/ios-shim \
|
|
109
|
+
--runs 20 \
|
|
110
|
+
--min-pass-rate 100 \
|
|
111
|
+
--max-failures 0 \
|
|
112
|
+
--evidence-out /path/to/mobile-app/traces/zmr-pilots/evidence.jsonl
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Scenario Example
|
|
116
|
+
|
|
117
|
+
ZMR scenarios are JSON today because JSON is strict, schema-validatable, and
|
|
118
|
+
easy for agents and code generators to emit.
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"name": "Login smoke",
|
|
123
|
+
"appId": "com.example.mobiletest",
|
|
124
|
+
"steps": [
|
|
125
|
+
{ "action": "launch" },
|
|
126
|
+
{ "action": "assertHealthy", "timeoutMs": 5000 },
|
|
127
|
+
{ "action": "tap", "selector": { "resourceId": "email" } },
|
|
128
|
+
{ "action": "typeText", "text": "user@example.com" },
|
|
129
|
+
{ "action": "tap", "selector": { "resourceId": "password" } },
|
|
130
|
+
{ "action": "typeText", "text": "password" },
|
|
131
|
+
{ "action": "tap", "selector": { "text": "Login" } },
|
|
132
|
+
{ "action": "waitVisible", "selector": { "text": "Welcome" }, "timeoutMs": 30000 }
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Validate before touching a device:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
zmr version --json
|
|
141
|
+
zmr schemas --json
|
|
142
|
+
zmr devices --json
|
|
143
|
+
zmr init --app --json --dir . --app-id com.example.mobiletest
|
|
144
|
+
zmr validate --json .zmr/login-smoke.json
|
|
145
|
+
zmr run .zmr/login-smoke.json --json --trace-dir traces/login-smoke
|
|
146
|
+
zmr explain --json traces/login-smoke
|
|
147
|
+
zmr import flow-yaml .zmr/legacy-flow.yaml --out .zmr/legacy-flow.json
|
|
148
|
+
zmr export traces/login-smoke --out traces/login-smoke-redacted.zmrtrace --redact
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Stable JSON outputs are documented with schemas:
|
|
152
|
+
`version-output.schema.json`, `schemas-output.schema.json`,
|
|
153
|
+
`capabilities-output.schema.json`, `init-output.schema.json`,
|
|
154
|
+
`devices-output.schema.json`, `validate-output.schema.json`,
|
|
155
|
+
`run-output.schema.json`, `explain-output.schema.json`,
|
|
156
|
+
`semantic-snapshot.schema.json`, `release-manifest.schema.json`,
|
|
157
|
+
`release-readiness-output.schema.json`, and `RELEASE_MANIFEST.json`.
|
|
158
|
+
|
|
159
|
+
See [docs/dsl.md](docs/dsl.md) for the DSL decision and roadmap.
|
|
160
|
+
|
|
161
|
+
## Agent And Language Clients
|
|
162
|
+
|
|
163
|
+
Clients are thin wrappers around `zmr serve --transport stdio`. They do not
|
|
164
|
+
replace the runner; they make it easier for agents and test code to call the
|
|
165
|
+
same JSON-RPC protocol.
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
zmr serve --transport stdio --config .zmr/config.json --trace-dir traces/zmr-agent
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Agents that support the Model Context Protocol can use the native MCP surface:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
zmr mcp --config .zmr/config.json --trace-dir traces/zmr-agent
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
The MCP server exposes mobile-specific tools such as `semantic_snapshot`,
|
|
178
|
+
`tap`, `type`, `wait_visible`, `trace_events`, and `trace_export`.
|
|
179
|
+
|
|
180
|
+
| Language | Entry point | Example |
|
|
181
|
+
| --- | --- | --- |
|
|
182
|
+
| TypeScript | `clients/typescript/index.mjs` + `index.d.ts` | `node clients/typescript/examples/fake-session.mjs` |
|
|
183
|
+
| Python | `clients/python/zmr_client.py` + `pyproject.toml` | `python3 clients/python/examples/fake_session.py` |
|
|
184
|
+
| Go | `clients/go/zmr/client.go` | `go run ./clients/go/examples/fake-session` |
|
|
185
|
+
| Rust | `clients/rust/src/lib.rs` | `cargo run --manifest-path clients/rust/Cargo.toml --example fake_session` |
|
|
186
|
+
| Swift | `clients/swift/Sources/ZMRClient` | `swift build --package-path clients/swift` |
|
|
187
|
+
| Kotlin | `clients/kotlin/src/main/kotlin/dev/zmr` | `gradle -p clients/kotlin build` |
|
|
188
|
+
|
|
189
|
+
Rust uses `src/lib.rs` because that is the idiomatic crate layout. TypeScript
|
|
190
|
+
uses `index.mjs` plus declarations, Python uses a pip-installable module, Go
|
|
191
|
+
uses a package directory under `clients/go/zmr`, Swift uses SwiftPM, and Kotlin
|
|
192
|
+
uses Gradle.
|
|
193
|
+
|
|
194
|
+
See [clients/README.md](clients/README.md), [docs/client-installation.md](docs/client-installation.md),
|
|
195
|
+
and [docs/ai-agents.md](docs/ai-agents.md).
|
|
196
|
+
|
|
197
|
+
## Platform Support
|
|
198
|
+
|
|
199
|
+
| Target | Status | Notes |
|
|
200
|
+
| --- | --- | --- |
|
|
201
|
+
| Android emulator | Supported | ADB/UI Automator, optional Android shim, emulator lifecycle helpers |
|
|
202
|
+
| Android physical device | Supported | Requires ADB connection and app build/install surface |
|
|
203
|
+
| iOS simulator | Supported | `simctl` plus app-local XCTest/XCUIAutomation shim for native selector actions, native waits, and bounded snapshots |
|
|
204
|
+
| iOS physical device | Supported, evidence-gated | `devicectl` lifecycle plus app-local XCTest/XCUIAutomation shim; run the physical pilot before claiming device reliability |
|
|
205
|
+
| Cloud device farms | Not yet | Planned after local matrix certification |
|
|
206
|
+
|
|
207
|
+
Current release: `0.1.0` developer preview. Protocol version:
|
|
208
|
+
`2026-04-28`. Latest local coverage run: `94.40%` line coverage.
|
|
209
|
+
|
|
210
|
+
## Documentation
|
|
211
|
+
|
|
212
|
+
- [FEATURES.md](FEATURES.md): complete feature list and limitations
|
|
213
|
+
- [docs/install.md](docs/install.md): source, archive, npm, and app setup
|
|
214
|
+
- [docs/app-integration.md](docs/app-integration.md): app-side Android/iOS shims
|
|
215
|
+
- [docs/protocol.md](docs/protocol.md): JSON-RPC methods and schemas
|
|
216
|
+
- [docs/ai-agents.md](docs/ai-agents.md): JSON-RPC and MCP agent workflows
|
|
217
|
+
- [docs/dsl.md](docs/dsl.md): scenario DSL decision and roadmap
|
|
218
|
+
- [docs/clients.md](docs/clients.md): language client guide
|
|
219
|
+
- [docs/client-installation.md](docs/client-installation.md): npm, Homebrew, TS, Python, Go, Rust, Swift, and Kotlin setup
|
|
220
|
+
- [docs/market-positioning.md](docs/market-positioning.md): competitive positioning
|
|
221
|
+
- [docs/adr/](docs/adr/): architecture decision records
|
|
222
|
+
- [docs/shipping.md](docs/shipping.md): release gate and support matrix
|
|
223
|
+
- [docs/release-audit.md](docs/release-audit.md): prompt-to-artifact completion audit
|
|
224
|
+
- [docs/trace-privacy.md](docs/trace-privacy.md): safe trace export
|
|
225
|
+
- [skills/zmr-mobile-testing/SKILL.md](skills/zmr-mobile-testing/SKILL.md): reusable agent skill
|
|
226
|
+
|
|
227
|
+
## Release Gate
|
|
228
|
+
|
|
229
|
+
Before publishing:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
./scripts/release-gate.sh
|
|
233
|
+
./scripts/build-release.sh
|
|
234
|
+
./scripts/verify-release-artifacts.sh
|
|
235
|
+
npm pack --dry-run
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
The release gate runs formatting, shell syntax checks, client tests, public
|
|
239
|
+
safety scans, Zig tests, the no-device demo, coverage, archive generation,
|
|
240
|
+
checksum/manifest verification, host archive smoke, and npm package dry-run.
|
|
241
|
+
|
|
242
|
+
Release-candidate evidence can be checked explicitly:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
zmr-release-readiness --evidence traces/release-candidate/<run>/evidence.jsonl \
|
|
246
|
+
--target dev-preview
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Use `--target production` only after repeated real app/device pilots exist, and
|
|
250
|
+
`--target market-claim` only after same-host/device benchmark comparison
|
|
251
|
+
evidence exists.
|
|
252
|
+
|
|
253
|
+
## License
|
|
254
|
+
|
|
255
|
+
MIT
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
ZMR is a local mobile automation runner. It can collect screenshots, UI trees,
|
|
4
|
+
logs, app ids, device metadata, and scenario inputs. Treat raw traces as
|
|
5
|
+
sensitive.
|
|
6
|
+
|
|
7
|
+
## Supported Versions
|
|
8
|
+
|
|
9
|
+
The current supported line is `0.1.x` dev preview. Security fixes should target
|
|
10
|
+
the latest dev-preview branch until a stable release exists.
|
|
11
|
+
|
|
12
|
+
## Reporting A Vulnerability
|
|
13
|
+
|
|
14
|
+
Open a private security advisory on GitHub when available. If private advisory
|
|
15
|
+
reporting is not enabled, contact the repository maintainer through the channel
|
|
16
|
+
listed in the project profile.
|
|
17
|
+
|
|
18
|
+
Include:
|
|
19
|
+
|
|
20
|
+
- ZMR version and platform.
|
|
21
|
+
- Reproduction steps.
|
|
22
|
+
- Whether the issue exposes screenshots, logs, trace data, credentials, or
|
|
23
|
+
device access.
|
|
24
|
+
- A minimal scenario or redacted trace bundle when possible.
|
|
25
|
+
|
|
26
|
+
Do not publish raw traces from private apps in public issues.
|
|
27
|
+
|
|
28
|
+
## Trace Handling
|
|
29
|
+
|
|
30
|
+
- Use `zmr export --redact` before sharing trace bundles.
|
|
31
|
+
- Do not share raw screenshot artifacts from private apps.
|
|
32
|
+
- Do not paste logs that include tokens, emails, API keys, or device identifiers.
|
|
33
|
+
- Prefer fake-device reproductions for public bug reports.
|
|
34
|
+
|
package/build.zig
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
|
|
3
|
+
pub fn build(b: *std.Build) void {
|
|
4
|
+
const target = b.standardTargetOptions(.{});
|
|
5
|
+
const optimize = b.standardOptimizeOption(.{});
|
|
6
|
+
|
|
7
|
+
const exe = b.addExecutable(.{
|
|
8
|
+
.name = "zmr",
|
|
9
|
+
.root_module = b.createModule(.{
|
|
10
|
+
.root_source_file = b.path("src/main.zig"),
|
|
11
|
+
.target = target,
|
|
12
|
+
.optimize = optimize,
|
|
13
|
+
}),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
b.installArtifact(exe);
|
|
17
|
+
|
|
18
|
+
const run_cmd = b.addRunArtifact(exe);
|
|
19
|
+
run_cmd.step.dependOn(b.getInstallStep());
|
|
20
|
+
if (b.args) |args| {
|
|
21
|
+
run_cmd.addArgs(args);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const run_step = b.step("run", "Run zmr");
|
|
25
|
+
run_step.dependOn(&run_cmd.step);
|
|
26
|
+
|
|
27
|
+
const unit_tests = b.addTest(.{
|
|
28
|
+
.root_module = b.createModule(.{
|
|
29
|
+
.root_source_file = b.path("src/main.zig"),
|
|
30
|
+
.target = target,
|
|
31
|
+
.optimize = optimize,
|
|
32
|
+
}),
|
|
33
|
+
});
|
|
34
|
+
const run_unit_tests = b.addRunArtifact(unit_tests);
|
|
35
|
+
|
|
36
|
+
const test_step = b.step("test", "Run unit tests");
|
|
37
|
+
test_step.dependOn(&run_unit_tests.step);
|
|
38
|
+
}
|
package/build.zig.zon
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# ZMR Language Clients
|
|
2
|
+
|
|
3
|
+
ZMR clients are small wrappers around the same newline-delimited JSON-RPC
|
|
4
|
+
protocol exposed by:
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
zmr serve --transport stdio --config .zmr/config.json --trace-dir traces/zmr-agent
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
They are intended for AI agents, CI harnesses, and app teams that want typed
|
|
11
|
+
or idiomatic calls without reimplementing JSON-RPC framing.
|
|
12
|
+
Each client includes `devices()` for `device.list`, including the portable
|
|
13
|
+
`ready` boolean, and a semantic snapshot helper for `observe.semanticSnapshot`
|
|
14
|
+
so agents can work from normalized roles, selectors, bounds, and recommended
|
|
15
|
+
actions instead of raw platform hierarchy classes.
|
|
16
|
+
The TypeScript, Python, Go, and Rust clients expose the same core control
|
|
17
|
+
surface: session lifecycle, app launch/stop/link/state, snapshot and semantic
|
|
18
|
+
snapshot, tap/type/erase/hide-keyboard/swipe/back/scroll, waits, assertions,
|
|
19
|
+
trace event polling, and trace export.
|
|
20
|
+
Use the `assertHealthy`/`assert_healthy` helper after launches, links, and major
|
|
21
|
+
navigation steps to catch native crash overlays and development-client failures
|
|
22
|
+
without hand-maintaining negative selectors in every client.
|
|
23
|
+
|
|
24
|
+
For install commands across package managers, see
|
|
25
|
+
[docs/client-installation.md](../docs/client-installation.md).
|
|
26
|
+
|
|
27
|
+
## TypeScript
|
|
28
|
+
|
|
29
|
+
Runtime: `clients/typescript/index.mjs`
|
|
30
|
+
Types: `clients/typescript/index.d.ts`
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
node clients/typescript/examples/fake-session.mjs
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
import { createZmrClient } from "./clients/typescript/index.mjs";
|
|
38
|
+
|
|
39
|
+
const zmr = createZmrClient({
|
|
40
|
+
command: "zmr",
|
|
41
|
+
args: ["serve", "--transport", "stdio", "--config", ".zmr/config.json"],
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Python
|
|
46
|
+
|
|
47
|
+
Runtime: `clients/python/zmr_client.py`
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
python3 -m pip install "git+https://github.com/johnmikel/zig-mobile-runner.git#subdirectory=clients/python"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
python3 clients/python/examples/fake_session.py
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from zmr_client import ZmrClient
|
|
59
|
+
|
|
60
|
+
with ZmrClient("zmr", ["serve", "--transport", "stdio", "--config", ".zmr/config.json"]) as zmr:
|
|
61
|
+
snapshot = zmr.snapshot()
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Go
|
|
65
|
+
|
|
66
|
+
Runtime: `clients/go/zmr/client.go`
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
go get github.com/johnmikel/zig-mobile-runner/clients/go@main
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
go run ./clients/go/examples/fake-session \
|
|
74
|
+
--zmr ./zig-out/bin/zmr \
|
|
75
|
+
--adb ./tests/fake-adb.sh \
|
|
76
|
+
--trace-dir traces/demo-go-client
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
```go
|
|
80
|
+
client, err := zmr.Start(ctx, "zmr", "serve", "--transport", "stdio", "--config", ".zmr/config.json")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Rust
|
|
84
|
+
|
|
85
|
+
Runtime: `clients/rust/src/lib.rs`
|
|
86
|
+
|
|
87
|
+
Cargo packages library code from `src/lib.rs` by convention. Because this repo
|
|
88
|
+
is not yet published as a Rust crate, consume the client from a local checkout:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
git submodule add https://github.com/johnmikel/zig-mobile-runner.git vendor/zig-mobile-runner
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
```toml
|
|
95
|
+
[dependencies]
|
|
96
|
+
zmr-client = { path = "vendor/zig-mobile-runner/clients/rust" }
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
cargo run --manifest-path clients/rust/Cargo.toml --example fake_session -- \
|
|
101
|
+
--zmr ./zig-out/bin/zmr \
|
|
102
|
+
--adb ./tests/fake-adb.sh \
|
|
103
|
+
--trace-dir traces/demo-rust-client
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
```rust
|
|
107
|
+
let mut client = zmr_client::Client::start("zmr", ["serve", "--transport", "stdio", "--config", ".zmr/config.json"])?;
|
|
108
|
+
let snapshot = client.snapshot()?;
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Swift
|
|
112
|
+
|
|
113
|
+
Runtime: `clients/swift/Sources/ZMRClient/ZMRClient.swift`
|
|
114
|
+
|
|
115
|
+
Use the Swift client from a local SwiftPM package path until it is published as
|
|
116
|
+
a standalone Swift package:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
git submodule add https://github.com/johnmikel/zig-mobile-runner.git vendor/zig-mobile-runner
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```swift
|
|
123
|
+
.package(path: "vendor/zig-mobile-runner/clients/swift")
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Swift is useful for macOS host-side automation next to iOS app code. It is not
|
|
127
|
+
an SDK embedded in the app under test.
|
|
128
|
+
|
|
129
|
+
## Kotlin
|
|
130
|
+
|
|
131
|
+
Runtime: `clients/kotlin/src/main/kotlin/dev/zmr/ZmrClient.kt`
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
git submodule add https://github.com/johnmikel/zig-mobile-runner.git vendor/zig-mobile-runner
|
|
135
|
+
gradle -p vendor/zig-mobile-runner/clients/kotlin build
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Kotlin is useful for Android teams that want host-side orchestration in Kotlin.
|
|
139
|
+
It still drives the external `zmr` binary.
|
|
140
|
+
|
|
141
|
+
Rust has `src/lib.rs` because Cargo packages libraries from that path by
|
|
142
|
+
convention. The other clients use the equivalent idiomatic layout for their
|
|
143
|
+
ecosystem: ESM entry file for TypeScript, a pip-installable Python module, a Go
|
|
144
|
+
package directory, a SwiftPM package, and a Gradle/Kotlin source package.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# ZMR Go Client
|
|
2
|
+
|
|
3
|
+
Small standard-library JSON-RPC client for driving `zmr serve --transport stdio`
|
|
4
|
+
from Go agents and test harnesses.
|
|
5
|
+
|
|
6
|
+
```go
|
|
7
|
+
client, err := zmr.Start(ctx, "zmr", "serve", "--transport", "stdio")
|
|
8
|
+
if err != nil {
|
|
9
|
+
panic(err)
|
|
10
|
+
}
|
|
11
|
+
defer client.Close()
|
|
12
|
+
|
|
13
|
+
snapshot, err := client.Snapshot(ctx)
|
|
14
|
+
healthy, err := client.AssertHealthy(ctx, 1000)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Run the fake-session example from the repository root:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
go run ./clients/go/examples/fake-session \
|
|
21
|
+
--zmr ./zig-out/bin/zmr \
|
|
22
|
+
--adb ./tests/fake-adb.sh \
|
|
23
|
+
--trace-dir traces/demo-go-client
|
|
24
|
+
```
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"encoding/json"
|
|
6
|
+
"flag"
|
|
7
|
+
"fmt"
|
|
8
|
+
"os"
|
|
9
|
+
"time"
|
|
10
|
+
|
|
11
|
+
"github.com/johnmikel/zig-mobile-runner/clients/go/zmr"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
func main() {
|
|
15
|
+
zmrBin := flag.String("zmr", "zig-out/bin/zmr", "zmr executable")
|
|
16
|
+
adb := flag.String("adb", "tests/fake-adb.sh", "adb executable or fake adb script")
|
|
17
|
+
device := flag.String("device", "fake-android-1", "device serial")
|
|
18
|
+
appID := flag.String("app-id", "com.example.mobiletest", "app id")
|
|
19
|
+
traceDir := flag.String("trace-dir", "traces/demo-go-client", "trace directory")
|
|
20
|
+
traceOut := flag.String("trace-out", "traces/demo-go-client-redacted.zmrtrace", "trace export path")
|
|
21
|
+
flag.Parse()
|
|
22
|
+
|
|
23
|
+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
24
|
+
defer cancel()
|
|
25
|
+
|
|
26
|
+
client, err := zmr.Start(ctx,
|
|
27
|
+
*zmrBin,
|
|
28
|
+
"serve",
|
|
29
|
+
"--transport", "stdio",
|
|
30
|
+
"--device", *device,
|
|
31
|
+
"--app-id", *appID,
|
|
32
|
+
"--adb", *adb,
|
|
33
|
+
"--trace-dir", *traceDir,
|
|
34
|
+
)
|
|
35
|
+
if err != nil {
|
|
36
|
+
fail(err)
|
|
37
|
+
}
|
|
38
|
+
defer client.Close()
|
|
39
|
+
|
|
40
|
+
capabilities, err := client.Capabilities(ctx)
|
|
41
|
+
if err != nil {
|
|
42
|
+
fail(err)
|
|
43
|
+
}
|
|
44
|
+
if _, err := client.CreateSession(ctx); err != nil {
|
|
45
|
+
fail(err)
|
|
46
|
+
}
|
|
47
|
+
if _, err := client.OpenLink(ctx, "exampleapp://go-client"); err != nil {
|
|
48
|
+
fail(err)
|
|
49
|
+
}
|
|
50
|
+
if _, err := client.WaitUntil(ctx, map[string]interface{}{"text": "Dashboard"}, 1000); err != nil {
|
|
51
|
+
fail(err)
|
|
52
|
+
}
|
|
53
|
+
if _, err := client.Tap(ctx, map[string]interface{}{"text": "Sign in"}); err != nil {
|
|
54
|
+
fail(err)
|
|
55
|
+
}
|
|
56
|
+
if _, err := client.TypeText(ctx, "agent@example.com", map[string]interface{}{"resourceId": "email-login-email-input"}); err != nil {
|
|
57
|
+
fail(err)
|
|
58
|
+
}
|
|
59
|
+
if _, err := client.AssertNotVisible(ctx, map[string]interface{}{"text": "Application has crashed"}, 100); err != nil {
|
|
60
|
+
fail(err)
|
|
61
|
+
}
|
|
62
|
+
if _, err := client.AssertHealthy(ctx, 100); err != nil {
|
|
63
|
+
fail(err)
|
|
64
|
+
}
|
|
65
|
+
snapshot, err := client.Snapshot(ctx)
|
|
66
|
+
if err != nil {
|
|
67
|
+
fail(err)
|
|
68
|
+
}
|
|
69
|
+
exported, err := client.ExportTrace(ctx, *traceOut, true, true)
|
|
70
|
+
if err != nil {
|
|
71
|
+
fail(err)
|
|
72
|
+
}
|
|
73
|
+
events, err := client.TraceEvents(ctx, 0, 10)
|
|
74
|
+
if err != nil {
|
|
75
|
+
fail(err)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
summary := map[string]interface{}{
|
|
79
|
+
"protocolVersion": capabilities.ProtocolVersion,
|
|
80
|
+
"activePackage": snapshot.ActivePackage,
|
|
81
|
+
"nodes": len(snapshot.Nodes),
|
|
82
|
+
"events": events.NextSeq,
|
|
83
|
+
"traceDir": *traceDir,
|
|
84
|
+
"traceOut": exported.Out,
|
|
85
|
+
}
|
|
86
|
+
encoded, _ := json.Marshal(summary)
|
|
87
|
+
fmt.Println(string(encoded))
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
func fail(err error) {
|
|
91
|
+
fmt.Fprintln(os.Stderr, err)
|
|
92
|
+
os.Exit(1)
|
|
93
|
+
}
|