@jevitate/cli 0.2.0 → 0.4.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 (217) 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 +48715 -40027
  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 +153 -0
  12. package/dist/campaign-api.js +447 -0
  13. package/dist/campaign-api.js.map +1 -0
  14. package/dist/campaign-cli.d.ts +8 -0
  15. package/dist/campaign-cli.js +109 -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 +46 -10
  23. package/dist/check-execute.js.map +1 -1
  24. package/dist/check-plan.d.ts +5 -0
  25. package/dist/check-plan.js +77 -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.d.ts +3 -2
  37. package/dist/defect-evidence.js +12 -8
  38. package/dist/defect-evidence.js.map +1 -1
  39. package/dist/demo-aspect-api.js +2 -1
  40. package/dist/demo-aspect-api.js.map +1 -1
  41. package/dist/demo-aspect-cli.js +3 -1
  42. package/dist/demo-aspect-cli.js.map +1 -1
  43. package/dist/demo-capture.d.ts +37 -3
  44. package/dist/demo-capture.js +119 -20
  45. package/dist/demo-capture.js.map +1 -1
  46. package/dist/explore-adversarial.d.ts +11 -4
  47. package/dist/explore-adversarial.js +22 -13
  48. package/dist/explore-adversarial.js.map +1 -1
  49. package/dist/explore-cli.js +282 -31
  50. package/dist/explore-cli.js.map +1 -1
  51. package/dist/explore-coverage.d.ts +17 -2
  52. package/dist/explore-coverage.js +20 -14
  53. package/dist/explore-coverage.js.map +1 -1
  54. package/dist/explore-feature.d.ts +0 -2
  55. package/dist/explore-feature.js +7 -9
  56. package/dist/explore-feature.js.map +1 -1
  57. package/dist/explore-goal.d.ts +25 -7
  58. package/dist/explore-goal.js +62 -19
  59. package/dist/explore-goal.js.map +1 -1
  60. package/dist/explore-shared.d.ts +53 -3
  61. package/dist/explore-shared.js +48 -1
  62. package/dist/explore-shared.js.map +1 -1
  63. package/dist/fixture-auth.d.ts +13 -2
  64. package/dist/fixture-auth.js +19 -6
  65. package/dist/fixture-auth.js.map +1 -1
  66. package/dist/fixture-cli.d.ts +29 -0
  67. package/dist/fixture-cli.js +121 -7
  68. package/dist/fixture-cli.js.map +1 -1
  69. package/dist/github-issue-filer.js +2 -1
  70. package/dist/github-issue-filer.js.map +1 -1
  71. package/dist/host-health-run.d.ts +2 -1
  72. package/dist/host-health-run.js +8 -2
  73. package/dist/host-health-run.js.map +1 -1
  74. package/dist/init-cli.js +34 -5
  75. package/dist/init-cli.js.map +1 -1
  76. package/dist/init-keys.d.ts +11 -0
  77. package/dist/init-keys.js +5 -2
  78. package/dist/init-keys.js.map +1 -1
  79. package/dist/journey-annotate-api.js +17 -4
  80. package/dist/journey-annotate-api.js.map +1 -1
  81. package/dist/journey-annotate-cli.js +3 -1
  82. package/dist/journey-annotate-cli.js.map +1 -1
  83. package/dist/journey-api.d.ts +26 -1
  84. package/dist/journey-api.js +51 -25
  85. package/dist/journey-api.js.map +1 -1
  86. package/dist/journey-cli.d.ts +1 -1
  87. package/dist/journey-cli.js +44 -3
  88. package/dist/journey-cli.js.map +1 -1
  89. package/dist/journey-demo-api.d.ts +2 -0
  90. package/dist/journey-demo-api.js +23 -10
  91. package/dist/journey-demo-api.js.map +1 -1
  92. package/dist/journey-demo-cli.js +5 -2
  93. package/dist/journey-demo-cli.js.map +1 -1
  94. package/dist/journey-prefix.d.ts +194 -0
  95. package/dist/journey-prefix.js +279 -0
  96. package/dist/journey-prefix.js.map +1 -0
  97. package/dist/key-report.d.ts +49 -0
  98. package/dist/key-report.js +101 -0
  99. package/dist/key-report.js.map +1 -0
  100. package/dist/kill-signal.js +0 -1
  101. package/dist/kill-signal.js.map +1 -1
  102. package/dist/ledger-api.js +3 -1
  103. package/dist/ledger-api.js.map +1 -1
  104. package/dist/log-correlation.d.ts +63 -1
  105. package/dist/log-correlation.js +125 -16
  106. package/dist/log-correlation.js.map +1 -1
  107. package/dist/log-sources.js +6 -5
  108. package/dist/log-sources.js.map +1 -1
  109. package/dist/log-trace.d.ts +80 -0
  110. package/dist/log-trace.js +210 -0
  111. package/dist/log-trace.js.map +1 -0
  112. package/dist/logs-cli.js +60 -1
  113. package/dist/logs-cli.js.map +1 -1
  114. package/dist/logs-retention.js +2 -1
  115. package/dist/logs-retention.js.map +1 -1
  116. package/dist/masked-input.d.ts +22 -0
  117. package/dist/masked-input.js +99 -0
  118. package/dist/masked-input.js.map +1 -0
  119. package/dist/mcp-api.d.ts +3 -1
  120. package/dist/mcp-api.js +49 -7
  121. package/dist/mcp-api.js.map +1 -1
  122. package/dist/mcp-args.d.ts +12 -0
  123. package/dist/mcp-args.js +33 -0
  124. package/dist/mcp-args.js.map +1 -1
  125. package/dist/mcp-cli-tools.d.ts +6 -1
  126. package/dist/mcp-cli-tools.js +103 -16
  127. package/dist/mcp-cli-tools.js.map +1 -1
  128. package/dist/mission-api.js +2 -1
  129. package/dist/mission-api.js.map +1 -1
  130. package/dist/mission-cli.js +3 -1
  131. package/dist/mission-cli.js.map +1 -1
  132. package/dist/mission-fixtures.d.ts +10 -1
  133. package/dist/mission-fixtures.js +49 -15
  134. package/dist/mission-fixtures.js.map +1 -1
  135. package/dist/mission-queue-runner.d.ts +2 -0
  136. package/dist/mission-queue-runner.js +18 -5
  137. package/dist/mission-queue-runner.js.map +1 -1
  138. package/dist/multi-run-cli.js +7 -1
  139. package/dist/multi-run-cli.js.map +1 -1
  140. package/dist/openrouter-call.js +3 -2
  141. package/dist/openrouter-call.js.map +1 -1
  142. package/dist/program.js +4 -0
  143. package/dist/program.js.map +1 -1
  144. package/dist/project-dir.js +3 -2
  145. package/dist/project-dir.js.map +1 -1
  146. package/dist/record-api.d.ts +3 -1
  147. package/dist/record-api.js +5 -2
  148. package/dist/record-api.js.map +1 -1
  149. package/dist/record-cli.js +14 -2
  150. package/dist/record-cli.js.map +1 -1
  151. package/dist/regression-api.d.ts +6 -0
  152. package/dist/regression-api.js +4 -2
  153. package/dist/regression-api.js.map +1 -1
  154. package/dist/regression-cli.js +56 -4
  155. package/dist/regression-cli.js.map +1 -1
  156. package/dist/report-api.js +2 -1
  157. package/dist/report-api.js.map +1 -1
  158. package/dist/resource-preflight.d.ts +87 -0
  159. package/dist/resource-preflight.js +160 -0
  160. package/dist/resource-preflight.js.map +1 -0
  161. package/dist/result-schema.d.ts +1 -1
  162. package/dist/result-schema.js +1 -1
  163. package/dist/run-key-preflight.d.ts +42 -0
  164. package/dist/run-key-preflight.js +82 -0
  165. package/dist/run-key-preflight.js.map +1 -0
  166. package/dist/run-screenshots.js +11 -5
  167. package/dist/run-screenshots.js.map +1 -1
  168. package/dist/runtime-env.js +3 -2
  169. package/dist/runtime-env.js.map +1 -1
  170. package/dist/server-log-verify.d.ts +2 -0
  171. package/dist/server-log-verify.js +8 -3
  172. package/dist/server-log-verify.js.map +1 -1
  173. package/dist/signal-triage.d.ts +147 -0
  174. package/dist/signal-triage.js +281 -0
  175. package/dist/signal-triage.js.map +1 -0
  176. package/dist/site-gate-cli.js +2 -1
  177. package/dist/site-gate-cli.js.map +1 -1
  178. package/dist/source-api.js +2 -1
  179. package/dist/source-api.js.map +1 -1
  180. package/dist/suite-explore-options.d.ts +36 -0
  181. package/dist/suite-explore-options.js +23 -0
  182. package/dist/suite-explore-options.js.map +1 -1
  183. package/dist/target-config.d.ts +12 -0
  184. package/dist/target-config.js +15 -0
  185. package/dist/target-config.js.map +1 -1
  186. package/dist/type-fixture-file.d.ts +9 -0
  187. package/dist/type-fixture-file.js +48 -0
  188. package/dist/type-fixture-file.js.map +1 -0
  189. package/dist/ui-api.js +4 -3
  190. package/dist/ui-api.js.map +1 -1
  191. package/dist/usability-capture.js +4 -3
  192. package/dist/usability-capture.js.map +1 -1
  193. package/dist/usage-config.d.ts +1 -1
  194. package/dist/usage-config.js +1 -1
  195. package/dist/ux-claim-probe.d.ts +45 -0
  196. package/dist/ux-claim-probe.js +386 -0
  197. package/dist/ux-claim-probe.js.map +1 -0
  198. package/dist/ux-cli.js +8 -1
  199. package/dist/ux-cli.js.map +1 -1
  200. package/dist/ux-product.d.ts +14 -0
  201. package/dist/ux-product.js +39 -0
  202. package/dist/ux-product.js.map +1 -0
  203. package/dist/ux-review.d.ts +4 -0
  204. package/dist/ux-review.js +27 -7
  205. package/dist/ux-review.js.map +1 -1
  206. package/dist/ux-usability.d.ts +34 -9
  207. package/dist/ux-usability.js +102 -16
  208. package/dist/ux-usability.js.map +1 -1
  209. package/dist/verify-fix-api.d.ts +21 -0
  210. package/dist/verify-fix-api.js +85 -33
  211. package/dist/verify-fix-api.js.map +1 -1
  212. package/dist/verify-fix-cli.js +7 -2
  213. package/dist/verify-fix-cli.js.map +1 -1
  214. package/package.json +11 -11
  215. package/skills/jevitate-explore/SKILL.md +13 -0
  216. package/skills/jevitate-getting-started/SKILL.md +2 -1
  217. package/skills/jevitate-test-campaign/SKILL.md +206 -0
@@ -0,0 +1,45 @@
1
+ import type { Page } from "playwright";
2
+ import { type SafetyConfig } from "@jevitate/explore";
3
+ import { type Control, type GuardProbe, type UxEvidence, type UxFinding } from "@jevitate/ux";
4
+ /** At most this many controls are probed per run; the rest are recorded as not probed (unverifiable). */
5
+ export declare const MAX_GUARD_PROBES = 25;
6
+ /** Why nothing was clicked without the opt-in. */
7
+ export declare const PROBE_OPT_IN_REASON = "not probed: clicking destructive controls is opt-in (--probe-guards)";
8
+ /** The fail-safe destructive-request classifier, shared with the find-out guard (#270): one vocabulary. */
9
+ export declare function looksDestructive(url: string, body?: string | null, method?: string): boolean;
10
+ export interface ProbeTarget {
11
+ readonly screen: UxEvidence;
12
+ readonly control: Control;
13
+ /** Redacted `role "name"`. */
14
+ readonly label: string;
15
+ /** controlKey over the REDACTED role/name (what the claim pipeline matches on). */
16
+ readonly key: string;
17
+ readonly route: string;
18
+ }
19
+ /** The destructive controls to probe (one per route × control) and the ones a `--deny` pattern refuses. */
20
+ export declare function planGuardProbes(screens: readonly UxEvidence[], secrets: readonly string[], safety?: SafetyConfig): {
21
+ readonly targets: ProbeTarget[];
22
+ readonly refused: GuardProbe[];
23
+ };
24
+ /** The opt-out record: every planned target is `skipped` (its guard claim: unverifiable). */
25
+ export declare function skippedProbes(targets: readonly ProbeTarget[]): GuardProbe[];
26
+ /**
27
+ * Probes each target, each on a fresh page in `runPage`'s context (call only with the opt-in).
28
+ * Never throws per target; a target that cannot be probed safely is `refused`/`failed`.
29
+ */
30
+ export declare function runGuardProbes(runPage: Page, targets: readonly ProbeTarget[], opts: {
31
+ readonly allowlist: readonly string[];
32
+ readonly secrets: readonly string[];
33
+ }): Promise<GuardProbe[]>;
34
+ /**
35
+ * Cropped, masked, boxed screenshots for the verified claim findings (`finding.screenshot`). Best
36
+ * effort per finding: one that cannot be located or masked has none. Fails CLOSED on masking: if
37
+ * the pixel mask cannot be installed or proven, no screenshot is written.
38
+ */
39
+ export declare function captureFindingShots(page: Page, findings: readonly UxFinding[], screens: ReadonlyMap<string, UxEvidence>, opts: {
40
+ readonly dir: string;
41
+ readonly allowlist: readonly string[];
42
+ readonly secrets: readonly string[];
43
+ }): Promise<UxFinding[]>;
44
+ /** Opens the dedicated probe page in the run's context; closing it deletes its video, if any. */
45
+ export declare function withProbePage<T>(runPage: Page, fn: (page: Page) => Promise<T>): Promise<T>;
@@ -0,0 +1,386 @@
1
+ // ux-claim-probe.ts — the CODE side of the UX claim pipeline (#198), on the live run's browser.
2
+ //
3
+ // 1. Guard probes — OPT-IN (`--probe-guards`). Without it nothing is clicked: every destructive
4
+ // control is recorded `skipped`, so its guard claim is reported unverifiable (coverage), never
5
+ // asserted and never silently dropped. With it, every control the shared safety policy calls
6
+ // DESTRUCTIVE (explore safety.ts's vocabulary) on an analyzed screen is clicked ONCE (per route ×
7
+ // control), each on a FRESH page in the run's context, fail-safe:
8
+ // - refused (→ unverifiable, reason recorded) when the page has an open WebSocket or
9
+ // EventSource, or a service worker controls it — writes over those cannot be blocked;
10
+ // - new WebSocket/EventSource connections are refused by the page once the probe is armed;
11
+ // - every request is aborted that is not GET/HEAD/OPTIONS, or whose URL (path or query) or body
12
+ // names a destructive verb (delete, remove, destroy, revoke, purge, archive… — also as an RPC
13
+ // name such as `DeleteUser`), whatever its method;
14
+ // - the page is closed as soon as a confirm/alert or a page dialog is observed: the probe never
15
+ // clicks anything inside a dialog (a native dialog is dismissed — cancel — then the page closes).
16
+ // It records whether a dialog or confirmation page guarded the click and which writes it
17
+ // attempted. A `--deny` control is never clicked (recorded `refused`).
18
+ // 2. Finding screenshots: each verified claim finding with a cited control or quoted text gets a
19
+ // cropped, secret-masked screenshot (the run's pixel mask, demo-capture.ts) with the cited
20
+ // element boxed (no click; the same request blocker is active).
21
+ //
22
+ // Both run on DEDICATED pages in the run's own browser context (same session), after the run's
23
+ // loop ended — never on the page the run's oracles listen to — and a probe page's video (if
24
+ // recorded) is deleted.
25
+ import { mkdir } from "node:fs/promises";
26
+ import { join } from "node:path";
27
+ import { SafetyPolicy, assertAuthorizedExploreTarget, controlRisk, endpointOf, looksDestructiveRequest, redactText, redactUrl } from "@jevitate/explore";
28
+ import { descriptorToLocator } from "@jevitate/recorder";
29
+ import { controlKey, redactEvidence, routeOf } from "@jevitate/ux";
30
+ import { MaskUnavailableError, SecretPixelMask, captureStepScreenshot } from "./demo-capture.js";
31
+ import { clock } from "@jevitate/domain";
32
+ const SAFE_METHODS = new Set(["GET", "HEAD", "OPTIONS"]);
33
+ const OPEN_DIALOGS = 'dialog[open], [role="dialog"], [role="alertdialog"], [aria-modal="true"]';
34
+ const PROBE_TIMEOUT_MS = 8_000;
35
+ /** At most this many controls are probed per run; the rest are recorded as not probed (unverifiable). */
36
+ export const MAX_GUARD_PROBES = 25;
37
+ const SETTLE_MS = 1_500;
38
+ /** Why nothing was clicked without the opt-in. */
39
+ export const PROBE_OPT_IN_REASON = "not probed: clicking destructive controls is opt-in (--probe-guards)";
40
+ /** The fail-safe destructive-request classifier, shared with the find-out guard (#270): one vocabulary. */
41
+ export function looksDestructive(url, body = null, method = "GET") {
42
+ return looksDestructiveRequest(method, url, body);
43
+ }
44
+ /** The destructive controls to probe (one per route × control) and the ones a `--deny` pattern refuses. */
45
+ export function planGuardProbes(screens, secrets, safety) {
46
+ // Only the operator's --deny patterns refuse here: the built-in destructive category is exactly
47
+ // what the (opted-in) probe exists to click — fail-safe, its writes blocked.
48
+ const deny = new SafetyPolicy({ deny: safety?.deny ?? [], allowDestructive: true });
49
+ const seen = new Set();
50
+ const targets = [];
51
+ const refused = [];
52
+ for (const screen of screens) {
53
+ const red = redactEvidence(screen, secrets);
54
+ const route = routeOf(red.url);
55
+ screen.controls.forEach((c, i) => {
56
+ if (controlRisk(c.name, c.role)?.risk !== "destructive")
57
+ return;
58
+ const rc = red.controls[i];
59
+ const key = controlKey(rc);
60
+ if (seen.has(`${route}|${key}`))
61
+ return;
62
+ seen.add(`${route}|${key}`);
63
+ const label = `${rc.role || "control"} "${rc.name}"`;
64
+ const verdict = deny.refuses({ name: c.name, role: c.role, descriptor: c.descriptor ?? {} });
65
+ if (verdict !== null) {
66
+ refused.push({ screenId: screen.screenId, route, control: label, controlKey: key, status: "refused", detail: verdict.reason });
67
+ return;
68
+ }
69
+ targets.push({ screen, control: c, label, key, route });
70
+ });
71
+ }
72
+ return { targets, refused };
73
+ }
74
+ /** The opt-out record: every planned target is `skipped` (its guard claim: unverifiable). */
75
+ export function skippedProbes(targets) {
76
+ return targets.map((t) => ({ screenId: t.screen.screenId, route: t.route, control: t.label, controlKey: t.key, status: "skipped", detail: PROBE_OPT_IN_REASON }));
77
+ }
78
+ function locatorFor(page, c) {
79
+ if (c.descriptor !== undefined)
80
+ return descriptorToLocator(page, c.descriptor);
81
+ return page.getByRole(c.role, { name: c.name, exact: true });
82
+ }
83
+ function errText(e) {
84
+ return (e instanceof Error ? e.message : String(e)).split("\n")[0].slice(0, 200);
85
+ }
86
+ /**
87
+ * Aborts every write and every destructive-looking request (see `looksDestructive`); records each
88
+ * one, in order. `allowDocument` is the screen's own URL: its load is let through.
89
+ */
90
+ function writeBlocker(secrets, seq, allowDocument) {
91
+ const blocked = [];
92
+ const handler = async (route) => {
93
+ const req = route.request();
94
+ const method = req.method().toUpperCase();
95
+ let body = null;
96
+ try {
97
+ body = req.postData();
98
+ }
99
+ catch {
100
+ body = null;
101
+ }
102
+ const own = allowDocument?.() !== undefined && req.isNavigationRequest() && req.url() === allowDocument();
103
+ if (own || (SAFE_METHODS.has(method) && !looksDestructive(req.url(), body, method))) {
104
+ await route.continue().catch(() => undefined);
105
+ return;
106
+ }
107
+ blocked.push({ at: seq.n++, endpoint: redactText(redactUrl(endpointOf(method, req.url())), secrets) });
108
+ await route.abort("blockedbyclient").catch(() => undefined);
109
+ };
110
+ return { blocked, handler };
111
+ }
112
+ async function openDialogs(page) {
113
+ return page
114
+ .evaluate((sel) => Array.from(document.querySelectorAll(sel)).filter((el) => {
115
+ const r = el.getBoundingClientRect();
116
+ const st = getComputedStyle(el);
117
+ return r.width > 0 && r.height > 0 && st.visibility !== "hidden" && st.display !== "none";
118
+ }).length, OPEN_DIALOGS)
119
+ .catch(() => 0);
120
+ }
121
+ /**
122
+ * BROWSER (init script on a probe page): counts WebSocket/EventSource connections the page opens,
123
+ * and refuses new ones once `window.__jevProbeArmed` is set (just before the click).
124
+ */
125
+ const CONNECTION_GUARD = `(() => {
126
+ const w = window;
127
+ w.__jevProbeConnections = 0;
128
+ for (const name of ["WebSocket", "EventSource"]) {
129
+ const Orig = w[name];
130
+ if (typeof Orig !== "function") continue;
131
+ const Wrapped = function (...args) {
132
+ if (w.__jevProbeArmed) throw new Error("jevitate guard probe: " + name + " refused while probing");
133
+ w.__jevProbeConnections += 1;
134
+ return new Orig(...args);
135
+ };
136
+ Wrapped.prototype = Orig.prototype;
137
+ for (const k of ["CONNECTING", "OPEN", "CLOSING", "CLOSED"]) if (k in Orig) Object.defineProperty(Wrapped, k, { value: Orig[k] });
138
+ w[name] = Wrapped;
139
+ }
140
+ })();`;
141
+ /** Why the page cannot be probed safely (a channel whose writes the probe cannot block), or null. */
142
+ async function unsafeChannel(page, sockets) {
143
+ const state = await page
144
+ .evaluate(() => {
145
+ const w = window;
146
+ return { connections: w.__jevProbeConnections ?? 0, sw: typeof navigator.serviceWorker !== "undefined" && navigator.serviceWorker.controller !== null };
147
+ })
148
+ .catch(() => null);
149
+ if (state === null)
150
+ return "the page's connections could not be inspected";
151
+ if (sockets > 0)
152
+ return "the page has an open WebSocket (its messages cannot be blocked)";
153
+ if (state.connections > 0)
154
+ return "the page opened a WebSocket or EventSource (its messages cannot be blocked)";
155
+ if (state.sw)
156
+ return "a service worker controls the page (its requests cannot be blocked)";
157
+ return null;
158
+ }
159
+ /** Opens a fresh probe page in `runPage`'s context; `close()` closes it and deletes its video. */
160
+ async function openProbePage(runPage) {
161
+ const page = await runPage.context().newPage();
162
+ let closed = false;
163
+ return {
164
+ page,
165
+ close: async () => {
166
+ if (closed)
167
+ return;
168
+ closed = true;
169
+ const video = page.video();
170
+ await page.close().catch(() => undefined);
171
+ await video?.delete().catch(() => undefined);
172
+ },
173
+ };
174
+ }
175
+ /**
176
+ * Probes each target, each on a fresh page in `runPage`'s context (call only with the opt-in).
177
+ * Never throws per target; a target that cannot be probed safely is `refused`/`failed`.
178
+ */
179
+ export async function runGuardProbes(runPage, targets, opts) {
180
+ const out = [];
181
+ for (const [i, t] of targets.entries()) {
182
+ const base = { screenId: t.screen.screenId, route: t.route, control: t.label, controlKey: t.key };
183
+ if (i >= MAX_GUARD_PROBES) {
184
+ out.push({ ...base, status: "failed", detail: `not probed: the run's probe cap (${MAX_GUARD_PROBES} controls) was reached` });
185
+ continue;
186
+ }
187
+ let probe;
188
+ try {
189
+ assertAuthorizedExploreTarget(t.screen.url, opts.allowlist);
190
+ probe = await openProbePage(runPage);
191
+ const page = probe.page;
192
+ const seq = { n: 0 };
193
+ let screenUrl = t.screen.url;
194
+ const { blocked, handler } = writeBlocker(opts.secrets, seq, () => screenUrl);
195
+ const dialogs = [];
196
+ let sockets = 0;
197
+ page.on("websocket", () => {
198
+ sockets += 1;
199
+ });
200
+ page.on("dialog", (d) => {
201
+ dialogs.push({ at: seq.n++, type: d.type() });
202
+ // Cancel — never accept: the probe never confirms anything. The page is closed right after.
203
+ void d.dismiss().catch(() => undefined);
204
+ });
205
+ await page.addInitScript({ content: CONNECTION_GUARD });
206
+ await page.route("**/*", handler);
207
+ await page.goto(t.screen.url, { waitUntil: "domcontentloaded", timeout: PROBE_TIMEOUT_MS });
208
+ await page.waitForLoadState("networkidle", { timeout: 2_000 }).catch(() => undefined);
209
+ screenUrl = undefined; // from here on, even a reload of the screen goes through the blocker
210
+ const loc = locatorFor(page, t.control).first();
211
+ if (!(await loc.isVisible().catch(() => false))) {
212
+ out.push({ ...base, status: "not-found", detail: "the control was not visible on a fresh load of its screen (it may need state the run built up)" });
213
+ continue;
214
+ }
215
+ const unsafe = await unsafeChannel(page, sockets);
216
+ if (unsafe !== null) {
217
+ out.push({ ...base, status: "refused", detail: `not probed: ${unsafe}` });
218
+ continue;
219
+ }
220
+ await page.evaluate(() => {
221
+ window.__jevProbeArmed = true;
222
+ });
223
+ const before = await openDialogs(page);
224
+ // Anything the page load itself sent is not the click's.
225
+ blocked.length = 0;
226
+ dialogs.length = 0;
227
+ const startUrl = page.url().split("#")[0];
228
+ await loc.click({ timeout: 3_000 });
229
+ let domDialog = false;
230
+ for (let waited = 0; waited < SETTLE_MS && blocked.length === 0 && dialogs.length === 0 && !domDialog; waited += 100) {
231
+ await clock.sleep(100);
232
+ domDialog = (await openDialogs(page)) > before;
233
+ }
234
+ // A dialog was observed: stop here — the page closes without anything inside it being clicked.
235
+ const sawDialog = dialogs.length > 0 || domDialog;
236
+ if (!sawDialog) {
237
+ await clock.sleep(150);
238
+ domDialog = (await openDialogs(page)) > before;
239
+ }
240
+ const navigated = !page.isClosed() && page.url().split("#")[0] !== startUrl;
241
+ const lateSocket = sockets > 0;
242
+ await probe.close();
243
+ const firstWrite = blocked[0]?.at ?? Number.POSITIVE_INFINITY;
244
+ const firstDialog = dialogs[0]?.at ?? Number.POSITIVE_INFINITY;
245
+ // A write attempted before any native dialog was NOT guarded by it (a confirm blocks the write
246
+ // until answered; the probe cancels it, so a guarded action never writes).
247
+ const guard = blocked.length > 0 && firstWrite < firstDialog ? "none" : dialogs.length > 0 ? "native-dialog" : domDialog ? "dom-dialog" : navigated && blocked.length === 0 ? "navigation" : "none";
248
+ if (lateSocket && guard === "none") {
249
+ out.push({ ...base, status: "refused", detail: "not judged: the click opened a WebSocket (its messages cannot be blocked)" });
250
+ continue;
251
+ }
252
+ const writes = [...new Set(blocked.map((b) => b.endpoint))];
253
+ out.push({
254
+ ...base,
255
+ status: "probed",
256
+ guard,
257
+ blockedWrites: writes,
258
+ detail: guard === "none"
259
+ ? writes.length > 0
260
+ ? `no guard: the click attempted ${writes.join(", ")} (aborted by the probe)`
261
+ : "no guard and no write attempted"
262
+ : `guarded by ${guard === "native-dialog" ? `a native ${dialogs[0]?.type ?? "dialog"} (cancelled)` : guard === "dom-dialog" ? "a dialog on the page (left unanswered)" : "a navigation to another page"}`,
263
+ });
264
+ }
265
+ catch (e) {
266
+ out.push({ ...base, status: "failed", detail: redactText(`the probe could not click it: ${errText(e)}`, opts.secrets) });
267
+ }
268
+ finally {
269
+ await probe?.close();
270
+ }
271
+ }
272
+ return out;
273
+ }
274
+ const PAD = 48;
275
+ const QUOTE_ATTR = "data-jevitate-quote";
276
+ /** BROWSER: wraps the first text-node occurrence of `q` under `el` in an inline marker span. */
277
+ function wrapQuote(el, q) {
278
+ const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
279
+ for (let n = walker.nextNode(); n !== null; n = walker.nextNode()) {
280
+ const at = (n.nodeValue ?? "").indexOf(q);
281
+ if (at < 0)
282
+ continue;
283
+ const range = document.createRange();
284
+ range.setStart(n, at);
285
+ range.setEnd(n, at + q.length);
286
+ const span = document.createElement("span");
287
+ span.setAttribute("data-jevitate-quote", "");
288
+ range.surroundContents(span);
289
+ return true;
290
+ }
291
+ return false;
292
+ }
293
+ /**
294
+ * Cropped, masked, boxed screenshots for the verified claim findings (`finding.screenshot`). Best
295
+ * effort per finding: one that cannot be located or masked has none. Fails CLOSED on masking: if
296
+ * the pixel mask cannot be installed or proven, no screenshot is written.
297
+ */
298
+ export async function captureFindingShots(page, findings, screens, opts) {
299
+ const mask = new SecretPixelMask(opts.secrets);
300
+ try {
301
+ await mask.install(page);
302
+ }
303
+ catch (e) {
304
+ if (e instanceof MaskUnavailableError)
305
+ return [...findings];
306
+ throw e;
307
+ }
308
+ const seq = { n: 0 };
309
+ let current;
310
+ const { handler } = writeBlocker(opts.secrets, seq, () => current);
311
+ await page.route("**/*", handler);
312
+ const out = [];
313
+ let n = 0;
314
+ try {
315
+ for (const f of findings) {
316
+ const screen = screens.get(f.screenId);
317
+ if (f.claim === undefined || screen === undefined) {
318
+ out.push(f);
319
+ continue;
320
+ }
321
+ const ref = f.evidenceRefs.find((r) => r.id.startsWith("control:"));
322
+ const control = ref === undefined ? undefined : screen.controls.find((c) => `control:${c.index}` === ref.id);
323
+ const quote = f.quotes[0];
324
+ if (control === undefined && quote === undefined) {
325
+ out.push(f);
326
+ continue;
327
+ }
328
+ try {
329
+ assertAuthorizedExploreTarget(screen.url, opts.allowlist);
330
+ current = screen.url;
331
+ await page.goto(screen.url, { waitUntil: "domcontentloaded", timeout: PROBE_TIMEOUT_MS });
332
+ current = undefined;
333
+ await page.waitForLoadState("networkidle", { timeout: 2_000 }).catch(() => undefined);
334
+ let loc = (control !== undefined ? locatorFor(page, control) : page.getByText(quote.slice(0, 120), { exact: false })).first();
335
+ if (!(await loc.isVisible().catch(() => false))) {
336
+ out.push(f);
337
+ continue;
338
+ }
339
+ // A quote is boxed tightly: its text is wrapped in an inline marker on this throwaway page
340
+ // (a block element's box would span the page). Falls back to the element.
341
+ if (control === undefined && (await loc.evaluate(wrapQuote, quote.slice(0, 120)).catch(() => false))) {
342
+ loc = page.locator(`[${QUOTE_ATTR}]`).first();
343
+ }
344
+ await loc.scrollIntoViewIfNeeded({ timeout: 2_000 });
345
+ const bb = await loc.boundingBox();
346
+ const vp = page.viewportSize() ?? { width: 1280, height: 720 };
347
+ if (bb === null) {
348
+ out.push(f);
349
+ continue;
350
+ }
351
+ const x = Math.max(0, Math.floor(bb.x - PAD));
352
+ const y = Math.max(0, Math.floor(bb.y - PAD));
353
+ const clip = { x, y, width: Math.max(1, Math.min(vp.width - x, Math.ceil(bb.width + 2 * PAD))), height: Math.max(1, Math.min(vp.height - y, Math.ceil(bb.height + 2 * PAD))) };
354
+ await mask.highlightLocator(page, loc);
355
+ await mkdir(opts.dir, { recursive: true });
356
+ n += 1;
357
+ const path = join(opts.dir, `finding-${n}.png`);
358
+ await captureStepScreenshot(page, path, { step: 0 }, [mask.layer()], clip);
359
+ await mask.clearHighlight(page);
360
+ const target = f.claim.target ?? (control !== undefined ? f.controls[0] ?? control.name : `text ${JSON.stringify(quote.slice(0, 80))}`);
361
+ const screenshot = { path, target, box: { x: Math.round(bb.x - x), y: Math.round(bb.y - y), width: Math.round(bb.width), height: Math.round(bb.height) } };
362
+ out.push(Object.freeze({ ...f, screenshot }));
363
+ }
364
+ catch {
365
+ out.push(f); // presentation only: a finding without a screenshot is still the finding
366
+ }
367
+ }
368
+ }
369
+ finally {
370
+ await page.unroute("**/*", handler).catch(() => undefined);
371
+ }
372
+ return out;
373
+ }
374
+ /** Opens the dedicated probe page in the run's context; closing it deletes its video, if any. */
375
+ export async function withProbePage(runPage, fn) {
376
+ const page = await runPage.context().newPage();
377
+ try {
378
+ return await fn(page);
379
+ }
380
+ finally {
381
+ const video = page.video();
382
+ await page.close().catch(() => undefined);
383
+ await video?.delete().catch(() => undefined);
384
+ }
385
+ }
386
+ //# sourceMappingURL=ux-claim-probe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ux-claim-probe.js","sourceRoot":"","sources":["../src/ux-claim-probe.ts"],"names":[],"mappings":"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"}
package/dist/ux-cli.js CHANGED
@@ -5,7 +5,7 @@ import { ok, fail } from "./envelope.js";
5
5
  import { withEngine } from "./engine.js";
6
6
  import { discoverRecordingSidecars, loadRecordingSidecars, runUxReview, UxAnalysisFailedError } from "./ux-api.js";
7
7
  import { UxConfigError } from "./ux-config.js";
8
- import { MinConfidenceError, QualityPolicyError, MaxFindingsPerRouteError } from "@jevitate/ux";
8
+ import { MinConfidenceError, QualityPolicyError, MaxFindingsPerRouteError, ProductFactsError } from "@jevitate/ux";
9
9
  import { emitJson, GatewaySelectionError, buildExploreGateways } from "./cli-shared.js";
10
10
  /** Registers `jevitate ux <recording>`. */
11
11
  export function registerUxCommands(program, deps) {
@@ -24,6 +24,8 @@ export function registerUxCommands(program, deps) {
24
24
  .option("--out <dir>", "directory to write the UX report")
25
25
  .option("--result <file>", "mission result JSON (as written alongside the Recording by `jevitate explore`) — supplies blocked/disabled-target evidence the Recording alone cannot carry; default: <stem>.result.json, else <stem>.transcript.json, next to the Recording")
26
26
  .option("--evidence <file>", "a live usability run's evidence sidecar (screens as analyzed + run signals); default: <stem>.evidence.json next to the Recording — with it, offline review reproduces the live run's findings")
27
+ .option("--product <file>", "product facts JSON (plans/prices, key journeys, each page's intended next step) the review checks screens against in code; default .jevitate/product.json in the project when present (docs/ux-findings.md)")
28
+ .option("--polish", "polish each verified finding's recommendation with one generation call (opt-in; the default prose is built from templates)")
27
29
  .option("--real", "use live Jev gateways (requires keys)", false)
28
30
  .option("--fake-ai", "use deterministic fake gateways", false)
29
31
  .option("--json", "emit a JSON envelope")
@@ -81,6 +83,8 @@ export function registerUxCommands(program, deps) {
81
83
  ...(o.minConfidence !== undefined ? { minConfidence: o.minConfidence } : {}),
82
84
  ...(o.show !== undefined ? { show: o.show } : {}),
83
85
  ...(o.maxFindingsPerPage !== undefined ? { maxFindingsPerRoute: o.maxFindingsPerPage } : {}),
86
+ ...(o.product !== undefined ? { product: o.product } : {}),
87
+ ...(o.polish === true ? { polish: true } : {}),
84
88
  outDir: o.out,
85
89
  ...sidecars,
86
90
  });
@@ -90,6 +94,9 @@ export function registerUxCommands(program, deps) {
90
94
  if (err instanceof UxAnalysisFailedError) {
91
95
  emitJson(program, fail("E_UX_ANALYSIS", err.message));
92
96
  }
97
+ else if (err instanceof ProductFactsError) {
98
+ emitJson(program, fail(err.code, err.message));
99
+ }
93
100
  else if (err instanceof MinConfidenceError || err instanceof QualityPolicyError || err instanceof MaxFindingsPerRouteError || err instanceof UxConfigError) {
94
101
  emitJson(program, fail("E_UX_ARGS", err.message));
95
102
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ux-cli.js","sourceRoot":"","sources":["../src/ux-cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ux-cli.js","sourceRoot":"","sources":["../src/ux-cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { type ProductFacts } from "@jevitate/ux";
2
+ import { type LayoutDeps } from "./project-dir.js";
3
+ export declare const PRODUCT_FACTS_FILE = "product.json";
4
+ export interface LoadedProductFacts {
5
+ readonly facts?: ProductFacts;
6
+ /** The file read, when one was. */
7
+ readonly path?: string;
8
+ }
9
+ /** The facts file to read: the flag, else `<project>/.jevitate/product.json` when present. */
10
+ export declare function productFactsPath(flag: string | undefined, deps?: LayoutDeps): string | undefined;
11
+ /** Reads and validates the product facts. Throws `ProductFactsError` for a missing/invalid file. */
12
+ export declare function loadProductFacts(flag: string | undefined, deps?: LayoutDeps): Promise<LoadedProductFacts>;
13
+ /** The report caveat when no facts were available (prices and intended next steps went unchecked). */
14
+ export declare const NO_PRODUCT_FACTS_CAVEAT = "no product facts (.jevitate/product.json or --product): prices, trial lengths and each page's intended next step were not checked against the product's own facts (docs/ux-findings.md).";
@@ -0,0 +1,39 @@
1
+ // ux-product.ts — finding and loading the product facts file for the UX review (#198).
2
+ //
3
+ // `--product <file>` wins; otherwise the project's `.jevitate/product.json` (found by walking up
4
+ // from the working directory, like every other in-repo `.jevitate/` file) is used when it exists.
5
+ // An unreadable or invalid file is a typed refusal (`E_UX_PRODUCT_INPUT`, usage error) BEFORE any
6
+ // browser opens — never a silently ignored file.
7
+ import { existsSync, statSync } from "node:fs";
8
+ import { readFile } from "node:fs/promises";
9
+ import { join } from "node:path";
10
+ import { ProductFactsError, parseProductFactsText } from "@jevitate/ux";
11
+ import { findProjectDir } from "./project-dir.js";
12
+ export const PRODUCT_FACTS_FILE = "product.json";
13
+ /** The facts file to read: the flag, else `<project>/.jevitate/product.json` when present. */
14
+ export function productFactsPath(flag, deps = {}) {
15
+ if (flag !== undefined)
16
+ return flag;
17
+ const dir = findProjectDir(deps);
18
+ if (dir === null)
19
+ return undefined;
20
+ const p = join(dir, PRODUCT_FACTS_FILE);
21
+ return existsSync(p) && statSync(p).isFile() ? p : undefined;
22
+ }
23
+ /** Reads and validates the product facts. Throws `ProductFactsError` for a missing/invalid file. */
24
+ export async function loadProductFacts(flag, deps = {}) {
25
+ const path = productFactsPath(flag, deps);
26
+ if (path === undefined)
27
+ return {};
28
+ let text;
29
+ try {
30
+ text = await readFile(path, "utf8");
31
+ }
32
+ catch (err) {
33
+ throw new ProductFactsError(path, [`cannot read the file: ${err instanceof Error ? err.message : String(err)}`]);
34
+ }
35
+ return { facts: parseProductFactsText(text, path), path };
36
+ }
37
+ /** The report caveat when no facts were available (prices and intended next steps went unchecked). */
38
+ export const NO_PRODUCT_FACTS_CAVEAT = "no product facts (.jevitate/product.json or --product): prices, trial lengths and each page's intended next step were not checked against the product's own facts (docs/ux-findings.md).";
39
+ //# sourceMappingURL=ux-product.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ux-product.js","sourceRoot":"","sources":["../src/ux-product.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,EAAE;AACF,iGAAiG;AACjG,kGAAkG;AAClG,kGAAkG;AAClG,iDAAiD;AACjD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAqB,MAAM,cAAc,CAAC;AAC3F,OAAO,EAAE,cAAc,EAAmB,MAAM,kBAAkB,CAAC;AAEnE,MAAM,CAAC,MAAM,kBAAkB,GAAG,cAAc,CAAC;AAQjD,8FAA8F;AAC9F,MAAM,UAAU,gBAAgB,CAAC,IAAwB,EAAE,OAAmB,EAAE;IAC9E,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,GAAG,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACnC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC;IACxC,OAAO,UAAU,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/D,CAAC;AAED,oGAAoG;AACpG,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAwB,EAAE,OAAmB,EAAE;IACpF,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC1C,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IAClC,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,iBAAiB,CAAC,IAAI,EAAE,CAAC,yBAAyB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IACnH,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,qBAAqB,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC;AAC5D,CAAC;AAED,sGAAsG;AACtG,MAAM,CAAC,MAAM,uBAAuB,GAClC,0LAA0L,CAAC"}
@@ -61,6 +61,10 @@ export interface RunUxReviewOptions {
61
61
  readonly evidenceUnavailable?: string;
62
62
  /** Tuning of the run-signal oracles (#96/#131). */
63
63
  readonly signals?: SignalOptions;
64
+ /** #198 `--product <file>`: product facts (default `.jevitate/product.json` in the project). */
65
+ readonly product?: string;
66
+ /** #198 `--polish`: polish each verified finding's recommendation with one generation call (opt-in). */
67
+ readonly polish?: boolean;
64
68
  }
65
69
  export interface RunUxReviewResult {
66
70
  readonly report: UxReport;