@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.
Files changed (210) hide show
  1. package/dist/ai-cli.d.ts +31 -11
  2. package/dist/ai-cli.js +100 -67
  3. package/dist/ai-cli.js.map +1 -1
  4. package/dist/author-journey-cli.js +3 -1
  5. package/dist/author-journey-cli.js.map +1 -1
  6. package/dist/bin.js +48218 -39969
  7. package/dist/browser-run-options.d.ts +32 -1
  8. package/dist/browser-run-options.js +69 -0
  9. package/dist/browser-run-options.js.map +1 -1
  10. package/dist/build-info.generated.js +2 -2
  11. package/dist/campaign-api.d.ts +151 -0
  12. package/dist/campaign-api.js +446 -0
  13. package/dist/campaign-api.js.map +1 -0
  14. package/dist/campaign-cli.d.ts +6 -0
  15. package/dist/campaign-cli.js +66 -0
  16. package/dist/campaign-cli.js.map +1 -0
  17. package/dist/check-api.js +3 -2
  18. package/dist/check-api.js.map +1 -1
  19. package/dist/check-budget.js +2 -1
  20. package/dist/check-budget.js.map +1 -1
  21. package/dist/check-execute.d.ts +4 -0
  22. package/dist/check-execute.js +41 -6
  23. package/dist/check-execute.js.map +1 -1
  24. package/dist/check-plan.d.ts +5 -0
  25. package/dist/check-plan.js +72 -4
  26. package/dist/check-plan.js.map +1 -1
  27. package/dist/check-suite.d.ts +14 -1
  28. package/dist/check-suite.js +26 -1
  29. package/dist/check-suite.js.map +1 -1
  30. package/dist/cli-output.d.ts +4 -0
  31. package/dist/cli-output.js +52 -5
  32. package/dist/cli-output.js.map +1 -1
  33. package/dist/cli-shared.d.ts +13 -3
  34. package/dist/cli-shared.js +52 -7
  35. package/dist/cli-shared.js.map +1 -1
  36. package/dist/defect-evidence.js +6 -3
  37. package/dist/defect-evidence.js.map +1 -1
  38. package/dist/demo-aspect-api.js +2 -1
  39. package/dist/demo-aspect-api.js.map +1 -1
  40. package/dist/demo-aspect-cli.js +3 -1
  41. package/dist/demo-aspect-cli.js.map +1 -1
  42. package/dist/demo-capture.d.ts +37 -3
  43. package/dist/demo-capture.js +119 -20
  44. package/dist/demo-capture.js.map +1 -1
  45. package/dist/explore-adversarial.d.ts +11 -4
  46. package/dist/explore-adversarial.js +21 -12
  47. package/dist/explore-adversarial.js.map +1 -1
  48. package/dist/explore-cli.js +262 -26
  49. package/dist/explore-cli.js.map +1 -1
  50. package/dist/explore-coverage.d.ts +17 -2
  51. package/dist/explore-coverage.js +19 -13
  52. package/dist/explore-coverage.js.map +1 -1
  53. package/dist/explore-feature.d.ts +0 -2
  54. package/dist/explore-feature.js +6 -8
  55. package/dist/explore-feature.js.map +1 -1
  56. package/dist/explore-goal.d.ts +25 -7
  57. package/dist/explore-goal.js +61 -18
  58. package/dist/explore-goal.js.map +1 -1
  59. package/dist/explore-shared.d.ts +27 -2
  60. package/dist/explore-shared.js +21 -1
  61. package/dist/explore-shared.js.map +1 -1
  62. package/dist/fixture-auth.d.ts +13 -2
  63. package/dist/fixture-auth.js +19 -6
  64. package/dist/fixture-auth.js.map +1 -1
  65. package/dist/fixture-cli.d.ts +29 -0
  66. package/dist/fixture-cli.js +121 -7
  67. package/dist/fixture-cli.js.map +1 -1
  68. package/dist/github-issue-filer.js +2 -1
  69. package/dist/github-issue-filer.js.map +1 -1
  70. package/dist/host-health-run.d.ts +2 -1
  71. package/dist/host-health-run.js +8 -2
  72. package/dist/host-health-run.js.map +1 -1
  73. package/dist/init-cli.js +34 -5
  74. package/dist/init-cli.js.map +1 -1
  75. package/dist/init-keys.d.ts +11 -0
  76. package/dist/init-keys.js +5 -2
  77. package/dist/init-keys.js.map +1 -1
  78. package/dist/journey-annotate-api.js +17 -4
  79. package/dist/journey-annotate-api.js.map +1 -1
  80. package/dist/journey-annotate-cli.js +3 -1
  81. package/dist/journey-annotate-cli.js.map +1 -1
  82. package/dist/journey-api.d.ts +26 -1
  83. package/dist/journey-api.js +51 -25
  84. package/dist/journey-api.js.map +1 -1
  85. package/dist/journey-cli.d.ts +1 -1
  86. package/dist/journey-cli.js +44 -3
  87. package/dist/journey-cli.js.map +1 -1
  88. package/dist/journey-demo-api.d.ts +2 -0
  89. package/dist/journey-demo-api.js +23 -10
  90. package/dist/journey-demo-api.js.map +1 -1
  91. package/dist/journey-demo-cli.js +5 -2
  92. package/dist/journey-demo-cli.js.map +1 -1
  93. package/dist/journey-prefix.d.ts +194 -0
  94. package/dist/journey-prefix.js +279 -0
  95. package/dist/journey-prefix.js.map +1 -0
  96. package/dist/key-report.d.ts +49 -0
  97. package/dist/key-report.js +101 -0
  98. package/dist/key-report.js.map +1 -0
  99. package/dist/kill-signal.js +0 -1
  100. package/dist/kill-signal.js.map +1 -1
  101. package/dist/ledger-api.js +3 -1
  102. package/dist/ledger-api.js.map +1 -1
  103. package/dist/log-correlation.d.ts +54 -1
  104. package/dist/log-correlation.js +83 -14
  105. package/dist/log-correlation.js.map +1 -1
  106. package/dist/log-sources.js +6 -5
  107. package/dist/log-sources.js.map +1 -1
  108. package/dist/log-trace.d.ts +80 -0
  109. package/dist/log-trace.js +210 -0
  110. package/dist/log-trace.js.map +1 -0
  111. package/dist/logs-retention.js +2 -1
  112. package/dist/logs-retention.js.map +1 -1
  113. package/dist/masked-input.d.ts +22 -0
  114. package/dist/masked-input.js +99 -0
  115. package/dist/masked-input.js.map +1 -0
  116. package/dist/mcp-api.d.ts +3 -1
  117. package/dist/mcp-api.js +49 -7
  118. package/dist/mcp-api.js.map +1 -1
  119. package/dist/mcp-args.d.ts +12 -0
  120. package/dist/mcp-args.js +33 -0
  121. package/dist/mcp-args.js.map +1 -1
  122. package/dist/mcp-cli-tools.d.ts +5 -1
  123. package/dist/mcp-cli-tools.js +73 -16
  124. package/dist/mcp-cli-tools.js.map +1 -1
  125. package/dist/mission-api.js +2 -1
  126. package/dist/mission-api.js.map +1 -1
  127. package/dist/mission-cli.js +2 -1
  128. package/dist/mission-cli.js.map +1 -1
  129. package/dist/mission-fixtures.d.ts +10 -1
  130. package/dist/mission-fixtures.js +49 -15
  131. package/dist/mission-fixtures.js.map +1 -1
  132. package/dist/mission-queue-runner.js +11 -2
  133. package/dist/mission-queue-runner.js.map +1 -1
  134. package/dist/multi-run-cli.js +7 -1
  135. package/dist/multi-run-cli.js.map +1 -1
  136. package/dist/openrouter-call.js +3 -2
  137. package/dist/openrouter-call.js.map +1 -1
  138. package/dist/program.js +4 -0
  139. package/dist/program.js.map +1 -1
  140. package/dist/project-dir.js +3 -2
  141. package/dist/project-dir.js.map +1 -1
  142. package/dist/record-api.d.ts +3 -1
  143. package/dist/record-api.js +5 -2
  144. package/dist/record-api.js.map +1 -1
  145. package/dist/record-cli.js +14 -2
  146. package/dist/record-cli.js.map +1 -1
  147. package/dist/regression-api.d.ts +6 -0
  148. package/dist/regression-api.js +4 -2
  149. package/dist/regression-api.js.map +1 -1
  150. package/dist/regression-cli.js +56 -4
  151. package/dist/regression-cli.js.map +1 -1
  152. package/dist/report-api.js +2 -1
  153. package/dist/report-api.js.map +1 -1
  154. package/dist/resource-preflight.d.ts +87 -0
  155. package/dist/resource-preflight.js +160 -0
  156. package/dist/resource-preflight.js.map +1 -0
  157. package/dist/result-schema.d.ts +1 -1
  158. package/dist/result-schema.js +1 -1
  159. package/dist/run-key-preflight.d.ts +42 -0
  160. package/dist/run-key-preflight.js +82 -0
  161. package/dist/run-key-preflight.js.map +1 -0
  162. package/dist/run-screenshots.js +11 -5
  163. package/dist/run-screenshots.js.map +1 -1
  164. package/dist/runtime-env.js +3 -2
  165. package/dist/runtime-env.js.map +1 -1
  166. package/dist/server-log-verify.d.ts +2 -0
  167. package/dist/server-log-verify.js +8 -3
  168. package/dist/server-log-verify.js.map +1 -1
  169. package/dist/site-gate-cli.js +2 -1
  170. package/dist/site-gate-cli.js.map +1 -1
  171. package/dist/source-api.js +2 -1
  172. package/dist/source-api.js.map +1 -1
  173. package/dist/suite-explore-options.d.ts +32 -0
  174. package/dist/suite-explore-options.js +22 -0
  175. package/dist/suite-explore-options.js.map +1 -1
  176. package/dist/target-config.d.ts +6 -0
  177. package/dist/target-config.js +9 -0
  178. package/dist/target-config.js.map +1 -1
  179. package/dist/type-fixture-file.d.ts +9 -0
  180. package/dist/type-fixture-file.js +48 -0
  181. package/dist/type-fixture-file.js.map +1 -0
  182. package/dist/ui-api.js +4 -3
  183. package/dist/ui-api.js.map +1 -1
  184. package/dist/usability-capture.js +4 -3
  185. package/dist/usability-capture.js.map +1 -1
  186. package/dist/usage-config.d.ts +1 -1
  187. package/dist/usage-config.js +1 -1
  188. package/dist/ux-claim-probe.d.ts +45 -0
  189. package/dist/ux-claim-probe.js +386 -0
  190. package/dist/ux-claim-probe.js.map +1 -0
  191. package/dist/ux-cli.js +8 -1
  192. package/dist/ux-cli.js.map +1 -1
  193. package/dist/ux-product.d.ts +14 -0
  194. package/dist/ux-product.js +39 -0
  195. package/dist/ux-product.js.map +1 -0
  196. package/dist/ux-review.d.ts +4 -0
  197. package/dist/ux-review.js +27 -7
  198. package/dist/ux-review.js.map +1 -1
  199. package/dist/ux-usability.d.ts +34 -9
  200. package/dist/ux-usability.js +100 -14
  201. package/dist/ux-usability.js.map +1 -1
  202. package/dist/verify-fix-api.d.ts +21 -0
  203. package/dist/verify-fix-api.js +85 -33
  204. package/dist/verify-fix-api.js.map +1 -1
  205. package/dist/verify-fix-cli.js +7 -2
  206. package/dist/verify-fix-cli.js.map +1 -1
  207. package/package.json +9 -9
  208. package/skills/jevitate-explore/SKILL.md +8 -0
  209. package/skills/jevitate-getting-started/SKILL.md +2 -1
  210. 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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"}
@@ -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 ?? Date.now();
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":"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"}
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 ? {} : { fixtureFlags: { fixtures } }),
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) {