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.
Files changed (213) hide show
  1. package/CHANGELOG.md +497 -0
  2. package/CONTRIBUTING.md +42 -0
  3. package/FEATURES.md +111 -0
  4. package/LICENSE +21 -0
  5. package/README.md +176 -0
  6. package/SECURITY.md +34 -0
  7. package/build.zig +38 -0
  8. package/build.zig.zon +7 -0
  9. package/clients/README.md +149 -0
  10. package/clients/go/README.md +24 -0
  11. package/clients/go/examples/fake-session/main.go +93 -0
  12. package/clients/go/go.mod +3 -0
  13. package/clients/go/zmr/client.go +432 -0
  14. package/clients/kotlin/README.md +35 -0
  15. package/clients/kotlin/build.gradle.kts +35 -0
  16. package/clients/kotlin/settings.gradle.kts +15 -0
  17. package/clients/kotlin/src/main/kotlin/dev/zmr/FakeSession.kt +86 -0
  18. package/clients/kotlin/src/main/kotlin/dev/zmr/ZmrClient.kt +67 -0
  19. package/clients/python/README.md +29 -0
  20. package/clients/python/examples/fake_session.py +48 -0
  21. package/clients/python/pyproject.toml +13 -0
  22. package/clients/python/zmr_client.py +202 -0
  23. package/clients/rust/Cargo.lock +107 -0
  24. package/clients/rust/Cargo.toml +10 -0
  25. package/clients/rust/README.md +19 -0
  26. package/clients/rust/examples/fake_session.rs +70 -0
  27. package/clients/rust/src/lib.rs +461 -0
  28. package/clients/swift/Package.swift +16 -0
  29. package/clients/swift/README.md +36 -0
  30. package/clients/swift/Sources/ZMRClient/ZMRClient.swift +114 -0
  31. package/clients/swift/Sources/ZMRFakeSession/main.swift +86 -0
  32. package/clients/typescript/README.md +34 -0
  33. package/clients/typescript/examples/fake-session.mjs +36 -0
  34. package/clients/typescript/index.d.ts +144 -0
  35. package/clients/typescript/index.mjs +192 -0
  36. package/clients/typescript/package.json +8 -0
  37. package/docs/adr/0001-agent-native-runner-boundary.md +31 -0
  38. package/docs/adr/0002-app-local-zmr-contract.md +39 -0
  39. package/docs/adr/0003-ios-simulator-xctest-shim.md +41 -0
  40. package/docs/adr/0004-benchmark-claims-and-baseline-collection.md +37 -0
  41. package/docs/adr/README.md +12 -0
  42. package/docs/ai-agents.md +154 -0
  43. package/docs/app-integration.md +330 -0
  44. package/docs/benchmarking.md +273 -0
  45. package/docs/client-installation.md +133 -0
  46. package/docs/clients.md +98 -0
  47. package/docs/config.md +175 -0
  48. package/docs/demo.md +259 -0
  49. package/docs/frameworks.md +72 -0
  50. package/docs/install.md +95 -0
  51. package/docs/npm.md +356 -0
  52. package/docs/protocol-fixtures/README.md +8 -0
  53. package/docs/protocol-fixtures/core-session.requests.jsonl +8 -0
  54. package/docs/protocol-fixtures/core-session.responses.jsonl +8 -0
  55. package/docs/protocol-versioning.md +65 -0
  56. package/docs/protocol.md +560 -0
  57. package/docs/scenario-authoring.md +88 -0
  58. package/docs/trace-privacy.md +88 -0
  59. package/docs/troubleshooting.md +256 -0
  60. package/examples/android-app-auth-probe.json +89 -0
  61. package/examples/android-app-error-state.json +13 -0
  62. package/examples/android-app-login-smoke.json +192 -0
  63. package/examples/android-app-onboarding.json +12 -0
  64. package/examples/android-app-referral-deep-link.json +12 -0
  65. package/examples/android-shim-smoke.json +19 -0
  66. package/examples/demo-failure.json +12 -0
  67. package/examples/demo-fake.json +14 -0
  68. package/examples/ios-dev-client-open-link.json +26 -0
  69. package/examples/ios-dev-client-route-snapshot.json +24 -0
  70. package/examples/ios-shim-smoke.json +23 -0
  71. package/examples/ios-smoke.json +9 -0
  72. package/go.work +3 -0
  73. package/npm/agents.mjs +183 -0
  74. package/npm/app-config.mjs +95 -0
  75. package/npm/build-zmr.mjs +21 -0
  76. package/npm/commands.mjs +104 -0
  77. package/npm/generated-files.mjs +50 -0
  78. package/npm/index.mjs +75 -0
  79. package/npm/init-app.mjs +80 -0
  80. package/npm/package-scripts.mjs +72 -0
  81. package/npm/postinstall.mjs +21 -0
  82. package/npm/scaffold.mjs +179 -0
  83. package/npm/scenarios.mjs +93 -0
  84. package/npm/setup.mjs +69 -0
  85. package/npm/wizard.mjs +117 -0
  86. package/npm/zmr.mjs +23 -0
  87. package/package.json +118 -0
  88. package/schemas/README.md +26 -0
  89. package/schemas/action-result.schema.json +27 -0
  90. package/schemas/capabilities-output.schema.json +98 -0
  91. package/schemas/devices-output.schema.json +25 -0
  92. package/schemas/doctor-output.schema.json +51 -0
  93. package/schemas/explain-output.schema.json +51 -0
  94. package/schemas/import-output.schema.json +23 -0
  95. package/schemas/init-output.schema.json +71 -0
  96. package/schemas/json-rpc.schema.json +55 -0
  97. package/schemas/release-manifest.schema.json +43 -0
  98. package/schemas/release-readiness-output.schema.json +127 -0
  99. package/schemas/run-output.schema.json +43 -0
  100. package/schemas/scenario.schema.json +128 -0
  101. package/schemas/schemas-output.schema.json +26 -0
  102. package/schemas/semantic-snapshot.schema.json +116 -0
  103. package/schemas/snapshot.schema.json +60 -0
  104. package/schemas/trace-event.schema.json +14 -0
  105. package/schemas/trace-manifest.schema.json +59 -0
  106. package/schemas/validate-output.schema.json +42 -0
  107. package/schemas/version-output.schema.json +23 -0
  108. package/schemas/zmr-config.schema.json +75 -0
  109. package/scripts/android-emulator.sh +126 -0
  110. package/scripts/assert-ios-physical-ready.sh +213 -0
  111. package/scripts/benchmark-command.sh +307 -0
  112. package/scripts/benchmark.sh +359 -0
  113. package/scripts/benchmark_gate.py +117 -0
  114. package/scripts/benchmark_result_row.py +88 -0
  115. package/scripts/compare-benchmarks.py +288 -0
  116. package/scripts/create-android-demo-app.sh +342 -0
  117. package/scripts/create-ios-demo-app.sh +261 -0
  118. package/scripts/demo-android-real.sh +232 -0
  119. package/scripts/demo-ios-real.sh +270 -0
  120. package/scripts/demo.sh +464 -0
  121. package/scripts/device-matrix.sh +338 -0
  122. package/scripts/ensure-ios-shim-target.rb +237 -0
  123. package/scripts/install-android-shim.sh +281 -0
  124. package/scripts/install-ios-shim.sh +589 -0
  125. package/scripts/pilot-gate.sh +560 -0
  126. package/scripts/release-readiness.py +838 -0
  127. package/scripts/release-readiness.sh +91 -0
  128. package/scripts/run-android-pilot.sh +561 -0
  129. package/scripts/run-ios-pilot.sh +509 -0
  130. package/shims/android/README.md +21 -0
  131. package/shims/android/ZMRShimInstrumentedTest.java +152 -0
  132. package/shims/android/protocol.md +18 -0
  133. package/shims/ios/README.md +50 -0
  134. package/shims/ios/ZMRShim.swift +110 -0
  135. package/shims/ios/ZMRShimUITestCase.swift +518 -0
  136. package/shims/ios/protocol.md +74 -0
  137. package/skills/zmr-mobile-testing/SKILL.md +127 -0
  138. package/src/android.zig +344 -0
  139. package/src/android_device_info.zig +99 -0
  140. package/src/android_emulator.zig +154 -0
  141. package/src/android_screen_recording.zig +112 -0
  142. package/src/android_shell.zig +112 -0
  143. package/src/bundle.zig +124 -0
  144. package/src/bundle_redaction.zig +272 -0
  145. package/src/bundle_tar.zig +123 -0
  146. package/src/cli_devices.zig +97 -0
  147. package/src/cli_doctor.zig +114 -0
  148. package/src/cli_import.zig +70 -0
  149. package/src/cli_info.zig +39 -0
  150. package/src/cli_init.zig +72 -0
  151. package/src/cli_output.zig +467 -0
  152. package/src/cli_run.zig +259 -0
  153. package/src/cli_serve.zig +287 -0
  154. package/src/cli_trace.zig +111 -0
  155. package/src/cli_validate.zig +41 -0
  156. package/src/command.zig +211 -0
  157. package/src/config.zig +305 -0
  158. package/src/config_diagnostics.zig +212 -0
  159. package/src/config_paths.zig +49 -0
  160. package/src/device_registry.zig +37 -0
  161. package/src/doctor.zig +412 -0
  162. package/src/doctor_hints.zig +52 -0
  163. package/src/errors.zig +55 -0
  164. package/src/fake_device.zig +163 -0
  165. package/src/health.zig +28 -0
  166. package/src/importer.zig +343 -0
  167. package/src/importer_json.zig +100 -0
  168. package/src/importer_model.zig +103 -0
  169. package/src/ios.zig +399 -0
  170. package/src/ios_devices.zig +219 -0
  171. package/src/ios_lifecycle.zig +72 -0
  172. package/src/ios_shim.zig +242 -0
  173. package/src/ios_snapshot.zig +20 -0
  174. package/src/json_fields.zig +80 -0
  175. package/src/json_rpc.zig +150 -0
  176. package/src/json_rpc_methods.zig +318 -0
  177. package/src/json_rpc_observation.zig +31 -0
  178. package/src/json_rpc_params.zig +52 -0
  179. package/src/json_rpc_protocol.zig +110 -0
  180. package/src/json_rpc_trace.zig +73 -0
  181. package/src/main.zig +131 -0
  182. package/src/mcp.zig +234 -0
  183. package/src/mcp_protocol.zig +64 -0
  184. package/src/mcp_trace.zig +83 -0
  185. package/src/report.zig +346 -0
  186. package/src/report_html.zig +63 -0
  187. package/src/report_values.zig +27 -0
  188. package/src/run_options.zig +152 -0
  189. package/src/runner.zig +280 -0
  190. package/src/runner_actions.zig +109 -0
  191. package/src/runner_config.zig +6 -0
  192. package/src/runner_diagnostics.zig +268 -0
  193. package/src/runner_events.zig +170 -0
  194. package/src/runner_native.zig +88 -0
  195. package/src/runner_waits.zig +300 -0
  196. package/src/scaffold.zig +472 -0
  197. package/src/scenario.zig +346 -0
  198. package/src/scenario_fields.zig +50 -0
  199. package/src/schema_registry.zig +53 -0
  200. package/src/selector.zig +84 -0
  201. package/src/semantic.zig +171 -0
  202. package/src/trace.zig +315 -0
  203. package/src/trace_json.zig +340 -0
  204. package/src/trace_summary.zig +218 -0
  205. package/src/trace_summary_diagnostic.zig +202 -0
  206. package/src/types.zig +120 -0
  207. package/src/uiautomator.zig +164 -0
  208. package/src/validation.zig +187 -0
  209. package/src/version.zig +22 -0
  210. package/viewer/app.js +373 -0
  211. package/viewer/index.html +126 -0
  212. package/viewer/parser.js +233 -0
  213. 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
+ [![CI](https://github.com/johnmikel/zeno-mobile-runner/actions/workflows/ci.yml/badge.svg)](https://github.com/johnmikel/zeno-mobile-runner/actions/workflows/ci.yml)
6
+ [![Release](https://img.shields.io/github/v/release/johnmikel/zeno-mobile-runner?include_prereleases)](https://github.com/johnmikel/zeno-mobile-runner/releases)
7
+ [![npm](https://img.shields.io/npm/v/zeno-mobile-runner)](https://www.npmjs.com/package/zeno-mobile-runner)
8
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](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,7 @@
1
+ .{
2
+ .name = .zig_mobile_runner,
3
+ .version = "0.0.1",
4
+ .minimum_zig_version = "0.15.2",
5
+ .paths = .{""},
6
+ .fingerprint = 0x5a9670ea13a33fab,
7
+ }
@@ -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
+ }
@@ -0,0 +1,3 @@
1
+ module github.com/johnmikel/zeno-mobile-runner/clients/go
2
+
3
+ go 1.22