@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,292 @@
1
+ import { existsSync } from "node:fs";
2
+ import { isAbsolute, resolve as resolvePath } from "node:path";
3
+ import { positiveIntArg } from "./cli-args.js";
4
+ import { FixtureSetupError, FixtureSpecError, MissionFixtures, SETUP_REF, UnboundSetupRefError, hookHash, identityNames, loadFixtureFile, parseFixtureSpec, referencedNames, substituteSetupRefs, } from "./mission-fixtures.js";
5
+ /** Adds `--fixtures`, `--before`, `--after`, `--allow-shell-hooks` and `--hook-timeout-ms`. */
6
+ export function withFixtureFlags(cmd) {
7
+ return cmd
8
+ .option("--fixtures <file>", "mission fixtures JSON {setup:[...], restore:[...]} (#140/#144): HTTP steps to an --allow origin, authenticated from " +
9
+ "--storage-state/--secret-field, run before the mission and restored after it — and around every replay. Outputs bind as ${setup.<name>}")
10
+ .option("--before <cmd>", "operator shell hook run before the mission and every replay (needs --allow-shell-hooks); may print {vars, secret}")
11
+ .option("--after <cmd>", "operator shell hook run after the mission and every replay (needs --allow-shell-hooks)")
12
+ .option("--allow-shell-hooks", "opt in to running --before/--after (operator commands; never model-chosen)", false)
13
+ .option("--hook-timeout-ms <ms>", "timeout for each --before/--after hook (default 60000; the process group is killed)", positiveIntArg)
14
+ .option("--fixture-identity <name=storageState>", "#243: a named identity fixture steps can authenticate as (`auth.identity`), separate from the mission's own session — e.g. mint an invite as the owner, run the mission cold (repeatable)", (v, prev) => [...prev, v], []);
15
+ }
16
+ const IDENTITY_NAME = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$/;
17
+ /**
18
+ * #243: every fixture identity the run can authenticate a step as → its storageState PATH. Lowest to
19
+ * highest precedence: the origin's targets.json personas, the identities a persisted result recorded,
20
+ * then `--fixture-identity`. Only paths are kept (the file is read per request, never logged); a
21
+ * missing file is refused here, before any browser or request.
22
+ */
23
+ export function resolveFixtureIdentities(flags, ctx, cwd = process.cwd()) {
24
+ const out = {};
25
+ for (const [name, p] of Object.entries(ctx.personas ?? {}))
26
+ if (p.storageState !== undefined)
27
+ out[name] = p.storageState;
28
+ for (const [name, path] of Object.entries(ctx.identities ?? {}))
29
+ out[name] = path;
30
+ const seen = new Set();
31
+ for (const spec of flags.fixtureIdentity ?? []) {
32
+ const eq = spec.indexOf("=");
33
+ if (eq <= 0)
34
+ throw new FixtureSpecError(`--fixture-identity must be <name>=<storageState>, got ${JSON.stringify(spec)}`);
35
+ const name = spec.slice(0, eq);
36
+ const file = spec.slice(eq + 1).trim();
37
+ if (!IDENTITY_NAME.test(name))
38
+ throw new FixtureSpecError(`--fixture-identity name ${JSON.stringify(name)} must be 1-64 of [A-Za-z0-9_.-], starting alphanumeric`);
39
+ if (seen.has(name))
40
+ throw new FixtureSpecError(`--fixture-identity ${name} is given twice`);
41
+ seen.add(name);
42
+ if (file === "")
43
+ throw new FixtureSpecError(`--fixture-identity ${name}: storage state path is empty`);
44
+ out[name] = isAbsolute(file) ? file : resolvePath(cwd, file);
45
+ }
46
+ for (const [name, path] of Object.entries(out)) {
47
+ if (!existsSync(path))
48
+ throw new FixtureSpecError(`fixture identity ${name}: storage state not found: ${path}`);
49
+ }
50
+ return out;
51
+ }
52
+ function hooksOf(flags) {
53
+ if (flags.before === undefined && flags.after === undefined)
54
+ return undefined;
55
+ const timeoutMs = flags.hookTimeoutMs === undefined ? undefined : Number(flags.hookTimeoutMs);
56
+ if (timeoutMs !== undefined && !(Number.isInteger(timeoutMs) && timeoutMs > 0)) {
57
+ throw new FixtureSpecError("--hook-timeout-ms must be a positive integer");
58
+ }
59
+ return {
60
+ ...(flags.before === undefined ? {} : { before: flags.before }),
61
+ ...(flags.after === undefined ? {} : { after: flags.after }),
62
+ ...(timeoutMs === undefined ? {} : { timeoutMs }),
63
+ };
64
+ }
65
+ /** The run's fixture lifecycle, or `undefined` when none was asked for. Throws `FixtureSpecError`. */
66
+ export function buildMissionFixtures(flags, ctx) {
67
+ const hooks = hooksOf(flags);
68
+ const file = flags.fixtures ?? ctx.targetFixtures;
69
+ const bounds = { allowlist: ctx.allowlist, baseUrl: ctx.baseUrl };
70
+ const spec = file !== undefined ? loadFixtureFile(file, bounds, { openRefs: hooks?.before !== undefined }) : ctx.spec;
71
+ const flagged = (flags.fixtureIdentity ?? []).map((x) => x.slice(0, Math.max(0, x.indexOf("="))));
72
+ if (spec === undefined && hooks === undefined) {
73
+ if (flagged.length > 0)
74
+ throw new FixtureSpecError("--fixture-identity names who a fixture step authenticates as: it needs --fixtures");
75
+ return undefined;
76
+ }
77
+ const secretFields = Object.fromEntries((ctx.secretFields ?? []).filter((f) => f.kind === "value").map((f) => [f.name, f.secret]));
78
+ // A named identity no step uses is a typo, not a no-op.
79
+ const usedNames = identityNames(spec);
80
+ for (const name of flagged) {
81
+ if (name !== "" && !usedNames.includes(name))
82
+ throw new FixtureSpecError(`--fixture-identity ${name}: no fixture step authenticates as ${name} (auth.identity)`);
83
+ }
84
+ // Only the identities the spec uses are checked for a file (an unused persona never blocks a run).
85
+ const used = new Set(identityNames(spec));
86
+ const bound = resolveFixtureIdentities(flags, {
87
+ ...(ctx.personas === undefined ? {} : { personas: Object.fromEntries(Object.entries(ctx.personas).filter(([n]) => used.has(n))) }),
88
+ ...(ctx.identities === undefined ? {} : { identities: Object.fromEntries(Object.entries(ctx.identities).filter(([n]) => used.has(n))) }),
89
+ });
90
+ return new MissionFixtures({
91
+ ...bounds,
92
+ ...(spec === undefined ? {} : { spec }),
93
+ ...(hooks === undefined ? {} : { hooks }),
94
+ allowShellHooks: flags.allowShellHooks === true,
95
+ auth: { ...(ctx.storageState === undefined ? {} : { storageStatePath: ctx.storageState }), secretFields, identities: bound },
96
+ ...(ctx.secrets === undefined ? {} : { secrets: ctx.secrets }),
97
+ });
98
+ }
99
+ /**
100
+ * Before any browser or request: every `${setup.x}` in `texts` must be an output the fixture
101
+ * declares (unless a `--before` hook may bind it), and a run without fixtures may not reference one.
102
+ */
103
+ export function checkSetupRefs(texts, fx) {
104
+ // `null`: a `--before` hook may bind more names at run time (still checked when substituted).
105
+ const declared = fx === undefined ? new Set() : fx.declaredOutputs();
106
+ const secret = fx?.declaredSecretOutputs() ?? new Set();
107
+ for (const [where, v] of Object.entries(texts)) {
108
+ for (const text of v === undefined ? [] : typeof v === "string" ? [v] : v) {
109
+ for (const name of referencedNames(text)) {
110
+ if (fx === undefined)
111
+ throw new UnboundSetupRefError(`${where} references \${setup.${name}} but the run has no --fixtures/--before`);
112
+ if (secret.has(name)) {
113
+ throw new UnboundSetupRefError(`${where} references the secret output \${setup.${name}}; secret outputs stay inside the fixture's own requests`);
114
+ }
115
+ if (declared !== null && !declared.has(name))
116
+ throw new UnboundSetupRefError(`${where} references \${setup.${name}}, which the fixture does not output`);
117
+ }
118
+ }
119
+ }
120
+ }
121
+ /** The origin `url` keeps whatever its `${setup.*}` references hold, or null when a value could move it. */
122
+ function fixedOrigin(url) {
123
+ const originWith = (fill) => {
124
+ try {
125
+ return new URL(url.replace(SETUP_REF, fill)).origin;
126
+ }
127
+ catch {
128
+ return null;
129
+ }
130
+ };
131
+ // A reference after a `/` (or in the query) fills the path: any value keeps the origin.
132
+ const a = originWith("0");
133
+ if (a !== null && a === originWith("x.evil.test"))
134
+ return a;
135
+ // #243: a reference right after the origin (`http://host${setup.link}`) is a root-relative path —
136
+ // allowed, and its VALUE must start with `/` (checked once bound, by substituteUrlSetupRefs).
137
+ const b = originWith("/0");
138
+ if (b !== null && b === originWith("/x.evil.test"))
139
+ return b;
140
+ return null;
141
+ }
142
+ /**
143
+ * `url` with each `${setup.*}` replaced by a placeholder that keeps its origin — what the run's
144
+ * origin, allowlist and target config are read from before setup binds the real values.
145
+ */
146
+ export function setupRefFreeUrl(url) {
147
+ if (!url.includes("${setup."))
148
+ return url;
149
+ const pathFill = (() => {
150
+ try {
151
+ return new URL(url.replace(SETUP_REF, "0")).origin === new URL(url.replace(SETUP_REF, "x.evil.test")).origin;
152
+ }
153
+ catch {
154
+ return false;
155
+ }
156
+ })();
157
+ return url.replace(SETUP_REF, pathFill || fixedOrigin(url) === null ? "0" : "/0");
158
+ }
159
+ /** Throws unless a `${setup.*}` reference in `url` leaves its origin fixed (the allowlist is decided before setup runs). */
160
+ export function checkUrlRefOrigin(url) {
161
+ if (!url.includes("${setup."))
162
+ return;
163
+ if (fixedOrigin(url) === null) {
164
+ throw new UnboundSetupRefError("--url: a ${setup.*} reference may fill the path or query, never the origin — put it after a `/` (`http://host:8093/projects/${setup.id}`), or right after the origin for a root-relative path value (`http://host:8093${setup.link}` with a value like `/invite/abc`)");
165
+ }
166
+ }
167
+ /**
168
+ * `--url` with its `${setup.*}` references bound — and the bound URL still on the origin
169
+ * {@link checkUrlRefOrigin} fixed: a value placed right after the origin must be a root-relative path
170
+ * (`/…`), never `@evil.test`, `:8080` or `.evil.test`.
171
+ */
172
+ export function substituteUrlSetupRefs(url, b) {
173
+ const out = substituteSetupRefs(url, b, { where: "--url" });
174
+ if (!url.includes("${setup."))
175
+ return out;
176
+ let origin = null;
177
+ try {
178
+ origin = new URL(out).origin;
179
+ }
180
+ catch {
181
+ // refused below
182
+ }
183
+ if (origin === null || origin !== fixedOrigin(url)) {
184
+ throw new UnboundSetupRefError("--url: a bound ${setup.*} value moved the URL off its origin — a value right after the origin must be a root-relative path starting with `/`");
185
+ }
186
+ return out;
187
+ }
188
+ /** Every string inside an invariants spec (#187), with the JSON path it sits at. */
189
+ function specStrings(v, path, out) {
190
+ if (typeof v === "string")
191
+ out.push([path, v]);
192
+ else if (Array.isArray(v))
193
+ v.forEach((x, i) => specStrings(x, `${path}[${i}]`, out));
194
+ else if (v !== null && typeof v === "object")
195
+ for (const [k, x] of Object.entries(v))
196
+ specStrings(x, path === "" ? k : `${path}.${k}`, out);
197
+ return out;
198
+ }
199
+ /** The `${setup.*}`-bearing strings of an invariants spec, keyed `--invariants <path>`, for {@link checkSetupRefs}. */
200
+ export function invariantSetupTexts(spec) {
201
+ return Object.fromEntries(specStrings(spec, "", []).filter(([, s]) => s.includes("${setup.")).map(([p, s]) => [`--invariants ${p}`, s]));
202
+ }
203
+ /**
204
+ * `${setup.x}` in an invariants spec (#187) — probe paths, `deniedAs.open`, capture routes — bound
205
+ * once setup ran. Origin-fixed like a fixture step: a bound value that moves a string off the origin
206
+ * it resolved to with the reference unfilled (`//evil.test/…`) is refused, never probed.
207
+ */
208
+ export function substituteSpecSetupRefs(spec, b, baseUrl) {
209
+ const walk = (v, path) => {
210
+ if (typeof v === "string") {
211
+ if (!v.includes("${setup."))
212
+ return v;
213
+ const where = `--invariants ${path}`;
214
+ const out = substituteSetupRefs(v, b, { where });
215
+ let before = null;
216
+ let after = null;
217
+ try {
218
+ before = new URL(v.replace(SETUP_REF, "0"), baseUrl).origin;
219
+ after = new URL(out, baseUrl).origin;
220
+ }
221
+ catch {
222
+ // not a URL — nothing to keep on an origin
223
+ }
224
+ if (before !== after)
225
+ throw new UnboundSetupRefError(`${where}: a \${setup.*} value may fill the path or query, never the origin`);
226
+ return out;
227
+ }
228
+ if (Array.isArray(v))
229
+ return v.map((x, i) => walk(x, `${path}[${i}]`));
230
+ if (v !== null && typeof v === "object")
231
+ return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, walk(x, path === "" ? k : `${path}.${k}`)]));
232
+ return v;
233
+ };
234
+ return walk(spec, "");
235
+ }
236
+ export function fixtureSetupFailedResult(err, fx) {
237
+ const reason = err instanceof FixtureSetupError ? err.message : `fixture setup failed: ${err.message}`;
238
+ return {
239
+ outcome: "inconclusive",
240
+ reason,
241
+ failure: { kind: "configuration", message: reason },
242
+ attribution: "configuration",
243
+ exitCode: 2,
244
+ fixtures: fx.record(),
245
+ };
246
+ }
247
+ /**
248
+ * Regression capture (#144): every reproduce/minimize replay restores + re-runs the fixture the
249
+ * failing Recording was made from. The spec is `--fixtures`, else the one saved in `--result`
250
+ * (re-validated against that mission's allowlist); shell hooks must be re-supplied (and match the
251
+ * mission's by hash). A Recording that carries a fixture identity is never replayed without one.
252
+ */
253
+ export function regressionFixtures(flags, recording, missionResult,
254
+ /** `--storage-state` (#129): wins over the one recorded with the mission. */
255
+ storageState) {
256
+ const result = isObject(missionResult) && isObject(missionResult.result) ? missionResult.result : undefined;
257
+ const target = isObject(result?.target) ? result.target : undefined;
258
+ const saved = isObject(result?.fixtures) ? result.fixtures : undefined;
259
+ const allowlist = Array.isArray(target?.allowlist) ? target.allowlist.filter((a) => typeof a === "string") : [recording.site];
260
+ const baseUrl = typeof target?.seedUrl === "string" ? target.seedUrl : recording.site;
261
+ const savedHooks = isObject(saved?.hooks) ? saved.hooks : {};
262
+ if ((savedHooks.before !== undefined || savedHooks.after !== undefined) &&
263
+ ((flags.before === undefined ? undefined : hookHash(flags.before)) !== savedHooks.before ||
264
+ (flags.after === undefined ? undefined : hookHash(flags.after)) !== savedHooks.after)) {
265
+ throw new FixtureSpecError("the mission ran with --before/--after shell hooks: re-supply the SAME commands with --allow-shell-hooks");
266
+ }
267
+ const spec = flags.fixtures === undefined && saved?.spec !== undefined
268
+ ? parseFixtureSpec(saved.spec, { allowlist, baseUrl }, { openRefs: flags.before !== undefined })
269
+ : undefined;
270
+ const fx = buildMissionFixtures(flags, {
271
+ allowlist,
272
+ baseUrl,
273
+ ...(storageState !== undefined
274
+ ? { storageState }
275
+ : typeof target?.storageStatePath === "string"
276
+ ? { storageState: target.storageStatePath }
277
+ : {}),
278
+ ...(spec === undefined ? {} : { spec }),
279
+ // #243: re-mint as the identities the mission's steps used (a --fixture-identity re-binds one).
280
+ ...(isObject(saved?.identities)
281
+ ? { identities: Object.fromEntries(Object.entries(saved.identities).filter((e) => typeof e[1] === "string")) }
282
+ : {}),
283
+ });
284
+ if (fx === undefined && recording.fixture !== undefined) {
285
+ throw new FixtureSpecError(`this Recording started from fixture ${recording.fixture.identity}: pass --fixtures (or --result with its saved fixture) so every replay restores the same state`);
286
+ }
287
+ return fx;
288
+ }
289
+ function isObject(v) {
290
+ return v !== null && typeof v === "object" && !Array.isArray(v);
291
+ }
292
+ //# sourceMappingURL=fixture-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixture-cli.js","sourceRoot":"","sources":["../src/fixture-cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import { type CredentialStore, type RetryDeps } from "@jevitate/ai-core";
2
+ import type { IssueFilerPort, IssueRef, NewIssue } from "@jevitate/domain";
3
+ /**
4
+ * The GitHub adapter for the domain's `IssueFilerPort` (owner ruling 3). It prefers the `gh` CLI
5
+ * when it is installed (the user's own authenticated session); otherwise it uses the REST API with
6
+ * `GITHUB_TOKEN` from jevitate's credential store. The token goes ONLY into the Authorization
7
+ * header — never into a message, a log line, an error, or an issue — and any error text is
8
+ * scrubbed of it before it leaves this module.
9
+ *
10
+ * Both transports are injectable so tests exercise the exact commands/requests without ever
11
+ * talking to GitHub.
12
+ */
13
+ export interface ExecResult {
14
+ readonly code: number;
15
+ readonly stdout: string;
16
+ readonly stderr: string;
17
+ }
18
+ /** Runs a command with optional stdin. No shell — arguments are passed verbatim (portable). */
19
+ export type Exec = (cmd: string, args: readonly string[], stdin?: string) => Promise<ExecResult>;
20
+ export type FetchLike = (url: string, init: {
21
+ method: string;
22
+ headers: Record<string, string>;
23
+ body?: string;
24
+ }) => Promise<{
25
+ readonly status: number;
26
+ text(): Promise<string>;
27
+ }>;
28
+ export declare const defaultExec: Exec;
29
+ export interface GitHubIssueFilerDeps {
30
+ readonly store: CredentialStore;
31
+ readonly exec?: Exec;
32
+ readonly fetch?: FetchLike;
33
+ readonly apiBase?: string;
34
+ /** Backoff for transient REST failures (injected clock in tests). */
35
+ readonly retry?: RetryDeps;
36
+ }
37
+ export declare class GitHubFilingError extends Error {
38
+ readonly code: "E_ISSUE_FILING";
39
+ /** The HTTP status, for a REST failure (drives the transient-retry decision). */
40
+ readonly status?: number;
41
+ constructor(message: string, status?: number);
42
+ }
43
+ export declare class GitHubIssueFiler implements IssueFilerPort {
44
+ #private;
45
+ constructor(deps: GitHubIssueFilerDeps);
46
+ /** `gh` when it is installed and runnable, else REST (which needs `GITHUB_TOKEN`). */
47
+ mode(): Promise<"gh" | "rest">;
48
+ findOpenByMarker(repo: string, marker: string): Promise<IssueRef | null>;
49
+ /**
50
+ * Creating an issue is NOT idempotent, so it is never blindly retried: a "failed" create (a 502,
51
+ * a dropped connection) may well have persisted. After a transient failure the fingerprint
52
+ * search runs again — if the issue now exists it IS the one this call created and is returned;
53
+ * only if it is still absent is the create tried again, on the backoff schedule. A body without a
54
+ * fingerprint marker cannot be checked, so it gets exactly one attempt. Same for both transports.
55
+ */
56
+ create(repo: string, issue: NewIssue): Promise<IssueRef>;
57
+ /**
58
+ * An occurrence comment is not idempotent either: it gets exactly one attempt (a duplicate
59
+ * comment is worse than a missing one — the draft on disk keeps the occurrence either way).
60
+ */
61
+ comment(repo: string, number: number, body: string): Promise<IssueRef>;
62
+ }
@@ -0,0 +1,189 @@
1
+ import { spawn } from "node:child_process";
2
+ import { BACKOFF_SCHEDULE_MS, isTransientError, jittered, redactText, retryTransient, } from "@jevitate/ai-core";
3
+ import { clock } from "@jevitate/domain";
4
+ export const defaultExec = (cmd, args, stdin) => new Promise((resolve) => {
5
+ let stdout = "";
6
+ let stderr = "";
7
+ const child = spawn(cmd, [...args], { stdio: ["pipe", "pipe", "pipe"] });
8
+ child.stdout.on("data", (d) => (stdout += d.toString("utf8")));
9
+ child.stderr.on("data", (d) => (stderr += d.toString("utf8")));
10
+ child.on("error", (e) => resolve({ code: -1, stdout, stderr: e.message }));
11
+ child.on("close", (code) => resolve({ code: code ?? -1, stdout, stderr }));
12
+ child.stdin.end(stdin ?? "");
13
+ });
14
+ export class GitHubFilingError extends Error {
15
+ code = "E_ISSUE_FILING";
16
+ /** The HTTP status, for a REST failure (drives the transient-retry decision). */
17
+ status;
18
+ constructor(message, status) {
19
+ super(message);
20
+ this.name = "GitHubFilingError";
21
+ if (status !== undefined)
22
+ this.status = status;
23
+ }
24
+ }
25
+ const ISSUE_NUMBER = /\/issues\/(\d+)/;
26
+ /** The dedup marker every jevitate draft body carries (see `fingerprintMarker`). */
27
+ const FINGERPRINT_MARKER = /<!-- jevitate-fingerprint: [0-9a-f]+ -->/;
28
+ function parseIssueUrl(url) {
29
+ const m = ISSUE_NUMBER.exec(url);
30
+ if (m?.[1] === undefined)
31
+ throw new GitHubFilingError(`unexpected issue URL: ${url}`);
32
+ return { number: Number(m[1]), url };
33
+ }
34
+ /** The bare fingerprint inside a `<!-- jevitate-fingerprint: … -->` marker (the searchable token). */
35
+ function markerToken(marker) {
36
+ return marker.replace(/^<!--\s*jevitate-fingerprint:\s*/, "").replace(/\s*-->$/, "");
37
+ }
38
+ function isRecord(v) {
39
+ return v !== null && typeof v === "object" && !Array.isArray(v);
40
+ }
41
+ export class GitHubIssueFiler {
42
+ #exec;
43
+ #fetch;
44
+ #store;
45
+ #apiBase;
46
+ #retry;
47
+ #mode;
48
+ constructor(deps) {
49
+ this.#exec = deps.exec ?? defaultExec;
50
+ this.#fetch = deps.fetch;
51
+ this.#store = deps.store;
52
+ this.#apiBase = deps.apiBase ?? "https://api.github.com";
53
+ this.#retry = deps.retry ?? {};
54
+ }
55
+ /** `gh` when it is installed and runnable, else REST (which needs `GITHUB_TOKEN`). */
56
+ mode() {
57
+ this.#mode ??= this.#exec("gh", ["--version"]).then((r) => (r.code === 0 ? "gh" : "rest"));
58
+ return this.#mode;
59
+ }
60
+ #scrub(text) {
61
+ const token = this.#store.read("GITHUB_TOKEN");
62
+ return token === undefined ? text : redactText(text, [token]);
63
+ }
64
+ async #gh(args, stdin) {
65
+ const r = await this.#exec("gh", args, stdin);
66
+ if (r.code !== 0) {
67
+ // gh reports API failures as "HTTP 502: Bad Gateway (…)": keep the status so the transient
68
+ // classification is the same as for the REST transport.
69
+ const status = /\bHTTP (\d{3})\b/.exec(r.stderr)?.[1];
70
+ throw new GitHubFilingError(this.#scrub(`gh ${args[0] ?? ""} ${args[1] ?? ""} failed: ${r.stderr.trim()}`), status === undefined ? undefined : Number(status));
71
+ }
72
+ return r.stdout;
73
+ }
74
+ /**
75
+ * An IDEMPOTENT REST call (a read), retried with backoff on transient failures (429/5xx/network);
76
+ * typed failure after. Writes never go through here — see `create` / `comment`.
77
+ */
78
+ async #rest(method, path) {
79
+ const r = await retryTransient(() => this.#restOnce(method, path), this.#retry);
80
+ if (r.ok)
81
+ return r.value;
82
+ const e = r.error;
83
+ throw e instanceof GitHubFilingError
84
+ ? e
85
+ : new GitHubFilingError(this.#scrub(`GitHub API ${method} ${path} failed: ${e instanceof Error ? e.message : String(e)}`));
86
+ }
87
+ async #restOnce(method, path, body) {
88
+ const token = this.#store.read("GITHUB_TOKEN");
89
+ if (token === undefined) {
90
+ throw new GitHubFilingError("filing needs the gh CLI or a GITHUB_TOKEN in jevitate's credential store");
91
+ }
92
+ const doFetch = this.#fetch ?? globalThis.fetch;
93
+ const res = await doFetch(`${this.#apiBase}${path}`, {
94
+ method,
95
+ headers: {
96
+ Authorization: `Bearer ${token}`,
97
+ Accept: "application/vnd.github+json",
98
+ "X-GitHub-Api-Version": "2022-11-28",
99
+ "User-Agent": "jevitate",
100
+ ...(body === undefined ? {} : { "Content-Type": "application/json" }),
101
+ },
102
+ ...(body === undefined ? {} : { body: JSON.stringify(body) }),
103
+ });
104
+ const text = await res.text();
105
+ if (res.status < 200 || res.status >= 300) {
106
+ throw new GitHubFilingError(this.#scrub(`GitHub API ${res.status} on ${method} ${path}: ${text.slice(0, 300)}`), res.status);
107
+ }
108
+ return text === "" ? null : JSON.parse(text);
109
+ }
110
+ async findOpenByMarker(repo, marker) {
111
+ const token = markerToken(marker);
112
+ if ((await this.mode()) === "gh") {
113
+ const out = await this.#gh([
114
+ "issue", "list", "--repo", repo, "--state", "open", "--search", `${token} in:body`,
115
+ "--json", "number,url,body", "--limit", "20",
116
+ ]);
117
+ const list = JSON.parse(out === "" ? "[]" : out);
118
+ if (!Array.isArray(list))
119
+ return null;
120
+ for (const item of list) {
121
+ if (isRecord(item) && typeof item.body === "string" && item.body.includes(marker) && typeof item.number === "number" && typeof item.url === "string") {
122
+ return { number: item.number, url: item.url };
123
+ }
124
+ }
125
+ return null;
126
+ }
127
+ const q = encodeURIComponent(`repo:${repo} is:issue is:open in:body ${token}`);
128
+ const res = await this.#rest("GET", `/search/issues?q=${q}&per_page=20`);
129
+ const items = isRecord(res) && Array.isArray(res.items) ? res.items : [];
130
+ for (const item of items) {
131
+ if (isRecord(item) && typeof item.body === "string" && item.body.includes(marker) && typeof item.number === "number" && typeof item.html_url === "string") {
132
+ return { number: item.number, url: item.html_url };
133
+ }
134
+ }
135
+ return null;
136
+ }
137
+ /** One create attempt, on whichever transport is in use. */
138
+ async #createOnce(repo, issue) {
139
+ if ((await this.mode()) === "gh") {
140
+ const out = await this.#gh(["issue", "create", "--repo", repo, "--title", issue.title, "--body-file", "-"], issue.body);
141
+ return parseIssueUrl(out.trim());
142
+ }
143
+ const res = await this.#restOnce("POST", `/repos/${repo}/issues`, { title: issue.title, body: issue.body });
144
+ if (!isRecord(res) || typeof res.number !== "number" || typeof res.html_url !== "string") {
145
+ throw new GitHubFilingError("GitHub API returned no issue");
146
+ }
147
+ return { number: res.number, url: res.html_url };
148
+ }
149
+ /**
150
+ * Creating an issue is NOT idempotent, so it is never blindly retried: a "failed" create (a 502,
151
+ * a dropped connection) may well have persisted. After a transient failure the fingerprint
152
+ * search runs again — if the issue now exists it IS the one this call created and is returned;
153
+ * only if it is still absent is the create tried again, on the backoff schedule. A body without a
154
+ * fingerprint marker cannot be checked, so it gets exactly one attempt. Same for both transports.
155
+ */
156
+ async create(repo, issue) {
157
+ const marker = FINGERPRINT_MARKER.exec(issue.body)?.[0];
158
+ const schedule = this.#retry.schedule ?? BACKOFF_SCHEDULE_MS;
159
+ const sleep = this.#retry.sleep ?? ((ms) => clock.sleep(ms));
160
+ const random = this.#retry.random ?? Math.random;
161
+ for (let attempt = 0;; attempt++) {
162
+ try {
163
+ return await this.#createOnce(repo, issue);
164
+ }
165
+ catch (e) {
166
+ const delay = schedule[attempt];
167
+ if (marker === undefined || delay === undefined || !isTransientError(e))
168
+ throw e;
169
+ const landed = await this.findOpenByMarker(repo, marker);
170
+ if (landed !== null)
171
+ return landed;
172
+ await sleep(jittered(delay, random));
173
+ }
174
+ }
175
+ }
176
+ /**
177
+ * An occurrence comment is not idempotent either: it gets exactly one attempt (a duplicate
178
+ * comment is worse than a missing one — the draft on disk keeps the occurrence either way).
179
+ */
180
+ async comment(repo, number, body) {
181
+ if ((await this.mode()) === "gh") {
182
+ const out = await this.#gh(["issue", "comment", String(number), "--repo", repo, "--body-file", "-"], body);
183
+ return { number, url: out.trim() };
184
+ }
185
+ const res = await this.#restOnce("POST", `/repos/${repo}/issues/${number}/comments`, { body });
186
+ return { number, url: isRecord(res) && typeof res.html_url === "string" ? res.html_url : "" };
187
+ }
188
+ }
189
+ //# sourceMappingURL=github-issue-filer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-issue-filer.js","sourceRoot":"","sources":["../src/github-issue-filer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { HostHealthSampler, degradedEnvironmentOutcome } from "@jevitate/explore";
2
+ import type { EnvironmentDegraded, HostHealthSummary, MissionFailure } from "@jevitate/domain";
3
+ import { type ResourceGovernor } from "@jevitate/playwright";
4
+ /**
5
+ * One host-health sampler per mission run (#203), shared by every strategy's runner: started (with
6
+ * one sample taken) BEFORE the browser opens, fed every transcript entry, handed to the mission so a
7
+ * hang/no-progress met on a starved host is marked `environment-degraded`, and summarised into the
8
+ * result's `hostHealth`. `injected` is the test seam (a deterministic fake host).
9
+ */
10
+ export declare function startHostHealth(injected?: HostHealthSampler): Promise<HostHealthSampler>;
11
+ /** What every result carries from the sampler, and the degraded-environment verdict rule applied. */
12
+ export interface HostHealthVerdict<O extends string> {
13
+ readonly outcome: O | "inconclusive";
14
+ /** Set when the rule turned the outcome `inconclusive` (reason `degraded-environment`). */
15
+ readonly failure?: MissionFailure;
16
+ readonly fields: {
17
+ readonly hostHealth: HostHealthSummary;
18
+ readonly environmentDegraded: EnvironmentDegraded[];
19
+ };
20
+ }
21
+ /**
22
+ * Takes the closing sample and applies the run-level rule: most steps starved → `inconclusive`
23
+ * (`degraded-environment`), never `clean` (see `degradedEnvironmentOutcome`).
24
+ */
25
+ export declare function finishHostHealth<O extends string>(health: HostHealthSampler, outcome: O, opts?: Parameters<typeof degradedEnvironmentOutcome>[2], governor?: ResourceGovernor): Promise<HostHealthVerdict<O>>;
@@ -0,0 +1,33 @@
1
+ import { HostHealthSampler, degradedEnvironmentOutcome } from "@jevitate/explore";
2
+ import { sharedResourceGovernor } from "@jevitate/playwright";
3
+ import { clock } from "@jevitate/domain";
4
+ /** When each run's sampler started (#205: the window the governor's summary covers). */
5
+ const runStarts = new WeakMap();
6
+ /**
7
+ * One host-health sampler per mission run (#203), shared by every strategy's runner: started (with
8
+ * one sample taken) BEFORE the browser opens, fed every transcript entry, handed to the mission so a
9
+ * hang/no-progress met on a starved host is marked `environment-degraded`, and summarised into the
10
+ * result's `hostHealth`. `injected` is the test seam (a deterministic fake host).
11
+ */
12
+ export async function startHostHealth(injected) {
13
+ const health = (injected ?? new HostHealthSampler()).start();
14
+ runStarts.set(health, clock.now());
15
+ await health.sample();
16
+ return health;
17
+ }
18
+ /**
19
+ * Takes the closing sample and applies the run-level rule: most steps starved → `inconclusive`
20
+ * (`degraded-environment`), never `clean` (see `degradedEnvironmentOutcome`).
21
+ */
22
+ export async function finishHostHealth(health, outcome, opts = {}, governor = sharedResourceGovernor()) {
23
+ await health.sample();
24
+ // #205: what resource governance did during this run (cap, slot, throttling, memory) rides along.
25
+ const hostHealth = { ...health.summary(), resources: governor.snapshot(runStarts.get(health) ?? 0) };
26
+ const verdict = degradedEnvironmentOutcome(outcome, hostHealth, opts);
27
+ return {
28
+ outcome: verdict.outcome,
29
+ ...(verdict.failure === undefined ? {} : { failure: verdict.failure }),
30
+ fields: { hostHealth, environmentDegraded: health.findings() },
31
+ };
32
+ }
33
+ //# sourceMappingURL=host-health-run.js.map