zeno-mobile-runner 0.1.2 → 0.1.8
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 +162 -3
- package/FEATURES.md +50 -7
- package/README.md +133 -7
- package/build.zig.zon +3 -3
- package/clients/README.md +60 -3
- package/clients/go/README.md +12 -0
- package/clients/go/zmr/client.go +142 -0
- package/clients/kotlin/README.md +18 -1
- package/clients/kotlin/build.gradle.kts +1 -1
- package/clients/kotlin/src/main/kotlin/dev/zmr/ZmrClient.kt +76 -1
- package/clients/python/README.md +19 -0
- package/clients/python/pyproject.toml +1 -1
- package/clients/python/zmr_client.py +33 -0
- package/clients/rust/Cargo.lock +1 -1
- package/clients/rust/Cargo.toml +1 -1
- package/clients/rust/README.md +25 -1
- package/clients/rust/src/lib.rs +201 -0
- package/clients/swift/README.md +18 -0
- package/clients/swift/Sources/ZMRClient/ZMRClient.swift +82 -0
- package/clients/typescript/README.md +16 -0
- package/clients/typescript/index.d.ts +12 -0
- package/clients/typescript/index.mjs +16 -0
- package/clients/typescript/package.json +1 -1
- package/docs/agent-discovery.md +202 -0
- package/docs/ai-agents.md +87 -6
- package/docs/benchmarking.md +10 -3
- package/docs/clients.md +10 -6
- package/docs/demo.md +4 -0
- package/docs/expo-smoke.md +79 -0
- package/docs/install.md +3 -2
- package/docs/npm.md +58 -4
- package/docs/production-readiness.md +123 -0
- package/docs/protocol-fixtures/core-session.responses.jsonl +1 -1
- package/docs/protocol.md +215 -16
- package/docs/scenario-authoring.md +3 -0
- package/docs/troubleshooting.md +1 -1
- package/npm/agents.mjs +16 -0
- package/npm/build-zmr.mjs +1 -1
- package/npm/commands.mjs +9 -5
- package/npm/postinstall.mjs +28 -2
- package/npm/verify-publish.mjs +36 -0
- package/package.json +2 -1
- 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 +4 -0
- package/schemas/discover-output.schema.json +83 -0
- package/schemas/draft-output.schema.json +58 -0
- package/schemas/explore-output.schema.json +94 -0
- package/schemas/inspect-output.schema.json +88 -0
- package/schemas/run-output.schema.json +2 -0
- package/scripts/install-ios-shim.sh +79 -14
- package/scripts/release-readiness.py +43 -0
- package/scripts/run-android-pilot.sh +35 -9
- package/scripts/run-ios-pilot.sh +11 -4
- package/shims/ios/ZMRShim.swift +3 -0
- package/shims/ios/ZMRShimUITestCase.swift +41 -11
- package/skills/zmr-mobile-testing/SKILL.md +28 -3
- package/src/cli_discover.zig +239 -0
- package/src/cli_draft.zig +924 -0
- package/src/cli_explore.zig +136 -0
- package/src/cli_inspect.zig +310 -0
- package/src/cli_output.zig +26 -2
- package/src/cli_run.zig +28 -0
- package/src/cli_trace.zig +8 -0
- package/src/errors.zig +9 -0
- package/src/ios.zig +11 -4
- package/src/ios_lifecycle.zig +36 -0
- package/src/ios_shim.zig +42 -0
- package/src/json_rpc_methods.zig +85 -11
- package/src/json_rpc_params.zig +8 -0
- package/src/json_rpc_protocol.zig +1 -1
- package/src/json_rpc_trace.zig +112 -0
- package/src/main.zig +24 -2
- package/src/mcp.zig +209 -6
- package/src/mcp_protocol.zig +29 -1
- package/src/mcp_trace.zig +126 -4
- package/src/report.zig +186 -0
- package/src/runner.zig +26 -4
- package/src/runner_actions.zig +10 -0
- package/src/runner_diagnostics.zig +31 -1
- package/src/runner_events.zig +70 -7
- package/src/runner_native.zig +17 -1
- package/src/runner_waits.zig +82 -19
- package/src/scaffold.zig +28 -12
- package/src/scenario.zig +32 -4
- package/src/schema_registry.zig +4 -0
- package/src/version.zig +1 -1
package/scripts/run-ios-pilot.sh
CHANGED
|
@@ -156,6 +156,11 @@ run() {
|
|
|
156
156
|
fi
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
run_zmr_report() {
|
|
160
|
+
local trace_dir="$1"
|
|
161
|
+
run "$ZMR_BIN" report "$trace_dir" --out "$trace_dir/report.html" --junit "$trace_dir/junit.xml"
|
|
162
|
+
}
|
|
163
|
+
|
|
159
164
|
is_retryable_simctl_text() {
|
|
160
165
|
local text="$1"
|
|
161
166
|
[[ "$text" == *"CoreSimulatorService connection became invalid"* ]] ||
|
|
@@ -455,7 +460,7 @@ if [[ "$RUNS" -eq 1 ]]; then
|
|
|
455
460
|
else
|
|
456
461
|
run "$ZMR_BIN" run examples/ios-smoke.json --platform ios --ios-device-type "$IOS_DEVICE_TYPE" --device "$DEVICE" --app-id "$APP_ID" --xcrun "$XCRUN" --trace-dir "$TRACE_DIR"
|
|
457
462
|
fi
|
|
458
|
-
|
|
463
|
+
run_zmr_report "$TRACE_DIR"
|
|
459
464
|
run "$ZMR_BIN" export "$TRACE_DIR" --out "$TRACE_ROOT/ios-smoke.zmrtrace"
|
|
460
465
|
run "$ZMR_BIN" export "$TRACE_DIR" --out "$TRACE_ROOT/ios-smoke-redacted.zmrtrace" --redact
|
|
461
466
|
|
|
@@ -463,7 +468,7 @@ if [[ "$RUNS" -eq 1 ]]; then
|
|
|
463
468
|
SHIM_TRACE_DIR="$TRACE_ROOT/ios-shim-smoke"
|
|
464
469
|
run rm -rf "$SHIM_TRACE_DIR"
|
|
465
470
|
run "$ZMR_BIN" run examples/ios-shim-smoke.json --platform ios --ios-device-type "$IOS_DEVICE_TYPE" --device "$DEVICE" --app-id "$APP_ID" --xcrun "$XCRUN" --ios-shim "$IOS_SHIM" --trace-dir "$SHIM_TRACE_DIR"
|
|
466
|
-
|
|
471
|
+
run_zmr_report "$SHIM_TRACE_DIR"
|
|
467
472
|
run "$ZMR_BIN" export "$SHIM_TRACE_DIR" --out "$TRACE_ROOT/ios-shim-smoke.zmrtrace"
|
|
468
473
|
run "$ZMR_BIN" export "$SHIM_TRACE_DIR" --out "$TRACE_ROOT/ios-shim-smoke-redacted.zmrtrace" --redact
|
|
469
474
|
fi
|
|
@@ -481,11 +486,11 @@ else
|
|
|
481
486
|
else
|
|
482
487
|
ZMR_BIN="$ZMR_BIN" run "$ROOT/scripts/benchmark.sh" --zmr examples/ios-smoke.json --device "$DEVICE" --platform ios --ios-device-type "$IOS_DEVICE_TYPE" --app-id "$APP_ID" --xcrun "$XCRUN" --runs "$RUNS" --trace-root "$TRACE_ROOT/ios-smoke-benchmark" "${benchmark_gate_args[@]}"
|
|
483
488
|
fi
|
|
484
|
-
|
|
489
|
+
run_zmr_report "$TRACE_ROOT/ios-smoke-benchmark"
|
|
485
490
|
|
|
486
491
|
if [[ -n "$IOS_SHIM" ]]; then
|
|
487
492
|
ZMR_BIN="$ZMR_BIN" run "$ROOT/scripts/benchmark.sh" --zmr examples/ios-shim-smoke.json --device "$DEVICE" --platform ios --ios-device-type "$IOS_DEVICE_TYPE" --app-id "$APP_ID" --xcrun "$XCRUN" --ios-shim "$IOS_SHIM" --runs "$RUNS" --trace-root "$TRACE_ROOT/ios-shim-smoke-benchmark" "${benchmark_gate_args[@]}"
|
|
488
|
-
|
|
493
|
+
run_zmr_report "$TRACE_ROOT/ios-shim-smoke-benchmark"
|
|
489
494
|
fi
|
|
490
495
|
fi
|
|
491
496
|
|
|
@@ -501,8 +506,10 @@ if [[ "$RUNS" -eq 1 ]]; then
|
|
|
501
506
|
else
|
|
502
507
|
echo "Benchmark reports:"
|
|
503
508
|
echo " $TRACE_ROOT/ios-smoke-benchmark/report.html"
|
|
509
|
+
echo " $TRACE_ROOT/ios-smoke-benchmark/junit.xml"
|
|
504
510
|
if [[ -n "$IOS_SHIM" ]]; then
|
|
505
511
|
echo " $TRACE_ROOT/ios-shim-smoke-benchmark/report.html"
|
|
512
|
+
echo " $TRACE_ROOT/ios-shim-smoke-benchmark/junit.xml"
|
|
506
513
|
fi
|
|
507
514
|
fi
|
|
508
515
|
echo "Viewer:"
|
package/shims/ios/ZMRShim.swift
CHANGED
|
@@ -128,7 +128,7 @@ final class ZMRShimUITestCase: XCTestCase {
|
|
|
128
128
|
guard isFastQueryable(parts: parts) else {
|
|
129
129
|
return error("selector.unsupported", "unsupported query selector: \(selector)")
|
|
130
130
|
}
|
|
131
|
-
let element =
|
|
131
|
+
let element = resolveElement(selector: selector, app: app, preferredTypes: [])
|
|
132
132
|
return [
|
|
133
133
|
"status": "ok",
|
|
134
134
|
"exists": element?.exists ?? false,
|
|
@@ -328,19 +328,49 @@ final class ZMRShimUITestCase: XCTestCase {
|
|
|
328
328
|
return fast
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
331
|
+
return resolveBroadElement(selector: selector, app: app)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
private func resolveBroadElement(selector: String, app: XCUIApplication) -> XCUIElement? {
|
|
335
|
+
guard let parts = selectorParts(selector) else {
|
|
336
|
+
return nil
|
|
336
337
|
}
|
|
337
|
-
|
|
338
|
-
|
|
338
|
+
|
|
339
|
+
let query: XCUIElementQuery?
|
|
340
|
+
switch parts.field {
|
|
341
|
+
case "text", "label":
|
|
342
|
+
let predicate = parts.contains
|
|
343
|
+
? NSPredicate(format: "label CONTAINS[c] %@", parts.value)
|
|
344
|
+
: NSPredicate(format: "label == %@", parts.value)
|
|
345
|
+
query = app.descendants(matching: .any).matching(predicate)
|
|
346
|
+
case "identifier", "resourceId":
|
|
347
|
+
let predicate = parts.contains
|
|
348
|
+
? NSPredicate(format: "identifier CONTAINS[c] %@", parts.value)
|
|
349
|
+
: NSPredicate(format: "identifier == %@", parts.value)
|
|
350
|
+
query = app.descendants(matching: .any).matching(predicate)
|
|
351
|
+
case "value":
|
|
352
|
+
let predicate = parts.contains
|
|
353
|
+
? NSPredicate(format: "value CONTAINS[c] %@", parts.value)
|
|
354
|
+
: NSPredicate(format: "value == %@", parts.value)
|
|
355
|
+
query = app.descendants(matching: .any).matching(predicate)
|
|
356
|
+
case "id":
|
|
357
|
+
if parts.value.hasPrefix("id:") {
|
|
358
|
+
let identifier = String(parts.value.dropFirst("id:".count))
|
|
359
|
+
query = app.descendants(matching: .any).matching(identifier: identifier)
|
|
360
|
+
} else if parts.value.hasPrefix("label:") {
|
|
361
|
+
let label = String(parts.value.dropFirst("label:".count))
|
|
362
|
+
query = app.descendants(matching: .any).matching(NSPredicate(format: "label == %@", label))
|
|
363
|
+
} else {
|
|
364
|
+
query = nil
|
|
365
|
+
}
|
|
366
|
+
default:
|
|
367
|
+
query = nil
|
|
339
368
|
}
|
|
340
|
-
|
|
341
|
-
|
|
369
|
+
|
|
370
|
+
guard let element = query?.firstMatch, element.exists else {
|
|
371
|
+
return nil
|
|
342
372
|
}
|
|
343
|
-
return
|
|
373
|
+
return element
|
|
344
374
|
}
|
|
345
375
|
|
|
346
376
|
private func resolveFastElement(selector: String, app: XCUIApplication, preferredTypes: [XCUIElement.ElementType]) -> XCUIElement? {
|
|
@@ -57,9 +57,34 @@ zmr mcp --config .zmr/config.json --trace-dir traces/zmr-agent
|
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
Use the `semantic_snapshot`, `tap`, `type`, `wait_visible`, `trace_events`, and
|
|
60
|
-
`trace_export` tools. Prefer
|
|
61
|
-
|
|
62
|
-
actions.
|
|
60
|
+
`trace_explore`, `trace_discover`, and `trace_export` tools. Prefer
|
|
61
|
+
`semantic_snapshot` because it normalizes Android and iOS hierarchy classes
|
|
62
|
+
into roles, selectors, bounds, and recommended actions.
|
|
63
|
+
|
|
64
|
+
After a session has produced trace artifacts, prefer the review-first
|
|
65
|
+
exploration handoff when a goal should travel with the generated scenario
|
|
66
|
+
candidate:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{"method":"trace.explore","params":{"out":".zmr/discovered/login-smoke.json","goal":"find a stable login smoke","includeActions":true,"validate":true,"force":true}}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
For MCP agents, call `trace_explore` with the same `out`, `goal`,
|
|
73
|
+
`includeActions`, `validate`, and `force` arguments. The offline CLI equivalent
|
|
74
|
+
is:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
zmr explore --from-trace traces/zmr-agent \
|
|
78
|
+
--out .zmr/discovered/login-smoke.json \
|
|
79
|
+
--goal "find a stable login smoke" \
|
|
80
|
+
--include-actions \
|
|
81
|
+
--validate \
|
|
82
|
+
--json
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Treat the output as a starting point. Its JSON includes `autonomous:false`,
|
|
86
|
+
`reviewRequired:true`, `guardrails`, replay coverage, validation, and next
|
|
87
|
+
commands; it does not crawl, discover credentials, or commit tests.
|
|
63
88
|
|
|
64
89
|
## Scenario Pattern
|
|
65
90
|
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
|
|
3
|
+
const cli_draft = @import("cli_draft.zig");
|
|
4
|
+
const cli_output = @import("cli_output.zig");
|
|
5
|
+
const trace = @import("trace.zig");
|
|
6
|
+
const validation = @import("validation.zig");
|
|
7
|
+
const version = @import("version.zig");
|
|
8
|
+
|
|
9
|
+
pub const ParsedArgs = struct {
|
|
10
|
+
from_trace: ?[]const u8 = null,
|
|
11
|
+
out_path: ?[]const u8 = null,
|
|
12
|
+
name: ?[]const u8 = null,
|
|
13
|
+
app_id: ?[]const u8 = null,
|
|
14
|
+
include_actions: bool = false,
|
|
15
|
+
validate: bool = false,
|
|
16
|
+
force: bool = false,
|
|
17
|
+
json: bool = false,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
pub const DiscoverSummary = struct {
|
|
21
|
+
ok: bool,
|
|
22
|
+
draft: cli_draft.DraftSummary,
|
|
23
|
+
validated: bool,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
pub const JsonOptions = struct {
|
|
27
|
+
mode: []const u8 = "discover",
|
|
28
|
+
goal: ?[]const u8 = null,
|
|
29
|
+
autonomous: ?bool = null,
|
|
30
|
+
review_required: ?bool = null,
|
|
31
|
+
guardrails: []const []const u8 = &.{},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
pub const OwnedDiscover = struct {
|
|
35
|
+
draft: cli_draft.OwnedDraft,
|
|
36
|
+
validation: ?validation.Result = null,
|
|
37
|
+
summary: DiscoverSummary,
|
|
38
|
+
|
|
39
|
+
pub fn deinit(self: *OwnedDiscover, allocator: std.mem.Allocator) void {
|
|
40
|
+
if (self.validation) |result| result.deinit(allocator);
|
|
41
|
+
self.draft.deinit(allocator);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
pub fn parseArgs(args: []const []const u8) !ParsedArgs {
|
|
46
|
+
var parsed = ParsedArgs{};
|
|
47
|
+
var index: usize = 0;
|
|
48
|
+
while (index < args.len) : (index += 1) {
|
|
49
|
+
const arg = args[index];
|
|
50
|
+
if (std.mem.eql(u8, arg, "--from-trace")) {
|
|
51
|
+
index += 1;
|
|
52
|
+
parsed.from_trace = if (index < args.len) args[index] else return error.MissingTraceDir;
|
|
53
|
+
} else if (std.mem.eql(u8, arg, "--out")) {
|
|
54
|
+
index += 1;
|
|
55
|
+
parsed.out_path = if (index < args.len) args[index] else return error.MissingDraftOut;
|
|
56
|
+
} else if (std.mem.eql(u8, arg, "--name")) {
|
|
57
|
+
index += 1;
|
|
58
|
+
parsed.name = if (index < args.len) args[index] else return error.MissingParam;
|
|
59
|
+
} else if (std.mem.eql(u8, arg, "--app-id")) {
|
|
60
|
+
index += 1;
|
|
61
|
+
parsed.app_id = if (index < args.len) args[index] else return error.MissingAppId;
|
|
62
|
+
} else if (std.mem.eql(u8, arg, "--include-actions")) {
|
|
63
|
+
parsed.include_actions = true;
|
|
64
|
+
} else if (std.mem.eql(u8, arg, "--validate")) {
|
|
65
|
+
parsed.validate = true;
|
|
66
|
+
} else if (std.mem.eql(u8, arg, "--force")) {
|
|
67
|
+
parsed.force = true;
|
|
68
|
+
} else if (std.mem.eql(u8, arg, "--json")) {
|
|
69
|
+
parsed.json = true;
|
|
70
|
+
} else {
|
|
71
|
+
return error.UnknownFlag;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (parsed.from_trace == null) return error.MissingTraceDir;
|
|
76
|
+
if (parsed.out_path == null) return error.MissingDraftOut;
|
|
77
|
+
return parsed;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
pub fn run(allocator: std.mem.Allocator, args: *std.process.ArgIterator) !void {
|
|
81
|
+
var raw_args = std.ArrayList([]const u8).empty;
|
|
82
|
+
defer raw_args.deinit(allocator);
|
|
83
|
+
while (args.next()) |arg| try raw_args.append(allocator, arg);
|
|
84
|
+
|
|
85
|
+
const parsed = try parseArgs(raw_args.items);
|
|
86
|
+
var discovered = try discoverFromTrace(allocator, parsed);
|
|
87
|
+
defer discovered.deinit(allocator);
|
|
88
|
+
|
|
89
|
+
const stdout = std.fs.File.stdout().deprecatedWriter();
|
|
90
|
+
if (parsed.json) {
|
|
91
|
+
try writeJson(stdout, discovered.summary, discovered.validation);
|
|
92
|
+
} else {
|
|
93
|
+
try stdout.print("wrote {s}\n", .{discovered.summary.draft.out_path});
|
|
94
|
+
if (discovered.validation) |result| {
|
|
95
|
+
if (result.ok) {
|
|
96
|
+
try stdout.print("validated {s}\n", .{discovered.summary.draft.out_path});
|
|
97
|
+
} else {
|
|
98
|
+
try stdout.print("validation failed {s}\n", .{discovered.summary.draft.out_path});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
try stdout.writeAll("next: zmr validate --json ");
|
|
102
|
+
try cli_output.writeShellArg(stdout, discovered.summary.draft.out_path);
|
|
103
|
+
try stdout.writeAll("\n");
|
|
104
|
+
}
|
|
105
|
+
if (!discovered.summary.ok) std.process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
pub fn discoverFromTrace(allocator: std.mem.Allocator, parsed: ParsedArgs) !OwnedDiscover {
|
|
109
|
+
var draft = try cli_draft.draftFromTrace(allocator, .{
|
|
110
|
+
.from_trace = parsed.from_trace,
|
|
111
|
+
.out_path = parsed.out_path,
|
|
112
|
+
.name = parsed.name,
|
|
113
|
+
.app_id = parsed.app_id,
|
|
114
|
+
.include_actions = parsed.include_actions,
|
|
115
|
+
.force = parsed.force,
|
|
116
|
+
.json = parsed.json,
|
|
117
|
+
});
|
|
118
|
+
errdefer draft.deinit(allocator);
|
|
119
|
+
|
|
120
|
+
var validation_result: ?validation.Result = null;
|
|
121
|
+
errdefer if (validation_result) |result| result.deinit(allocator);
|
|
122
|
+
if (parsed.validate) {
|
|
123
|
+
validation_result = try validation.validateFile(allocator, draft.summary.out_path);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const ok = draft.summary.ok and (validation_result == null or validation_result.?.ok);
|
|
127
|
+
return .{
|
|
128
|
+
.draft = draft,
|
|
129
|
+
.validation = validation_result,
|
|
130
|
+
.summary = .{
|
|
131
|
+
.ok = ok,
|
|
132
|
+
.draft = draft.summary,
|
|
133
|
+
.validated = parsed.validate,
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
pub fn writeJson(writer: anytype, summary: DiscoverSummary, validation_result: ?validation.Result) !void {
|
|
139
|
+
try writeJsonWithOptions(writer, summary, validation_result, .{});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
pub fn writeJsonWithOptions(writer: anytype, summary: DiscoverSummary, validation_result: ?validation.Result, options: JsonOptions) !void {
|
|
143
|
+
const draft = summary.draft;
|
|
144
|
+
try writer.writeAll("{\"ok\":");
|
|
145
|
+
try writer.writeAll(if (summary.ok) "true" else "false");
|
|
146
|
+
try writer.writeAll(",\"mode\":");
|
|
147
|
+
try trace.writeJsonString(writer, options.mode);
|
|
148
|
+
try writer.writeAll(",\"schemaVersion\":1");
|
|
149
|
+
try writer.writeAll(",\"runnerVersion\":");
|
|
150
|
+
try trace.writeJsonString(writer, version.runner_version);
|
|
151
|
+
try writer.writeAll(",\"protocolVersion\":");
|
|
152
|
+
try trace.writeJsonString(writer, version.protocol_version);
|
|
153
|
+
if (options.goal) |goal| {
|
|
154
|
+
try writer.writeAll(",\"goal\":");
|
|
155
|
+
try trace.writeJsonString(writer, goal);
|
|
156
|
+
}
|
|
157
|
+
if (options.autonomous) |autonomous| {
|
|
158
|
+
try writer.writeAll(",\"autonomous\":");
|
|
159
|
+
try writer.writeAll(if (autonomous) "true" else "false");
|
|
160
|
+
}
|
|
161
|
+
if (options.review_required) |review_required| {
|
|
162
|
+
try writer.writeAll(",\"reviewRequired\":");
|
|
163
|
+
try writer.writeAll(if (review_required) "true" else "false");
|
|
164
|
+
}
|
|
165
|
+
if (options.guardrails.len > 0) {
|
|
166
|
+
try writer.writeAll(",\"guardrails\":[");
|
|
167
|
+
for (options.guardrails, 0..) |guardrail, index| {
|
|
168
|
+
if (index > 0) try writer.writeAll(",");
|
|
169
|
+
try trace.writeJsonString(writer, guardrail);
|
|
170
|
+
}
|
|
171
|
+
try writer.writeAll("]");
|
|
172
|
+
}
|
|
173
|
+
try writer.writeAll(",\"out\":");
|
|
174
|
+
try trace.writeJsonString(writer, draft.out_path);
|
|
175
|
+
try writer.writeAll(",\"traceDir\":");
|
|
176
|
+
try trace.writeJsonString(writer, draft.trace_dir);
|
|
177
|
+
try writer.writeAll(",\"sourceSnapshot\":");
|
|
178
|
+
try trace.writeJsonString(writer, draft.source_snapshot);
|
|
179
|
+
try writer.writeAll(",\"name\":");
|
|
180
|
+
try trace.writeJsonString(writer, draft.name);
|
|
181
|
+
try writer.writeAll(",\"appId\":");
|
|
182
|
+
if (draft.app_id) |actual| {
|
|
183
|
+
try trace.writeJsonString(writer, actual);
|
|
184
|
+
} else {
|
|
185
|
+
try writer.writeAll("null");
|
|
186
|
+
}
|
|
187
|
+
try writer.print(",\"selectorCount\":{d},\"stepCount\":{d}", .{ draft.selector_count, draft.step_count });
|
|
188
|
+
try cli_draft.writeReplayJson(writer, draft.replay);
|
|
189
|
+
try writer.writeAll(",\"warnings\":[");
|
|
190
|
+
for (draft.warnings, 0..) |warning, index| {
|
|
191
|
+
if (index > 0) try writer.writeAll(",");
|
|
192
|
+
try trace.writeJsonString(writer, warning);
|
|
193
|
+
}
|
|
194
|
+
try writer.writeAll("],\"validated\":");
|
|
195
|
+
try writer.writeAll(if (summary.validated) "true" else "false");
|
|
196
|
+
try writer.writeAll(",\"validation\":");
|
|
197
|
+
if (validation_result) |result| {
|
|
198
|
+
try writeValidationObject(writer, draft.out_path, result);
|
|
199
|
+
} else {
|
|
200
|
+
try writer.writeAll("null");
|
|
201
|
+
}
|
|
202
|
+
try writer.writeAll(",\"nextCommands\":[\"zmr validate --json ");
|
|
203
|
+
try cli_output.writeShellArgJsonContent(writer, draft.out_path);
|
|
204
|
+
try writer.writeAll("\",\"zmr run ");
|
|
205
|
+
try cli_output.writeShellArgJsonContent(writer, draft.out_path);
|
|
206
|
+
try writer.writeAll(" --json --trace-dir ");
|
|
207
|
+
try cli_output.writeShellArgJsonContent(writer, draft.trace_dir);
|
|
208
|
+
try writer.writeAll("\"]}\n");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
fn writeValidationObject(writer: anytype, path: []const u8, result: validation.Result) !void {
|
|
212
|
+
try writer.writeAll("{\"ok\":");
|
|
213
|
+
try writer.writeAll(if (result.ok) "true" else "false");
|
|
214
|
+
try writer.writeAll(",\"path\":");
|
|
215
|
+
try trace.writeJsonString(writer, path);
|
|
216
|
+
if (result.ok) {
|
|
217
|
+
try writer.writeAll(",\"name\":");
|
|
218
|
+
try trace.writeJsonString(writer, result.name.?);
|
|
219
|
+
try writer.writeAll(",\"appId\":");
|
|
220
|
+
if (result.app_id) |app_id| {
|
|
221
|
+
try trace.writeJsonString(writer, app_id);
|
|
222
|
+
} else {
|
|
223
|
+
try writer.writeAll("null");
|
|
224
|
+
}
|
|
225
|
+
try writer.print(",\"stepCount\":{d}", .{result.step_count});
|
|
226
|
+
} else {
|
|
227
|
+
try writer.writeAll(",\"errorCode\":");
|
|
228
|
+
try trace.writeJsonString(writer, result.error_code.?);
|
|
229
|
+
try writer.writeAll(",\"message\":");
|
|
230
|
+
try trace.writeJsonString(writer, result.message.?);
|
|
231
|
+
if (result.path) |field_path| {
|
|
232
|
+
try writer.writeAll(",\"fieldPath\":");
|
|
233
|
+
try trace.writeJsonString(writer, field_path);
|
|
234
|
+
}
|
|
235
|
+
if (result.line) |line| try writer.print(",\"line\":{d}", .{line});
|
|
236
|
+
if (result.column) |column| try writer.print(",\"column\":{d}", .{column});
|
|
237
|
+
}
|
|
238
|
+
try writer.writeAll("}");
|
|
239
|
+
}
|