@jevitate/cli 0.2.0 → 0.3.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/dist/ai-cli.d.ts +31 -11
- package/dist/ai-cli.js +100 -67
- package/dist/ai-cli.js.map +1 -1
- package/dist/author-journey-cli.js +3 -1
- package/dist/author-journey-cli.js.map +1 -1
- package/dist/bin.js +48218 -39969
- package/dist/browser-run-options.d.ts +32 -1
- package/dist/browser-run-options.js +69 -0
- package/dist/browser-run-options.js.map +1 -1
- package/dist/build-info.generated.js +2 -2
- package/dist/campaign-api.d.ts +151 -0
- package/dist/campaign-api.js +446 -0
- package/dist/campaign-api.js.map +1 -0
- package/dist/campaign-cli.d.ts +6 -0
- package/dist/campaign-cli.js +66 -0
- package/dist/campaign-cli.js.map +1 -0
- package/dist/check-api.js +3 -2
- package/dist/check-api.js.map +1 -1
- package/dist/check-budget.js +2 -1
- package/dist/check-budget.js.map +1 -1
- package/dist/check-execute.d.ts +4 -0
- package/dist/check-execute.js +41 -6
- package/dist/check-execute.js.map +1 -1
- package/dist/check-plan.d.ts +5 -0
- package/dist/check-plan.js +72 -4
- package/dist/check-plan.js.map +1 -1
- package/dist/check-suite.d.ts +14 -1
- package/dist/check-suite.js +26 -1
- package/dist/check-suite.js.map +1 -1
- package/dist/cli-output.d.ts +4 -0
- package/dist/cli-output.js +52 -5
- package/dist/cli-output.js.map +1 -1
- package/dist/cli-shared.d.ts +13 -3
- package/dist/cli-shared.js +52 -7
- package/dist/cli-shared.js.map +1 -1
- package/dist/defect-evidence.js +6 -3
- package/dist/defect-evidence.js.map +1 -1
- package/dist/demo-aspect-api.js +2 -1
- package/dist/demo-aspect-api.js.map +1 -1
- package/dist/demo-aspect-cli.js +3 -1
- package/dist/demo-aspect-cli.js.map +1 -1
- package/dist/demo-capture.d.ts +37 -3
- package/dist/demo-capture.js +119 -20
- package/dist/demo-capture.js.map +1 -1
- package/dist/explore-adversarial.d.ts +11 -4
- package/dist/explore-adversarial.js +21 -12
- package/dist/explore-adversarial.js.map +1 -1
- package/dist/explore-cli.js +262 -26
- package/dist/explore-cli.js.map +1 -1
- package/dist/explore-coverage.d.ts +17 -2
- package/dist/explore-coverage.js +19 -13
- package/dist/explore-coverage.js.map +1 -1
- package/dist/explore-feature.d.ts +0 -2
- package/dist/explore-feature.js +6 -8
- package/dist/explore-feature.js.map +1 -1
- package/dist/explore-goal.d.ts +25 -7
- package/dist/explore-goal.js +61 -18
- package/dist/explore-goal.js.map +1 -1
- package/dist/explore-shared.d.ts +27 -2
- package/dist/explore-shared.js +21 -1
- package/dist/explore-shared.js.map +1 -1
- package/dist/fixture-auth.d.ts +13 -2
- package/dist/fixture-auth.js +19 -6
- package/dist/fixture-auth.js.map +1 -1
- package/dist/fixture-cli.d.ts +29 -0
- package/dist/fixture-cli.js +121 -7
- package/dist/fixture-cli.js.map +1 -1
- package/dist/github-issue-filer.js +2 -1
- package/dist/github-issue-filer.js.map +1 -1
- package/dist/host-health-run.d.ts +2 -1
- package/dist/host-health-run.js +8 -2
- package/dist/host-health-run.js.map +1 -1
- package/dist/init-cli.js +34 -5
- package/dist/init-cli.js.map +1 -1
- package/dist/init-keys.d.ts +11 -0
- package/dist/init-keys.js +5 -2
- package/dist/init-keys.js.map +1 -1
- package/dist/journey-annotate-api.js +17 -4
- package/dist/journey-annotate-api.js.map +1 -1
- package/dist/journey-annotate-cli.js +3 -1
- package/dist/journey-annotate-cli.js.map +1 -1
- package/dist/journey-api.d.ts +26 -1
- package/dist/journey-api.js +51 -25
- package/dist/journey-api.js.map +1 -1
- package/dist/journey-cli.d.ts +1 -1
- package/dist/journey-cli.js +44 -3
- package/dist/journey-cli.js.map +1 -1
- package/dist/journey-demo-api.d.ts +2 -0
- package/dist/journey-demo-api.js +23 -10
- package/dist/journey-demo-api.js.map +1 -1
- package/dist/journey-demo-cli.js +5 -2
- package/dist/journey-demo-cli.js.map +1 -1
- package/dist/journey-prefix.d.ts +194 -0
- package/dist/journey-prefix.js +279 -0
- package/dist/journey-prefix.js.map +1 -0
- package/dist/key-report.d.ts +49 -0
- package/dist/key-report.js +101 -0
- package/dist/key-report.js.map +1 -0
- package/dist/kill-signal.js +0 -1
- package/dist/kill-signal.js.map +1 -1
- package/dist/ledger-api.js +3 -1
- package/dist/ledger-api.js.map +1 -1
- package/dist/log-correlation.d.ts +54 -1
- package/dist/log-correlation.js +83 -14
- package/dist/log-correlation.js.map +1 -1
- package/dist/log-sources.js +6 -5
- package/dist/log-sources.js.map +1 -1
- package/dist/log-trace.d.ts +80 -0
- package/dist/log-trace.js +210 -0
- package/dist/log-trace.js.map +1 -0
- package/dist/logs-retention.js +2 -1
- package/dist/logs-retention.js.map +1 -1
- package/dist/masked-input.d.ts +22 -0
- package/dist/masked-input.js +99 -0
- package/dist/masked-input.js.map +1 -0
- package/dist/mcp-api.d.ts +3 -1
- package/dist/mcp-api.js +49 -7
- package/dist/mcp-api.js.map +1 -1
- package/dist/mcp-args.d.ts +12 -0
- package/dist/mcp-args.js +33 -0
- package/dist/mcp-args.js.map +1 -1
- package/dist/mcp-cli-tools.d.ts +5 -1
- package/dist/mcp-cli-tools.js +73 -16
- package/dist/mcp-cli-tools.js.map +1 -1
- package/dist/mission-api.js +2 -1
- package/dist/mission-api.js.map +1 -1
- package/dist/mission-cli.js +2 -1
- package/dist/mission-cli.js.map +1 -1
- package/dist/mission-fixtures.d.ts +10 -1
- package/dist/mission-fixtures.js +49 -15
- package/dist/mission-fixtures.js.map +1 -1
- package/dist/mission-queue-runner.js +11 -2
- package/dist/mission-queue-runner.js.map +1 -1
- package/dist/multi-run-cli.js +7 -1
- package/dist/multi-run-cli.js.map +1 -1
- package/dist/openrouter-call.js +3 -2
- package/dist/openrouter-call.js.map +1 -1
- package/dist/program.js +4 -0
- package/dist/program.js.map +1 -1
- package/dist/project-dir.js +3 -2
- package/dist/project-dir.js.map +1 -1
- package/dist/record-api.d.ts +3 -1
- package/dist/record-api.js +5 -2
- package/dist/record-api.js.map +1 -1
- package/dist/record-cli.js +14 -2
- package/dist/record-cli.js.map +1 -1
- package/dist/regression-api.d.ts +6 -0
- package/dist/regression-api.js +4 -2
- package/dist/regression-api.js.map +1 -1
- package/dist/regression-cli.js +56 -4
- package/dist/regression-cli.js.map +1 -1
- package/dist/report-api.js +2 -1
- package/dist/report-api.js.map +1 -1
- package/dist/resource-preflight.d.ts +87 -0
- package/dist/resource-preflight.js +160 -0
- package/dist/resource-preflight.js.map +1 -0
- package/dist/result-schema.d.ts +1 -1
- package/dist/result-schema.js +1 -1
- package/dist/run-key-preflight.d.ts +42 -0
- package/dist/run-key-preflight.js +82 -0
- package/dist/run-key-preflight.js.map +1 -0
- package/dist/run-screenshots.js +11 -5
- package/dist/run-screenshots.js.map +1 -1
- package/dist/runtime-env.js +3 -2
- package/dist/runtime-env.js.map +1 -1
- package/dist/server-log-verify.d.ts +2 -0
- package/dist/server-log-verify.js +8 -3
- package/dist/server-log-verify.js.map +1 -1
- package/dist/site-gate-cli.js +2 -1
- package/dist/site-gate-cli.js.map +1 -1
- package/dist/source-api.js +2 -1
- package/dist/source-api.js.map +1 -1
- package/dist/suite-explore-options.d.ts +32 -0
- package/dist/suite-explore-options.js +22 -0
- package/dist/suite-explore-options.js.map +1 -1
- package/dist/target-config.d.ts +6 -0
- package/dist/target-config.js +9 -0
- package/dist/target-config.js.map +1 -1
- package/dist/type-fixture-file.d.ts +9 -0
- package/dist/type-fixture-file.js +48 -0
- package/dist/type-fixture-file.js.map +1 -0
- package/dist/ui-api.js +4 -3
- package/dist/ui-api.js.map +1 -1
- package/dist/usability-capture.js +4 -3
- package/dist/usability-capture.js.map +1 -1
- package/dist/usage-config.d.ts +1 -1
- package/dist/usage-config.js +1 -1
- package/dist/ux-claim-probe.d.ts +45 -0
- package/dist/ux-claim-probe.js +386 -0
- package/dist/ux-claim-probe.js.map +1 -0
- package/dist/ux-cli.js +8 -1
- package/dist/ux-cli.js.map +1 -1
- package/dist/ux-product.d.ts +14 -0
- package/dist/ux-product.js +39 -0
- package/dist/ux-product.js.map +1 -0
- package/dist/ux-review.d.ts +4 -0
- package/dist/ux-review.js +27 -7
- package/dist/ux-review.js.map +1 -1
- package/dist/ux-usability.d.ts +34 -9
- package/dist/ux-usability.js +100 -14
- package/dist/ux-usability.js.map +1 -1
- package/dist/verify-fix-api.d.ts +21 -0
- package/dist/verify-fix-api.js +85 -33
- package/dist/verify-fix-api.js.map +1 -1
- package/dist/verify-fix-cli.js +7 -2
- package/dist/verify-fix-cli.js.map +1 -1
- package/package.json +9 -9
- package/skills/jevitate-explore/SKILL.md +8 -0
- package/skills/jevitate-getting-started/SKILL.md +2 -1
- package/skills/jevitate-test-campaign/SKILL.md +203 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { redactUrl } from "@jevitate/ai-core";
|
|
2
|
+
import { LogSpecError } from "./log-lines.js";
|
|
3
|
+
import { clock } from "@jevitate/domain";
|
|
4
|
+
/**
|
|
5
|
+
* Request ↔ backend-log correlation by TRACE / CORRELATION ID (#204), instead of by time window.
|
|
6
|
+
*
|
|
7
|
+
* The browser side (`RequestIdLedger`) records, for every request the run's page sends, the ids it
|
|
8
|
+
* carries: W3C `traceparent` (its trace-id), `x-request-id`, `x-correlation-id`, `request-id`,
|
|
9
|
+
* `x-amzn-trace-id` (its `Root`), `x-b3-traceid`, `x-cloud-trace-context` (its trace) — on the
|
|
10
|
+
* REQUEST (a frontend with OpenTelemetry) or the RESPONSE (a server-generated request id) — plus
|
|
11
|
+
* any operator-named header (`--log-correlation-header`). The log side (`lineIds`) finds those
|
|
12
|
+
* ids in a backend log line: any whitespace/punctuation-delimited token equal to a recorded id
|
|
13
|
+
* (exact — never a prefix or a substring of a longer token).
|
|
14
|
+
*
|
|
15
|
+
* `ServerLogRuntime` then attaches a line that carries a run's id to EXACTLY that request (method,
|
|
16
|
+
* URL, status) and to the step that sent it — whatever time the line landed — and falls back to the
|
|
17
|
+
* time window only for lines with no id. Once ids demonstrably correlate (at least one line matched
|
|
18
|
+
* a run's id), a line carrying a DIFFERENT id (`foreignIds`: a `trace_id=`/`request_id=` key, a
|
|
19
|
+
* `traceparent`, or an operator `--log-id-pattern`) belongs to other work — another user, a
|
|
20
|
+
* background job — and is never attributed to this run.
|
|
21
|
+
*
|
|
22
|
+
* Ids are not secrets, but every URL is redacted and every attached line is redacted with the run's
|
|
23
|
+
* secrets, as before.
|
|
24
|
+
*/
|
|
25
|
+
/** Headers whose value identifies a request across the browser and the backend (lower-case). */
|
|
26
|
+
export const DEFAULT_CORRELATION_HEADERS = [
|
|
27
|
+
"traceparent",
|
|
28
|
+
"x-request-id",
|
|
29
|
+
"x-correlation-id",
|
|
30
|
+
"request-id",
|
|
31
|
+
"x-amzn-trace-id",
|
|
32
|
+
"x-b3-traceid",
|
|
33
|
+
"x-cloud-trace-context",
|
|
34
|
+
];
|
|
35
|
+
/** An id shorter than this would match unrelated tokens (a "1", a "200"): never used. */
|
|
36
|
+
const MIN_ID_CHARS = 8;
|
|
37
|
+
const MAX_ID_CHARS = 128;
|
|
38
|
+
const ID_SHAPE = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
|
|
39
|
+
const TRACEPARENT = /^[0-9a-f]{2}-([0-9a-f]{32})-[0-9a-f]{16}-[0-9a-f]{2}$/i;
|
|
40
|
+
const HEADER_NAME = /^[A-Za-z0-9!#$%&'*+.^_`|~-]{1,64}$/;
|
|
41
|
+
/** Most requests the ledger keeps (the oldest are dropped past it). */
|
|
42
|
+
const MAX_REQUESTS = 5_000;
|
|
43
|
+
/** Normalizes an id for comparison (hex trace ids are case-insensitive in practice). */
|
|
44
|
+
const norm = (id) => id.toLowerCase();
|
|
45
|
+
function usable(id) {
|
|
46
|
+
const v = id.trim();
|
|
47
|
+
if (v.length < MIN_ID_CHARS || v.length > MAX_ID_CHARS || !ID_SHAPE.test(v))
|
|
48
|
+
return null;
|
|
49
|
+
// An all-zero trace id is the W3C "invalid" value: it identifies nothing.
|
|
50
|
+
if (/^0+$/.test(v.replace(/[-:.]/g, "")))
|
|
51
|
+
return null;
|
|
52
|
+
return v;
|
|
53
|
+
}
|
|
54
|
+
/** The correlation id(s) a header value carries. */
|
|
55
|
+
export function idsFromHeader(name, value) {
|
|
56
|
+
const n = name.toLowerCase();
|
|
57
|
+
const v = value.trim();
|
|
58
|
+
let candidates;
|
|
59
|
+
if (n === "traceparent") {
|
|
60
|
+
const m = TRACEPARENT.exec(v);
|
|
61
|
+
candidates = m === null ? [] : [m[1]];
|
|
62
|
+
}
|
|
63
|
+
else if (n === "x-amzn-trace-id") {
|
|
64
|
+
const root = /(?:^|;)\s*Root=([^;]+)/i.exec(v);
|
|
65
|
+
candidates = root === null ? [] : [root[1]];
|
|
66
|
+
}
|
|
67
|
+
else if (n === "x-cloud-trace-context") {
|
|
68
|
+
candidates = [v.split("/")[0] ?? ""];
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
candidates = v.split(",").map((s) => s.trim());
|
|
72
|
+
}
|
|
73
|
+
return candidates.map(usable).filter((x) => x !== null);
|
|
74
|
+
}
|
|
75
|
+
/** Validates `--log-correlation-header` names (fails closed on the first bad one). */
|
|
76
|
+
export function parseCorrelationHeaders(raw) {
|
|
77
|
+
return raw.map((h) => {
|
|
78
|
+
const v = h.trim().toLowerCase();
|
|
79
|
+
if (!HEADER_NAME.test(v))
|
|
80
|
+
throw new LogSpecError(`--log-correlation-header: not a header name: ${JSON.stringify(h)}`);
|
|
81
|
+
return v;
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Parses `--log-id-pattern` values: `/regex/flags` whose first capture group (or whole match) is a
|
|
86
|
+
* correlation id in a log line of the operator's own format. Bounded and compiled once.
|
|
87
|
+
*/
|
|
88
|
+
export function parseLogIdPatterns(raw) {
|
|
89
|
+
return raw.map((p) => {
|
|
90
|
+
const m = /^\/(.+)\/([a-z]*)$/s.exec(p);
|
|
91
|
+
if (m === null)
|
|
92
|
+
throw new LogSpecError(`--log-id-pattern must be /regex/flags, got ${JSON.stringify(p)}`);
|
|
93
|
+
if (m[1].length > 500)
|
|
94
|
+
throw new LogSpecError("--log-id-pattern is too long (max 500 chars)");
|
|
95
|
+
try {
|
|
96
|
+
const flags = m[2].replace(/[gy]/g, "");
|
|
97
|
+
return new RegExp(m[1], `${flags}g`);
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
throw new LogSpecError(`--log-id-pattern: invalid regex ${JSON.stringify(p)}: ${e instanceof Error ? e.message : String(e)}`);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/** Records every request a run's page sends with its correlation ids (#204). */
|
|
105
|
+
export class RequestIdLedger {
|
|
106
|
+
#headers;
|
|
107
|
+
#now;
|
|
108
|
+
#byRequest = new WeakMap();
|
|
109
|
+
#byId = new Map();
|
|
110
|
+
#requests = [];
|
|
111
|
+
constructor(opts = {}) {
|
|
112
|
+
this.#headers = new Set([...DEFAULT_CORRELATION_HEADERS, ...(opts.headers ?? []).map((h) => h.toLowerCase())]);
|
|
113
|
+
this.#now = opts.now ?? clock.now;
|
|
114
|
+
}
|
|
115
|
+
/** Starts listening on a page (call before its first navigation). */
|
|
116
|
+
observe(page) {
|
|
117
|
+
page.on("request", (r) => {
|
|
118
|
+
let rec;
|
|
119
|
+
let headers;
|
|
120
|
+
try {
|
|
121
|
+
rec = { method: r.method().toUpperCase(), url: redactUrl(r.url()), status: null, startedAtMs: this.#now(), ids: [] };
|
|
122
|
+
headers = r.headers();
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return; // a request whose page is already gone: nothing to correlate
|
|
126
|
+
}
|
|
127
|
+
this.#byRequest.set(r, rec);
|
|
128
|
+
this.#requests.push(rec);
|
|
129
|
+
if (this.#requests.length > MAX_REQUESTS) {
|
|
130
|
+
const old = this.#requests.shift();
|
|
131
|
+
for (const id of old?.ids ?? [])
|
|
132
|
+
if (this.#byId.get(norm(id)) === old)
|
|
133
|
+
this.#byId.delete(norm(id));
|
|
134
|
+
}
|
|
135
|
+
this.#addIds(rec, headers);
|
|
136
|
+
});
|
|
137
|
+
page.on("response", (res) => {
|
|
138
|
+
try {
|
|
139
|
+
const rec = this.#byRequest.get(res.request());
|
|
140
|
+
if (rec === undefined)
|
|
141
|
+
return;
|
|
142
|
+
rec.status = res.status();
|
|
143
|
+
this.#addIds(rec, res.headers());
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
// a response whose page is already gone: nothing more to learn
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
#addIds(rec, headers) {
|
|
151
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
152
|
+
if (!this.#headers.has(name.toLowerCase()))
|
|
153
|
+
continue;
|
|
154
|
+
for (const id of idsFromHeader(name, value)) {
|
|
155
|
+
if (rec.ids.includes(id))
|
|
156
|
+
continue;
|
|
157
|
+
rec.ids.push(id);
|
|
158
|
+
// The same id on two requests (a trace spanning several calls): the first sender owns it.
|
|
159
|
+
if (!this.#byId.has(norm(id)))
|
|
160
|
+
this.#byId.set(norm(id), rec);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/** How many requests carried at least one id. */
|
|
165
|
+
get requestsWithIds() {
|
|
166
|
+
return this.#requests.filter((r) => r.ids.length > 0).length;
|
|
167
|
+
}
|
|
168
|
+
/** The request a log line belongs to (by an id it carries), or undefined. */
|
|
169
|
+
requestFor(raw) {
|
|
170
|
+
if (this.#byId.size === 0)
|
|
171
|
+
return undefined;
|
|
172
|
+
for (const token of tokens(raw)) {
|
|
173
|
+
const hit = this.#byId.get(norm(token));
|
|
174
|
+
if (hit !== undefined)
|
|
175
|
+
return { request: hit, id: token };
|
|
176
|
+
const tp = TRACEPARENT.exec(token);
|
|
177
|
+
if (tp !== null) {
|
|
178
|
+
const t = this.#byId.get(norm(tp[1]));
|
|
179
|
+
if (t !== undefined)
|
|
180
|
+
return { request: t, id: tp[1] };
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/** The tokens of a log line an id can be (delimited by anything an id cannot contain). */
|
|
187
|
+
function tokens(raw) {
|
|
188
|
+
return raw.split(/[^A-Za-z0-9._:-]+/).filter((t) => t.length >= MIN_ID_CHARS && t.length <= 200);
|
|
189
|
+
}
|
|
190
|
+
/** Built-in id keys: `trace_id=…`, `"traceId": "…"`, `request-id: …`, `correlationId=…`, `x-request-id=…`. */
|
|
191
|
+
const KEYED_ID = /(?:^|[^A-Za-z0-9])(?:x-)?(?:trace|request|req|correlation|corr)[_.-]?id["']?\s*[:=]\s*["']?([A-Za-z0-9][A-Za-z0-9._:-]{7,127})/gi;
|
|
192
|
+
const TRACEPARENT_IN_TEXT = /\b[0-9a-f]{2}-([0-9a-f]{32})-[0-9a-f]{16}-[0-9a-f]{2}\b/gi;
|
|
193
|
+
/**
|
|
194
|
+
* The correlation ids a log line DECLARES (#204): a keyed id (`trace_id=`, `"requestId":`, …), a
|
|
195
|
+
* `traceparent`, or an operator `--log-id-pattern` match. Used to tell another request's line
|
|
196
|
+
* (foreign) from one with no id at all (time-window fallback).
|
|
197
|
+
*/
|
|
198
|
+
export function declaredIds(raw, patterns = []) {
|
|
199
|
+
const out = new Set();
|
|
200
|
+
for (const re of [KEYED_ID, TRACEPARENT_IN_TEXT, ...patterns]) {
|
|
201
|
+
re.lastIndex = 0;
|
|
202
|
+
for (const m of raw.matchAll(re)) {
|
|
203
|
+
const id = usable(m[1] ?? m[0]);
|
|
204
|
+
if (id !== null)
|
|
205
|
+
out.add(id);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return [...out];
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=log-trace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-trace.js","sourceRoot":"","sources":["../src/log-trace.ts"],"names":[],"mappings":"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"}
|
package/dist/logs-retention.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync, readdirSync, rmSync, rmdirSync, statSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { resolveDataDir } from "./data-dir.js";
|
|
4
|
+
import { clock } from "@jevitate/domain";
|
|
4
5
|
export const DEFAULT_LOGS_RETENTION = { ttlDays: 14, keepLatest: 50 };
|
|
5
6
|
export class LogsConfigError extends Error {
|
|
6
7
|
code = "E_LOGS_CONFIG";
|
|
@@ -65,7 +66,7 @@ function runsIn(root) {
|
|
|
65
66
|
}
|
|
66
67
|
/** Applies `retention` to the logs under `root`. `dryRun` reports what would go, deleting nothing. */
|
|
67
68
|
export function pruneLogs(root, retention, opts = {}) {
|
|
68
|
-
const nowMs = opts.nowMs ??
|
|
69
|
+
const nowMs = opts.nowMs ?? clock.now();
|
|
69
70
|
const { runs, dateDirs } = runsIn(root);
|
|
70
71
|
const newestFirst = [...runs].sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
71
72
|
const cutoff = nowMs - retention.ttlDays * 86_400_000;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logs-retention.js","sourceRoot":"","sources":["../src/logs-retention.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACjF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"logs-retention.js","sourceRoot":"","sources":["../src/logs-retention.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #269 — masked key entry. The previous prompt muted readline's echo by hooking its private
|
|
3
|
+
* `_writeToOutput`, but readline's line refresh still moved the cursor and cleared the screen below
|
|
4
|
+
* it on every keystroke: the instructions printed before the prompt vanished as soon as the user
|
|
5
|
+
* typed ("flash off too fast to read"), and nothing showed that input was being received.
|
|
6
|
+
*
|
|
7
|
+
* This reader never clears anything: the instructions stay on their own line, and each typed (or
|
|
8
|
+
* pasted) character is echoed as `•` — never the character itself. Backspace erases one mark,
|
|
9
|
+
* Ctrl-U erases all, Enter finishes, Ctrl-C / Ctrl-D (on an empty entry) cancel. Without a TTY
|
|
10
|
+
* (piped input) it reads one line and echoes nothing. The value is only ever returned to the
|
|
11
|
+
* caller; it is never written to `output`.
|
|
12
|
+
*/
|
|
13
|
+
export declare const MASK_CHAR = "\u2022";
|
|
14
|
+
export interface MaskedInput extends NodeJS.ReadableStream {
|
|
15
|
+
readonly isTTY?: boolean;
|
|
16
|
+
setRawMode?(mode: boolean): unknown;
|
|
17
|
+
}
|
|
18
|
+
/** Key entry was cancelled (Ctrl-C, Ctrl-D, or the input ended): nothing was entered. */
|
|
19
|
+
export declare class KeyEntryCancelledError extends Error {
|
|
20
|
+
constructor();
|
|
21
|
+
}
|
|
22
|
+
export declare function readMaskedLine(input: MaskedInput, output: NodeJS.WritableStream, message: string): Promise<string>;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #269 — masked key entry. The previous prompt muted readline's echo by hooking its private
|
|
3
|
+
* `_writeToOutput`, but readline's line refresh still moved the cursor and cleared the screen below
|
|
4
|
+
* it on every keystroke: the instructions printed before the prompt vanished as soon as the user
|
|
5
|
+
* typed ("flash off too fast to read"), and nothing showed that input was being received.
|
|
6
|
+
*
|
|
7
|
+
* This reader never clears anything: the instructions stay on their own line, and each typed (or
|
|
8
|
+
* pasted) character is echoed as `•` — never the character itself. Backspace erases one mark,
|
|
9
|
+
* Ctrl-U erases all, Enter finishes, Ctrl-C / Ctrl-D (on an empty entry) cancel. Without a TTY
|
|
10
|
+
* (piped input) it reads one line and echoes nothing. The value is only ever returned to the
|
|
11
|
+
* caller; it is never written to `output`.
|
|
12
|
+
*/
|
|
13
|
+
export const MASK_CHAR = "•";
|
|
14
|
+
/** Key entry was cancelled (Ctrl-C, Ctrl-D, or the input ended): nothing was entered. */
|
|
15
|
+
export class KeyEntryCancelledError extends Error {
|
|
16
|
+
constructor() {
|
|
17
|
+
super("key entry cancelled — nothing was stored");
|
|
18
|
+
this.name = "KeyEntryCancelledError";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function readMaskedLine(input, output, message) {
|
|
22
|
+
const raw = input.isTTY === true && typeof input.setRawMode === "function";
|
|
23
|
+
output.write(`${message}\n> `);
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
let value = "";
|
|
26
|
+
let done = false;
|
|
27
|
+
const finish = (err) => {
|
|
28
|
+
if (done)
|
|
29
|
+
return;
|
|
30
|
+
done = true;
|
|
31
|
+
input.removeListener("data", onData);
|
|
32
|
+
input.removeListener("end", onEnd);
|
|
33
|
+
if (raw)
|
|
34
|
+
input.setRawMode?.(false);
|
|
35
|
+
input.pause();
|
|
36
|
+
output.write("\n");
|
|
37
|
+
if (err === null)
|
|
38
|
+
resolve(value);
|
|
39
|
+
else
|
|
40
|
+
reject(err);
|
|
41
|
+
};
|
|
42
|
+
const onEnd = () => {
|
|
43
|
+
// Piped input that ends without a newline still delivers what it held; an empty one is a cancel.
|
|
44
|
+
if (value.length > 0)
|
|
45
|
+
finish(null);
|
|
46
|
+
else
|
|
47
|
+
finish(new KeyEntryCancelledError());
|
|
48
|
+
};
|
|
49
|
+
const onData = (chunk) => {
|
|
50
|
+
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
51
|
+
// An escape sequence (arrow keys, function keys) is navigation, never part of a key.
|
|
52
|
+
if (raw && text.startsWith("\u001b"))
|
|
53
|
+
return;
|
|
54
|
+
for (const ch of text) {
|
|
55
|
+
if (ch === "\r" || ch === "\n") {
|
|
56
|
+
finish(null);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (!raw) {
|
|
60
|
+
value += ch;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (ch === "\u0003") {
|
|
64
|
+
finish(new KeyEntryCancelledError());
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (ch === "\u0004") {
|
|
68
|
+
if (value.length === 0)
|
|
69
|
+
finish(new KeyEntryCancelledError());
|
|
70
|
+
else
|
|
71
|
+
finish(null);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (ch === "\u007f" || ch === "\b") {
|
|
75
|
+
if (value.length > 0) {
|
|
76
|
+
value = [...value].slice(0, -1).join("");
|
|
77
|
+
output.write("\b \b");
|
|
78
|
+
}
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (ch === "\u0015") {
|
|
82
|
+
output.write("\b \b".repeat([...value].length));
|
|
83
|
+
value = "";
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (ch < " ")
|
|
87
|
+
continue; // other control characters
|
|
88
|
+
value += ch;
|
|
89
|
+
output.write(MASK_CHAR);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
if (raw)
|
|
93
|
+
input.setRawMode?.(true);
|
|
94
|
+
input.on("data", onData);
|
|
95
|
+
input.on("end", onEnd);
|
|
96
|
+
input.resume();
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=masked-input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"masked-input.js","sourceRoot":"","sources":["../src/masked-input.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp-api.d.ts
CHANGED
|
@@ -51,13 +51,15 @@ export interface McpJourneyRunOptions {
|
|
|
51
51
|
readonly emulation?: EmulationSpec;
|
|
52
52
|
readonly screenshots?: ScreenshotsSpec;
|
|
53
53
|
readonly fixtures?: (site: string) => MissionFixtures | undefined;
|
|
54
|
+
/** #303: record and compare each replayed step's action delta (`--action-deltas`). */
|
|
55
|
+
readonly actionDeltas?: boolean;
|
|
54
56
|
}
|
|
55
57
|
/**
|
|
56
58
|
* #255: what `verify_fix` hands the runner — the finding (resolved result path + fingerprint) and
|
|
57
59
|
* the CLI-parity replay options. Operator-only settings (shell hooks, `cmd:` log sources, re-sending
|
|
58
60
|
* paid/destructive hang writes, redaction literals) are never MCP arguments: targets.json decides.
|
|
59
61
|
*/
|
|
60
|
-
export type McpVerifyFixArgs = Pick<RunVerifyFixOptions, "resultPath" | "fingerprint"> & Partial<Pick<RunVerifyFixOptions, "storageState" | "browser" | "replays" | "invariantFiles" | "fixtureFlags" | "emulation" | "allowEmulationOverride" | "screenshots">>;
|
|
62
|
+
export type McpVerifyFixArgs = Pick<RunVerifyFixOptions, "resultPath" | "fingerprint"> & Partial<Pick<RunVerifyFixOptions, "storageState" | "browser" | "replays" | "invariantFiles" | "fixtureFlags" | "emulation" | "allowEmulationOverride" | "screenshots" | "actionDeltas">>;
|
|
61
63
|
export interface McpApiDeps {
|
|
62
64
|
/** Journeys store directory (`~/.jevitate/journeys` in production). */
|
|
63
65
|
journeysDir: string;
|
package/dist/mcp-api.js
CHANGED
|
@@ -15,17 +15,25 @@ import { ParamValidationError } from "@jevitate/journey";
|
|
|
15
15
|
import { safeRunPolicy as defaultRunPolicy } from "@jevitate/domain";
|
|
16
16
|
import { JourneyRequiresAuthError, UnknownJourneyError, runJourneyProgrammatically } from "./journey-api.js";
|
|
17
17
|
import { runVerifyFix } from "./verify-fix-api.js";
|
|
18
|
+
import { ExtensionMismatchError } from "./browser-run-options.js";
|
|
18
19
|
import { browserRunFromFlags, emulationFromFlags } from "./cli-shared.js";
|
|
19
20
|
import { environmentFromFlags, isEnvironmentError } from "./environments.js";
|
|
20
21
|
import { buildMissionFixtures, checkSetupRefs } from "./fixture-cli.js";
|
|
21
22
|
import { FixtureSetupError, FixtureSpecError, UnboundSetupRefError } from "./mission-fixtures.js";
|
|
22
23
|
import { parseScreenshotsArg } from "./run-screenshots.js";
|
|
23
24
|
import { makeExploreSelfHealer } from "./self-heal-adapter.js";
|
|
24
|
-
import { McpArgError, argErrorBody, optBool, optEnum, optInt, optPath, optRecordVideo, optScreenshots, optString, optStringArray, optStringMap, optViewport } from "./mcp-args.js";
|
|
25
|
+
import { McpArgError, argErrorBody, optBool, optEnum, optInt, optNamedSessions, optPath, optExtensions, optRecordVideo, optScreenshots, optString, optStringArray, optStringMap, optViewport } from "./mcp-args.js";
|
|
25
26
|
import { defaultMcpPathRoots } from "./mcp-paths.js";
|
|
26
27
|
import { CLI_TOOL_SPECS, cliToolInputSchema, runCliTool } from "./mcp-cli-tools.js";
|
|
27
28
|
import { currentEngineInfo } from "./engine.js";
|
|
28
29
|
import { loadTargetsFile } from "./target-config.js";
|
|
30
|
+
import { resourcePreflight } from "./resource-preflight.js";
|
|
31
|
+
/** #205: a native tool's `maxBrowsers` / `maxBrowserMemory` (MiB) arguments, typed like the CLI flags. */
|
|
32
|
+
function governanceArgs(args) {
|
|
33
|
+
const maxBrowsers = optInt(args, "maxBrowsers", 1);
|
|
34
|
+
const maxBrowserMemory = optInt(args, "maxBrowserMemory", 1);
|
|
35
|
+
return { ...(maxBrowsers === undefined ? {} : { maxBrowsers }), ...(maxBrowserMemory === undefined ? {} : { maxBrowserMemory }) };
|
|
36
|
+
}
|
|
29
37
|
const ALLOWED = new Set(ALLOWED_TOOLS);
|
|
30
38
|
const FORBIDDEN = new Set(FORBIDDEN_TOOLS);
|
|
31
39
|
/**
|
|
@@ -96,6 +104,7 @@ export function buildMcpTools(deps) {
|
|
|
96
104
|
...(o?.emulation === undefined ? {} : { emulation: o.emulation }),
|
|
97
105
|
...(o?.screenshots === undefined ? {} : { screenshots: o.screenshots }),
|
|
98
106
|
...(o?.fixtures === undefined ? {} : { fixtures: o.fixtures }),
|
|
107
|
+
...(o?.actionDeltas === true ? { actionDeltas: true } : {}),
|
|
99
108
|
})));
|
|
100
109
|
const pathRoots = deps.pathRoots ?? defaultMcpPathRoots();
|
|
101
110
|
// queue_exploration: enqueue over the SAME promoted fs store `jevitate
|
|
@@ -292,6 +301,7 @@ export function buildMcpTools(deps) {
|
|
|
292
301
|
...(a.emulation === undefined ? {} : { emulation: a.emulation }),
|
|
293
302
|
...(a.allowEmulationOverride === undefined ? {} : { allowEmulationOverride: a.allowEmulationOverride }),
|
|
294
303
|
...(a.screenshots === undefined ? {} : { screenshots: a.screenshots }),
|
|
304
|
+
...(a.actionDeltas === true ? { actionDeltas: true } : {}),
|
|
295
305
|
}));
|
|
296
306
|
/** #255: verify_fix's CLI-parity replay options, validated like `verify-fix`'s flags (typed, before any browser). */
|
|
297
307
|
const verifyFixOptions = (args) => {
|
|
@@ -301,6 +311,7 @@ export function buildMcpTools(deps) {
|
|
|
301
311
|
const replays = optInt(args, "replays", 1);
|
|
302
312
|
const invariantFiles = (optStringArray(args, "invariants") ?? []).map((f, i) => optPath({ [`invariants[${i}]`]: f }, `invariants[${i}]`, pathRoots));
|
|
303
313
|
const fixtures = optPath(args, "fixtures", pathRoots);
|
|
314
|
+
const fixtureIdentity = optNamedSessions(args, "fixtureIdentity", pathRoots);
|
|
304
315
|
const allowEmulationOverride = optBool(args, "allowEmulationOverride");
|
|
305
316
|
const headed = optBool(args, "headed");
|
|
306
317
|
const slowMo = optInt(args, "slowMo", 0);
|
|
@@ -308,19 +319,25 @@ export function buildMcpTools(deps) {
|
|
|
308
319
|
const shots = optScreenshots(args, pathRoots);
|
|
309
320
|
const viewport = optViewport(args);
|
|
310
321
|
const device = optString(args, "device");
|
|
322
|
+
const extension = optExtensions(args, pathRoots);
|
|
323
|
+
const governance = governanceArgs(args);
|
|
324
|
+
const actionDeltas = optBool(args, "actionDeltas");
|
|
311
325
|
try {
|
|
312
|
-
const browser = browserRunFromFlags({ browserArg: [], ...(headed === undefined ? {} : { headed }), ...(slowMo === undefined ? {} : { slowMo }), ...(recordVideo === undefined ? {} : { recordVideo }) });
|
|
326
|
+
const browser = browserRunFromFlags({ browserArg: [], ...governance, ...(extension === undefined ? {} : { extension }), ...(headed === undefined ? {} : { headed }), ...(slowMo === undefined ? {} : { slowMo }), ...(recordVideo === undefined ? {} : { recordVideo }) });
|
|
313
327
|
const screenshots = parseScreenshotsArg(shots);
|
|
314
328
|
const emulation = emulationFromFlags({ ...(viewport === undefined ? {} : { viewport }), ...(device === undefined ? {} : { device }) });
|
|
315
329
|
return {
|
|
316
330
|
...(storageState === undefined ? {} : { storageState }),
|
|
317
331
|
...(replays === undefined ? {} : { replays }),
|
|
318
332
|
...(invariantFiles.length === 0 ? {} : { invariantFiles }),
|
|
319
|
-
...(fixtures === undefined
|
|
333
|
+
...(fixtures === undefined && fixtureIdentity === undefined
|
|
334
|
+
? {}
|
|
335
|
+
: { fixtureFlags: { ...(fixtures === undefined ? {} : { fixtures }), ...(fixtureIdentity === undefined ? {} : { fixtureIdentity }) } }),
|
|
320
336
|
...(allowEmulationOverride === undefined ? {} : { allowEmulationOverride }),
|
|
321
337
|
...(browser === undefined ? {} : { browser }),
|
|
322
338
|
...(screenshots === undefined ? {} : { screenshots }),
|
|
323
339
|
...(emulation === undefined ? {} : { emulation }),
|
|
340
|
+
...(actionDeltas === true ? { actionDeltas: true } : {}),
|
|
324
341
|
};
|
|
325
342
|
}
|
|
326
343
|
catch (err) {
|
|
@@ -346,6 +363,10 @@ export function buildMcpTools(deps) {
|
|
|
346
363
|
throw err;
|
|
347
364
|
return errorResult(body);
|
|
348
365
|
}
|
|
366
|
+
// #205: the same pre-run checks as `jevitate verify-fix` (orphan sweep, starved-host refusal).
|
|
367
|
+
const starved = await resourcePreflight({});
|
|
368
|
+
if (starved !== null)
|
|
369
|
+
return errorResult({ error: "refused", code: starved.error?.code, message: starved.error?.message });
|
|
349
370
|
const ref = await resolveResultId(args.id, "verify_fix");
|
|
350
371
|
if ("response" in ref) {
|
|
351
372
|
// A mission still queued/running has nothing to verify yet: never a pass.
|
|
@@ -398,11 +419,13 @@ export function buildMcpTools(deps) {
|
|
|
398
419
|
const headed = optBool(args, "headed");
|
|
399
420
|
const slowMo = optInt(args, "slowMo", 0);
|
|
400
421
|
const recordVideo = optRecordVideo(args, pathRoots);
|
|
422
|
+
const extension = optExtensions(args, pathRoots);
|
|
423
|
+
const governance = governanceArgs(args);
|
|
401
424
|
let browser;
|
|
402
425
|
let screenshots;
|
|
403
426
|
let emulation;
|
|
404
427
|
try {
|
|
405
|
-
browser = browserRunFromFlags({ browserArg: [], ...(headed === undefined ? {} : { headed }), ...(slowMo === undefined ? {} : { slowMo }), ...(recordVideo === undefined ? {} : { recordVideo }) });
|
|
428
|
+
browser = browserRunFromFlags({ browserArg: [], ...governance, ...(extension === undefined ? {} : { extension }), ...(headed === undefined ? {} : { headed }), ...(slowMo === undefined ? {} : { slowMo }), ...(recordVideo === undefined ? {} : { recordVideo }) });
|
|
406
429
|
screenshots = parseScreenshotsArg(optScreenshots(args, pathRoots));
|
|
407
430
|
const viewport = optViewport(args);
|
|
408
431
|
const device = optString(args, "device");
|
|
@@ -414,9 +437,11 @@ export function buildMcpTools(deps) {
|
|
|
414
437
|
throw new McpArgError(err instanceof Error ? err.message : String(err));
|
|
415
438
|
}
|
|
416
439
|
const fixturesFile = optPath(args, "fixtures", pathRoots);
|
|
440
|
+
const fixtureIdentity = optNamedSessions(args, "fixtureIdentity", pathRoots);
|
|
417
441
|
// The environment's hooks apply as on the CLI; they need --allow-shell-hooks, which MCP never sets.
|
|
418
442
|
const fixtureFlags = {
|
|
419
443
|
...(fixturesFile === undefined ? {} : { fixtures: fixturesFile }),
|
|
444
|
+
...(fixtureIdentity === undefined ? {} : { fixtureIdentity }),
|
|
420
445
|
...(environment?.hooks?.before === undefined ? {} : { before: environment.hooks.before }),
|
|
421
446
|
...(environment?.hooks?.after === undefined ? {} : { after: environment.hooks.after }),
|
|
422
447
|
};
|
|
@@ -461,6 +486,7 @@ export function buildMcpTools(deps) {
|
|
|
461
486
|
...(browser === undefined ? {} : { browser }),
|
|
462
487
|
...(emulation === undefined ? {} : { emulation }),
|
|
463
488
|
...(screenshots === undefined ? {} : { screenshots }),
|
|
489
|
+
...(optBool(args, "actionDeltas") === true ? { actionDeltas: true } : {}),
|
|
464
490
|
},
|
|
465
491
|
...(usage === undefined ? {} : { usage }),
|
|
466
492
|
};
|
|
@@ -470,13 +496,14 @@ export function buildMcpTools(deps) {
|
|
|
470
496
|
description: "Replay a finding's reproduction (by mission result id — or a finished queue_exploration missionId — + fingerprint) N times in fresh browsers (default 3) — `jevitate verify-fix`. status: fixed (signal absent on every replay) | still-reproduces | intermittent (fired on some but not all replays — never a pass) | inconclusive (replay could not reach the step — never a pass). " +
|
|
471
497
|
"#255 (same as the CLI flags): 'replays' (>= 1); 'recordVideo' (true or a directory: the before/after evidence pair — `evidence.before` is the run's own clip, `evidence.after` a captioned replay — plus videoPaths); 'screenshots'; 'headed'/'slowMo'; " +
|
|
472
498
|
"'storageState' (a path inside the project or ~/.jevitate, never a repo's .jevitate/; overrides the mission's session); 'viewport' {width,height} or 'device' (refused when it differs from the finding's recorded emulation unless 'allowEmulationOverride'); " +
|
|
473
|
-
"'invariants' (invariant file paths re-checking a declared-invariant defect); 'fixtures' (a fixtures file overriding the mission's saved one). Operator-only settings (shell hooks, cmd: log sources, re-sending paid/destructive hang writes) come from targets.json, never an argument.",
|
|
499
|
+
"'invariants' (invariant file paths re-checking a declared-invariant defect); 'fixtures' (a fixtures file overriding the mission's saved one); 'fixtureIdentity' (#243: 'name=<storageState path>' entries re-binding a fixture step's auth.identity; default: the identities the mission recorded); 'extension' (#256: unpacked extension directories — refused unless the same build the finding was recorded with); 'actionDeltas' (#303, opt-in: the defect step's replayed delta vs the recorded one, as evidence on each attempt); 'maxBrowsers'/'maxBrowserMemory' (#205: machine-wide browser cap, browser memory ceiling in MiB). Operator-only settings (shell hooks, cmd: log sources, re-sending paid/destructive hang writes) come from targets.json, never an argument.",
|
|
474
500
|
inputSchema: {
|
|
475
501
|
type: "object",
|
|
476
502
|
properties: {
|
|
477
503
|
id: { type: "string" },
|
|
478
504
|
fingerprint: { type: "string" },
|
|
479
505
|
replays: { type: "integer", minimum: 1 },
|
|
506
|
+
actionDeltas: { type: "boolean" },
|
|
480
507
|
recordVideo: { type: ["boolean", "string"] },
|
|
481
508
|
screenshots: { type: ["boolean", "string"] },
|
|
482
509
|
headed: { type: "boolean" },
|
|
@@ -487,6 +514,10 @@ export function buildMcpTools(deps) {
|
|
|
487
514
|
allowEmulationOverride: { type: "boolean" },
|
|
488
515
|
invariants: { type: "array", items: { type: "string" } },
|
|
489
516
|
fixtures: { type: "string" },
|
|
517
|
+
fixtureIdentity: { type: "array", items: { type: "string" } },
|
|
518
|
+
extension: { type: "array", items: { type: "string" } },
|
|
519
|
+
maxBrowsers: { type: "integer", minimum: 1 },
|
|
520
|
+
maxBrowserMemory: { type: "integer", minimum: 1 },
|
|
490
521
|
},
|
|
491
522
|
required: ["id", "fingerprint"],
|
|
492
523
|
},
|
|
@@ -509,8 +540,8 @@ export function buildMcpTools(deps) {
|
|
|
509
540
|
"server's own browser, never returned or logged. A Journey that declares metadata.requiresAuth refuses " +
|
|
510
541
|
"with a clear error when no storageState is given. #255 (same as the CLI flags): 'env' (a named environment in .jevitate/environments.json; its session from ~/.jevitate/targets.json) and/or 'baseUrl'; " +
|
|
511
542
|
"'headed' (needs a display) and 'slowMo'; 'recordVideo' (true, or a directory) → videoPaths; 'screenshots' (true | screens | steps | mode:<dir> | <dir>) → screenshotPaths; " +
|
|
512
|
-
"'viewport' {width,height} or 'device' (mutually exclusive); 'fixtures' (a fixtures JSON path: setup before, restore after; an environment's shell hooks are never run over MCP — they refuse as the CLI does without --allow-shell-hooks); " +
|
|
513
|
-
"'selfHeal' fail-closed (default) | hybrid | full with 'real' or 'fakeAi' (a write step never self-heals). A site-policy refusal is {error: throttled, retryAfter}.",
|
|
543
|
+
"'viewport' {width,height} or 'device' (mutually exclusive); 'fixtures' (a fixtures JSON path: setup before, restore after; 'fixtureIdentity' (#243) 'name=<storageState path>' entries name who a step with auth.identity authenticates as; an environment's shell hooks are never run over MCP — they refuse as the CLI does without --allow-shell-hooks); " +
|
|
544
|
+
"'extension' (#256: unpacked extension directories inside the project or ~/.jevitate; a Journey recorded with extensions needs the same build); 'selfHeal' fail-closed (default) | hybrid | full with 'real' or 'fakeAi' (a write step never self-heals); 'actionDeltas' (#303, opt-in: each replayed step's action delta, compared with the recorded one → actionDeltas); 'maxBrowsers'/'maxBrowserMemory' (#205: machine-wide browser cap, browser memory ceiling in MiB). A site-policy refusal is {error: throttled, retryAfter}.",
|
|
514
545
|
inputSchema: {
|
|
515
546
|
type: "object",
|
|
516
547
|
properties: {
|
|
@@ -526,9 +557,14 @@ export function buildMcpTools(deps) {
|
|
|
526
557
|
viewport: { type: "object", properties: { width: { type: "integer" }, height: { type: "integer" } }, required: ["width", "height"] },
|
|
527
558
|
device: { type: "string" },
|
|
528
559
|
fixtures: { type: "string" },
|
|
560
|
+
fixtureIdentity: { type: "array", items: { type: "string" } },
|
|
529
561
|
selfHeal: { type: "string", enum: ["fail-closed", "hybrid", "full"] },
|
|
530
562
|
real: { type: "boolean" },
|
|
531
563
|
fakeAi: { type: "boolean" },
|
|
564
|
+
extension: { type: "array", items: { type: "string" } },
|
|
565
|
+
maxBrowsers: { type: "integer", minimum: 1 },
|
|
566
|
+
maxBrowserMemory: { type: "integer", minimum: 1 },
|
|
567
|
+
actionDeltas: { type: "boolean" },
|
|
532
568
|
},
|
|
533
569
|
required: ["id"],
|
|
534
570
|
},
|
|
@@ -550,6 +586,10 @@ export function buildMcpTools(deps) {
|
|
|
550
586
|
return errorResult({ error: "setup_required", message: err.message });
|
|
551
587
|
throw err;
|
|
552
588
|
}
|
|
589
|
+
// #205: the same pre-run checks as `jevitate journey run` (orphan sweep, starved-host refusal).
|
|
590
|
+
const starved = await resourcePreflight({});
|
|
591
|
+
if (starved !== null)
|
|
592
|
+
return errorResult({ error: "refused", code: starved.error?.code, message: starved.error?.message });
|
|
553
593
|
try {
|
|
554
594
|
const result = await runJourney(args.id, resolved.params, resolved.storageState, resolved.options);
|
|
555
595
|
// #163: a self-healing run's model usage lands on its result, as on the CLI.
|
|
@@ -564,6 +604,8 @@ export function buildMcpTools(deps) {
|
|
|
564
604
|
return errorResult({ error: "not_found", id: args.id, message: err.message });
|
|
565
605
|
if (err instanceof JourneyRequiresAuthError)
|
|
566
606
|
return errorResult({ error: "invalid_args", code: "E_JOURNEY_REQUIRES_AUTH", message: err.message });
|
|
607
|
+
if (err instanceof ExtensionMismatchError)
|
|
608
|
+
return errorResult({ error: "invalid_args", code: err.code, message: err.message });
|
|
567
609
|
if (err instanceof ParamValidationError)
|
|
568
610
|
return errorResult({ error: "invalid_args", code: "E_INVALID_PARAMS", message: err.message });
|
|
569
611
|
if (isEnvironmentError(err) || err instanceof FixtureSpecError || err instanceof UnboundSetupRefError) {
|