@jevitate/cli 0.1.0 → 0.2.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 (375) hide show
  1. package/README.md +23 -0
  2. package/dist/ai-cli.js +40 -30
  3. package/dist/ai-cli.js.map +1 -1
  4. package/dist/author-journey-cli.d.ts +4 -0
  5. package/dist/author-journey-cli.js +109 -0
  6. package/dist/author-journey-cli.js.map +1 -0
  7. package/dist/bin.js +58077 -22454
  8. package/dist/bin.js.map +1 -1
  9. package/dist/browser-run-options.d.ts +58 -0
  10. package/dist/browser-run-options.js +106 -0
  11. package/dist/browser-run-options.js.map +1 -0
  12. package/dist/build-info.generated.d.ts +2 -0
  13. package/dist/build-info.generated.js +10 -0
  14. package/dist/build-info.generated.js.map +1 -0
  15. package/dist/check-api.d.ts +5 -0
  16. package/dist/check-api.js +233 -0
  17. package/dist/check-api.js.map +1 -0
  18. package/dist/check-budget.d.ts +26 -0
  19. package/dist/check-budget.js +89 -0
  20. package/dist/check-budget.js.map +1 -0
  21. package/dist/check-cli.d.ts +31 -0
  22. package/dist/check-cli.js +109 -0
  23. package/dist/check-cli.js.map +1 -0
  24. package/dist/check-execute.d.ts +25 -0
  25. package/dist/check-execute.js +385 -0
  26. package/dist/check-execute.js.map +1 -0
  27. package/dist/check-plan.d.ts +113 -0
  28. package/dist/check-plan.js +448 -0
  29. package/dist/check-plan.js.map +1 -0
  30. package/dist/check-suite.d.ts +171 -0
  31. package/dist/check-suite.js +515 -0
  32. package/dist/check-suite.js.map +1 -0
  33. package/dist/check-types.d.ts +181 -0
  34. package/dist/check-types.js +35 -0
  35. package/dist/check-types.js.map +1 -0
  36. package/dist/cli-args.d.ts +26 -0
  37. package/dist/cli-args.js +50 -0
  38. package/dist/cli-args.js.map +1 -0
  39. package/dist/cli-output.d.ts +102 -0
  40. package/dist/cli-output.js +516 -0
  41. package/dist/cli-output.js.map +1 -0
  42. package/dist/cli-reference.d.ts +3 -0
  43. package/dist/cli-reference.js +81 -0
  44. package/dist/cli-reference.js.map +1 -0
  45. package/dist/cli-refusal.d.ts +12 -0
  46. package/dist/cli-refusal.js +52 -0
  47. package/dist/cli-refusal.js.map +1 -0
  48. package/dist/cli-shared.d.ts +330 -0
  49. package/dist/cli-shared.js +492 -0
  50. package/dist/cli-shared.js.map +1 -0
  51. package/dist/cli-surface.d.ts +33 -0
  52. package/dist/cli-surface.js +48 -0
  53. package/dist/cli-surface.js.map +1 -0
  54. package/dist/conversation-options.d.ts +15 -0
  55. package/dist/conversation-options.js +10 -0
  56. package/dist/conversation-options.js.map +1 -0
  57. package/dist/credentials-file.d.ts +15 -0
  58. package/dist/credentials-file.js +56 -0
  59. package/dist/credentials-file.js.map +1 -0
  60. package/dist/data-dir.d.ts +1 -1
  61. package/dist/data-dir.js +1 -1
  62. package/dist/defect-evidence.d.ts +141 -0
  63. package/dist/defect-evidence.js +502 -0
  64. package/dist/defect-evidence.js.map +1 -0
  65. package/dist/demo-aspect-api.d.ts +209 -0
  66. package/dist/demo-aspect-api.js +397 -0
  67. package/dist/demo-aspect-api.js.map +1 -0
  68. package/dist/demo-aspect-cli.d.ts +3 -0
  69. package/dist/demo-aspect-cli.js +241 -0
  70. package/dist/demo-aspect-cli.js.map +1 -0
  71. package/dist/demo-capture.d.ts +108 -0
  72. package/dist/demo-capture.js +438 -0
  73. package/dist/demo-capture.js.map +1 -0
  74. package/dist/engine.d.ts +35 -0
  75. package/dist/engine.js +25 -0
  76. package/dist/engine.js.map +1 -0
  77. package/dist/environments.d.ts +155 -0
  78. package/dist/environments.js +364 -0
  79. package/dist/environments.js.map +1 -0
  80. package/dist/exit-codes.d.ts +46 -0
  81. package/dist/exit-codes.js +91 -0
  82. package/dist/exit-codes.js.map +1 -0
  83. package/dist/explore-adversarial.d.ts +137 -0
  84. package/dist/explore-adversarial.js +218 -0
  85. package/dist/explore-adversarial.js.map +1 -0
  86. package/dist/explore-api.d.ts +7 -187
  87. package/dist/explore-api.js +7 -301
  88. package/dist/explore-api.js.map +1 -1
  89. package/dist/explore-author.d.ts +79 -0
  90. package/dist/explore-author.js +86 -0
  91. package/dist/explore-author.js.map +1 -0
  92. package/dist/explore-cli.d.ts +7 -0
  93. package/dist/explore-cli.js +963 -0
  94. package/dist/explore-cli.js.map +1 -0
  95. package/dist/explore-coverage.d.ts +158 -0
  96. package/dist/explore-coverage.js +240 -0
  97. package/dist/explore-coverage.js.map +1 -0
  98. package/dist/explore-feature.d.ts +127 -0
  99. package/dist/explore-feature.js +220 -0
  100. package/dist/explore-feature.js.map +1 -0
  101. package/dist/explore-goal.d.ts +253 -0
  102. package/dist/explore-goal.js +374 -0
  103. package/dist/explore-goal.js.map +1 -0
  104. package/dist/explore-shared.d.ts +152 -0
  105. package/dist/explore-shared.js +129 -0
  106. package/dist/explore-shared.js.map +1 -0
  107. package/dist/explore-specs.d.ts +53 -0
  108. package/dist/explore-specs.js +333 -0
  109. package/dist/explore-specs.js.map +1 -0
  110. package/dist/findings-filing.d.ts +64 -0
  111. package/dist/findings-filing.js +108 -0
  112. package/dist/findings-filing.js.map +1 -0
  113. package/dist/fixture-auth.d.ts +52 -0
  114. package/dist/fixture-auth.js +82 -0
  115. package/dist/fixture-auth.js.map +1 -0
  116. package/dist/fixture-cli.d.ts +70 -0
  117. package/dist/fixture-cli.js +178 -0
  118. package/dist/fixture-cli.js.map +1 -0
  119. package/dist/github-issue-filer.d.ts +62 -0
  120. package/dist/github-issue-filer.js +188 -0
  121. package/dist/github-issue-filer.js.map +1 -0
  122. package/dist/host-health-run.d.ts +24 -0
  123. package/dist/host-health-run.js +27 -0
  124. package/dist/host-health-run.js.map +1 -0
  125. package/dist/http-5xx-outcome.d.ts +6 -0
  126. package/dist/http-5xx-outcome.js +39 -0
  127. package/dist/http-5xx-outcome.js.map +1 -0
  128. package/dist/inbox-cli.d.ts +7 -0
  129. package/dist/inbox-cli.js +197 -0
  130. package/dist/inbox-cli.js.map +1 -0
  131. package/dist/index.d.ts +1 -0
  132. package/dist/index.js +1 -0
  133. package/dist/index.js.map +1 -1
  134. package/dist/init-cli.d.ts +4 -0
  135. package/dist/init-cli.js +127 -0
  136. package/dist/init-cli.js.map +1 -0
  137. package/dist/init-keys.d.ts +19 -2
  138. package/dist/init-keys.js +15 -4
  139. package/dist/init-keys.js.map +1 -1
  140. package/dist/init-next-steps.d.ts +35 -0
  141. package/dist/init-next-steps.js +79 -0
  142. package/dist/init-next-steps.js.map +1 -0
  143. package/dist/invariants-file.d.ts +34 -0
  144. package/dist/invariants-file.js +68 -0
  145. package/dist/invariants-file.js.map +1 -0
  146. package/dist/invariants-validate.d.ts +30 -0
  147. package/dist/invariants-validate.js +105 -0
  148. package/dist/invariants-validate.js.map +1 -0
  149. package/dist/journey-annotate-api.d.ts +76 -0
  150. package/dist/journey-annotate-api.js +256 -0
  151. package/dist/journey-annotate-api.js.map +1 -0
  152. package/dist/journey-annotate-cli.d.ts +9 -0
  153. package/dist/journey-annotate-cli.js +179 -0
  154. package/dist/journey-annotate-cli.js.map +1 -0
  155. package/dist/journey-api.d.ts +79 -2
  156. package/dist/journey-api.js +136 -19
  157. package/dist/journey-api.js.map +1 -1
  158. package/dist/journey-cli.d.ts +4 -0
  159. package/dist/journey-cli.js +370 -0
  160. package/dist/journey-cli.js.map +1 -0
  161. package/dist/journey-demo-api.d.ts +112 -0
  162. package/dist/journey-demo-api.js +392 -0
  163. package/dist/journey-demo-api.js.map +1 -0
  164. package/dist/journey-demo-cli.d.ts +10 -0
  165. package/dist/journey-demo-cli.js +155 -0
  166. package/dist/journey-demo-cli.js.map +1 -0
  167. package/dist/kill-signal.d.ts +193 -0
  168. package/dist/kill-signal.js +284 -0
  169. package/dist/kill-signal.js.map +1 -0
  170. package/dist/launch-armed.d.ts +37 -0
  171. package/dist/launch-armed.js +31 -0
  172. package/dist/launch-armed.js.map +1 -0
  173. package/dist/ledger-api.d.ts +106 -0
  174. package/dist/ledger-api.js +237 -0
  175. package/dist/ledger-api.js.map +1 -0
  176. package/dist/ledger-cli.d.ts +18 -0
  177. package/dist/ledger-cli.js +107 -0
  178. package/dist/ledger-cli.js.map +1 -0
  179. package/dist/load-api.d.ts +29 -11
  180. package/dist/load-api.js +23 -15
  181. package/dist/load-api.js.map +1 -1
  182. package/dist/load-cli.d.ts +4 -0
  183. package/dist/load-cli.js +97 -0
  184. package/dist/load-cli.js.map +1 -0
  185. package/dist/log-correlation.d.ts +147 -0
  186. package/dist/log-correlation.js +325 -0
  187. package/dist/log-correlation.js.map +1 -0
  188. package/dist/log-lines.d.ts +131 -0
  189. package/dist/log-lines.js +389 -0
  190. package/dist/log-lines.js.map +1 -0
  191. package/dist/log-sources.d.ts +71 -0
  192. package/dist/log-sources.js +235 -0
  193. package/dist/log-sources.js.map +1 -0
  194. package/dist/logs-cli.d.ts +3 -0
  195. package/dist/logs-cli.js +51 -0
  196. package/dist/logs-cli.js.map +1 -0
  197. package/dist/logs-retention.d.ts +28 -0
  198. package/dist/logs-retention.js +84 -0
  199. package/dist/logs-retention.js.map +1 -0
  200. package/dist/mcp-api.d.ts +89 -1
  201. package/dist/mcp-api.js +472 -18
  202. package/dist/mcp-api.js.map +1 -1
  203. package/dist/mcp-args.d.ts +38 -0
  204. package/dist/mcp-args.js +131 -0
  205. package/dist/mcp-args.js.map +1 -0
  206. package/dist/mcp-cli-bridge.d.ts +34 -0
  207. package/dist/mcp-cli-bridge.js +62 -0
  208. package/dist/mcp-cli-bridge.js.map +1 -0
  209. package/dist/mcp-cli-runner.d.ts +29 -0
  210. package/dist/mcp-cli-runner.js +44 -0
  211. package/dist/mcp-cli-runner.js.map +1 -0
  212. package/dist/mcp-cli-tools.d.ts +125 -0
  213. package/dist/mcp-cli-tools.js +680 -0
  214. package/dist/mcp-cli-tools.js.map +1 -0
  215. package/dist/mcp-paths.d.ts +28 -0
  216. package/dist/mcp-paths.js +76 -0
  217. package/dist/mcp-paths.js.map +1 -0
  218. package/dist/mission-actors.d.ts +40 -0
  219. package/dist/mission-actors.js +72 -0
  220. package/dist/mission-actors.js.map +1 -0
  221. package/dist/mission-api.d.ts +35 -0
  222. package/dist/mission-api.js +58 -2
  223. package/dist/mission-api.js.map +1 -1
  224. package/dist/mission-cli.d.ts +4 -0
  225. package/dist/mission-cli.js +247 -0
  226. package/dist/mission-cli.js.map +1 -0
  227. package/dist/mission-exit.d.ts +22 -0
  228. package/dist/mission-exit.js +26 -0
  229. package/dist/mission-exit.js.map +1 -0
  230. package/dist/mission-fixtures.d.ts +225 -0
  231. package/dist/mission-fixtures.js +828 -0
  232. package/dist/mission-fixtures.js.map +1 -0
  233. package/dist/mission-journal.d.ts +60 -0
  234. package/dist/mission-journal.js +115 -0
  235. package/dist/mission-journal.js.map +1 -0
  236. package/dist/mission-queue-cli.d.ts +10 -0
  237. package/dist/mission-queue-cli.js +198 -0
  238. package/dist/mission-queue-cli.js.map +1 -0
  239. package/dist/mission-queue-runner.d.ts +126 -0
  240. package/dist/mission-queue-runner.js +397 -0
  241. package/dist/mission-queue-runner.js.map +1 -0
  242. package/dist/multi-run-cli.d.ts +37 -0
  243. package/dist/multi-run-cli.js +110 -0
  244. package/dist/multi-run-cli.js.map +1 -0
  245. package/dist/multi-run.d.ts +339 -0
  246. package/dist/multi-run.js +619 -0
  247. package/dist/multi-run.js.map +1 -0
  248. package/dist/openrouter-call.d.ts +22 -0
  249. package/dist/openrouter-call.js +63 -0
  250. package/dist/openrouter-call.js.map +1 -0
  251. package/dist/profile-cli.d.ts +4 -0
  252. package/dist/profile-cli.js +57 -0
  253. package/dist/profile-cli.js.map +1 -0
  254. package/dist/program.d.ts +3 -75
  255. package/dist/program.js +119 -1921
  256. package/dist/program.js.map +1 -1
  257. package/dist/project-dir.d.ts +96 -0
  258. package/dist/project-dir.js +220 -0
  259. package/dist/project-dir.js.map +1 -0
  260. package/dist/record-api.d.ts +10 -6
  261. package/dist/record-api.js +39 -28
  262. package/dist/record-api.js.map +1 -1
  263. package/dist/record-cli.d.ts +4 -0
  264. package/dist/record-cli.js +64 -0
  265. package/dist/record-cli.js.map +1 -0
  266. package/dist/recording-cli.d.ts +4 -0
  267. package/dist/recording-cli.js +248 -0
  268. package/dist/recording-cli.js.map +1 -0
  269. package/dist/regression-api.d.ts +101 -0
  270. package/dist/regression-api.js +384 -5
  271. package/dist/regression-api.js.map +1 -1
  272. package/dist/regression-cli.d.ts +4 -0
  273. package/dist/regression-cli.js +232 -0
  274. package/dist/regression-cli.js.map +1 -0
  275. package/dist/report-api.d.ts +146 -0
  276. package/dist/report-api.js +430 -0
  277. package/dist/report-api.js.map +1 -0
  278. package/dist/report-cli.d.ts +12 -0
  279. package/dist/report-cli.js +147 -0
  280. package/dist/report-cli.js.map +1 -0
  281. package/dist/result-schema.d.ts +22 -0
  282. package/dist/result-schema.js +28 -0
  283. package/dist/result-schema.js.map +1 -0
  284. package/dist/run-index.d.ts +29 -0
  285. package/dist/run-index.js +70 -0
  286. package/dist/run-index.js.map +1 -0
  287. package/dist/run-screenshots.d.ts +123 -0
  288. package/dist/run-screenshots.js +229 -0
  289. package/dist/run-screenshots.js.map +1 -0
  290. package/dist/runtime-env.d.ts +8 -0
  291. package/dist/runtime-env.js +22 -0
  292. package/dist/runtime-env.js.map +1 -0
  293. package/dist/secret-args.d.ts +23 -0
  294. package/dist/secret-args.js +45 -0
  295. package/dist/secret-args.js.map +1 -0
  296. package/dist/serve-cli.d.ts +4 -0
  297. package/dist/serve-cli.js +95 -0
  298. package/dist/serve-cli.js.map +1 -0
  299. package/dist/server-log-verify.d.ts +34 -0
  300. package/dist/server-log-verify.js +115 -0
  301. package/dist/server-log-verify.js.map +1 -0
  302. package/dist/session-check.d.ts +14 -0
  303. package/dist/session-check.js +44 -0
  304. package/dist/session-check.js.map +1 -0
  305. package/dist/site-cli.d.ts +4 -0
  306. package/dist/site-cli.js +127 -0
  307. package/dist/site-cli.js.map +1 -0
  308. package/dist/site-gate-cli.d.ts +28 -0
  309. package/dist/site-gate-cli.js +58 -0
  310. package/dist/site-gate-cli.js.map +1 -0
  311. package/dist/source-api.js +18 -3
  312. package/dist/source-api.js.map +1 -1
  313. package/dist/source-cli.d.ts +4 -0
  314. package/dist/source-cli.js +284 -0
  315. package/dist/source-cli.js.map +1 -0
  316. package/dist/source-run-api.d.ts +27 -1
  317. package/dist/source-run-api.js +22 -11
  318. package/dist/source-run-api.js.map +1 -1
  319. package/dist/storage-state-snapshot.d.ts +50 -0
  320. package/dist/storage-state-snapshot.js +87 -0
  321. package/dist/storage-state-snapshot.js.map +1 -0
  322. package/dist/suite-explore-options.d.ts +289 -0
  323. package/dist/suite-explore-options.js +140 -0
  324. package/dist/suite-explore-options.js.map +1 -0
  325. package/dist/target-config.d.ts +109 -0
  326. package/dist/target-config.js +216 -0
  327. package/dist/target-config.js.map +1 -0
  328. package/dist/transcript-file.d.ts +12 -0
  329. package/dist/transcript-file.js +23 -0
  330. package/dist/transcript-file.js.map +1 -0
  331. package/dist/ui-api.d.ts +3 -0
  332. package/dist/ui-api.js +7 -1
  333. package/dist/ui-api.js.map +1 -1
  334. package/dist/usability-capture.d.ts +43 -0
  335. package/dist/usability-capture.js +326 -0
  336. package/dist/usability-capture.js.map +1 -0
  337. package/dist/usage-config.d.ts +36 -0
  338. package/dist/usage-config.js +114 -0
  339. package/dist/usage-config.js.map +1 -0
  340. package/dist/ux-api.d.ts +3 -75
  341. package/dist/ux-api.js +3 -220
  342. package/dist/ux-api.js.map +1 -1
  343. package/dist/ux-cli.d.ts +4 -0
  344. package/dist/ux-cli.js +102 -0
  345. package/dist/ux-cli.js.map +1 -0
  346. package/dist/ux-config.d.ts +29 -0
  347. package/dist/ux-config.js +103 -0
  348. package/dist/ux-config.js.map +1 -0
  349. package/dist/ux-evidence.d.ts +54 -0
  350. package/dist/ux-evidence.js +197 -0
  351. package/dist/ux-evidence.js.map +1 -0
  352. package/dist/ux-review.d.ts +112 -0
  353. package/dist/ux-review.js +151 -0
  354. package/dist/ux-review.js.map +1 -0
  355. package/dist/ux-usability.d.ts +238 -0
  356. package/dist/ux-usability.js +494 -0
  357. package/dist/ux-usability.js.map +1 -0
  358. package/dist/verify-fix-api.d.ts +173 -0
  359. package/dist/verify-fix-api.js +454 -0
  360. package/dist/verify-fix-api.js.map +1 -0
  361. package/dist/verify-fix-cli.d.ts +4 -0
  362. package/dist/verify-fix-cli.js +110 -0
  363. package/dist/verify-fix-cli.js.map +1 -0
  364. package/package.json +19 -14
  365. package/skills/jevitate-ci-check/SKILL.md +77 -0
  366. package/skills/jevitate-demo/SKILL.md +61 -0
  367. package/skills/jevitate-explore/SKILL.md +88 -87
  368. package/skills/jevitate-getting-started/SKILL.md +88 -0
  369. package/skills/jevitate-load-test/SKILL.md +11 -2
  370. package/skills/jevitate-mission-scope/SKILL.md +19 -11
  371. package/skills/jevitate-record/SKILL.md +23 -5
  372. package/skills/jevitate-run-journey/SKILL.md +61 -17
  373. package/skills/jevitate-sources/SKILL.md +57 -14
  374. package/skills/jevitate-ux-review/SKILL.md +106 -16
  375. package/skills/jevitate-verify-fix/SKILL.md +76 -0
@@ -0,0 +1,326 @@
1
+ // usability-capture.ts — what a live usability run measures besides the rubric evidence (#96/#98):
2
+ //
3
+ // - a per-step SCREENSHOT of every screen the run decides on, with every secret-bearing element
4
+ // MASKED by Playwright before the pixels are captured (password/OTP inputs, bound secret
5
+ // fields, and any element whose text or value holds a registered secret) — fail-closed: when
6
+ // the secret scan cannot run, no screenshot is written at all;
7
+ // - the REQUESTS the page made (fetch/xhr/document: method, endpoint, status, start/end), each
8
+ // attributed to the step whose action it followed;
9
+ // - per-screen facts the signal oracles need (url, signature, visible text, a busy indicator,
10
+ // the page heading — #131);
11
+ // - per write request, a one-way PAYLOAD DIGEST (#131 duplicate create): volatile keys dropped,
12
+ // never for a body holding a secret or a credential-named field, and never the body itself.
13
+ //
14
+ // Everything stored here is redacted (redactUrl + the run's secrets) before it is kept; nothing
15
+ // here ever reaches a model. The capture is advisory: a failure to screenshot or to read the page
16
+ // never affects the run.
17
+ import { createHash } from "node:crypto";
18
+ import { mkdirSync } from "node:fs";
19
+ import { join } from "node:path";
20
+ import { DEMO_OVERLAY_HIDE_STYLE, endpointOf, redactText, redactUrl } from "@jevitate/explore";
21
+ /** Secret-bearing inputs — the same predicate as `@jevitate/recorder`'s `isSecretField`, in CSS. */
22
+ export const SECRET_INPUT_SELECTOR = [
23
+ 'input[type="password" i]',
24
+ '[autocomplete~="current-password" i]',
25
+ '[autocomplete~="new-password" i]',
26
+ '[autocomplete~="one-time-code" i]',
27
+ '[autocomplete~="otp" i]',
28
+ ].join(", ");
29
+ /** The attribute the in-page secret scan marks an element with (removed after the screenshot). */
30
+ const MASK_ATTR = "data-jevitate-mask";
31
+ /** The opaque box drawn over every masked element (Playwright's own default, pinned). */
32
+ export const MASK_COLOR = "#FF00FF";
33
+ const KEPT_TYPES = new Set(["fetch", "xhr", "document"]);
34
+ const SCREENSHOT_TIMEOUT_MS = 5_000;
35
+ function withTimeout(p, ms) {
36
+ let timer;
37
+ return Promise.race([
38
+ p,
39
+ new Promise((_, reject) => {
40
+ timer = setTimeout(() => reject(new Error(`timed out after ${ms}ms`)), ms);
41
+ }),
42
+ ]).finally(() => clearTimeout(timer));
43
+ }
44
+ /** A bound secret field's matcher → a locator for every element it could match. */
45
+ function boundFieldLocator(page, f) {
46
+ const v = JSON.stringify(f.matcher.value);
47
+ switch (f.matcher.key) {
48
+ case "label":
49
+ return page.getByLabel(f.matcher.value, { exact: true });
50
+ case "testId":
51
+ return page.getByTestId(f.matcher.value);
52
+ case "type":
53
+ return page.locator(`input[type=${v} i]`);
54
+ case "id":
55
+ return page.locator(`[id=${v}]`);
56
+ case "name":
57
+ return page.locator(`[name=${v}]`);
58
+ }
59
+ }
60
+ /**
61
+ * BROWSER CODE — marks every element whose text or form value holds a registered secret, and
62
+ * reports whether a busy/progress/status indicator is on screen. The secrets are handed to the
63
+ * browser that already holds them (it rendered or was typed them); they never leave it.
64
+ */
65
+ function scanPage(args) {
66
+ let marked = 0;
67
+ if (args.secrets.length > 0) {
68
+ const holds = (s) => !!s && args.secrets.some((x) => s.includes(x));
69
+ for (const el of Array.from(document.querySelectorAll("input, textarea, select"))) {
70
+ if (holds(el.value)) {
71
+ el.setAttribute(args.attr, "");
72
+ marked++;
73
+ }
74
+ }
75
+ const walker = document.createTreeWalker(document.body ?? document.documentElement, NodeFilter.SHOW_TEXT);
76
+ for (let n = walker.nextNode(); n !== null; n = walker.nextNode()) {
77
+ if (holds(n.nodeValue) && n.parentElement !== null) {
78
+ n.parentElement.setAttribute(args.attr, "");
79
+ marked++;
80
+ }
81
+ }
82
+ }
83
+ const busy = document.querySelector('[aria-busy="true"], [role="progressbar"], progress, .spinner, [class*="spinner" i], [class*="loading" i]') !== null ||
84
+ Array.from(document.querySelectorAll('[role="status"], [aria-live]')).some((el) => (el.textContent ?? "").trim().length > 0);
85
+ const h1 = document.querySelector('h1, [role="heading"][aria-level="1"]');
86
+ const heading = ((h1?.innerText ?? h1?.textContent ?? "").trim() || document.title || "").replace(/\s+/g, " ").slice(0, 200);
87
+ return { marked, busy, heading };
88
+ }
89
+ /** Body keys that change between two submissions of the same thing (ids, timestamps, nonces). */
90
+ const VOLATILE_KEY = /^(id|_id|uuid|guid|key|nonce|timestamp|ts|time|date|created_?at|updated_?at|client_?id|request_?id|idempotency_?key|trace_?id|csrf.*|xsrf.*)$/i;
91
+ /** A body with a field named like a credential is never digested. */
92
+ const CREDENTIAL_KEY = /pass|secret|token|otp|totp|pin|cvc|cvv|card|auth|session|cookie/i;
93
+ const MAX_DIGEST_BODY = 64_000;
94
+ class CredentialBody extends Error {
95
+ }
96
+ function canonical(v) {
97
+ if (Array.isArray(v))
98
+ return v.map(canonical);
99
+ if (v !== null && typeof v === "object") {
100
+ const out = {};
101
+ for (const k of Object.keys(v).sort()) {
102
+ if (CREDENTIAL_KEY.test(k))
103
+ throw new CredentialBody();
104
+ if (VOLATILE_KEY.test(k))
105
+ continue;
106
+ out[k] = canonical(v[k]);
107
+ }
108
+ return out;
109
+ }
110
+ return v;
111
+ }
112
+ /**
113
+ * A one-way digest of a write request's body (#131): two creates with the same digest sent the
114
+ * same payload. `undefined` — no digest — for a body that is absent, too large, not JSON or form
115
+ * data, or holds a secret or a credential-named field. The body itself is never kept.
116
+ */
117
+ export function payloadDigest(method, endpoint, body, secrets) {
118
+ if (body === null || body.length === 0 || body.length > MAX_DIGEST_BODY)
119
+ return undefined;
120
+ if (secrets.some((x) => x.length > 0 && body.includes(x)))
121
+ return undefined;
122
+ let value;
123
+ try {
124
+ value = JSON.parse(body);
125
+ }
126
+ catch {
127
+ if (!/^[^=&\s]+=[^&]*(&[^=&\s]+=[^&]*)*$/.test(body))
128
+ return undefined;
129
+ value = Object.fromEntries(new URLSearchParams(body));
130
+ }
131
+ try {
132
+ const canon = JSON.stringify(canonical(value));
133
+ return createHash("sha256").update(`${method.toUpperCase()} ${endpoint}\n${canon}`).digest("hex").slice(0, 32);
134
+ }
135
+ catch {
136
+ return undefined; // a credential-named field: never digested
137
+ }
138
+ }
139
+ function unmark(attr) {
140
+ for (const el of Array.from(document.querySelectorAll(`[${attr}]`)))
141
+ el.removeAttribute(attr);
142
+ }
143
+ /** A request's own `content-type` header (#110: tells a gRPC-web/Connect read from a write). */
144
+ function requestContentType(r) {
145
+ try {
146
+ const v = r.headers()["content-type"];
147
+ return typeof v === "string" && v !== "" ? v : undefined;
148
+ }
149
+ catch {
150
+ return undefined;
151
+ }
152
+ }
153
+ const WRITE_METHODS = new Set(["POST", "PUT", "PATCH", "DELETE"]);
154
+ function postDataOf(r) {
155
+ try {
156
+ return r.postData();
157
+ }
158
+ catch {
159
+ return null;
160
+ }
161
+ }
162
+ export class UsabilityCapture {
163
+ #opts;
164
+ #now;
165
+ #requests = [];
166
+ #live = new Map();
167
+ #screens = [];
168
+ /** Step number → the screenshot of the screen that step was decided on. */
169
+ #shotByStep = new Map();
170
+ /** Status reads still in flight (awaited before the capture is handed out). */
171
+ #statusReads = [];
172
+ #entries = 0;
173
+ #step = 0;
174
+ #detached = false;
175
+ constructor(opts) {
176
+ this.#opts = opts;
177
+ this.#now = opts.now ?? Date.now;
178
+ opts.page.on("request", this.#onRequest);
179
+ opts.page.on("requestfinished", this.#onFinished);
180
+ opts.page.on("requestfailed", this.#onFailed);
181
+ }
182
+ #redact(s) {
183
+ return redactText(s, this.#opts.secrets);
184
+ }
185
+ #onRequest = (r) => {
186
+ if (!KEPT_TYPES.has(r.resourceType()))
187
+ return;
188
+ const endpoint = this.#redact(endpointOf(r.method(), r.url()));
189
+ const payloadKey = WRITE_METHODS.has(r.method().toUpperCase()) ? payloadDigest(r.method(), endpoint, postDataOf(r), this.#opts.secrets) : undefined;
190
+ const rec = {
191
+ id: this.#requests.length,
192
+ method: r.method(),
193
+ endpoint,
194
+ url: this.#redact(redactUrl(r.url())),
195
+ resourceType: r.resourceType(),
196
+ startedAt: this.#now(),
197
+ endedAt: null,
198
+ status: null,
199
+ step: this.#step,
200
+ ...(payloadKey === undefined ? {} : { payloadKey }),
201
+ };
202
+ const contentType = requestContentType(r);
203
+ if (contentType !== undefined)
204
+ rec.contentType = contentType;
205
+ this.#requests.push(rec);
206
+ this.#live.set(r, rec);
207
+ };
208
+ #onFinished = (r) => {
209
+ const rec = this.#live.get(r);
210
+ if (rec === undefined)
211
+ return;
212
+ this.#live.delete(r);
213
+ rec.endedAt = this.#now();
214
+ this.#statusReads.push(r.response().then((res) => {
215
+ rec.status = res?.status() ?? null;
216
+ }, () => undefined));
217
+ };
218
+ #onFailed = (r) => {
219
+ const rec = this.#live.get(r);
220
+ if (rec === undefined)
221
+ return;
222
+ this.#live.delete(r);
223
+ rec.endedAt = this.#now();
224
+ rec.failed = true;
225
+ };
226
+ /** TranscriptLog listener: counts the steps recorded so far (a snapshot belongs to the next one). */
227
+ noteEntry(_entry, all) {
228
+ this.#entries = all.length;
229
+ }
230
+ /** The transcript with each step's screenshot path added (what the journal persists). */
231
+ withScreenshots(entries) {
232
+ return entries.map((e) => {
233
+ const shot = this.#shotByStep.get(e.step);
234
+ return shot === undefined ? e : { ...e, screenshot: shot };
235
+ });
236
+ }
237
+ /**
238
+ * One observed screen: its (masked) screenshot and its facts. Returns the screenshot path, or
239
+ * `null` when none was written (fail-closed secret scan, or the page did not answer in time).
240
+ */
241
+ async observe(snap, visibleText) {
242
+ const step = this.#entries + 1;
243
+ this.#step = step;
244
+ const index = this.#screens.length;
245
+ const { page, secrets } = this.#opts;
246
+ let busy = false;
247
+ let heading = "";
248
+ let shot = null;
249
+ let scanned = false;
250
+ try {
251
+ ({ busy, heading } = await withTimeout(page.evaluate(scanPage, { secrets: [...secrets], attr: MASK_ATTR }), SCREENSHOT_TIMEOUT_MS));
252
+ scanned = true;
253
+ }
254
+ catch {
255
+ scanned = false; // cannot prove where a secret is ⇒ no screenshot (fail-closed)
256
+ }
257
+ if (scanned) {
258
+ const path = join(this.#opts.screenshotDir, `screen-${index + 1}-step-${step}.png`);
259
+ const mask = [
260
+ page.locator(SECRET_INPUT_SELECTOR),
261
+ page.locator(`[${MASK_ATTR}]`),
262
+ ...(this.#opts.secretFields ?? []).map((f) => boundFieldLocator(page, f)),
263
+ ];
264
+ try {
265
+ mkdirSync(this.#opts.screenshotDir, { recursive: true });
266
+ // #245: the demo overlay (when shown) is hidden for the capture — evidence never contains it.
267
+ await page.screenshot({ path, mask, maskColor: MASK_COLOR, fullPage: true, style: DEMO_OVERLAY_HIDE_STYLE, timeout: SCREENSHOT_TIMEOUT_MS });
268
+ shot = path;
269
+ }
270
+ catch {
271
+ shot = null;
272
+ }
273
+ await withTimeout(page.evaluate(unmark, MASK_ATTR), SCREENSHOT_TIMEOUT_MS).catch(() => undefined);
274
+ }
275
+ if (shot !== null)
276
+ this.#shotByStep.set(step, shot);
277
+ this.#screens.push({
278
+ index,
279
+ step,
280
+ at: this.#now(),
281
+ url: this.#redact(redactUrl(snap.url)),
282
+ signature: snap.signature,
283
+ visibleText: this.#redact(visibleText),
284
+ busy,
285
+ ...(shot === null ? {} : { screenshot: shot }),
286
+ ...(heading.length === 0 ? {} : { heading: this.#redact(heading) }),
287
+ });
288
+ return shot;
289
+ }
290
+ /** Every screenshot written, in order. */
291
+ screenshots() {
292
+ return this.#screens.flatMap((s) => (s.screenshot === undefined ? [] : [s.screenshot]));
293
+ }
294
+ /** Stops listening; the capture is frozen from here. */
295
+ detach() {
296
+ if (this.#detached)
297
+ return;
298
+ this.#detached = true;
299
+ this.#opts.page.off("request", this.#onRequest);
300
+ this.#opts.page.off("requestfinished", this.#onFinished);
301
+ this.#opts.page.off("requestfailed", this.#onFailed);
302
+ }
303
+ /** The run's signal capture (steps from the final transcript). */
304
+ async signalCapture(transcript, typedValues) {
305
+ this.detach();
306
+ await Promise.allSettled(this.#statusReads);
307
+ const steps = transcript.map((e) => ({
308
+ step: e.step,
309
+ op: e.op,
310
+ target: e.target,
311
+ actOk: e.actOk,
312
+ url: e.url,
313
+ ...(e.descriptor === undefined ? {} : { descriptor: e.descriptor }),
314
+ ...(e.reason === undefined ? {} : { reason: e.reason }),
315
+ ...(e.href === undefined ? {} : { href: e.href }),
316
+ ...(e.ariaCurrent === undefined ? {} : { ariaCurrent: e.ariaCurrent }),
317
+ // #131: the transcript's own (already redacted) value/message/reply — redacted again here.
318
+ ...(e.value === undefined ? {} : { value: this.#redact(e.value) }),
319
+ ...(e.message === undefined ? {} : { message: this.#redact(e.message) }),
320
+ ...(e.reply === undefined || !e.reply.received ? {} : { reply: this.#redact(e.reply.text) }),
321
+ }));
322
+ const requests = this.#requests.map((r) => ({ ...r }));
323
+ return { steps, requests, screens: [...this.#screens], endedAt: this.#now(), typedValues };
324
+ }
325
+ }
326
+ //# sourceMappingURL=usability-capture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usability-capture.js","sourceRoot":"","sources":["../src/usability-capture.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,EAAE;AACF,kGAAkG;AAClG,6FAA6F;AAC7F,iGAAiG;AACjG,mEAAmE;AACnE,iGAAiG;AACjG,uDAAuD;AACvD,gGAAgG;AAChG,gCAAgC;AAChC,kGAAkG;AAClG,gGAAgG;AAChG,EAAE;AACF,gGAAgG;AAChG,kGAAkG;AAClG,yBAAyB;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,uBAAuB,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAyD,MAAM,mBAAmB,CAAC;AAGtJ,oGAAoG;AACpG,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,0BAA0B;IAC1B,sCAAsC;IACtC,kCAAkC;IAClC,mCAAmC;IACnC,yBAAyB;CAC1B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,kGAAkG;AAClG,MAAM,SAAS,GAAG,oBAAoB,CAAC;AACvC,yFAAyF;AACzF,MAAM,CAAC,MAAM,UAAU,GAAG,SAAS,CAAC;AACpC,MAAM,UAAU,GAAE,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC;AACxD,MAAM,qBAAqB,GAAG,KAAK,CAAC;AAEpC,SAAS,WAAW,CAAI,CAAa,EAAE,EAAU;IAC/C,IAAI,KAAgD,CAAC;IACrD,OAAO,OAAO,CAAC,IAAI,CAAC;QAClB,CAAC;QACD,IAAI,OAAO,CAAI,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;YAC3B,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7E,CAAC,CAAC;KACH,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;AACxC,CAAC;AAED,mFAAmF;AACnF,SAAS,iBAAiB,CAAC,IAAU,EAAE,CAAc;IACnD,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC1C,QAAQ,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QACtB,KAAK,OAAO;YACV,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3D,KAAK,QAAQ;YACX,OAAO,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC3C,KAAK,MAAM;YACT,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAC5C,KAAK,IAAI;YACP,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACnC,KAAK,MAAM;YACT,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IACvC,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,QAAQ,CAAC,IAAkD;IAClE,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,CAAC,CAA4B,EAAW,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QACxG,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,yBAAyB,CAAC,CAAC,EAAE,CAAC;YAClF,IAAI,KAAK,CAAE,EAAuB,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC1C,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAC/B,MAAM,EAAE,CAAC;YACX,CAAC;QACH,CAAC;QACD,MAAM,MAAM,GAAG,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,eAAe,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC;QAC1G,KAAK,IAAI,CAAC,GAAG,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;YAClE,IAAI,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,aAAa,KAAK,IAAI,EAAE,CAAC;gBACnD,CAAC,CAAC,aAAa,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAC5C,MAAM,EAAE,CAAC;YACX,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,IAAI,GACR,QAAQ,CAAC,aAAa,CAAC,0GAA0G,CAAC,KAAK,IAAI;QAC3I,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,8BAA8B,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC/H,MAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,sCAAsC,CAAuB,CAAC;IAChG,MAAM,OAAO,GAAG,CAAC,CAAC,EAAE,EAAE,SAAS,IAAI,EAAE,EAAE,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,QAAQ,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC7H,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AACnC,CAAC;AAED,iGAAiG;AACjG,MAAM,YAAY,GAAG,gJAAgJ,CAAC;AACtK,qEAAqE;AACrE,MAAM,cAAc,GAAG,kEAAkE,CAAC;AAC1F,MAAM,eAAe,GAAG,MAAM,CAAC;AAE/B,MAAM,cAAe,SAAQ,KAAK;CAAG;AAErC,SAAS,SAAS,CAAC,CAAU;IAC3B,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC9C,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QACxC,MAAM,GAAG,GAA4B,EAAE,CAAC;QACxC,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAA4B,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YACjE,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;gBAAE,MAAM,IAAI,cAAc,EAAE,CAAC;YACvD,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;gBAAE,SAAS;YACnC,GAAG,CAAC,CAAC,CAAC,GAAG,SAAS,CAAE,CAA6B,CAAC,CAAC,CAAC,CAAC,CAAC;QACxD,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,MAAc,EAAE,QAAgB,EAAE,IAAmB,EAAE,OAA0B;IAC7G,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,eAAe;QAAE,OAAO,SAAS,CAAC;IAC1F,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IAC5E,IAAI,KAAc,CAAC;IACnB,IAAI,CAAC;QACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,CAAC,oCAAoC,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,SAAS,CAAC;QACvE,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;IACxD,CAAC;IACD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QAC/C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,WAAW,EAAE,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACjH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC,CAAC,2CAA2C;IAC/D,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,IAAY;IAC1B,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC;QAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;AAChG,CAAC;AA2BD,gGAAgG;AAChG,SAAS,kBAAkB,CAAC,CAAU;IACpC,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,cAAc,CAAC,CAAC;QACtC,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;AAElE,SAAS,UAAU,CAAC,CAAU;IAC5B,IAAI,CAAC;QACH,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;IACtB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,OAAO,gBAAgB;IAClB,KAAK,CAA0B;IAC/B,IAAI,CAAe;IACnB,SAAS,GAAkB,EAAE,CAAC;IAC9B,KAAK,GAAG,IAAI,GAAG,EAAwB,CAAC;IACxC,QAAQ,GAAmB,EAAE,CAAC;IACvC,2EAA2E;IAClE,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAC;IACjD,+EAA+E;IACtE,YAAY,GAAoB,EAAE,CAAC;IAC5C,QAAQ,GAAG,CAAC,CAAC;IACb,KAAK,GAAG,CAAC,CAAC;IACV,SAAS,GAAG,KAAK,CAAC;IAElB,YAAY,IAA6B;QACvC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;QACjC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACzC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,iBAAiB,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QAClD,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,eAAe,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IAChD,CAAC;IAED,OAAO,CAAC,CAAS;QACf,OAAO,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3C,CAAC;IAEQ,UAAU,GAAG,CAAC,CAAU,EAAQ,EAAE;QACzC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC;YAAE,OAAO;QAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAC/D,MAAM,UAAU,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACpJ,MAAM,GAAG,GAAgB;YACvB,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM;YACzB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;YAClB,QAAQ;YACR,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;YACrC,YAAY,EAAE,CAAC,CAAC,YAAY,EAAE;YAC9B,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE;YACtB,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,IAAI;YACZ,IAAI,EAAE,IAAI,CAAC,KAAK;YAChB,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC;SACpD,CAAC;QACF,MAAM,WAAW,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAAC;QAC1C,IAAI,WAAW,KAAK,SAAS;YAAE,GAAG,CAAC,WAAW,GAAG,WAAW,CAAC;QAC7D,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACzB,CAAC,CAAC;IAEO,WAAW,GAAG,CAAC,CAAU,EAAQ,EAAE;QAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC9B,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO;QAC9B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACrB,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC1B,IAAI,CAAC,YAAY,CAAC,IAAI,CACpB,CAAC,CAAC,QAAQ,EAAE,CAAC,IAAI,CACf,CAAC,GAAG,EAAE,EAAE;YACN,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE,MAAM,EAAE,IAAI,IAAI,CAAC;QACrC,CAAC,EACD,GAAG,EAAE,CAAC,SAAS,CAChB,CACF,CAAC;IACJ,CAAC,CAAC;IAEO,SAAS,GAAG,CAAC,CAAU,EAAQ,EAAE;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC9B,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO;QAC9B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACrB,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC1B,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC;IACpB,CAAC,CAAC;IAEF,qGAAqG;IACrG,SAAS,CAAC,MAAuB,EAAE,GAA+B;QAChE,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC;IAC7B,CAAC;IAED,yFAAyF;IACzF,eAAe,CAAC,OAAmC;QACjD,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACvB,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC1C,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;QAC7D,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO,CAAC,IAAc,EAAE,WAAmB;QAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;QAC/B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QACnC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACrC,IAAI,IAAI,GAAG,KAAK,CAAC;QACjB,IAAI,OAAO,GAAG,EAAE,CAAC;QACjB,IAAI,IAAI,GAAkB,IAAI,CAAC;QAC/B,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC;YACH,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,qBAAqB,CAAC,CAAC,CAAC;YACpI,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,GAAG,KAAK,CAAC,CAAC,+DAA+D;QAClF,CAAC;QACD,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,UAAU,KAAK,GAAG,CAAC,SAAS,IAAI,MAAM,CAAC,CAAC;YACpF,MAAM,IAAI,GAAc;gBACtB,IAAI,CAAC,OAAO,CAAC,qBAAqB,CAAC;gBACnC,IAAI,CAAC,OAAO,CAAC,IAAI,SAAS,GAAG,CAAC;gBAC9B,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;aAC1E,CAAC;YACF,IAAI,CAAC;gBACH,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBACzD,8FAA8F;gBAC9F,MAAM,IAAI,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,uBAAuB,EAAE,OAAO,EAAE,qBAAqB,EAAE,CAAC,CAAC;gBAC7I,IAAI,GAAG,IAAI,CAAC;YACd,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,GAAG,IAAI,CAAC;YACd,CAAC;YACD,MAAM,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,qBAAqB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACpG,CAAC;QACD,IAAI,IAAI,KAAK,IAAI;YAAE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACpD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;YACjB,KAAK;YACL,IAAI;YACJ,EAAE,EAAE,IAAI,CAAC,IAAI,EAAE;YACf,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC;YACtC,IAAI;YACJ,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;YAC9C,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;SACpE,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;IAED,0CAA0C;IAC1C,WAAW;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAC1F,CAAC;IAED,wDAAwD;IACxD,MAAM;QACJ,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAC3B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAChD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACzD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACvD,CAAC;IAED,kEAAkE;IAClE,KAAK,CAAC,aAAa,CAAC,UAAsC,EAAE,WAA8B;QACxF,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC5C,MAAM,KAAK,GAAiB,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACjD,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,GAAG,EAAE,CAAC,CAAC,GAAG;YACV,GAAG,CAAC,CAAC,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC;YACnE,GAAG,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;YACvD,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YACjD,GAAG,CAAC,CAAC,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;YACtE,2FAA2F;YAC3F,GAAG,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;YAClE,GAAG,CAAC,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;YACxE,GAAG,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;SAC7F,CAAC,CAAC,CAAC;QACJ,MAAM,QAAQ,GAAoB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACxE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,CAAC;IAC7F,CAAC;CACF"}
@@ -0,0 +1,36 @@
1
+ import type { ConfiguredModelPrices, JevPricing, UsagePricing } from "@jevitate/ai-core";
2
+ /**
3
+ * The usage-pricing slice of `~/.jevitate/config.json` (#136):
4
+ *
5
+ * ```json
6
+ * { "usage": {
7
+ * "jevUnitPriceUsd": 0.006,
8
+ * "modelPrices": { "openai/gpt-4o": { "inputUsdPerMtok": 2.5, "outputUsdPerMtok": 10 } } } }
9
+ * ```
10
+ *
11
+ * `jevUnitPriceUsd` prices each Jev judgment call when the provider itself reports no cost (it
12
+ * overrides the built-in Jev price table). `modelPrices` (#163) prices any model per million tokens
13
+ * — Jev or generation — overriding the built-in tables (and pricing a model they don't list). A
14
+ * missing file (or key) is "not configured"; a malformed file or a non-numeric/negative value fails
15
+ * closed rather than being silently ignored.
16
+ */
17
+ export declare class UsageConfigError extends Error {
18
+ readonly code: "E_USAGE_CONFIG";
19
+ }
20
+ /** `usage.jevUnitPriceUsd` from the config file. Undefined when the file or key is absent. */
21
+ export declare function loadJevUnitPriceUsd(path?: string): number | undefined;
22
+ /**
23
+ * Resolves the Jev per-judgment unit price (#136): `JEVITATE_JEV_UNIT_PRICE_USD` (env) beats
24
+ * `usage.jevUnitPriceUsd` (config); undefined when neither is set — `jevUsd` then stays unpriced,
25
+ * exactly as it did before #136. The result's `source` is what `UsageCounts.jevPriceSource` echoes,
26
+ * so a priced run's number is never unexplained. A malformed value fails closed (never silently
27
+ * ignored, never silently mispriced): the CLI surfaces it as a setup error before any run starts.
28
+ */
29
+ export declare function resolveJevUnitPrice(env?: Readonly<Record<string, string | undefined>>, configPath?: string): JevPricing | undefined;
30
+ /** `usage.modelPrices` from the config file (#163). Undefined when the file or key is absent. */
31
+ export declare function loadModelPrices(path?: string): ConfiguredModelPrices | undefined;
32
+ /**
33
+ * Every configured price (#163): the Jev unit price (`resolveJevUnitPrice`) and `usage.modelPrices`.
34
+ * Whatever is not configured is priced from the built-in tables in `@jevitate/ai-core`'s usage.ts.
35
+ */
36
+ export declare function resolveUsagePricing(env?: Readonly<Record<string, string | undefined>>, configPath?: string): UsagePricing;
@@ -0,0 +1,114 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { resolveDataDir } from "./data-dir.js";
3
+ /**
4
+ * The usage-pricing slice of `~/.jevitate/config.json` (#136):
5
+ *
6
+ * ```json
7
+ * { "usage": {
8
+ * "jevUnitPriceUsd": 0.006,
9
+ * "modelPrices": { "openai/gpt-4o": { "inputUsdPerMtok": 2.5, "outputUsdPerMtok": 10 } } } }
10
+ * ```
11
+ *
12
+ * `jevUnitPriceUsd` prices each Jev judgment call when the provider itself reports no cost (it
13
+ * overrides the built-in Jev price table). `modelPrices` (#163) prices any model per million tokens
14
+ * — Jev or generation — overriding the built-in tables (and pricing a model they don't list). A
15
+ * missing file (or key) is "not configured"; a malformed file or a non-numeric/negative value fails
16
+ * closed rather than being silently ignored.
17
+ */
18
+ export class UsageConfigError extends Error {
19
+ code = "E_USAGE_CONFIG";
20
+ }
21
+ const ENV_VAR = "JEVITATE_JEV_UNIT_PRICE_USD";
22
+ function loadUsageSection(path) {
23
+ let raw;
24
+ try {
25
+ raw = readFileSync(path, "utf8");
26
+ }
27
+ catch (e) {
28
+ if (typeof e === "object" && e !== null && "code" in e && e.code === "ENOENT")
29
+ return undefined;
30
+ throw new UsageConfigError(`cannot read ${path}: ${e instanceof Error ? e.message : String(e)}`);
31
+ }
32
+ let parsed;
33
+ try {
34
+ parsed = JSON.parse(raw);
35
+ }
36
+ catch {
37
+ throw new UsageConfigError(`${path} is not valid JSON`);
38
+ }
39
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
40
+ throw new UsageConfigError(`${path} must be a JSON object`);
41
+ }
42
+ const usage = parsed.usage;
43
+ if (usage === undefined)
44
+ return undefined;
45
+ if (usage === null || typeof usage !== "object" || Array.isArray(usage)) {
46
+ throw new UsageConfigError(`${path}: "usage" must be an object`);
47
+ }
48
+ return usage;
49
+ }
50
+ function positiveNumber(v, at) {
51
+ if (typeof v !== "number" || !Number.isFinite(v) || v < 0) {
52
+ throw new UsageConfigError(`${at} must be a non-negative number, got ${JSON.stringify(v)}`);
53
+ }
54
+ return v;
55
+ }
56
+ /** `usage.jevUnitPriceUsd` from the config file. Undefined when the file or key is absent. */
57
+ export function loadJevUnitPriceUsd(path = resolveDataDir(["config.json"])) {
58
+ const v = loadUsageSection(path)?.jevUnitPriceUsd;
59
+ if (v === undefined)
60
+ return undefined;
61
+ return positiveNumber(v, `${path}: usage.jevUnitPriceUsd`);
62
+ }
63
+ /**
64
+ * Resolves the Jev per-judgment unit price (#136): `JEVITATE_JEV_UNIT_PRICE_USD` (env) beats
65
+ * `usage.jevUnitPriceUsd` (config); undefined when neither is set — `jevUsd` then stays unpriced,
66
+ * exactly as it did before #136. The result's `source` is what `UsageCounts.jevPriceSource` echoes,
67
+ * so a priced run's number is never unexplained. A malformed value fails closed (never silently
68
+ * ignored, never silently mispriced): the CLI surfaces it as a setup error before any run starts.
69
+ */
70
+ export function resolveJevUnitPrice(env = process.env, configPath = resolveDataDir(["config.json"])) {
71
+ const raw = env[ENV_VAR];
72
+ if (raw !== undefined && raw !== "") {
73
+ const n = Number(raw);
74
+ if (!Number.isFinite(n) || n < 0) {
75
+ throw new UsageConfigError(`${ENV_VAR} must be a non-negative number, got ${JSON.stringify(raw)}`);
76
+ }
77
+ return { unitPriceUsd: n, source: `env:${ENV_VAR}` };
78
+ }
79
+ const fromConfig = loadJevUnitPriceUsd(configPath);
80
+ if (fromConfig === undefined)
81
+ return undefined;
82
+ return { unitPriceUsd: fromConfig, source: `config:${configPath} usage.jevUnitPriceUsd` };
83
+ }
84
+ /** `usage.modelPrices` from the config file (#163). Undefined when the file or key is absent. */
85
+ export function loadModelPrices(path = resolveDataDir(["config.json"])) {
86
+ const v = loadUsageSection(path)?.modelPrices;
87
+ if (v === undefined)
88
+ return undefined;
89
+ if (v === null || typeof v !== "object" || Array.isArray(v)) {
90
+ throw new UsageConfigError(`${path}: usage.modelPrices must be an object of { "<model>": { inputUsdPerMtok, outputUsdPerMtok } }`);
91
+ }
92
+ const prices = {};
93
+ for (const [model, p] of Object.entries(v)) {
94
+ if (p === null || typeof p !== "object" || Array.isArray(p)) {
95
+ throw new UsageConfigError(`${path}: usage.modelPrices.${model} must be { inputUsdPerMtok, outputUsdPerMtok }`);
96
+ }
97
+ const o = p;
98
+ prices[model] = {
99
+ inputUsdPerMtok: positiveNumber(o.inputUsdPerMtok, `${path}: usage.modelPrices.${model}.inputUsdPerMtok`),
100
+ outputUsdPerMtok: positiveNumber(o.outputUsdPerMtok, `${path}: usage.modelPrices.${model}.outputUsdPerMtok`),
101
+ };
102
+ }
103
+ return { prices, source: `config:${path} usage.modelPrices` };
104
+ }
105
+ /**
106
+ * Every configured price (#163): the Jev unit price (`resolveJevUnitPrice`) and `usage.modelPrices`.
107
+ * Whatever is not configured is priced from the built-in tables in `@jevitate/ai-core`'s usage.ts.
108
+ */
109
+ export function resolveUsagePricing(env = process.env, configPath = resolveDataDir(["config.json"])) {
110
+ const jevUnitPrice = resolveJevUnitPrice(env, configPath);
111
+ const modelPrices = loadModelPrices(configPath);
112
+ return { ...(jevUnitPrice === undefined ? {} : { jevUnitPrice }), ...(modelPrices === undefined ? {} : { modelPrices }) };
113
+ }
114
+ //# sourceMappingURL=usage-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage-config.js","sourceRoot":"","sources":["../src/usage-config.ts"],"names":[],"mappings":"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"}
package/dist/ux-api.d.ts CHANGED
@@ -1,75 +1,3 @@
1
- import type { JudgmentPort, GenerationPort } from "@jevitate/ai-core";
2
- import { type BrowserPort } from "@jevitate/playwright";
3
- import type { Recording } from "@jevitate/recording";
4
- import { type Snapshot, type Bounds } from "@jevitate/explore";
5
- import { type AppContext, type ScreenRef, type UxEvidence, type UxReport } from "@jevitate/ux";
6
- /** A live Snapshot (+ extracted page text) → one screen's UxEvidence. */
7
- export declare function snapshotToEvidence(snap: Snapshot, visibleText: string, appContext: AppContext, job: string | undefined, history: readonly ScreenRef[]): UxEvidence;
8
- /**
9
- * A saved Recording → per-page UxEvidence. A Recording is deterministic and
10
- * thin: it carries the CONTROLS the user touched (via each step's descriptor)
11
- * and the page url, but NOT the full visible text or a11y geometry. So offline
12
- * analysis is honestly partial — items needing `visibleText`/`a11yFacts` Skip,
13
- * and coverage reports it. (The live mode carries the rich per-screen snapshot.)
14
- */
15
- export declare function recordingToEvidence(recording: Recording, appContext: AppContext, job?: string): UxEvidence[];
16
- export interface RunUxReviewOptions {
17
- readonly recording: Recording;
18
- readonly appContext: AppContext;
19
- readonly judge: JudgmentPort;
20
- readonly secrets?: readonly string[];
21
- readonly judgmentBudget?: number;
22
- /** Where to write the report. Default `~/.jevitate/ux-reports`. */
23
- readonly outDir?: string;
24
- readonly nowIso?: () => string;
25
- }
26
- export interface RunUxReviewResult {
27
- readonly report: UxReport;
28
- readonly reportPath: string;
29
- }
30
- /**
31
- * Offline UX review. FAIL-FAST: an analyzer `failed` outcome throws
32
- * `UxAnalysisFailedError` (the CLI maps it to a non-zero fail envelope) — never
33
- * a fabricated "clean" report.
34
- */
35
- export declare function runUxReview(opts: RunUxReviewOptions): Promise<RunUxReviewResult>;
36
- export declare class UxAnalysisFailedError extends Error {
37
- readonly screenId?: string | undefined;
38
- readonly rubricItemId?: string | undefined;
39
- readonly code: "E_UX_ANALYSIS";
40
- constructor(reason: string, screenId?: string | undefined, rubricItemId?: string | undefined);
41
- }
42
- export interface RunUsabilityMissionOptions {
43
- readonly url: string;
44
- readonly job: string;
45
- readonly allowlist: readonly string[];
46
- readonly appContext: AppContext;
47
- readonly judge: JudgmentPort;
48
- readonly gen: GenerationPort;
49
- readonly bounds?: Partial<Bounds>;
50
- readonly secrets?: readonly string[];
51
- readonly judgmentBudget?: number;
52
- readonly outDir?: string;
53
- readonly browserPortFactory?: () => BrowserPort;
54
- readonly nowIso?: () => string;
55
- /** Test seam: extract a page's visible text. Default reads the live page. */
56
- readonly extractText?: (session: {
57
- page: {
58
- evaluate: (fn: () => string) => Promise<string>;
59
- };
60
- }) => Promise<string>;
61
- }
62
- export interface RunUsabilityMissionResult {
63
- readonly report: UxReport;
64
- readonly reportPath: string;
65
- readonly stop: string;
66
- readonly screensObserved: number;
67
- }
68
- /**
69
- * Live usability mission. Guardrail #1 authorizes BEFORE opening a browser.
70
- * Reuses explore()'s loop (goal = the job) and, via the additive onSnapshot
71
- * hook, collects one screen's evidence per observation — then analyzes ONCE
72
- * after the run. A UX finding NEVER gates the loop (the hook's result is
73
- * ignored by explore). The temp profile dir is always removed.
74
- */
75
- export declare function runUsabilityMission(opts: RunUsabilityMissionOptions): Promise<RunUsabilityMissionResult>;
1
+ export { type RunUsabilityMissionOptions, type RunUsabilityMissionResult, UsabilityInvariantsUnsupportedError, runUsabilityMission } from "./ux-usability.js";
2
+ export { type LoadedSidecars, type RecordingSidecars, type RunUxReviewOptions, type RunUxReviewResult, UxAnalysisFailedError, discoverRecordingSidecars, loadRecordingSidecars, runUxReview } from "./ux-review.js";
3
+ export { type MissionTranscriptEntryLike, captureFromTranscript, extractTypedValues, recordingToEvidence, snapshotToEvidence } from "./ux-evidence.js";