@jevitate/cli 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (410) hide show
  1. package/README.md +23 -0
  2. package/dist/ai-cli.d.ts +31 -11
  3. package/dist/ai-cli.js +131 -88
  4. package/dist/ai-cli.js.map +1 -1
  5. package/dist/author-journey-cli.d.ts +4 -0
  6. package/dist/author-journey-cli.js +111 -0
  7. package/dist/author-journey-cli.js.map +1 -0
  8. package/dist/bin.js +60349 -16477
  9. package/dist/bin.js.map +1 -1
  10. package/dist/browser-run-options.d.ts +89 -0
  11. package/dist/browser-run-options.js +175 -0
  12. package/dist/browser-run-options.js.map +1 -0
  13. package/dist/build-info.generated.d.ts +2 -0
  14. package/dist/build-info.generated.js +10 -0
  15. package/dist/build-info.generated.js.map +1 -0
  16. package/dist/campaign-api.d.ts +151 -0
  17. package/dist/campaign-api.js +446 -0
  18. package/dist/campaign-api.js.map +1 -0
  19. package/dist/campaign-cli.d.ts +6 -0
  20. package/dist/campaign-cli.js +66 -0
  21. package/dist/campaign-cli.js.map +1 -0
  22. package/dist/check-api.d.ts +5 -0
  23. package/dist/check-api.js +234 -0
  24. package/dist/check-api.js.map +1 -0
  25. package/dist/check-budget.d.ts +26 -0
  26. package/dist/check-budget.js +90 -0
  27. package/dist/check-budget.js.map +1 -0
  28. package/dist/check-cli.d.ts +31 -0
  29. package/dist/check-cli.js +109 -0
  30. package/dist/check-cli.js.map +1 -0
  31. package/dist/check-execute.d.ts +29 -0
  32. package/dist/check-execute.js +420 -0
  33. package/dist/check-execute.js.map +1 -0
  34. package/dist/check-plan.d.ts +118 -0
  35. package/dist/check-plan.js +516 -0
  36. package/dist/check-plan.js.map +1 -0
  37. package/dist/check-suite.d.ts +184 -0
  38. package/dist/check-suite.js +540 -0
  39. package/dist/check-suite.js.map +1 -0
  40. package/dist/check-types.d.ts +181 -0
  41. package/dist/check-types.js +35 -0
  42. package/dist/check-types.js.map +1 -0
  43. package/dist/cli-args.d.ts +26 -0
  44. package/dist/cli-args.js +50 -0
  45. package/dist/cli-args.js.map +1 -0
  46. package/dist/cli-output.d.ts +106 -0
  47. package/dist/cli-output.js +563 -0
  48. package/dist/cli-output.js.map +1 -0
  49. package/dist/cli-reference.d.ts +3 -0
  50. package/dist/cli-reference.js +81 -0
  51. package/dist/cli-reference.js.map +1 -0
  52. package/dist/cli-refusal.d.ts +12 -0
  53. package/dist/cli-refusal.js +52 -0
  54. package/dist/cli-refusal.js.map +1 -0
  55. package/dist/cli-shared.d.ts +340 -0
  56. package/dist/cli-shared.js +537 -0
  57. package/dist/cli-shared.js.map +1 -0
  58. package/dist/cli-surface.d.ts +33 -0
  59. package/dist/cli-surface.js +48 -0
  60. package/dist/cli-surface.js.map +1 -0
  61. package/dist/conversation-options.d.ts +15 -0
  62. package/dist/conversation-options.js +10 -0
  63. package/dist/conversation-options.js.map +1 -0
  64. package/dist/credentials-file.d.ts +15 -0
  65. package/dist/credentials-file.js +56 -0
  66. package/dist/credentials-file.js.map +1 -0
  67. package/dist/data-dir.d.ts +1 -1
  68. package/dist/data-dir.js +1 -1
  69. package/dist/defect-evidence.d.ts +141 -0
  70. package/dist/defect-evidence.js +505 -0
  71. package/dist/defect-evidence.js.map +1 -0
  72. package/dist/demo-aspect-api.d.ts +209 -0
  73. package/dist/demo-aspect-api.js +398 -0
  74. package/dist/demo-aspect-api.js.map +1 -0
  75. package/dist/demo-aspect-cli.d.ts +3 -0
  76. package/dist/demo-aspect-cli.js +243 -0
  77. package/dist/demo-aspect-cli.js.map +1 -0
  78. package/dist/demo-capture.d.ts +142 -0
  79. package/dist/demo-capture.js +537 -0
  80. package/dist/demo-capture.js.map +1 -0
  81. package/dist/engine.d.ts +35 -0
  82. package/dist/engine.js +25 -0
  83. package/dist/engine.js.map +1 -0
  84. package/dist/environments.d.ts +155 -0
  85. package/dist/environments.js +364 -0
  86. package/dist/environments.js.map +1 -0
  87. package/dist/exit-codes.d.ts +46 -0
  88. package/dist/exit-codes.js +91 -0
  89. package/dist/exit-codes.js.map +1 -0
  90. package/dist/explore-adversarial.d.ts +144 -0
  91. package/dist/explore-adversarial.js +227 -0
  92. package/dist/explore-adversarial.js.map +1 -0
  93. package/dist/explore-api.d.ts +7 -187
  94. package/dist/explore-api.js +7 -301
  95. package/dist/explore-api.js.map +1 -1
  96. package/dist/explore-author.d.ts +79 -0
  97. package/dist/explore-author.js +86 -0
  98. package/dist/explore-author.js.map +1 -0
  99. package/dist/explore-cli.d.ts +7 -0
  100. package/dist/explore-cli.js +1199 -0
  101. package/dist/explore-cli.js.map +1 -0
  102. package/dist/explore-coverage.d.ts +173 -0
  103. package/dist/explore-coverage.js +246 -0
  104. package/dist/explore-coverage.js.map +1 -0
  105. package/dist/explore-feature.d.ts +125 -0
  106. package/dist/explore-feature.js +218 -0
  107. package/dist/explore-feature.js.map +1 -0
  108. package/dist/explore-goal.d.ts +271 -0
  109. package/dist/explore-goal.js +417 -0
  110. package/dist/explore-goal.js.map +1 -0
  111. package/dist/explore-shared.d.ts +177 -0
  112. package/dist/explore-shared.js +149 -0
  113. package/dist/explore-shared.js.map +1 -0
  114. package/dist/explore-specs.d.ts +53 -0
  115. package/dist/explore-specs.js +333 -0
  116. package/dist/explore-specs.js.map +1 -0
  117. package/dist/findings-filing.d.ts +64 -0
  118. package/dist/findings-filing.js +108 -0
  119. package/dist/findings-filing.js.map +1 -0
  120. package/dist/fixture-auth.d.ts +63 -0
  121. package/dist/fixture-auth.js +95 -0
  122. package/dist/fixture-auth.js.map +1 -0
  123. package/dist/fixture-cli.d.ts +99 -0
  124. package/dist/fixture-cli.js +292 -0
  125. package/dist/fixture-cli.js.map +1 -0
  126. package/dist/github-issue-filer.d.ts +62 -0
  127. package/dist/github-issue-filer.js +189 -0
  128. package/dist/github-issue-filer.js.map +1 -0
  129. package/dist/host-health-run.d.ts +25 -0
  130. package/dist/host-health-run.js +33 -0
  131. package/dist/host-health-run.js.map +1 -0
  132. package/dist/http-5xx-outcome.d.ts +6 -0
  133. package/dist/http-5xx-outcome.js +39 -0
  134. package/dist/http-5xx-outcome.js.map +1 -0
  135. package/dist/inbox-cli.d.ts +7 -0
  136. package/dist/inbox-cli.js +197 -0
  137. package/dist/inbox-cli.js.map +1 -0
  138. package/dist/index.d.ts +1 -0
  139. package/dist/index.js +1 -0
  140. package/dist/index.js.map +1 -1
  141. package/dist/init-cli.d.ts +4 -0
  142. package/dist/init-cli.js +156 -0
  143. package/dist/init-cli.js.map +1 -0
  144. package/dist/init-keys.d.ts +30 -2
  145. package/dist/init-keys.js +19 -5
  146. package/dist/init-keys.js.map +1 -1
  147. package/dist/init-next-steps.d.ts +35 -0
  148. package/dist/init-next-steps.js +79 -0
  149. package/dist/init-next-steps.js.map +1 -0
  150. package/dist/invariants-file.d.ts +34 -0
  151. package/dist/invariants-file.js +68 -0
  152. package/dist/invariants-file.js.map +1 -0
  153. package/dist/invariants-validate.d.ts +30 -0
  154. package/dist/invariants-validate.js +105 -0
  155. package/dist/invariants-validate.js.map +1 -0
  156. package/dist/journey-annotate-api.d.ts +76 -0
  157. package/dist/journey-annotate-api.js +269 -0
  158. package/dist/journey-annotate-api.js.map +1 -0
  159. package/dist/journey-annotate-cli.d.ts +9 -0
  160. package/dist/journey-annotate-cli.js +181 -0
  161. package/dist/journey-annotate-cli.js.map +1 -0
  162. package/dist/journey-api.d.ts +104 -2
  163. package/dist/journey-api.js +163 -20
  164. package/dist/journey-api.js.map +1 -1
  165. package/dist/journey-cli.d.ts +4 -0
  166. package/dist/journey-cli.js +411 -0
  167. package/dist/journey-cli.js.map +1 -0
  168. package/dist/journey-demo-api.d.ts +114 -0
  169. package/dist/journey-demo-api.js +405 -0
  170. package/dist/journey-demo-api.js.map +1 -0
  171. package/dist/journey-demo-cli.d.ts +10 -0
  172. package/dist/journey-demo-cli.js +158 -0
  173. package/dist/journey-demo-cli.js.map +1 -0
  174. package/dist/journey-prefix.d.ts +194 -0
  175. package/dist/journey-prefix.js +279 -0
  176. package/dist/journey-prefix.js.map +1 -0
  177. package/dist/key-report.d.ts +49 -0
  178. package/dist/key-report.js +101 -0
  179. package/dist/key-report.js.map +1 -0
  180. package/dist/kill-signal.d.ts +193 -0
  181. package/dist/kill-signal.js +283 -0
  182. package/dist/kill-signal.js.map +1 -0
  183. package/dist/launch-armed.d.ts +37 -0
  184. package/dist/launch-armed.js +31 -0
  185. package/dist/launch-armed.js.map +1 -0
  186. package/dist/ledger-api.d.ts +106 -0
  187. package/dist/ledger-api.js +239 -0
  188. package/dist/ledger-api.js.map +1 -0
  189. package/dist/ledger-cli.d.ts +18 -0
  190. package/dist/ledger-cli.js +107 -0
  191. package/dist/ledger-cli.js.map +1 -0
  192. package/dist/load-api.d.ts +29 -11
  193. package/dist/load-api.js +23 -15
  194. package/dist/load-api.js.map +1 -1
  195. package/dist/load-cli.d.ts +4 -0
  196. package/dist/load-cli.js +97 -0
  197. package/dist/load-cli.js.map +1 -0
  198. package/dist/log-correlation.d.ts +200 -0
  199. package/dist/log-correlation.js +394 -0
  200. package/dist/log-correlation.js.map +1 -0
  201. package/dist/log-lines.d.ts +131 -0
  202. package/dist/log-lines.js +389 -0
  203. package/dist/log-lines.js.map +1 -0
  204. package/dist/log-sources.d.ts +71 -0
  205. package/dist/log-sources.js +236 -0
  206. package/dist/log-sources.js.map +1 -0
  207. package/dist/log-trace.d.ts +80 -0
  208. package/dist/log-trace.js +210 -0
  209. package/dist/log-trace.js.map +1 -0
  210. package/dist/logs-cli.d.ts +3 -0
  211. package/dist/logs-cli.js +51 -0
  212. package/dist/logs-cli.js.map +1 -0
  213. package/dist/logs-retention.d.ts +28 -0
  214. package/dist/logs-retention.js +85 -0
  215. package/dist/logs-retention.js.map +1 -0
  216. package/dist/masked-input.d.ts +22 -0
  217. package/dist/masked-input.js +99 -0
  218. package/dist/masked-input.js.map +1 -0
  219. package/dist/mcp-api.d.ts +91 -1
  220. package/dist/mcp-api.js +514 -18
  221. package/dist/mcp-api.js.map +1 -1
  222. package/dist/mcp-args.d.ts +50 -0
  223. package/dist/mcp-args.js +164 -0
  224. package/dist/mcp-args.js.map +1 -0
  225. package/dist/mcp-cli-bridge.d.ts +34 -0
  226. package/dist/mcp-cli-bridge.js +62 -0
  227. package/dist/mcp-cli-bridge.js.map +1 -0
  228. package/dist/mcp-cli-runner.d.ts +29 -0
  229. package/dist/mcp-cli-runner.js +44 -0
  230. package/dist/mcp-cli-runner.js.map +1 -0
  231. package/dist/mcp-cli-tools.d.ts +129 -0
  232. package/dist/mcp-cli-tools.js +737 -0
  233. package/dist/mcp-cli-tools.js.map +1 -0
  234. package/dist/mcp-paths.d.ts +28 -0
  235. package/dist/mcp-paths.js +76 -0
  236. package/dist/mcp-paths.js.map +1 -0
  237. package/dist/mission-actors.d.ts +40 -0
  238. package/dist/mission-actors.js +72 -0
  239. package/dist/mission-actors.js.map +1 -0
  240. package/dist/mission-api.d.ts +35 -0
  241. package/dist/mission-api.js +60 -3
  242. package/dist/mission-api.js.map +1 -1
  243. package/dist/mission-cli.d.ts +4 -0
  244. package/dist/mission-cli.js +248 -0
  245. package/dist/mission-cli.js.map +1 -0
  246. package/dist/mission-exit.d.ts +22 -0
  247. package/dist/mission-exit.js +26 -0
  248. package/dist/mission-exit.js.map +1 -0
  249. package/dist/mission-fixtures.d.ts +234 -0
  250. package/dist/mission-fixtures.js +862 -0
  251. package/dist/mission-fixtures.js.map +1 -0
  252. package/dist/mission-journal.d.ts +60 -0
  253. package/dist/mission-journal.js +115 -0
  254. package/dist/mission-journal.js.map +1 -0
  255. package/dist/mission-queue-cli.d.ts +10 -0
  256. package/dist/mission-queue-cli.js +198 -0
  257. package/dist/mission-queue-cli.js.map +1 -0
  258. package/dist/mission-queue-runner.d.ts +126 -0
  259. package/dist/mission-queue-runner.js +406 -0
  260. package/dist/mission-queue-runner.js.map +1 -0
  261. package/dist/multi-run-cli.d.ts +37 -0
  262. package/dist/multi-run-cli.js +116 -0
  263. package/dist/multi-run-cli.js.map +1 -0
  264. package/dist/multi-run.d.ts +339 -0
  265. package/dist/multi-run.js +619 -0
  266. package/dist/multi-run.js.map +1 -0
  267. package/dist/openrouter-call.d.ts +22 -0
  268. package/dist/openrouter-call.js +64 -0
  269. package/dist/openrouter-call.js.map +1 -0
  270. package/dist/profile-cli.d.ts +4 -0
  271. package/dist/profile-cli.js +57 -0
  272. package/dist/profile-cli.js.map +1 -0
  273. package/dist/program.d.ts +3 -75
  274. package/dist/program.js +123 -1921
  275. package/dist/program.js.map +1 -1
  276. package/dist/project-dir.d.ts +96 -0
  277. package/dist/project-dir.js +221 -0
  278. package/dist/project-dir.js.map +1 -0
  279. package/dist/record-api.d.ts +13 -7
  280. package/dist/record-api.js +44 -30
  281. package/dist/record-api.js.map +1 -1
  282. package/dist/record-cli.d.ts +4 -0
  283. package/dist/record-cli.js +76 -0
  284. package/dist/record-cli.js.map +1 -0
  285. package/dist/recording-cli.d.ts +4 -0
  286. package/dist/recording-cli.js +248 -0
  287. package/dist/recording-cli.js.map +1 -0
  288. package/dist/regression-api.d.ts +107 -0
  289. package/dist/regression-api.js +387 -6
  290. package/dist/regression-api.js.map +1 -1
  291. package/dist/regression-cli.d.ts +4 -0
  292. package/dist/regression-cli.js +284 -0
  293. package/dist/regression-cli.js.map +1 -0
  294. package/dist/report-api.d.ts +146 -0
  295. package/dist/report-api.js +431 -0
  296. package/dist/report-api.js.map +1 -0
  297. package/dist/report-cli.d.ts +12 -0
  298. package/dist/report-cli.js +147 -0
  299. package/dist/report-cli.js.map +1 -0
  300. package/dist/resource-preflight.d.ts +87 -0
  301. package/dist/resource-preflight.js +160 -0
  302. package/dist/resource-preflight.js.map +1 -0
  303. package/dist/result-schema.d.ts +22 -0
  304. package/dist/result-schema.js +28 -0
  305. package/dist/result-schema.js.map +1 -0
  306. package/dist/run-index.d.ts +29 -0
  307. package/dist/run-index.js +70 -0
  308. package/dist/run-index.js.map +1 -0
  309. package/dist/run-key-preflight.d.ts +42 -0
  310. package/dist/run-key-preflight.js +82 -0
  311. package/dist/run-key-preflight.js.map +1 -0
  312. package/dist/run-screenshots.d.ts +123 -0
  313. package/dist/run-screenshots.js +235 -0
  314. package/dist/run-screenshots.js.map +1 -0
  315. package/dist/runtime-env.d.ts +8 -0
  316. package/dist/runtime-env.js +23 -0
  317. package/dist/runtime-env.js.map +1 -0
  318. package/dist/secret-args.d.ts +23 -0
  319. package/dist/secret-args.js +45 -0
  320. package/dist/secret-args.js.map +1 -0
  321. package/dist/serve-cli.d.ts +4 -0
  322. package/dist/serve-cli.js +95 -0
  323. package/dist/serve-cli.js.map +1 -0
  324. package/dist/server-log-verify.d.ts +36 -0
  325. package/dist/server-log-verify.js +120 -0
  326. package/dist/server-log-verify.js.map +1 -0
  327. package/dist/session-check.d.ts +14 -0
  328. package/dist/session-check.js +44 -0
  329. package/dist/session-check.js.map +1 -0
  330. package/dist/site-cli.d.ts +4 -0
  331. package/dist/site-cli.js +127 -0
  332. package/dist/site-cli.js.map +1 -0
  333. package/dist/site-gate-cli.d.ts +28 -0
  334. package/dist/site-gate-cli.js +59 -0
  335. package/dist/site-gate-cli.js.map +1 -0
  336. package/dist/source-api.js +20 -4
  337. package/dist/source-api.js.map +1 -1
  338. package/dist/source-cli.d.ts +4 -0
  339. package/dist/source-cli.js +284 -0
  340. package/dist/source-cli.js.map +1 -0
  341. package/dist/source-run-api.d.ts +27 -1
  342. package/dist/source-run-api.js +22 -11
  343. package/dist/source-run-api.js.map +1 -1
  344. package/dist/storage-state-snapshot.d.ts +50 -0
  345. package/dist/storage-state-snapshot.js +87 -0
  346. package/dist/storage-state-snapshot.js.map +1 -0
  347. package/dist/suite-explore-options.d.ts +321 -0
  348. package/dist/suite-explore-options.js +162 -0
  349. package/dist/suite-explore-options.js.map +1 -0
  350. package/dist/target-config.d.ts +115 -0
  351. package/dist/target-config.js +225 -0
  352. package/dist/target-config.js.map +1 -0
  353. package/dist/transcript-file.d.ts +12 -0
  354. package/dist/transcript-file.js +23 -0
  355. package/dist/transcript-file.js.map +1 -0
  356. package/dist/type-fixture-file.d.ts +9 -0
  357. package/dist/type-fixture-file.js +48 -0
  358. package/dist/type-fixture-file.js.map +1 -0
  359. package/dist/ui-api.d.ts +3 -0
  360. package/dist/ui-api.js +11 -4
  361. package/dist/ui-api.js.map +1 -1
  362. package/dist/usability-capture.d.ts +43 -0
  363. package/dist/usability-capture.js +327 -0
  364. package/dist/usability-capture.js.map +1 -0
  365. package/dist/usage-config.d.ts +36 -0
  366. package/dist/usage-config.js +114 -0
  367. package/dist/usage-config.js.map +1 -0
  368. package/dist/ux-api.d.ts +3 -75
  369. package/dist/ux-api.js +3 -220
  370. package/dist/ux-api.js.map +1 -1
  371. package/dist/ux-claim-probe.d.ts +45 -0
  372. package/dist/ux-claim-probe.js +386 -0
  373. package/dist/ux-claim-probe.js.map +1 -0
  374. package/dist/ux-cli.d.ts +4 -0
  375. package/dist/ux-cli.js +109 -0
  376. package/dist/ux-cli.js.map +1 -0
  377. package/dist/ux-config.d.ts +29 -0
  378. package/dist/ux-config.js +103 -0
  379. package/dist/ux-config.js.map +1 -0
  380. package/dist/ux-evidence.d.ts +54 -0
  381. package/dist/ux-evidence.js +197 -0
  382. package/dist/ux-evidence.js.map +1 -0
  383. package/dist/ux-product.d.ts +14 -0
  384. package/dist/ux-product.js +39 -0
  385. package/dist/ux-product.js.map +1 -0
  386. package/dist/ux-review.d.ts +116 -0
  387. package/dist/ux-review.js +171 -0
  388. package/dist/ux-review.js.map +1 -0
  389. package/dist/ux-usability.d.ts +263 -0
  390. package/dist/ux-usability.js +580 -0
  391. package/dist/ux-usability.js.map +1 -0
  392. package/dist/verify-fix-api.d.ts +194 -0
  393. package/dist/verify-fix-api.js +506 -0
  394. package/dist/verify-fix-api.js.map +1 -0
  395. package/dist/verify-fix-cli.d.ts +4 -0
  396. package/dist/verify-fix-cli.js +115 -0
  397. package/dist/verify-fix-cli.js.map +1 -0
  398. package/package.json +23 -18
  399. package/skills/jevitate-ci-check/SKILL.md +77 -0
  400. package/skills/jevitate-demo/SKILL.md +61 -0
  401. package/skills/jevitate-explore/SKILL.md +96 -87
  402. package/skills/jevitate-getting-started/SKILL.md +89 -0
  403. package/skills/jevitate-load-test/SKILL.md +11 -2
  404. package/skills/jevitate-mission-scope/SKILL.md +19 -11
  405. package/skills/jevitate-record/SKILL.md +23 -5
  406. package/skills/jevitate-run-journey/SKILL.md +61 -17
  407. package/skills/jevitate-sources/SKILL.md +57 -14
  408. package/skills/jevitate-test-campaign/SKILL.md +203 -0
  409. package/skills/jevitate-ux-review/SKILL.md +106 -16
  410. package/skills/jevitate-verify-fix/SKILL.md +76 -0
@@ -0,0 +1,87 @@
1
+ import { Command } from "commander";
2
+ import { type OrphanCleanup, type ProcessTable, type ResourceGovernor, type ResourceLimits } from "@jevitate/playwright";
3
+ import { type JsonEnvelope } from "./envelope.js";
4
+ /**
5
+ * #205: the CLI side of resource governance — run once before every browser-driving command (the
6
+ * action wrapper `withBrowserLaunchFlags` installs) and by `jevitate doctor`.
7
+ *
8
+ * Before a browser run starts:
9
+ * 1. ORPHAN SWEEP (once per process): browsers a killed jevitate left behind — marked with their
10
+ * owner, owner gone — are closed, and stale machine slots are cleared. Only jevitate-marked
11
+ * processes are ever signalled.
12
+ * 2. STARVED-HOST REFUSAL: when the host is clearly starved (`throttleDecision` → `starved`: load
13
+ * ≥ 4/core, or < 512 MiB available) the run refuses to start with `E_HOST_STARVED` (exit 2: it
14
+ * could not do its work) instead of timing out and reporting the app as hung — unless
15
+ * `--ignore-host-load`, in which case it runs throttled and its result records that.
16
+ *
17
+ * Both are skipped under `JEVITATE_RESOURCE_GOVERNANCE=off`.
18
+ */
19
+ /** Raw commander values of the governance flags. */
20
+ export interface GovernanceFlags {
21
+ maxBrowsers?: number;
22
+ /** MiB. */
23
+ maxBrowserMemory?: number;
24
+ ignoreHostLoad?: boolean;
25
+ }
26
+ /** The run's `ResourceLimits` from its flags, or undefined when none was given. */
27
+ export declare function resourceLimitsFromFlags(o: GovernanceFlags): ResourceLimits | undefined;
28
+ export interface PreflightDeps {
29
+ readonly governor?: ResourceGovernor;
30
+ /** Restricts what the orphan sweep reads (and so may signal) — tests pass their own processes only. */
31
+ readonly processTable?: ProcessTable;
32
+ readonly warn?: (line: string) => void;
33
+ }
34
+ /** Test seam: forget that this process already swept (the sweep runs once per process). */
35
+ export declare function resetPreflightSweep(): void;
36
+ /** The startup sweep: closes orphaned jevitate browsers and clears stale machine slots (once per process). */
37
+ export declare function sweepOnce(deps?: PreflightDeps): Promise<void>;
38
+ /**
39
+ * The pre-run checks; returns the refusal envelope when the run must not start (`E_HOST_STARVED`),
40
+ * else null.
41
+ */
42
+ export declare function resourcePreflight(o: GovernanceFlags, deps?: PreflightDeps): Promise<JsonEnvelope<never> | null>;
43
+ /**
44
+ * Installs the pre-run checks on a browser-driving command: its action (registered AFTER this, by
45
+ * the command's own `.action(...)`) runs only when `resourcePreflight` lets it; a refusal is emitted
46
+ * through the shared refusal path and the action never runs.
47
+ */
48
+ export declare function withResourcePreflight(cmd: Command, deps?: PreflightDeps): Command;
49
+ export interface DoctorReport {
50
+ readonly host: string;
51
+ readonly governance: {
52
+ readonly enabled: boolean;
53
+ readonly maxBrowsers: number;
54
+ readonly memoryCeilingBytes: number | null;
55
+ };
56
+ readonly load: {
57
+ readonly level: string;
58
+ readonly reasons: readonly string[];
59
+ readonly loadPerCore: number | null;
60
+ readonly memAvailableBytes: number | null;
61
+ };
62
+ readonly slots: Array<{
63
+ readonly index: number;
64
+ readonly pid: number | null;
65
+ readonly host: string | null;
66
+ readonly acquiredAt: string | null;
67
+ readonly stale: string | null;
68
+ }>;
69
+ /** Every jevitate-launched browser on this machine; null where the platform has no process reader. */
70
+ readonly browsers: Array<{
71
+ readonly pid: number;
72
+ readonly ownerPid: number;
73
+ readonly orphan: string | null;
74
+ }> | null;
75
+ readonly cleanup?: {
76
+ readonly orphans: OrphanCleanup["orphans"];
77
+ readonly staleSlotsCleared: number;
78
+ };
79
+ }
80
+ /** `jevitate doctor`'s report; with `cleanup`, orphaned browsers are closed and stale slots cleared first. */
81
+ export declare function doctorReport(opts?: {
82
+ readonly cleanup?: boolean;
83
+ } & PreflightDeps): Promise<DoctorReport>;
84
+ /** The human `jevitate doctor` text. */
85
+ export declare function formatDoctor(r: DoctorReport): string;
86
+ /** Registers `jevitate doctor [--cleanup] [--json]`. */
87
+ export declare function registerDoctorCommand(program: Command, deps?: PreflightDeps): void;
@@ -0,0 +1,160 @@
1
+ import { hostname } from "node:os";
2
+ import { cleanupOrphanBrowsers, jevitateBrowsers, sharedResourceGovernor, } from "@jevitate/playwright";
3
+ import { fail, ok } from "./envelope.js";
4
+ import { emitJsonOrRefusal } from "./cli-refusal.js";
5
+ /** The run's `ResourceLimits` from its flags, or undefined when none was given. */
6
+ export function resourceLimitsFromFlags(o) {
7
+ const limits = {
8
+ ...(o.maxBrowsers === undefined ? {} : { maxBrowsers: o.maxBrowsers }),
9
+ ...(o.maxBrowserMemory === undefined ? {} : { memoryCeilingBytes: o.maxBrowserMemory * 1024 ** 2 }),
10
+ };
11
+ return Object.keys(limits).length > 0 ? limits : undefined;
12
+ }
13
+ let swept = false;
14
+ /** Test seam: forget that this process already swept (the sweep runs once per process). */
15
+ export function resetPreflightSweep() {
16
+ swept = false;
17
+ }
18
+ /** The startup sweep: closes orphaned jevitate browsers and clears stale machine slots (once per process). */
19
+ export async function sweepOnce(deps = {}) {
20
+ if (swept)
21
+ return;
22
+ swept = true;
23
+ const governor = deps.governor ?? sharedResourceGovernor();
24
+ const warn = deps.warn ?? ((line) => process.stderr.write(line));
25
+ try {
26
+ const cleanup = await cleanupOrphanBrowsers({ ...(deps.processTable === undefined ? {} : { table: deps.processTable }) });
27
+ const closed = cleanup.orphans.filter((o) => o.result === "terminated" || o.result === "killed");
28
+ if (closed.length > 0)
29
+ warn(`jevitate: closed ${closed.length} orphaned browser process(es) left by a jevitate that exited: pid ${closed.map((o) => o.pid).join(", ")}\n`);
30
+ governor.slots.clearStale();
31
+ }
32
+ catch (err) {
33
+ // Housekeeping, not the run's business: reported, and the run goes on.
34
+ warn(`warning: orphan cleanup skipped: ${err instanceof Error ? err.message : String(err)}\n`);
35
+ }
36
+ }
37
+ /**
38
+ * The pre-run checks; returns the refusal envelope when the run must not start (`E_HOST_STARVED`),
39
+ * else null.
40
+ */
41
+ export async function resourcePreflight(o, deps = {}) {
42
+ const governor = deps.governor ?? sharedResourceGovernor();
43
+ if (!governor.config.enabled)
44
+ return null;
45
+ await sweepOnce({ ...deps, governor });
46
+ const decision = await governor.decide();
47
+ if (decision.level !== "starved" || o.ignoreHostLoad === true)
48
+ return null;
49
+ return fail("E_HOST_STARVED", `the host is starved (${decision.reasons.join("; ")}): a browser run now would only time out and read as the app hanging, so it did not start. ` +
50
+ "Wait for the load to drop (see `jevitate doctor`), or pass --ignore-host-load to run anyway (throttled; the result records it)");
51
+ }
52
+ function rootOf(cmd) {
53
+ let c = cmd;
54
+ while (c.parent !== null)
55
+ c = c.parent;
56
+ return c;
57
+ }
58
+ /**
59
+ * Installs the pre-run checks on a browser-driving command: its action (registered AFTER this, by
60
+ * the command's own `.action(...)`) runs only when `resourcePreflight` lets it; a refusal is emitted
61
+ * through the shared refusal path and the action never runs.
62
+ */
63
+ export function withResourcePreflight(cmd, deps = {}) {
64
+ const register = cmd.action.bind(cmd);
65
+ cmd.action = (fn) => register(async function (...args) {
66
+ const refusal = await resourcePreflight(this.opts(), deps);
67
+ if (refusal !== null) {
68
+ emitJsonOrRefusal(rootOf(this), refusal);
69
+ return;
70
+ }
71
+ await fn.apply(this, args);
72
+ });
73
+ return cmd;
74
+ }
75
+ const slotRow = (s) => ({
76
+ index: s.index,
77
+ pid: s.holder?.pid ?? null,
78
+ host: s.holder?.host ?? null,
79
+ acquiredAt: s.holder?.acquiredAt ?? null,
80
+ stale: s.stale,
81
+ });
82
+ /** `jevitate doctor`'s report; with `cleanup`, orphaned browsers are closed and stale slots cleared first. */
83
+ export async function doctorReport(opts = {}) {
84
+ const governor = opts.governor ?? sharedResourceGovernor();
85
+ const table = opts.processTable;
86
+ let cleanup;
87
+ if (opts.cleanup === true) {
88
+ const orphans = await cleanupOrphanBrowsers({ ...(table === undefined ? {} : { table }) });
89
+ const cleared = governor.slots.clearStale();
90
+ cleanup = { orphans: orphans.orphans, staleSlotsCleared: cleared.length };
91
+ }
92
+ const decision = await governor.decide();
93
+ const browsers = jevitateBrowsers(...(table === undefined ? [] : [table]));
94
+ return {
95
+ host: hostname(),
96
+ governance: { enabled: governor.config.enabled, maxBrowsers: governor.config.maxBrowsers, memoryCeilingBytes: governor.config.memoryCeilingBytes },
97
+ load: {
98
+ level: decision.level,
99
+ reasons: decision.reasons,
100
+ loadPerCore: decision.sample.loadPerCore ?? null,
101
+ memAvailableBytes: decision.sample.memAvailableBytes ?? null,
102
+ },
103
+ slots: governor.slots.list().map(slotRow),
104
+ browsers: browsers === undefined ? null : browsers.map((b) => ({ pid: b.pid, ownerPid: b.owner.pid, orphan: b.orphan })),
105
+ ...(cleanup === undefined ? {} : { cleanup }),
106
+ };
107
+ }
108
+ const mib = (n) => `${Math.round(n / 1024 ** 2)} MiB`;
109
+ /** The human `jevitate doctor` text. */
110
+ export function formatDoctor(r) {
111
+ const lines = [];
112
+ const g = r.governance;
113
+ lines.push(`resource governance: ${g.enabled ? "on" : "off (JEVITATE_RESOURCE_GOVERNANCE=off)"} · max browsers ${g.maxBrowsers} · memory ceiling ${g.memoryCeilingBytes === null ? "off" : mib(g.memoryCeilingBytes)}`);
114
+ const load = [
115
+ r.load.loadPerCore === null ? null : `load ${r.load.loadPerCore.toFixed(2)}/core`,
116
+ r.load.memAvailableBytes === null ? null : `${mib(r.load.memAvailableBytes)} available`,
117
+ ].filter((x) => x !== null);
118
+ lines.push(`host: ${r.load.level}${load.length === 0 ? "" : ` (${load.join(", ")})`}${r.load.reasons.length === 0 ? "" : ` — ${r.load.reasons.join("; ")}`}`);
119
+ lines.push(`machine browser slots held: ${r.slots.length}`);
120
+ for (const s of r.slots)
121
+ lines.push(` slot ${s.index}: ${s.pid === null ? "unreadable" : `pid ${s.pid} on ${s.host}`}${s.stale === null ? "" : ` — stale: ${s.stale}`}`);
122
+ if (r.browsers === null)
123
+ lines.push("jevitate browsers: not listed (no process reader on this platform)");
124
+ else {
125
+ const orphans = r.browsers.filter((b) => b.orphan !== null);
126
+ lines.push(`jevitate browsers: ${r.browsers.length} running, ${orphans.length} orphaned`);
127
+ for (const b of orphans)
128
+ lines.push(` pid ${b.pid}: ${b.orphan}`);
129
+ }
130
+ if (r.cleanup !== undefined) {
131
+ const closed = r.cleanup.orphans.filter((o) => o.result === "terminated" || o.result === "killed").length;
132
+ lines.push(`cleanup: closed ${closed} orphaned browser(s), cleared ${r.cleanup.staleSlotsCleared} stale slot(s)`);
133
+ }
134
+ else if (r.browsers?.some((b) => b.orphan !== null) === true || r.slots.some((s) => s.stale !== null)) {
135
+ lines.push("next: jevitate doctor --cleanup");
136
+ }
137
+ return `${lines.join("\n")}\n`;
138
+ }
139
+ /** Registers `jevitate doctor [--cleanup] [--json]`. */
140
+ export function registerDoctorCommand(program, deps = {}) {
141
+ program
142
+ .command("doctor")
143
+ .description("resource governance on this machine (#205): host load, machine-wide browser slots, jevitate browsers and orphans left by a killed run")
144
+ .option("--cleanup", "close orphaned jevitate browsers (only processes jevitate launched, whose jevitate exited) and clear stale browser slots")
145
+ .option("--json", "emit a JSON envelope")
146
+ .action(async function () {
147
+ const o = this.opts();
148
+ try {
149
+ const report = await doctorReport({ ...deps, ...(o.cleanup === true ? { cleanup: true } : {}) });
150
+ if (o.json === true)
151
+ emitJsonOrRefusal(program, ok(report));
152
+ else
153
+ program.configureOutput().writeOut?.(formatDoctor(report));
154
+ }
155
+ catch (err) {
156
+ emitJsonOrRefusal(program, fail("E_DOCTOR", err instanceof Error ? err.message : String(err)));
157
+ }
158
+ });
159
+ }
160
+ //# sourceMappingURL=resource-preflight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-preflight.js","sourceRoot":"","sources":["../src/resource-preflight.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { ServerLogDefect } from "./log-correlation.js";
2
+ /**
3
+ * The versioned result schema every explore strategy's result follows (#195 part 5) — defined in
4
+ * `@jevitate/domain` (zod + TS) and re-exported here as part of the CLI's published surface. See
5
+ * `mission-result.ts` there for the contract; this module holds the helpers the runners fill the
6
+ * common fields with, so every strategy fills them the same way.
7
+ */
8
+ export { MISSION_RESULT_SCHEMA_VERSION, MissionResultSchema, PersistedMissionResultSchema, RESULT_STRATEGIES, RESULT_MISSION_OUTCOMES, ResultDefectSchema, type MissionResult, type MissionResultCore, type PersistedMissionResult, type ResultDefect, type ResultMissionOutcome, type ResultStrategy, } from "@jevitate/domain";
9
+ /** A `server-log` defect a strategy reports but never gates on (usability: every UX finding is advisory). */
10
+ export type AdvisoryServerLogDefect = ServerLogDefect & {
11
+ readonly advisory: true;
12
+ };
13
+ /**
14
+ * All of a run's defects in ONE list (#195): the strategy's own (hard-signal / declared-invariant)
15
+ * defects first, then its `server-log` defects — each fingerprint once. What `defects` holds on every
16
+ * strategy's result (the 0.2.0 `serverLogDefects` alias of the server-log subset was removed in 0.3.0).
17
+ */
18
+ export declare function unifiedDefects<D extends {
19
+ readonly fingerprint: string;
20
+ }>(own: readonly D[] | undefined, serverLog: readonly ServerLogDefect[] | undefined): Array<D | ServerLogDefect>;
21
+ /** A usability run's `server-log` defects, marked advisory: reported in `defects`, never gating its outcome. */
22
+ export declare function advisoryDefects(serverLog: readonly ServerLogDefect[] | undefined): AdvisoryServerLogDefect[];
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The versioned result schema every explore strategy's result follows (#195 part 5) — defined in
3
+ * `@jevitate/domain` (zod + TS) and re-exported here as part of the CLI's published surface. See
4
+ * `mission-result.ts` there for the contract; this module holds the helpers the runners fill the
5
+ * common fields with, so every strategy fills them the same way.
6
+ */
7
+ export { MISSION_RESULT_SCHEMA_VERSION, MissionResultSchema, PersistedMissionResultSchema, RESULT_STRATEGIES, RESULT_MISSION_OUTCOMES, ResultDefectSchema, } from "@jevitate/domain";
8
+ /**
9
+ * All of a run's defects in ONE list (#195): the strategy's own (hard-signal / declared-invariant)
10
+ * defects first, then its `server-log` defects — each fingerprint once. What `defects` holds on every
11
+ * strategy's result (the 0.2.0 `serverLogDefects` alias of the server-log subset was removed in 0.3.0).
12
+ */
13
+ export function unifiedDefects(own, serverLog) {
14
+ const seen = new Set();
15
+ const out = [];
16
+ for (const d of [...(own ?? []), ...(serverLog ?? [])]) {
17
+ if (seen.has(d.fingerprint))
18
+ continue;
19
+ seen.add(d.fingerprint);
20
+ out.push(d);
21
+ }
22
+ return out;
23
+ }
24
+ /** A usability run's `server-log` defects, marked advisory: reported in `defects`, never gating its outcome. */
25
+ export function advisoryDefects(serverLog) {
26
+ return unifiedDefects([], serverLog).map((d) => ({ ...d, advisory: true }));
27
+ }
28
+ //# sourceMappingURL=result-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result-schema.js","sourceRoot":"","sources":["../src/result-schema.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,OAAO,EACL,6BAA6B,EAC7B,mBAAmB,EACnB,4BAA4B,EAC5B,iBAAiB,EACjB,uBAAuB,EACvB,kBAAkB,GAOnB,MAAM,kBAAkB,CAAC;AAK1B;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,GAA6B,EAC7B,SAAiD;IAEjD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,GAAG,GAA+B,EAAE,CAAC;IAC3C,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QACvD,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC;YAAE,SAAS;QACtC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;QACxB,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACd,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,gHAAgH;AAChH,MAAM,UAAU,eAAe,CAAC,SAAiD;IAC/E,OAAO,cAAc,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,IAAa,EAAE,CAAC,CAAC,CAAC;AACvF,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { type LayoutDeps } from "./project-dir.js";
2
+ /**
3
+ * #213: which runs belong to THIS project — so a bare `jevitate report`/`diff` reads the current
4
+ * project's runs, never every app's `~/.jevitate/logs` (a fresh project merged 167 unrelated runs),
5
+ * and still finds the runs written to an `--out` dir.
6
+ *
7
+ * Mechanism: every persisted result is recorded, best-effort, as one line
8
+ * `{"project": <key>, "path": <absolute result path>}` in the per-user index `~/.jevitate/run-index.jsonl`
9
+ * (machine-local, like the logs themselves; never in a repo). The project key is the directory that
10
+ * holds the project's `.jevitate/`, else the git root of the working directory, else the working
11
+ * directory itself. A bare report reads: the project's own `.jevitate/logs` (only this project writes
12
+ * there) plus every indexed result of this project that still exists — wherever it was written.
13
+ *
14
+ * `JEVITATE_RUN_INDEX=off` turns recording off (the test suite does, so it never touches the real index).
15
+ */
16
+ export interface RunIndexDeps extends LayoutDeps {
17
+ readonly env?: NodeJS.ProcessEnv;
18
+ /** Test seam: the index file (default `~/.jevitate/run-index.jsonl`). */
19
+ readonly indexPath?: string;
20
+ }
21
+ export declare function runIndexPath(deps?: RunIndexDeps): string;
22
+ /** The current project's key: the dir holding its `.jevitate/`, else the git root, else the cwd. */
23
+ export declare function projectKey(deps?: LayoutDeps): string;
24
+ /** Records one persisted result for the current project. Never throws: an index is a convenience. */
25
+ export declare function recordRun(resultPath: string, deps?: RunIndexDeps): void;
26
+ /** Every indexed result of the current project that still exists, oldest first, each once. */
27
+ export declare function indexedRunsFor(deps?: RunIndexDeps): string[];
28
+ /** The current project's own dated logs dirs (`<repo>/.jevitate/logs/<date>`) — none outside a project. */
29
+ export declare function projectLogDirs(deps?: LayoutDeps): string[];
@@ -0,0 +1,70 @@
1
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { findGitRoot, findProjectDir, homeDataRoot } from "./project-dir.js";
4
+ export function runIndexPath(deps = {}) {
5
+ return deps.indexPath ?? join(homeDataRoot(deps), "run-index.jsonl");
6
+ }
7
+ /** The current project's key: the dir holding its `.jevitate/`, else the git root, else the cwd. */
8
+ export function projectKey(deps = {}) {
9
+ const project = findProjectDir(deps);
10
+ if (project !== null)
11
+ return dirname(project);
12
+ const cwd = resolve((deps.cwd ?? (() => process.cwd()))());
13
+ return findGitRoot(cwd) ?? cwd;
14
+ }
15
+ /** Records one persisted result for the current project. Never throws: an index is a convenience. */
16
+ export function recordRun(resultPath, deps = {}) {
17
+ if ((deps.env ?? process.env)["JEVITATE_RUN_INDEX"] === "off")
18
+ return;
19
+ try {
20
+ const path = runIndexPath(deps);
21
+ mkdirSync(dirname(path), { recursive: true });
22
+ appendFileSync(path, `${JSON.stringify({ project: projectKey(deps), path: resolve(resultPath) })}\n`, { encoding: "utf8", mode: 0o600 });
23
+ }
24
+ catch {
25
+ // best-effort: a run's result is never replaced by an index failure
26
+ }
27
+ }
28
+ /** Every indexed result of the current project that still exists, oldest first, each once. */
29
+ export function indexedRunsFor(deps = {}) {
30
+ const path = runIndexPath(deps);
31
+ if (!existsSync(path))
32
+ return [];
33
+ const key = projectKey(deps);
34
+ const out = new Set();
35
+ let text;
36
+ try {
37
+ text = readFileSync(path, "utf8");
38
+ }
39
+ catch {
40
+ return [];
41
+ }
42
+ for (const line of text.split("\n")) {
43
+ if (line.trim() === "")
44
+ continue;
45
+ try {
46
+ const e = JSON.parse(line);
47
+ if (e.project === key && typeof e.path === "string" && existsSync(e.path))
48
+ out.add(e.path);
49
+ }
50
+ catch {
51
+ continue; // a torn line names nothing
52
+ }
53
+ }
54
+ return [...out];
55
+ }
56
+ /** The current project's own dated logs dirs (`<repo>/.jevitate/logs/<date>`) — none outside a project. */
57
+ export function projectLogDirs(deps = {}) {
58
+ const project = findProjectDir(deps);
59
+ if (project === null)
60
+ return [];
61
+ const root = join(project, "logs");
62
+ if (!existsSync(root))
63
+ return [];
64
+ return readdirSync(root)
65
+ .filter((d) => /^\d{4}-\d{2}-\d{2}$/.test(d))
66
+ .sort()
67
+ .reverse()
68
+ .map((d) => join(root, d));
69
+ }
70
+ //# sourceMappingURL=run-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-index.js","sourceRoot":"","sources":["../src/run-index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import { MissingCredentialError, type CredentialKey, type CredentialStore, type Feature, type VerifyFetch } from "@jevitate/ai-core";
2
+ /**
3
+ * #291 (runs): a run that needs live AI keys (`explore --real`, `check`, `journey run/annotate/demo
4
+ * --real`, `demo`, `ux`, …) verifies them ONCE at startup with the same live, non-billable auth call
5
+ * `ai status` / `ai setup` use, and fails fast with a typed setup refusal (`E_AI_SETUP_REQUIRED`,
6
+ * exit 64) when a provider rejects a key — instead of spending the run and ending `inconclusive`
7
+ * with "model decision unavailable".
8
+ *
9
+ * - Cheap: one GET per key, cached per process (by key name + a hash of the value — the value itself
10
+ * is never kept here) — a multi-run, a retry or a self-heal re-build never re-checks it.
11
+ * - Only an `invalid` verdict (401/403) refuses. An `unreachable` provider (offline, 429, 5xx) does
12
+ * not block: the run's own calls retry and fail typed, as before; that verdict is not cached.
13
+ * - `JEVITATE_NO_KEY_VERIFY=1` skips the check (offline CI). `--fake-ai` and injected gateways never
14
+ * reach this code (no network).
15
+ * - Names, providers and statuses only: no message here carries a key value.
16
+ */
17
+ /** The env opt-out (offline CI): `1` / `true` / `yes` skips the startup key check. */
18
+ export declare const NO_KEY_VERIFY_ENV = "JEVITATE_NO_KEY_VERIFY";
19
+ /** A key the provider rejected — a `MissingCredentialError`, so every run surface refuses it as `E_AI_SETUP_REQUIRED`. */
20
+ export declare class InvalidCredentialError extends MissingCredentialError {
21
+ readonly invalid: Array<{
22
+ readonly key: CredentialKey;
23
+ readonly httpStatus: number;
24
+ readonly looksLike: CredentialKey | null;
25
+ }>;
26
+ constructor(feature: Feature, invalid: Array<{
27
+ readonly key: CredentialKey;
28
+ readonly httpStatus: number;
29
+ readonly looksLike: CredentialKey | null;
30
+ }>);
31
+ }
32
+ /** Test seam: forget the per-process verdicts. */
33
+ export declare function resetKeyPreflightCache(): void;
34
+ export declare function keyPreflightDisabled(env: Record<string, string | undefined>): boolean;
35
+ /**
36
+ * Verifies every key `features` need (present keys only: a missing one was already refused by
37
+ * `requireKeys`). Throws `InvalidCredentialError` for the first feature with a rejected key.
38
+ */
39
+ export declare function preflightRunKeys(features: readonly Feature[], store: CredentialStore, opts: {
40
+ readonly env: Record<string, string | undefined>;
41
+ readonly fetchFn: VerifyFetch;
42
+ }): Promise<void>;
@@ -0,0 +1,82 @@
1
+ import { createHash } from "node:crypto";
2
+ import { FEATURE_KEYS, KEY_PROVIDERS, KEY_VERIFY_URLS, MissingCredentialError, looksLikeOtherKey, verifyKey, } from "@jevitate/ai-core";
3
+ /**
4
+ * #291 (runs): a run that needs live AI keys (`explore --real`, `check`, `journey run/annotate/demo
5
+ * --real`, `demo`, `ux`, …) verifies them ONCE at startup with the same live, non-billable auth call
6
+ * `ai status` / `ai setup` use, and fails fast with a typed setup refusal (`E_AI_SETUP_REQUIRED`,
7
+ * exit 64) when a provider rejects a key — instead of spending the run and ending `inconclusive`
8
+ * with "model decision unavailable".
9
+ *
10
+ * - Cheap: one GET per key, cached per process (by key name + a hash of the value — the value itself
11
+ * is never kept here) — a multi-run, a retry or a self-heal re-build never re-checks it.
12
+ * - Only an `invalid` verdict (401/403) refuses. An `unreachable` provider (offline, 429, 5xx) does
13
+ * not block: the run's own calls retry and fail typed, as before; that verdict is not cached.
14
+ * - `JEVITATE_NO_KEY_VERIFY=1` skips the check (offline CI). `--fake-ai` and injected gateways never
15
+ * reach this code (no network).
16
+ * - Names, providers and statuses only: no message here carries a key value.
17
+ */
18
+ /** The env opt-out (offline CI): `1` / `true` / `yes` skips the startup key check. */
19
+ export const NO_KEY_VERIFY_ENV = "JEVITATE_NO_KEY_VERIFY";
20
+ /** A key the provider rejected — a `MissingCredentialError`, so every run surface refuses it as `E_AI_SETUP_REQUIRED`. */
21
+ export class InvalidCredentialError extends MissingCredentialError {
22
+ invalid;
23
+ constructor(feature, invalid) {
24
+ super(feature, invalid.map((i) => i.key));
25
+ this.invalid = invalid;
26
+ this.name = "InvalidCredentialError";
27
+ const what = invalid
28
+ .map((i) => `${i.key} (${KEY_PROVIDERS[i.key]}) was rejected by the provider (HTTP ${i.httpStatus})${i.looksLike === null ? "" : ` — it looks like ${/^[aeiou]/i.test(KEY_PROVIDERS[i.looksLike]) ? "an" : "a"} ${KEY_PROVIDERS[i.looksLike]} key (${i.looksLike})`}`)
29
+ .join("; ");
30
+ this.message =
31
+ `feature '${feature}': ${what} — replace it: \`jevitate ai setup ${feature} --replace\` ` +
32
+ `(or set ${NO_KEY_VERIFY_ENV}=1 to skip this startup check offline)`;
33
+ }
34
+ }
35
+ const cache = new Map();
36
+ function cacheKey(key, value) {
37
+ return `${key}:${createHash("sha256").update(value).digest("hex")}`;
38
+ }
39
+ /** Test seam: forget the per-process verdicts. */
40
+ export function resetKeyPreflightCache() {
41
+ cache.clear();
42
+ }
43
+ export function keyPreflightDisabled(env) {
44
+ return /^(?:1|true|yes)$/i.test((env[NO_KEY_VERIFY_ENV] ?? "").trim());
45
+ }
46
+ async function verdictFor(key, value, fetchFn) {
47
+ const id = cacheKey(key, value);
48
+ const hit = cache.get(id);
49
+ if (hit !== undefined)
50
+ return hit;
51
+ const pending = verifyKey(key, value, fetchFn);
52
+ cache.set(id, pending);
53
+ const verdict = await pending;
54
+ // An unreachable provider proves nothing: a later build may check again.
55
+ if (verdict.status === "unreachable")
56
+ cache.delete(id);
57
+ return verdict;
58
+ }
59
+ /**
60
+ * Verifies every key `features` need (present keys only: a missing one was already refused by
61
+ * `requireKeys`). Throws `InvalidCredentialError` for the first feature with a rejected key.
62
+ */
63
+ export async function preflightRunKeys(features, store, opts) {
64
+ if (keyPreflightDisabled(opts.env))
65
+ return;
66
+ for (const feature of features) {
67
+ const invalid = [];
68
+ for (const key of FEATURE_KEYS[feature]) {
69
+ if (KEY_VERIFY_URLS[key] === undefined)
70
+ continue;
71
+ const value = store.read(key);
72
+ if (value === undefined || value.trim() === "")
73
+ continue;
74
+ const verdict = await verdictFor(key, value.trim(), opts.fetchFn);
75
+ if (verdict.status === "invalid")
76
+ invalid.push({ key, httpStatus: verdict.httpStatus, looksLike: looksLikeOtherKey(key, value.trim()) });
77
+ }
78
+ if (invalid.length > 0)
79
+ throw new InvalidCredentialError(feature, invalid);
80
+ }
81
+ }
82
+ //# sourceMappingURL=run-key-preflight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-key-preflight.js","sourceRoot":"","sources":["../src/run-key-preflight.ts"],"names":[],"mappings":"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"}