zeno-mobile-runner 0.1.2
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 +497 -0
- package/CONTRIBUTING.md +42 -0
- package/FEATURES.md +111 -0
- package/LICENSE +21 -0
- package/README.md +176 -0
- package/SECURITY.md +34 -0
- package/build.zig +38 -0
- package/build.zig.zon +7 -0
- package/clients/README.md +149 -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 +154 -0
- package/docs/app-integration.md +330 -0
- package/docs/benchmarking.md +273 -0
- package/docs/client-installation.md +133 -0
- package/docs/clients.md +98 -0
- package/docs/config.md +175 -0
- package/docs/demo.md +259 -0
- package/docs/frameworks.md +72 -0
- package/docs/install.md +95 -0
- package/docs/npm.md +356 -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/scenario-authoring.md +88 -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 +118 -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 +518 -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 +131 -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,176 @@
|
|
|
1
|
+
# Zeno Mobile Runner
|
|
2
|
+
|
|
3
|
+
> Agent-native mobile UI automation for React Native, Expo, Flutter, and native Android/iOS apps.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/johnmikel/zeno-mobile-runner/actions/workflows/ci.yml)
|
|
6
|
+
[](https://github.com/johnmikel/zeno-mobile-runner/releases)
|
|
7
|
+
[](https://www.npmjs.com/package/zeno-mobile-runner)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
|
|
10
|
+
ZMR gives AI agents and test harnesses a typed mobile control plane. It can
|
|
11
|
+
install and launch apps, observe the UI, choose an action, wait for the screen to
|
|
12
|
+
settle, assert state, and export a replayable trace. The runner does not embed an
|
|
13
|
+
LLM. Agents stay outside and use ZMR through CLI JSON, scenarios, JSON-RPC, MCP,
|
|
14
|
+
or optional protocol clients.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
Inside a mobile app repo:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install --save-dev zeno-mobile-runner
|
|
22
|
+
npx zmr-wizard --app-id com.example.mobiletest --package-json
|
|
23
|
+
npx zmr doctor --strict --json --config .zmr/config.json
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Run a generated smoke scenario:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm run zmr:validate
|
|
30
|
+
npm run zmr:android
|
|
31
|
+
npm run zmr:ios
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## React Native, Expo, and Flutter
|
|
35
|
+
|
|
36
|
+
ZMR works below the JavaScript or Dart framework layer. It drives the installed
|
|
37
|
+
Android or iOS app through platform lifecycle commands, deep links, accessibility
|
|
38
|
+
semantics, screenshots, logs, selector actions, waits, assertions, and traces.
|
|
39
|
+
|
|
40
|
+
- **React Native:** prefer `testID`, `accessibilityLabel`, stable text, and deep
|
|
41
|
+
links for direct navigation into important states.
|
|
42
|
+
- **Expo development builds:** pass `--expo-dev-client-scheme <scheme>` to the
|
|
43
|
+
wizard so ZMR scaffolds dev-client open-link scenarios.
|
|
44
|
+
- **Flutter:** ZMR supports Flutter apps at the Android and iOS app level when
|
|
45
|
+
the app exposes stable semantics labels, text, deep links, or native ids. It is
|
|
46
|
+
not a Flutter widget-tree driver and does not inspect Flutter internals.
|
|
47
|
+
- **Native Android/iOS:** use resource ids, content descriptions, accessibility
|
|
48
|
+
identifiers, XCTest labels, and app-owned deep links.
|
|
49
|
+
|
|
50
|
+
See [docs/frameworks.md](docs/frameworks.md) and
|
|
51
|
+
[docs/app-integration.md](docs/app-integration.md) for app-side setup guidance.
|
|
52
|
+
|
|
53
|
+
## Why ZMR
|
|
54
|
+
|
|
55
|
+
- **Agent-native protocol:** structured snapshots, semantic mobile trees,
|
|
56
|
+
actions, waits, assertions, live trace events, and redacted trace export over
|
|
57
|
+
JSON-RPC or MCP.
|
|
58
|
+
- **Trace-first debugging:** every run can produce screenshots, UI trees, logs,
|
|
59
|
+
timings, action inputs, assertion results, and an HTML report.
|
|
60
|
+
- **Fast local core:** Zig owns orchestration, subprocess control, selectors,
|
|
61
|
+
waits, retries, scenario execution, and packaged binaries.
|
|
62
|
+
- **App-local setup:** `.zmr/config.json`, smoke scenarios, shim commands, and
|
|
63
|
+
traces live in the app repo.
|
|
64
|
+
- **Android and iOS:** Android uses ADB/UI Automator plus an optional native
|
|
65
|
+
shim. iOS simulators use `simctl`; physical iOS devices use `devicectl`;
|
|
66
|
+
selector-grade iOS automation uses the XCTest/XCUIAutomation shim.
|
|
67
|
+
|
|
68
|
+
## Scenario Example
|
|
69
|
+
|
|
70
|
+
ZMR scenarios are JSON so agents and build scripts can generate, validate, and
|
|
71
|
+
mutate them without a second DSL.
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"name": "Login smoke",
|
|
76
|
+
"appId": "com.example.mobiletest",
|
|
77
|
+
"steps": [
|
|
78
|
+
{ "action": "launch" },
|
|
79
|
+
{ "action": "assertHealthy", "timeoutMs": 5000 },
|
|
80
|
+
{ "action": "tap", "selector": { "resourceId": "email" } },
|
|
81
|
+
{ "action": "typeText", "text": "user@example.com" },
|
|
82
|
+
{ "action": "tap", "selector": { "resourceId": "password" } },
|
|
83
|
+
{ "action": "typeText", "text": "password" },
|
|
84
|
+
{ "action": "tap", "selector": { "text": "Login" } },
|
|
85
|
+
{ "action": "waitVisible", "selector": { "text": "Welcome" }, "timeoutMs": 30000 }
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Useful commands:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
zmr version --json
|
|
94
|
+
zmr schemas --json
|
|
95
|
+
zmr devices --json
|
|
96
|
+
zmr init --app --json --dir . --app-id com.example.mobiletest
|
|
97
|
+
zmr validate --json .zmr/login-smoke.json
|
|
98
|
+
zmr run .zmr/login-smoke.json --json --trace-dir traces/login-smoke
|
|
99
|
+
zmr explain --json traces/login-smoke
|
|
100
|
+
zmr import flow-yaml .zmr/legacy-flow.yaml --out .zmr/legacy-flow.json
|
|
101
|
+
zmr export traces/login-smoke --out traces/login-smoke-redacted.zmrtrace --redact
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
See [docs/scenario-authoring.md](docs/scenario-authoring.md) for selector and
|
|
105
|
+
wait guidance.
|
|
106
|
+
|
|
107
|
+
## Agent Workflow
|
|
108
|
+
|
|
109
|
+
Agents can use the CLI, JSON-RPC, or MCP surface. Start JSON-RPC over stdio:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
zmr serve --transport stdio --config .zmr/config.json --trace-dir traces/zmr-agent
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Agents that support the Model Context Protocol can use the native MCP surface:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
zmr mcp --config .zmr/config.json --trace-dir traces/zmr-agent
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The MCP server exposes mobile-specific tools such as `semantic_snapshot`, `tap`,
|
|
122
|
+
`type`, `wait_visible`, `trace_events`, and `trace_export`.
|
|
123
|
+
|
|
124
|
+
## Optional Protocol Clients
|
|
125
|
+
|
|
126
|
+
Clients are thin wrappers around `zmr serve --transport stdio`. They do not
|
|
127
|
+
replace the runner; they make it easier for agents and test code to call the
|
|
128
|
+
same JSON-RPC protocol.
|
|
129
|
+
|
|
130
|
+
TypeScript and Python are the most common starting points for app teams and
|
|
131
|
+
agent harnesses. Go, Rust, Swift, and Kotlin clients are reference integrations
|
|
132
|
+
for teams that want to embed the protocol from those ecosystems.
|
|
133
|
+
|
|
134
|
+
| Language | Entry point | Example |
|
|
135
|
+
| --- | --- | --- |
|
|
136
|
+
| TypeScript | `clients/typescript/index.mjs` + `index.d.ts` | `node clients/typescript/examples/fake-session.mjs` |
|
|
137
|
+
| Python | `clients/python/zmr_client.py` + `pyproject.toml` | `python3 clients/python/examples/fake_session.py` |
|
|
138
|
+
| Go | `clients/go/zmr/client.go` | `go run ./clients/go/examples/fake-session` |
|
|
139
|
+
| Rust | `clients/rust/src/lib.rs` | `cargo run --manifest-path clients/rust/Cargo.toml --example fake_session` |
|
|
140
|
+
| Swift | `clients/swift/Sources/ZMRClient` | `swift build --package-path clients/swift` |
|
|
141
|
+
| Kotlin | `clients/kotlin/src/main/kotlin/dev/zmr` | `gradle -p clients/kotlin build` |
|
|
142
|
+
|
|
143
|
+
See [clients/README.md](clients/README.md), [docs/clients.md](docs/clients.md),
|
|
144
|
+
and [docs/client-installation.md](docs/client-installation.md).
|
|
145
|
+
|
|
146
|
+
## Platform Support
|
|
147
|
+
|
|
148
|
+
| Target | Status | Notes |
|
|
149
|
+
| --- | --- | --- |
|
|
150
|
+
| Android emulator | Supported | ADB/UI Automator, optional Android shim, emulator lifecycle helpers |
|
|
151
|
+
| Android physical device | Supported | Requires ADB connection and app build/install surface |
|
|
152
|
+
| iOS simulator | Supported | `simctl` plus app-local XCTest/XCUIAutomation shim for native selector actions, native waits, and bounded snapshots |
|
|
153
|
+
| iOS physical device | Supported, validate locally | `devicectl` lifecycle plus app-local XCTest/XCUIAutomation shim; run pilots on your own app/device before relying on it in CI |
|
|
154
|
+
| Cloud device farms | Not included | ZMR is focused on local and self-managed device targets in this preview |
|
|
155
|
+
|
|
156
|
+
Current release: `0.1.2` developer preview. Protocol version:
|
|
157
|
+
`2026-04-28`.
|
|
158
|
+
|
|
159
|
+
## Documentation
|
|
160
|
+
|
|
161
|
+
- [FEATURES.md](FEATURES.md): complete feature list and limitations
|
|
162
|
+
- [docs/install.md](docs/install.md): source, npm, Homebrew, and app setup
|
|
163
|
+
- [docs/frameworks.md](docs/frameworks.md): React Native, Expo, Flutter, and native app guidance
|
|
164
|
+
- [docs/app-integration.md](docs/app-integration.md): app-side Android/iOS shims
|
|
165
|
+
- [docs/scenario-authoring.md](docs/scenario-authoring.md): selectors, waits, and scenario design
|
|
166
|
+
- [docs/protocol.md](docs/protocol.md): JSON-RPC methods and schemas
|
|
167
|
+
- [docs/ai-agents.md](docs/ai-agents.md): JSON-RPC and MCP agent workflows
|
|
168
|
+
- [docs/clients.md](docs/clients.md): language client guide
|
|
169
|
+
- [docs/client-installation.md](docs/client-installation.md): npm, Homebrew, TS, Python, Go, Rust, Swift, and Kotlin setup
|
|
170
|
+
- [docs/trace-privacy.md](docs/trace-privacy.md): safe trace export
|
|
171
|
+
- [docs/troubleshooting.md](docs/troubleshooting.md): common setup and runtime issues
|
|
172
|
+
- [skills/zmr-mobile-testing/SKILL.md](skills/zmr-mobile-testing/SKILL.md): reusable agent skill
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
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,149 @@
|
|
|
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. TypeScript and
|
|
12
|
+
Python are the most direct starting points for app and agent automation. Go,
|
|
13
|
+
Rust, Swift, and Kotlin stay available as reference integrations for teams that
|
|
14
|
+
want to embed host-side orchestration in those ecosystems.
|
|
15
|
+
Each client includes `devices()` for `device.list`, including the portable
|
|
16
|
+
`ready` boolean, and a semantic snapshot helper for `observe.semanticSnapshot`
|
|
17
|
+
so agents can work from normalized roles, selectors, bounds, and recommended
|
|
18
|
+
actions instead of raw platform hierarchy classes.
|
|
19
|
+
The TypeScript and Python clients expose the broadest app-facing control
|
|
20
|
+
surface: session lifecycle, app launch/stop/link/state, snapshot and semantic
|
|
21
|
+
snapshot, tap/type/erase/hide-keyboard/swipe/back/scroll, waits, assertions,
|
|
22
|
+
trace event polling, and trace export. The Go, Rust, Swift, and Kotlin clients
|
|
23
|
+
show the same protocol shape in other host languages and are useful starting
|
|
24
|
+
points for custom integration work.
|
|
25
|
+
Use the `assertHealthy`/`assert_healthy` helper after launches, links, and major
|
|
26
|
+
navigation steps to catch native crash overlays and development-client failures
|
|
27
|
+
without hand-maintaining negative selectors in every client.
|
|
28
|
+
|
|
29
|
+
For install commands across package managers, see
|
|
30
|
+
[docs/client-installation.md](../docs/client-installation.md).
|
|
31
|
+
|
|
32
|
+
## TypeScript
|
|
33
|
+
|
|
34
|
+
Runtime: `clients/typescript/index.mjs`
|
|
35
|
+
Types: `clients/typescript/index.d.ts`
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
node clients/typescript/examples/fake-session.mjs
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
import { createZmrClient } from "./clients/typescript/index.mjs";
|
|
43
|
+
|
|
44
|
+
const zmr = createZmrClient({
|
|
45
|
+
command: "zmr",
|
|
46
|
+
args: ["serve", "--transport", "stdio", "--config", ".zmr/config.json"],
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Python
|
|
51
|
+
|
|
52
|
+
Runtime: `clients/python/zmr_client.py`
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
python3 -m pip install "git+https://github.com/johnmikel/zeno-mobile-runner.git#subdirectory=clients/python"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
python3 clients/python/examples/fake_session.py
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from zmr_client import ZmrClient
|
|
64
|
+
|
|
65
|
+
with ZmrClient("zmr", ["serve", "--transport", "stdio", "--config", ".zmr/config.json"]) as zmr:
|
|
66
|
+
snapshot = zmr.snapshot()
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Go
|
|
70
|
+
|
|
71
|
+
Runtime: `clients/go/zmr/client.go`
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
go get github.com/johnmikel/zeno-mobile-runner/clients/go@main
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
go run ./clients/go/examples/fake-session \
|
|
79
|
+
--zmr ./zig-out/bin/zmr \
|
|
80
|
+
--adb ./tests/fake-adb.sh \
|
|
81
|
+
--trace-dir traces/demo-go-client
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
```go
|
|
85
|
+
client, err := zmr.Start(ctx, "zmr", "serve", "--transport", "stdio", "--config", ".zmr/config.json")
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Rust
|
|
89
|
+
|
|
90
|
+
Runtime: `clients/rust/src/lib.rs`
|
|
91
|
+
|
|
92
|
+
Cargo packages library code from `src/lib.rs` by convention. Because this repo
|
|
93
|
+
is not yet published as a Rust crate, consume the client from a local checkout:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
git submodule add https://github.com/johnmikel/zeno-mobile-runner.git vendor/zeno-mobile-runner
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
```toml
|
|
100
|
+
[dependencies]
|
|
101
|
+
zmr-client = { path = "vendor/zeno-mobile-runner/clients/rust" }
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
cargo run --manifest-path clients/rust/Cargo.toml --example fake_session -- \
|
|
106
|
+
--zmr ./zig-out/bin/zmr \
|
|
107
|
+
--adb ./tests/fake-adb.sh \
|
|
108
|
+
--trace-dir traces/demo-rust-client
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
```rust
|
|
112
|
+
let mut client = zmr_client::Client::start("zmr", ["serve", "--transport", "stdio", "--config", ".zmr/config.json"])?;
|
|
113
|
+
let snapshot = client.snapshot()?;
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Swift
|
|
117
|
+
|
|
118
|
+
Runtime: `clients/swift/Sources/ZMRClient/ZMRClient.swift`
|
|
119
|
+
|
|
120
|
+
Use the Swift client from a local SwiftPM package path until it is published as
|
|
121
|
+
a standalone Swift package:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
git submodule add https://github.com/johnmikel/zeno-mobile-runner.git vendor/zeno-mobile-runner
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
```swift
|
|
128
|
+
.package(path: "vendor/zeno-mobile-runner/clients/swift")
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Swift is useful for macOS host-side automation next to iOS app code. It is not
|
|
132
|
+
an SDK embedded in the app under test.
|
|
133
|
+
|
|
134
|
+
## Kotlin
|
|
135
|
+
|
|
136
|
+
Runtime: `clients/kotlin/src/main/kotlin/dev/zmr/ZmrClient.kt`
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
git submodule add https://github.com/johnmikel/zeno-mobile-runner.git vendor/zeno-mobile-runner
|
|
140
|
+
gradle -p vendor/zeno-mobile-runner/clients/kotlin build
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Kotlin is useful for Android teams that want host-side orchestration in Kotlin.
|
|
144
|
+
It still drives the external `zmr` binary.
|
|
145
|
+
|
|
146
|
+
Rust has `src/lib.rs` because Cargo packages libraries from that path by
|
|
147
|
+
convention. The other clients use the equivalent idiomatic layout for their
|
|
148
|
+
ecosystem: ESM entry file for TypeScript, a pip-installable Python module, a Go
|
|
149
|
+
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/zeno-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
|
+
}
|