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/src/scaffold.zig
ADDED
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
|
|
3
|
+
pub const app_config_file = ".zmr/config.json";
|
|
4
|
+
pub const app_android_smoke_file = ".zmr/android-smoke.json";
|
|
5
|
+
pub const app_ios_smoke_file = ".zmr/ios-smoke.json";
|
|
6
|
+
pub const app_device_matrix_file = ".zmr/device-matrix.json";
|
|
7
|
+
pub const app_agents_file = ".zmr/AGENTS.md";
|
|
8
|
+
|
|
9
|
+
pub const app_created_files = [_][]const u8{
|
|
10
|
+
app_config_file,
|
|
11
|
+
app_android_smoke_file,
|
|
12
|
+
app_ios_smoke_file,
|
|
13
|
+
app_device_matrix_file,
|
|
14
|
+
app_agents_file,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
pub const app_script_names = [_][]const u8{
|
|
18
|
+
"doctor",
|
|
19
|
+
"schemas",
|
|
20
|
+
"validate",
|
|
21
|
+
"android",
|
|
22
|
+
"androidReport",
|
|
23
|
+
"androidReliability",
|
|
24
|
+
"ios",
|
|
25
|
+
"iosReport",
|
|
26
|
+
"iosReliability",
|
|
27
|
+
"matrix",
|
|
28
|
+
"pilotGate",
|
|
29
|
+
"readiness",
|
|
30
|
+
"serve",
|
|
31
|
+
"mcp",
|
|
32
|
+
"explain",
|
|
33
|
+
"exportTrace",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
pub fn writeStarterScenario(
|
|
37
|
+
allocator: std.mem.Allocator,
|
|
38
|
+
path: []const u8,
|
|
39
|
+
app_id: []const u8,
|
|
40
|
+
force: bool,
|
|
41
|
+
) !void {
|
|
42
|
+
if (!force) {
|
|
43
|
+
std.fs.cwd().access(path, .{}) catch |err| switch (err) {
|
|
44
|
+
error.FileNotFound => {},
|
|
45
|
+
else => return err,
|
|
46
|
+
};
|
|
47
|
+
if (std.fs.cwd().access(path, .{})) |_| return error.PathAlreadyExists else |err| switch (err) {
|
|
48
|
+
error.FileNotFound => {},
|
|
49
|
+
else => return err,
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (std.fs.path.dirname(path)) |parent| {
|
|
54
|
+
if (parent.len > 0) try std.fs.cwd().makePath(parent);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
var file = try std.fs.cwd().createFile(path, .{ .truncate = true });
|
|
58
|
+
defer file.close();
|
|
59
|
+
var buffer: [4096]u8 = undefined;
|
|
60
|
+
var file_writer = file.writer(&buffer);
|
|
61
|
+
const writer = &file_writer.interface;
|
|
62
|
+
try writer.writeAll(
|
|
63
|
+
\\{
|
|
64
|
+
\\ "name": "Starter mobile smoke",
|
|
65
|
+
\\ "appId": "
|
|
66
|
+
);
|
|
67
|
+
try writeJsonStringContent(writer, app_id);
|
|
68
|
+
try writer.writeAll(
|
|
69
|
+
\\",
|
|
70
|
+
\\ "steps": [
|
|
71
|
+
\\ { "action": "launch" },
|
|
72
|
+
\\ { "action": "assertHealthy" },
|
|
73
|
+
\\ { "action": "snapshot" }
|
|
74
|
+
\\ ]
|
|
75
|
+
\\}
|
|
76
|
+
\\
|
|
77
|
+
);
|
|
78
|
+
try writer.flush();
|
|
79
|
+
_ = allocator;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
pub fn writeAppScaffold(
|
|
83
|
+
allocator: std.mem.Allocator,
|
|
84
|
+
dir: []const u8,
|
|
85
|
+
app_id: []const u8,
|
|
86
|
+
force: bool,
|
|
87
|
+
) !void {
|
|
88
|
+
const zmr_dir = try std.fs.path.join(allocator, &.{ dir, ".zmr" });
|
|
89
|
+
defer allocator.free(zmr_dir);
|
|
90
|
+
try std.fs.cwd().makePath(zmr_dir);
|
|
91
|
+
|
|
92
|
+
const config_path = try std.fs.path.join(allocator, &.{ zmr_dir, appFileBasename(app_config_file) });
|
|
93
|
+
defer allocator.free(config_path);
|
|
94
|
+
const android_path = try std.fs.path.join(allocator, &.{ zmr_dir, appFileBasename(app_android_smoke_file) });
|
|
95
|
+
defer allocator.free(android_path);
|
|
96
|
+
const ios_path = try std.fs.path.join(allocator, &.{ zmr_dir, appFileBasename(app_ios_smoke_file) });
|
|
97
|
+
defer allocator.free(ios_path);
|
|
98
|
+
const matrix_path = try std.fs.path.join(allocator, &.{ zmr_dir, appFileBasename(app_device_matrix_file) });
|
|
99
|
+
defer allocator.free(matrix_path);
|
|
100
|
+
const agents_path = try std.fs.path.join(allocator, &.{ zmr_dir, appFileBasename(app_agents_file) });
|
|
101
|
+
defer allocator.free(agents_path);
|
|
102
|
+
|
|
103
|
+
try writeAppConfig(config_path, app_id, true);
|
|
104
|
+
try writePlatformSmoke(android_path, "Android smoke", app_id, force);
|
|
105
|
+
try writePlatformSmoke(ios_path, "iOS smoke", app_id, force);
|
|
106
|
+
try writeDeviceMatrix(matrix_path, app_id, true);
|
|
107
|
+
try writeAgentInstructions(agents_path, app_id, true);
|
|
108
|
+
try ensureTraceGitignore(allocator, dir);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
fn appFileBasename(path: []const u8) []const u8 {
|
|
112
|
+
return std.fs.path.basename(path);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
fn writeAppConfig(path: []const u8, app_id: []const u8, force: bool) !void {
|
|
116
|
+
var file = try createOutputFile(path, force);
|
|
117
|
+
defer file.close();
|
|
118
|
+
var buffer: [8192]u8 = undefined;
|
|
119
|
+
var file_writer = file.writer(&buffer);
|
|
120
|
+
const writer = &file_writer.interface;
|
|
121
|
+
try writer.writeAll(
|
|
122
|
+
\\{
|
|
123
|
+
\\ "schemaVersion": 1,
|
|
124
|
+
\\ "appId": "
|
|
125
|
+
);
|
|
126
|
+
try writeJsonStringContent(writer, app_id);
|
|
127
|
+
try writer.writeAll(
|
|
128
|
+
\\",
|
|
129
|
+
\\ "android": {
|
|
130
|
+
\\ "enabled": true,
|
|
131
|
+
\\ "defaultDevice": "emulator-5554",
|
|
132
|
+
\\ "smokeScenario": ".zmr/android-smoke.json",
|
|
133
|
+
\\ "traceDir": "traces/zmr-android"
|
|
134
|
+
\\ },
|
|
135
|
+
\\ "ios": {
|
|
136
|
+
\\ "enabled": true,
|
|
137
|
+
\\ "defaultDevice": "booted",
|
|
138
|
+
\\ "smokeScenario": ".zmr/ios-smoke.json",
|
|
139
|
+
\\ "traceDir": "traces/zmr-ios"
|
|
140
|
+
\\ },
|
|
141
|
+
\\ "artifacts": {
|
|
142
|
+
\\ "screenshots": true,
|
|
143
|
+
\\ "hierarchy": true,
|
|
144
|
+
\\ "logs": true,
|
|
145
|
+
\\ "screenRecording": false
|
|
146
|
+
\\ },
|
|
147
|
+
\\ "scripts": {
|
|
148
|
+
\\ "doctor": "zmr doctor --strict --json --config .zmr/config.json",
|
|
149
|
+
\\ "schemas": "zmr schemas --json",
|
|
150
|
+
\\ "validate": "zmr validate --json .zmr/android-smoke.json && zmr validate --json .zmr/ios-smoke.json",
|
|
151
|
+
\\ "android": "zmr run .zmr/android-smoke.json --device emulator-5554 --trace-dir traces/zmr-android",
|
|
152
|
+
\\ "androidReport": "zmr report traces/zmr-android --out traces/zmr-android/report.html",
|
|
153
|
+
\\ "androidReliability": "export ZMR_BIN=\"${ZMR_BIN:-zmr}\"; zmr-benchmark --zmr .zmr/android-smoke.json --device emulator-5554 --app-id
|
|
154
|
+
);
|
|
155
|
+
try writer.writeAll(" ");
|
|
156
|
+
try writeJsonShellArg(writer, app_id);
|
|
157
|
+
try writer.writeAll(
|
|
158
|
+
\\ --runs 20 --trace-root traces/zmr-android-reliability --min-pass-rate 100 --max-failures 0 --max-p95-ms 30000 && \"$ZMR_BIN\" report traces/zmr-android-reliability --out traces/zmr-android-reliability/report.html",
|
|
159
|
+
\\ "ios": "zmr run .zmr/ios-smoke.json --platform ios --device booted --trace-dir traces/zmr-ios",
|
|
160
|
+
\\ "iosReport": "zmr report traces/zmr-ios --out traces/zmr-ios/report.html",
|
|
161
|
+
\\ "iosReliability": "export ZMR_BIN=\"${ZMR_BIN:-zmr}\"; zmr-benchmark --zmr .zmr/ios-smoke.json --platform ios --device booted --app-id
|
|
162
|
+
);
|
|
163
|
+
try writer.writeAll(" ");
|
|
164
|
+
try writeJsonShellArg(writer, app_id);
|
|
165
|
+
try writer.writeAll(
|
|
166
|
+
\\ --xcrun xcrun --runs 20 --trace-root traces/zmr-ios-reliability --min-pass-rate 100 --max-failures 0 --max-p95-ms 45000 && \"$ZMR_BIN\" report traces/zmr-ios-reliability --out traces/zmr-ios-reliability/report.html",
|
|
167
|
+
\\ "matrix": "ZMR_BIN=${ZMR_BIN:-zmr} zmr-device-matrix --matrix .zmr/device-matrix.json --trace-root traces/zmr-matrix --min-pass-rate 100 --max-failures 0",
|
|
168
|
+
\\ "pilotGate": "zmr-pilot-gate --android --ios --android-app-root . --android-app-id
|
|
169
|
+
);
|
|
170
|
+
try writer.writeAll(" ");
|
|
171
|
+
try writeJsonShellArg(writer, app_id);
|
|
172
|
+
try writer.writeAll(
|
|
173
|
+
\\ --android-device emulator-5554 --ios-app-root . --ios-app-path ./build/Debug-iphonesimulator/Sample.app --ios-app-id
|
|
174
|
+
);
|
|
175
|
+
try writer.writeAll(" ");
|
|
176
|
+
try writeJsonShellArg(writer, app_id);
|
|
177
|
+
try writer.writeAll(
|
|
178
|
+
\\ --ios-device booted --runs 20 --min-pass-rate 100 --max-failures 0 --evidence-out traces/zmr-pilots/evidence.jsonl",
|
|
179
|
+
\\ "readiness": "zmr-release-readiness --evidence traces/zmr-pilots/evidence.jsonl --target production --json",
|
|
180
|
+
\\ "serve": "zmr serve --transport stdio --config .zmr/config.json --trace-dir traces/zmr-agent",
|
|
181
|
+
\\ "mcp": "zmr mcp --config .zmr/config.json --trace-dir traces/zmr-agent",
|
|
182
|
+
\\ "explain": "zmr explain traces/zmr-agent --json",
|
|
183
|
+
\\ "exportTrace": "zmr export traces/zmr-agent --out traces/zmr-agent-redacted.zmrtrace --redact"
|
|
184
|
+
\\ }
|
|
185
|
+
\\}
|
|
186
|
+
\\
|
|
187
|
+
);
|
|
188
|
+
try writer.flush();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
fn writeDeviceMatrix(path: []const u8, app_id: []const u8, force: bool) !void {
|
|
192
|
+
var file = try createOutputFile(path, force);
|
|
193
|
+
defer file.close();
|
|
194
|
+
var buffer: [8192]u8 = undefined;
|
|
195
|
+
var file_writer = file.writer(&buffer);
|
|
196
|
+
const writer = &file_writer.interface;
|
|
197
|
+
try writer.writeAll(
|
|
198
|
+
\\{
|
|
199
|
+
\\ "runs": 1,
|
|
200
|
+
\\ "appId": "
|
|
201
|
+
);
|
|
202
|
+
try writeJsonStringContent(writer, app_id);
|
|
203
|
+
try writer.writeAll(
|
|
204
|
+
\\",
|
|
205
|
+
\\ "devices": [
|
|
206
|
+
\\ {
|
|
207
|
+
\\ "name": "android-emulator",
|
|
208
|
+
\\ "platform": "android",
|
|
209
|
+
\\ "serial": "emulator-5554",
|
|
210
|
+
\\ "scenario": ".zmr/android-smoke.json",
|
|
211
|
+
\\ "adb": "adb"
|
|
212
|
+
\\ },
|
|
213
|
+
\\ {
|
|
214
|
+
\\ "name": "ios-simulator",
|
|
215
|
+
\\ "platform": "ios",
|
|
216
|
+
\\ "iosDeviceType": "simulator",
|
|
217
|
+
\\ "serial": "booted",
|
|
218
|
+
\\ "scenario": ".zmr/ios-smoke.json",
|
|
219
|
+
\\ "xcrun": "xcrun"
|
|
220
|
+
\\ }
|
|
221
|
+
\\ ]
|
|
222
|
+
\\}
|
|
223
|
+
\\
|
|
224
|
+
);
|
|
225
|
+
try writer.flush();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
fn writePlatformSmoke(path: []const u8, name: []const u8, app_id: []const u8, force: bool) !void {
|
|
229
|
+
if (!force and try pathExists(path)) return;
|
|
230
|
+
var file = try createOutputFile(path, force);
|
|
231
|
+
defer file.close();
|
|
232
|
+
var buffer: [4096]u8 = undefined;
|
|
233
|
+
var file_writer = file.writer(&buffer);
|
|
234
|
+
const writer = &file_writer.interface;
|
|
235
|
+
try writer.writeAll(
|
|
236
|
+
\\{
|
|
237
|
+
\\ "name": "
|
|
238
|
+
);
|
|
239
|
+
try writeJsonStringContent(writer, name);
|
|
240
|
+
try writer.writeAll(
|
|
241
|
+
\\",
|
|
242
|
+
\\ "appId": "
|
|
243
|
+
);
|
|
244
|
+
try writeJsonStringContent(writer, app_id);
|
|
245
|
+
try writer.writeAll(
|
|
246
|
+
\\",
|
|
247
|
+
\\ "steps": [
|
|
248
|
+
\\ { "action": "launch" },
|
|
249
|
+
\\ { "action": "assertHealthy" },
|
|
250
|
+
\\ { "action": "snapshot" }
|
|
251
|
+
\\ ]
|
|
252
|
+
\\}
|
|
253
|
+
\\
|
|
254
|
+
);
|
|
255
|
+
try writer.flush();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
fn pathExists(path: []const u8) !bool {
|
|
259
|
+
std.fs.cwd().access(path, .{}) catch |err| switch (err) {
|
|
260
|
+
error.FileNotFound => return false,
|
|
261
|
+
else => return err,
|
|
262
|
+
};
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
fn writeJsonShellArg(writer: anytype, value: []const u8) !void {
|
|
267
|
+
if (isShellSafe(value)) {
|
|
268
|
+
try writeJsonStringContent(writer, value);
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
try writer.writeAll("'");
|
|
272
|
+
for (value) |ch| {
|
|
273
|
+
if (ch == '\'') try writeJsonStringContent(writer, "'\\''") else try writeJsonStringContent(writer, &[_]u8{ch});
|
|
274
|
+
}
|
|
275
|
+
try writer.writeAll("'");
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
fn writeShellArg(writer: anytype, value: []const u8) !void {
|
|
279
|
+
if (isShellSafe(value)) {
|
|
280
|
+
try writer.writeAll(value);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
try writer.writeAll("'");
|
|
284
|
+
for (value) |ch| {
|
|
285
|
+
if (ch == '\'') try writer.writeAll("'\\''") else try writer.writeAll(&[_]u8{ch});
|
|
286
|
+
}
|
|
287
|
+
try writer.writeAll("'");
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
fn isShellSafe(value: []const u8) bool {
|
|
291
|
+
if (value.len == 0) return false;
|
|
292
|
+
for (value) |ch| {
|
|
293
|
+
switch (ch) {
|
|
294
|
+
'A'...'Z', 'a'...'z', '0'...'9', '_', '.', '/', ':', '=', '@', '%', '+', ',', '-' => {},
|
|
295
|
+
else => return false,
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
fn writeAgentInstructions(path: []const u8, app_id: []const u8, force: bool) !void {
|
|
302
|
+
var file = try createOutputFile(path, force);
|
|
303
|
+
defer file.close();
|
|
304
|
+
var buffer: [4096]u8 = undefined;
|
|
305
|
+
var file_writer = file.writer(&buffer);
|
|
306
|
+
const writer = &file_writer.interface;
|
|
307
|
+
try writer.writeAll(
|
|
308
|
+
\\# ZMR Agent Instructions
|
|
309
|
+
\\
|
|
310
|
+
\\App id: `
|
|
311
|
+
);
|
|
312
|
+
try writer.writeAll(app_id);
|
|
313
|
+
try writer.writeAll(
|
|
314
|
+
\\`
|
|
315
|
+
\\
|
|
316
|
+
\\Start from the app checkout. Keep generated scenarios and config under `.zmr/`, and write run output under `traces/`.
|
|
317
|
+
\\
|
|
318
|
+
\\## Setup Checks
|
|
319
|
+
\\
|
|
320
|
+
\\```bash
|
|
321
|
+
\\zmr doctor --strict --json --config .zmr/config.json
|
|
322
|
+
\\zmr schemas --json
|
|
323
|
+
\\zmr validate --json .zmr/android-smoke.json && zmr validate --json .zmr/ios-smoke.json
|
|
324
|
+
\\```
|
|
325
|
+
\\
|
|
326
|
+
\\## Interactive Agent Session
|
|
327
|
+
\\
|
|
328
|
+
\\```bash
|
|
329
|
+
\\zmr serve --transport stdio --config .zmr/config.json --trace-dir traces/zmr-agent
|
|
330
|
+
\\zmr mcp --config .zmr/config.json --trace-dir traces/zmr-agent
|
|
331
|
+
\\```
|
|
332
|
+
\\
|
|
333
|
+
\\Use `semantic_snapshot` before choosing tap or type actions. Prefer selectors from accessibility identifiers, resource ids, labels, or exact text before coordinates. Export redacted traces before sharing artifacts.
|
|
334
|
+
\\
|
|
335
|
+
\\## Failure Triage
|
|
336
|
+
\\
|
|
337
|
+
\\```bash
|
|
338
|
+
\\zmr explain traces/zmr-agent --json
|
|
339
|
+
\\```
|
|
340
|
+
\\
|
|
341
|
+
\\Use the JSON explanation before editing selectors. It includes the terminal status, partial visual-capture diagnostics, and the last useful failure context.
|
|
342
|
+
\\
|
|
343
|
+
\\## Trace Sharing
|
|
344
|
+
\\
|
|
345
|
+
\\```bash
|
|
346
|
+
\\zmr export traces/zmr-agent --out traces/zmr-agent-redacted.zmrtrace --redact
|
|
347
|
+
\\```
|
|
348
|
+
\\
|
|
349
|
+
\\Add `--omit-screenshots` when visual artifacts may contain sensitive data.
|
|
350
|
+
\\
|
|
351
|
+
\\## Direct Smoke Runs
|
|
352
|
+
\\
|
|
353
|
+
\\```bash
|
|
354
|
+
\\zmr run .zmr/android-smoke.json --device emulator-5554 --trace-dir traces/zmr-android
|
|
355
|
+
\\zmr report traces/zmr-android --out traces/zmr-android/report.html
|
|
356
|
+
\\export ZMR_BIN="${ZMR_BIN:-zmr}"; zmr-benchmark --zmr .zmr/android-smoke.json --device emulator-5554 --app-id
|
|
357
|
+
);
|
|
358
|
+
try writer.writeAll(" ");
|
|
359
|
+
try writeShellArg(writer, app_id);
|
|
360
|
+
try writer.writeAll(
|
|
361
|
+
\\ --runs 20 --trace-root traces/zmr-android-reliability --min-pass-rate 100 --max-failures 0 --max-p95-ms 30000 && "$ZMR_BIN" report traces/zmr-android-reliability --out traces/zmr-android-reliability/report.html
|
|
362
|
+
\\zmr run .zmr/ios-smoke.json --platform ios --device booted --trace-dir traces/zmr-ios
|
|
363
|
+
\\zmr report traces/zmr-ios --out traces/zmr-ios/report.html
|
|
364
|
+
\\export ZMR_BIN="${ZMR_BIN:-zmr}"; zmr-benchmark --zmr .zmr/ios-smoke.json --platform ios --device booted --app-id
|
|
365
|
+
);
|
|
366
|
+
try writer.writeAll(" ");
|
|
367
|
+
try writeShellArg(writer, app_id);
|
|
368
|
+
try writer.writeAll(
|
|
369
|
+
\\ --xcrun xcrun --runs 20 --trace-root traces/zmr-ios-reliability --min-pass-rate 100 --max-failures 0 --max-p95-ms 45000 && "$ZMR_BIN" report traces/zmr-ios-reliability --out traces/zmr-ios-reliability/report.html
|
|
370
|
+
\\```
|
|
371
|
+
\\
|
|
372
|
+
\\## Release Claims
|
|
373
|
+
\\
|
|
374
|
+
\\```bash
|
|
375
|
+
\\zmr-release-readiness --evidence traces/zmr-pilots/evidence.jsonl --target production --json
|
|
376
|
+
\\```
|
|
377
|
+
\\
|
|
378
|
+
\\Do not claim production readiness from smoke runs alone. Use `satisfied` for proven requirements; do not infer readiness from raw `passed` evidence. Use `recommendedWording` and keep `claimLimitations` intact when summarizing readiness. When readiness is blocked, follow `nextSteps[].commands` in order.
|
|
379
|
+
\\
|
|
380
|
+
\\## App Commands
|
|
381
|
+
\\
|
|
382
|
+
\\```bash
|
|
383
|
+
\\zmr doctor --strict --json --config .zmr/config.json
|
|
384
|
+
\\zmr schemas --json
|
|
385
|
+
\\zmr validate --json .zmr/android-smoke.json && zmr validate --json .zmr/ios-smoke.json
|
|
386
|
+
\\zmr run .zmr/android-smoke.json --device emulator-5554 --trace-dir traces/zmr-android
|
|
387
|
+
\\zmr report traces/zmr-android --out traces/zmr-android/report.html
|
|
388
|
+
\\export ZMR_BIN="${ZMR_BIN:-zmr}"; zmr-benchmark --zmr .zmr/android-smoke.json --device emulator-5554 --app-id
|
|
389
|
+
);
|
|
390
|
+
try writer.writeAll(" ");
|
|
391
|
+
try writeShellArg(writer, app_id);
|
|
392
|
+
try writer.writeAll(
|
|
393
|
+
\\ --runs 20 --trace-root traces/zmr-android-reliability --min-pass-rate 100 --max-failures 0 --max-p95-ms 30000 && "$ZMR_BIN" report traces/zmr-android-reliability --out traces/zmr-android-reliability/report.html
|
|
394
|
+
\\zmr run .zmr/ios-smoke.json --platform ios --device booted --trace-dir traces/zmr-ios
|
|
395
|
+
\\zmr report traces/zmr-ios --out traces/zmr-ios/report.html
|
|
396
|
+
\\export ZMR_BIN="${ZMR_BIN:-zmr}"; zmr-benchmark --zmr .zmr/ios-smoke.json --platform ios --device booted --app-id
|
|
397
|
+
);
|
|
398
|
+
try writer.writeAll(" ");
|
|
399
|
+
try writeShellArg(writer, app_id);
|
|
400
|
+
try writer.writeAll(
|
|
401
|
+
\\ --xcrun xcrun --runs 20 --trace-root traces/zmr-ios-reliability --min-pass-rate 100 --max-failures 0 --max-p95-ms 45000 && "$ZMR_BIN" report traces/zmr-ios-reliability --out traces/zmr-ios-reliability/report.html
|
|
402
|
+
\\ZMR_BIN=${ZMR_BIN:-zmr} zmr-device-matrix --matrix .zmr/device-matrix.json --trace-root traces/zmr-matrix --min-pass-rate 100 --max-failures 0
|
|
403
|
+
);
|
|
404
|
+
try writer.writeAll("zmr-pilot-gate --android --ios --android-app-root . --android-app-id ");
|
|
405
|
+
try writeShellArg(writer, app_id);
|
|
406
|
+
try writer.writeAll(" --android-device emulator-5554 --ios-app-root . --ios-app-path ./build/Debug-iphonesimulator/Sample.app --ios-app-id ");
|
|
407
|
+
try writeShellArg(writer, app_id);
|
|
408
|
+
try writer.writeAll(
|
|
409
|
+
\\ --ios-device booted --runs 20 --min-pass-rate 100 --max-failures 0 --evidence-out traces/zmr-pilots/evidence.jsonl
|
|
410
|
+
\\zmr-release-readiness --evidence traces/zmr-pilots/evidence.jsonl --target production --json
|
|
411
|
+
\\zmr serve --transport stdio --config .zmr/config.json --trace-dir traces/zmr-agent
|
|
412
|
+
\\zmr mcp --config .zmr/config.json --trace-dir traces/zmr-agent
|
|
413
|
+
\\zmr explain traces/zmr-agent --json
|
|
414
|
+
\\zmr export traces/zmr-agent --out traces/zmr-agent-redacted.zmrtrace --redact
|
|
415
|
+
\\```
|
|
416
|
+
\\
|
|
417
|
+
);
|
|
418
|
+
try writer.flush();
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
fn createOutputFile(path: []const u8, force: bool) !std.fs.File {
|
|
422
|
+
if (std.fs.path.dirname(path)) |parent| {
|
|
423
|
+
if (parent.len > 0) try std.fs.cwd().makePath(parent);
|
|
424
|
+
}
|
|
425
|
+
if (!force) return try std.fs.cwd().createFile(path, .{ .exclusive = true });
|
|
426
|
+
return try std.fs.cwd().createFile(path, .{ .truncate = true });
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
fn ensureTraceGitignore(allocator: std.mem.Allocator, dir: []const u8) !void {
|
|
430
|
+
const path = try std.fs.path.join(allocator, &.{ dir, ".gitignore" });
|
|
431
|
+
defer allocator.free(path);
|
|
432
|
+
|
|
433
|
+
const existing = std.fs.cwd().readFileAlloc(allocator, path, 1024 * 1024) catch |err| switch (err) {
|
|
434
|
+
error.FileNotFound => "",
|
|
435
|
+
else => return err,
|
|
436
|
+
};
|
|
437
|
+
const had_existing_file = existing.ptr != "".ptr;
|
|
438
|
+
defer if (had_existing_file) allocator.free(existing);
|
|
439
|
+
|
|
440
|
+
if (std.mem.indexOf(u8, existing, "traces/") != null) return;
|
|
441
|
+
|
|
442
|
+
var file = try std.fs.cwd().createFile(path, .{ .truncate = true });
|
|
443
|
+
defer file.close();
|
|
444
|
+
var buffer: [4096]u8 = undefined;
|
|
445
|
+
var file_writer = file.writer(&buffer);
|
|
446
|
+
const writer = &file_writer.interface;
|
|
447
|
+
if (existing.len > 0) {
|
|
448
|
+
try writer.writeAll(existing);
|
|
449
|
+
if (!std.mem.endsWith(u8, existing, "\n")) try writer.writeAll("\n");
|
|
450
|
+
try writer.writeAll("\n");
|
|
451
|
+
}
|
|
452
|
+
try writer.writeAll(
|
|
453
|
+
\\# ZMR local run artifacts
|
|
454
|
+
\\traces/
|
|
455
|
+
\\
|
|
456
|
+
);
|
|
457
|
+
try writer.flush();
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
fn writeJsonStringContent(writer: anytype, value: []const u8) !void {
|
|
461
|
+
for (value) |ch| {
|
|
462
|
+
switch (ch) {
|
|
463
|
+
'"' => try writer.writeAll("\\\""),
|
|
464
|
+
'\\' => try writer.writeAll("\\\\"),
|
|
465
|
+
'\n' => try writer.writeAll("\\n"),
|
|
466
|
+
'\r' => try writer.writeAll("\\r"),
|
|
467
|
+
'\t' => try writer.writeAll("\\t"),
|
|
468
|
+
0...7, 11, 12, 14...31 => try writer.print("\\u{x:0>4}", .{ch}),
|
|
469
|
+
else => try writer.writeAll(&.{ch}),
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|