@e2edev/e2e 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (399) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +104 -0
  3. package/dist/agent/act-validation.d.ts +27 -0
  4. package/dist/agent/act-validation.d.ts.map +1 -0
  5. package/dist/agent/act-validation.js +121 -0
  6. package/dist/agent/act-validation.js.map +1 -0
  7. package/dist/agent/act.d.ts +27 -0
  8. package/dist/agent/act.d.ts.map +1 -0
  9. package/dist/agent/act.js +775 -0
  10. package/dist/agent/act.js.map +1 -0
  11. package/dist/agent/actions.d.ts +66 -0
  12. package/dist/agent/actions.d.ts.map +1 -0
  13. package/dist/agent/actions.js +82 -0
  14. package/dist/agent/actions.js.map +1 -0
  15. package/dist/agent/ai-sdk.d.ts +16 -0
  16. package/dist/agent/ai-sdk.d.ts.map +1 -0
  17. package/dist/agent/ai-sdk.js +59 -0
  18. package/dist/agent/ai-sdk.js.map +1 -0
  19. package/dist/agent/call-options.d.ts +10 -0
  20. package/dist/agent/call-options.d.ts.map +1 -0
  21. package/dist/agent/call-options.js +28 -0
  22. package/dist/agent/call-options.js.map +1 -0
  23. package/dist/agent/default-agent.d.ts +22 -0
  24. package/dist/agent/default-agent.d.ts.map +1 -0
  25. package/dist/agent/default-agent.js +354 -0
  26. package/dist/agent/default-agent.js.map +1 -0
  27. package/dist/agent/error.d.ts +53 -0
  28. package/dist/agent/error.d.ts.map +1 -0
  29. package/dist/agent/error.js +94 -0
  30. package/dist/agent/error.js.map +1 -0
  31. package/dist/agent/executor.d.ts +238 -0
  32. package/dist/agent/executor.d.ts.map +1 -0
  33. package/dist/agent/executor.js +56 -0
  34. package/dist/agent/executor.js.map +1 -0
  35. package/dist/agent/index.d.ts +15 -0
  36. package/dist/agent/index.d.ts.map +1 -0
  37. package/dist/agent/index.js +304 -0
  38. package/dist/agent/index.js.map +1 -0
  39. package/dist/agent/invocation.d.ts +194 -0
  40. package/dist/agent/invocation.d.ts.map +1 -0
  41. package/dist/agent/invocation.js +389 -0
  42. package/dist/agent/invocation.js.map +1 -0
  43. package/dist/agent/ledger.d.ts +24 -0
  44. package/dist/agent/ledger.d.ts.map +1 -0
  45. package/dist/agent/ledger.js +48 -0
  46. package/dist/agent/ledger.js.map +1 -0
  47. package/dist/agent/loop-guards.d.ts +35 -0
  48. package/dist/agent/loop-guards.d.ts.map +1 -0
  49. package/dist/agent/loop-guards.js +110 -0
  50. package/dist/agent/loop-guards.js.map +1 -0
  51. package/dist/agent/model/adapter.d.ts +97 -0
  52. package/dist/agent/model/adapter.d.ts.map +1 -0
  53. package/dist/agent/model/adapter.js +40 -0
  54. package/dist/agent/model/adapter.js.map +1 -0
  55. package/dist/agent/model/router.d.ts +36 -0
  56. package/dist/agent/model/router.d.ts.map +1 -0
  57. package/dist/agent/model/router.js +45 -0
  58. package/dist/agent/model/router.js.map +1 -0
  59. package/dist/agent/model/schema.d.ts +16 -0
  60. package/dist/agent/model/schema.d.ts.map +1 -0
  61. package/dist/agent/model/schema.js +37 -0
  62. package/dist/agent/model/schema.js.map +1 -0
  63. package/dist/agent/model/sdk.d.ts +28 -0
  64. package/dist/agent/model/sdk.d.ts.map +1 -0
  65. package/dist/agent/model/sdk.js +258 -0
  66. package/dist/agent/model/sdk.js.map +1 -0
  67. package/dist/agent/observation.d.ts +70 -0
  68. package/dist/agent/observation.d.ts.map +1 -0
  69. package/dist/agent/observation.js +174 -0
  70. package/dist/agent/observation.js.map +1 -0
  71. package/dist/agent/phases.d.ts +71 -0
  72. package/dist/agent/phases.d.ts.map +1 -0
  73. package/dist/agent/phases.js +124 -0
  74. package/dist/agent/phases.js.map +1 -0
  75. package/dist/agent/prompts.d.ts +40 -0
  76. package/dist/agent/prompts.d.ts.map +1 -0
  77. package/dist/agent/prompts.js +104 -0
  78. package/dist/agent/prompts.js.map +1 -0
  79. package/dist/agent/protocol.d.ts +31 -0
  80. package/dist/agent/protocol.d.ts.map +1 -0
  81. package/dist/agent/protocol.js +67 -0
  82. package/dist/agent/protocol.js.map +1 -0
  83. package/dist/agent/public.d.ts +12 -0
  84. package/dist/agent/public.d.ts.map +1 -0
  85. package/dist/agent/public.js +12 -0
  86. package/dist/agent/public.js.map +1 -0
  87. package/dist/agent/replay.d.ts +61 -0
  88. package/dist/agent/replay.d.ts.map +1 -0
  89. package/dist/agent/replay.js +185 -0
  90. package/dist/agent/replay.js.map +1 -0
  91. package/dist/agent/secrets.d.ts +22 -0
  92. package/dist/agent/secrets.d.ts.map +1 -0
  93. package/dist/agent/secrets.js +63 -0
  94. package/dist/agent/secrets.js.map +1 -0
  95. package/dist/agent/step-cache.d.ts +156 -0
  96. package/dist/agent/step-cache.d.ts.map +1 -0
  97. package/dist/agent/step-cache.js +301 -0
  98. package/dist/agent/step-cache.js.map +1 -0
  99. package/dist/agent/tool-loop.d.ts +51 -0
  100. package/dist/agent/tool-loop.d.ts.map +1 -0
  101. package/dist/agent/tool-loop.js +331 -0
  102. package/dist/agent/tool-loop.js.map +1 -0
  103. package/dist/agent/tool.d.ts +39 -0
  104. package/dist/agent/tool.d.ts.map +1 -0
  105. package/dist/agent/tool.js +52 -0
  106. package/dist/agent/tool.js.map +1 -0
  107. package/dist/backend/contract.d.ts +186 -0
  108. package/dist/backend/contract.d.ts.map +1 -0
  109. package/dist/backend/contract.js +65 -0
  110. package/dist/backend/contract.js.map +1 -0
  111. package/dist/backend/index.d.ts +316 -0
  112. package/dist/backend/index.d.ts.map +1 -0
  113. package/dist/backend/index.js +219 -0
  114. package/dist/backend/index.js.map +1 -0
  115. package/dist/backend/session.d.ts +23 -0
  116. package/dist/backend/session.d.ts.map +1 -0
  117. package/dist/backend/session.js +213 -0
  118. package/dist/backend/session.js.map +1 -0
  119. package/dist/backend/surface.d.ts +83 -0
  120. package/dist/backend/surface.d.ts.map +1 -0
  121. package/dist/backend/surface.js +11 -0
  122. package/dist/backend/surface.js.map +1 -0
  123. package/dist/cache/anchors.d.ts +37 -0
  124. package/dist/cache/anchors.d.ts.map +1 -0
  125. package/dist/cache/anchors.js +89 -0
  126. package/dist/cache/anchors.js.map +1 -0
  127. package/dist/cache/context.d.ts +74 -0
  128. package/dist/cache/context.d.ts.map +1 -0
  129. package/dist/cache/context.js +82 -0
  130. package/dist/cache/context.js.map +1 -0
  131. package/dist/cache/decide.d.ts +42 -0
  132. package/dist/cache/decide.d.ts.map +1 -0
  133. package/dist/cache/decide.js +47 -0
  134. package/dist/cache/decide.js.map +1 -0
  135. package/dist/cache/identity.d.ts +113 -0
  136. package/dist/cache/identity.d.ts.map +1 -0
  137. package/dist/cache/identity.js +109 -0
  138. package/dist/cache/identity.js.map +1 -0
  139. package/dist/cache/recorder.d.ts +76 -0
  140. package/dist/cache/recorder.d.ts.map +1 -0
  141. package/dist/cache/recorder.js +143 -0
  142. package/dist/cache/recorder.js.map +1 -0
  143. package/dist/cache/relocate.d.ts +69 -0
  144. package/dist/cache/relocate.d.ts.map +1 -0
  145. package/dist/cache/relocate.js +125 -0
  146. package/dist/cache/relocate.js.map +1 -0
  147. package/dist/cache/store.d.ts +68 -0
  148. package/dist/cache/store.d.ts.map +1 -0
  149. package/dist/cache/store.js +106 -0
  150. package/dist/cache/store.js.map +1 -0
  151. package/dist/cache/trace.d.ts +146 -0
  152. package/dist/cache/trace.d.ts.map +1 -0
  153. package/dist/cache/trace.js +249 -0
  154. package/dist/cache/trace.js.map +1 -0
  155. package/dist/cli/bin.d.ts +3 -0
  156. package/dist/cli/bin.d.ts.map +1 -0
  157. package/dist/cli/bin.js +4 -0
  158. package/dist/cli/bin.js.map +1 -0
  159. package/dist/cli/index.d.ts +4 -0
  160. package/dist/cli/index.d.ts.map +1 -0
  161. package/dist/cli/index.js +108 -0
  162. package/dist/cli/index.js.map +1 -0
  163. package/dist/cli/init.d.ts +6 -0
  164. package/dist/cli/init.d.ts.map +1 -0
  165. package/dist/cli/init.js +101 -0
  166. package/dist/cli/init.js.map +1 -0
  167. package/dist/collect/collect.d.ts +44 -0
  168. package/dist/collect/collect.d.ts.map +1 -0
  169. package/dist/collect/collect.js +89 -0
  170. package/dist/collect/collect.js.map +1 -0
  171. package/dist/collect/registry.d.ts +47 -0
  172. package/dist/collect/registry.d.ts.map +1 -0
  173. package/dist/collect/registry.js +262 -0
  174. package/dist/collect/registry.js.map +1 -0
  175. package/dist/collect/select.d.ts +54 -0
  176. package/dist/collect/select.d.ts.map +1 -0
  177. package/dist/collect/select.js +230 -0
  178. package/dist/collect/select.js.map +1 -0
  179. package/dist/config/agent.d.ts +91 -0
  180. package/dist/config/agent.d.ts.map +1 -0
  181. package/dist/config/agent.js +232 -0
  182. package/dist/config/agent.js.map +1 -0
  183. package/dist/config/load.d.ts +23 -0
  184. package/dist/config/load.d.ts.map +1 -0
  185. package/dist/config/load.js +75 -0
  186. package/dist/config/load.js.map +1 -0
  187. package/dist/config/resolve.d.ts +94 -0
  188. package/dist/config/resolve.d.ts.map +1 -0
  189. package/dist/config/resolve.js +500 -0
  190. package/dist/config/resolve.js.map +1 -0
  191. package/dist/config/validate.d.ts +6 -0
  192. package/dist/config/validate.d.ts.map +1 -0
  193. package/dist/config/validate.js +21 -0
  194. package/dist/config/validate.js.map +1 -0
  195. package/dist/credentials.d.ts +7 -0
  196. package/dist/credentials.d.ts.map +1 -0
  197. package/dist/credentials.js +42 -0
  198. package/dist/credentials.js.map +1 -0
  199. package/dist/expect/async.d.ts +5 -0
  200. package/dist/expect/async.d.ts.map +1 -0
  201. package/dist/expect/async.js +186 -0
  202. package/dist/expect/async.js.map +1 -0
  203. package/dist/expect/index.d.ts +7 -0
  204. package/dist/expect/index.d.ts.map +1 -0
  205. package/dist/expect/index.js +23 -0
  206. package/dist/expect/index.js.map +1 -0
  207. package/dist/expect/values.d.ts +4 -0
  208. package/dist/expect/values.d.ts.map +1 -0
  209. package/dist/expect/values.js +108 -0
  210. package/dist/expect/values.js.map +1 -0
  211. package/dist/index.d.ts +13 -0
  212. package/dist/index.d.ts.map +1 -0
  213. package/dist/index.js +15 -0
  214. package/dist/index.js.map +1 -0
  215. package/dist/internal/ai-trace.d.ts +151 -0
  216. package/dist/internal/ai-trace.d.ts.map +1 -0
  217. package/dist/internal/ai-trace.js +360 -0
  218. package/dist/internal/ai-trace.js.map +1 -0
  219. package/dist/internal/brands.d.ts +9 -0
  220. package/dist/internal/brands.d.ts.map +1 -0
  221. package/dist/internal/brands.js +9 -0
  222. package/dist/internal/brands.js.map +1 -0
  223. package/dist/internal/debug.d.ts +43 -0
  224. package/dist/internal/debug.d.ts.map +1 -0
  225. package/dist/internal/debug.js +102 -0
  226. package/dist/internal/debug.js.map +1 -0
  227. package/dist/internal/errors.d.ts +98 -0
  228. package/dist/internal/errors.d.ts.map +1 -0
  229. package/dist/internal/errors.js +212 -0
  230. package/dist/internal/errors.js.map +1 -0
  231. package/dist/internal/globs.d.ts +29 -0
  232. package/dist/internal/globs.d.ts.map +1 -0
  233. package/dist/internal/globs.js +126 -0
  234. package/dist/internal/globs.js.map +1 -0
  235. package/dist/internal/ids.d.ts +32 -0
  236. package/dist/internal/ids.d.ts.map +1 -0
  237. package/dist/internal/ids.js +124 -0
  238. package/dist/internal/ids.js.map +1 -0
  239. package/dist/internal/json-value.d.ts +15 -0
  240. package/dist/internal/json-value.d.ts.map +1 -0
  241. package/dist/internal/json-value.js +61 -0
  242. package/dist/internal/json-value.js.map +1 -0
  243. package/dist/internal/package-version.d.ts +6 -0
  244. package/dist/internal/package-version.d.ts.map +1 -0
  245. package/dist/internal/package-version.js +14 -0
  246. package/dist/internal/package-version.js.map +1 -0
  247. package/dist/internal/realm-slot.d.ts +13 -0
  248. package/dist/internal/realm-slot.d.ts.map +1 -0
  249. package/dist/internal/realm-slot.js +23 -0
  250. package/dist/internal/realm-slot.js.map +1 -0
  251. package/dist/internal/redact.d.ts +33 -0
  252. package/dist/internal/redact.d.ts.map +1 -0
  253. package/dist/internal/redact.js +56 -0
  254. package/dist/internal/redact.js.map +1 -0
  255. package/dist/internal/regexp.d.ts +10 -0
  256. package/dist/internal/regexp.d.ts.map +1 -0
  257. package/dist/internal/regexp.js +27 -0
  258. package/dist/internal/regexp.js.map +1 -0
  259. package/dist/internal/text.d.ts +39 -0
  260. package/dist/internal/text.d.ts.map +1 -0
  261. package/dist/internal/text.js +66 -0
  262. package/dist/internal/text.js.map +1 -0
  263. package/dist/internal/time.d.ts +37 -0
  264. package/dist/internal/time.d.ts.map +1 -0
  265. package/dist/internal/time.js +91 -0
  266. package/dist/internal/time.js.map +1 -0
  267. package/dist/internal/trace.d.ts +18 -0
  268. package/dist/internal/trace.d.ts.map +1 -0
  269. package/dist/internal/trace.js +46 -0
  270. package/dist/internal/trace.js.map +1 -0
  271. package/dist/internal/urls.d.ts +31 -0
  272. package/dist/internal/urls.d.ts.map +1 -0
  273. package/dist/internal/urls.js +95 -0
  274. package/dist/internal/urls.js.map +1 -0
  275. package/dist/locator/engine.d.ts +65 -0
  276. package/dist/locator/engine.d.ts.map +1 -0
  277. package/dist/locator/engine.js +164 -0
  278. package/dist/locator/engine.js.map +1 -0
  279. package/dist/locator/expression.d.ts +19 -0
  280. package/dist/locator/expression.d.ts.map +1 -0
  281. package/dist/locator/expression.js +85 -0
  282. package/dist/locator/expression.js.map +1 -0
  283. package/dist/locator/screen.d.ts +36 -0
  284. package/dist/locator/screen.d.ts.map +1 -0
  285. package/dist/locator/screen.js +281 -0
  286. package/dist/locator/screen.js.map +1 -0
  287. package/dist/report/build.d.ts +214 -0
  288. package/dist/report/build.d.ts.map +1 -0
  289. package/dist/report/build.js +346 -0
  290. package/dist/report/build.js.map +1 -0
  291. package/dist/report/code-frame.d.ts +20 -0
  292. package/dist/report/code-frame.d.ts.map +1 -0
  293. package/dist/report/code-frame.js +73 -0
  294. package/dist/report/code-frame.js.map +1 -0
  295. package/dist/report/debug-steps.d.ts +14 -0
  296. package/dist/report/debug-steps.d.ts.map +1 -0
  297. package/dist/report/debug-steps.js +85 -0
  298. package/dist/report/debug-steps.js.map +1 -0
  299. package/dist/report/list.d.ts +80 -0
  300. package/dist/report/list.d.ts.map +1 -0
  301. package/dist/report/list.js +372 -0
  302. package/dist/report/list.js.map +1 -0
  303. package/dist/report/live-status.d.ts +51 -0
  304. package/dist/report/live-status.d.ts.map +1 -0
  305. package/dist/report/live-status.js +162 -0
  306. package/dist/report/live-status.js.map +1 -0
  307. package/dist/report/write.d.ts +4 -0
  308. package/dist/report/write.d.ts.map +1 -0
  309. package/dist/report/write.js +13 -0
  310. package/dist/report/write.js.map +1 -0
  311. package/dist/run/app-process.d.ts +18 -0
  312. package/dist/run/app-process.d.ts.map +1 -0
  313. package/dist/run/app-process.js +112 -0
  314. package/dist/run/app-process.js.map +1 -0
  315. package/dist/run/artifacts.d.ts +51 -0
  316. package/dist/run/artifacts.d.ts.map +1 -0
  317. package/dist/run/artifacts.js +114 -0
  318. package/dist/run/artifacts.js.map +1 -0
  319. package/dist/run/events.d.ts +100 -0
  320. package/dist/run/events.d.ts.map +1 -0
  321. package/dist/run/events.js +59 -0
  322. package/dist/run/events.js.map +1 -0
  323. package/dist/run/execute.d.ts +119 -0
  324. package/dist/run/execute.d.ts.map +1 -0
  325. package/dist/run/execute.js +555 -0
  326. package/dist/run/execute.js.map +1 -0
  327. package/dist/run/fixtures.d.ts +40 -0
  328. package/dist/run/fixtures.d.ts.map +1 -0
  329. package/dist/run/fixtures.js +408 -0
  330. package/dist/run/fixtures.js.map +1 -0
  331. package/dist/run/in-process.d.ts +24 -0
  332. package/dist/run/in-process.d.ts.map +1 -0
  333. package/dist/run/in-process.js +120 -0
  334. package/dist/run/in-process.js.map +1 -0
  335. package/dist/run/public.d.ts +19 -0
  336. package/dist/run/public.d.ts.map +1 -0
  337. package/dist/run/public.js +11 -0
  338. package/dist/run/public.js.map +1 -0
  339. package/dist/run/realm.d.ts +52 -0
  340. package/dist/run/realm.d.ts.map +1 -0
  341. package/dist/run/realm.js +101 -0
  342. package/dist/run/realm.js.map +1 -0
  343. package/dist/run/records.d.ts +87 -0
  344. package/dist/run/records.d.ts.map +1 -0
  345. package/dist/run/records.js +3 -0
  346. package/dist/run/records.js.map +1 -0
  347. package/dist/run/retry.d.ts +18 -0
  348. package/dist/run/retry.d.ts.map +1 -0
  349. package/dist/run/retry.js +33 -0
  350. package/dist/run/retry.js.map +1 -0
  351. package/dist/run/runner.d.ts +49 -0
  352. package/dist/run/runner.d.ts.map +1 -0
  353. package/dist/run/runner.js +375 -0
  354. package/dist/run/runner.js.map +1 -0
  355. package/dist/run/scheduler.d.ts +45 -0
  356. package/dist/run/scheduler.d.ts.map +1 -0
  357. package/dist/run/scheduler.js +512 -0
  358. package/dist/run/scheduler.js.map +1 -0
  359. package/dist/run/serial.d.ts +52 -0
  360. package/dist/run/serial.d.ts.map +1 -0
  361. package/dist/run/serial.js +247 -0
  362. package/dist/run/serial.js.map +1 -0
  363. package/dist/run/sessions.d.ts +54 -0
  364. package/dist/run/sessions.d.ts.map +1 -0
  365. package/dist/run/sessions.js +221 -0
  366. package/dist/run/sessions.js.map +1 -0
  367. package/dist/run/steps.d.ts +196 -0
  368. package/dist/run/steps.d.ts.map +1 -0
  369. package/dist/run/steps.js +151 -0
  370. package/dist/run/steps.js.map +1 -0
  371. package/dist/run/unit-runner.d.ts +30 -0
  372. package/dist/run/unit-runner.d.ts.map +1 -0
  373. package/dist/run/unit-runner.js +11 -0
  374. package/dist/run/unit-runner.js.map +1 -0
  375. package/dist/run/units.d.ts +50 -0
  376. package/dist/run/units.d.ts.map +1 -0
  377. package/dist/run/units.js +103 -0
  378. package/dist/run/units.js.map +1 -0
  379. package/dist/run/worker/entry.d.ts +8 -0
  380. package/dist/run/worker/entry.d.ts.map +1 -0
  381. package/dist/run/worker/entry.js +137 -0
  382. package/dist/run/worker/entry.js.map +1 -0
  383. package/dist/run/worker/handle.d.ts +10 -0
  384. package/dist/run/worker/handle.d.ts.map +1 -0
  385. package/dist/run/worker/handle.js +74 -0
  386. package/dist/run/worker/handle.js.map +1 -0
  387. package/dist/run/worker/protocol.d.ts +129 -0
  388. package/dist/run/worker/protocol.d.ts.map +1 -0
  389. package/dist/run/worker/protocol.js +18 -0
  390. package/dist/run/worker/protocol.js.map +1 -0
  391. package/dist/run/worker/session.d.ts +69 -0
  392. package/dist/run/worker/session.d.ts.map +1 -0
  393. package/dist/run/worker/session.js +123 -0
  394. package/dist/run/worker/session.js.map +1 -0
  395. package/dist/types.d.ts +630 -0
  396. package/dist/types.d.ts.map +1 -0
  397. package/dist/types.js +6 -0
  398. package/dist/types.js.map +1 -0
  399. package/package.json +91 -0
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Live in-progress block for TTY reporters: the currently executing pairs and
3
+ * one progress counter line, repainted below the permanent log. Constructed
4
+ * without a raw control channel it is a no-op, so callers never branch.
5
+ */
6
+ import pc from 'picocolors';
7
+ const ESC = '\u001b';
8
+ const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
9
+ const SPINNER_INTERVAL_MS = 120;
10
+ /**
11
+ * Clamps one status line to the terminal width, skipping ANSI sequences when
12
+ * counting. The erase sequence counts physical rows, so a line that wraps
13
+ * would leave stale fragments behind on every repaint.
14
+ */
15
+ function clampToWidth(text, columns) {
16
+ // Two columns of margin: the ellipsis takes one, and a line that reaches
17
+ // the exact width puts some terminals into pending-wrap, which breaks the
18
+ // erase row count the same way real wrapping does.
19
+ const max = Math.max(4, columns - 2);
20
+ let width = 0;
21
+ let out = '';
22
+ for (let i = 0; i < text.length;) {
23
+ const char = text[i];
24
+ if (char === ESC) {
25
+ const end = text.indexOf('m', i);
26
+ if (end === -1)
27
+ break;
28
+ out += text.slice(i, end + 1);
29
+ i = end + 1;
30
+ continue;
31
+ }
32
+ if (width >= max)
33
+ return `${out}${ESC}[0m…`;
34
+ out += char;
35
+ width += 1;
36
+ i += 1;
37
+ }
38
+ return out;
39
+ }
40
+ export class LiveStatus {
41
+ raw;
42
+ /**
43
+ * Entries currently executing, keyed by the caller, in start order. An
44
+ * undefined text counts toward the progress line without rendering a row,
45
+ * for entries whose story streams permanently above the block.
46
+ */
47
+ running = new Map();
48
+ /** Lines the status block currently occupies below the log. */
49
+ statusLines = 0;
50
+ /** Planned entries; 0 until the plan is announced. */
51
+ total = 0;
52
+ /** Entries with a reported result. */
53
+ done = 0;
54
+ /** Current spinner frame; advanced by the animation timer. */
55
+ frame = 0;
56
+ timer;
57
+ constructor(raw) {
58
+ this.raw = raw;
59
+ }
60
+ /** Animates while any entry renders; stopped and unref'd otherwise. */
61
+ syncTimer(visible) {
62
+ if (this.raw === undefined)
63
+ return;
64
+ if (visible === 0) {
65
+ if (this.timer !== undefined)
66
+ clearInterval(this.timer);
67
+ this.timer = undefined;
68
+ return;
69
+ }
70
+ if (this.timer !== undefined)
71
+ return;
72
+ this.timer = setInterval(() => {
73
+ this.frame = (this.frame + 1) % SPINNER_FRAMES.length;
74
+ this.redraw();
75
+ }, SPINNER_INTERVAL_MS);
76
+ this.timer.unref();
77
+ }
78
+ /** Announces how many entries the run will execute. */
79
+ plan(total) {
80
+ this.total = total;
81
+ this.redraw();
82
+ }
83
+ /** Shows one started entry in the block; undefined counts it invisibly. */
84
+ start(key, text) {
85
+ this.running.set(key, text);
86
+ this.redraw();
87
+ }
88
+ /** Counts one finished entry and removes it from the block. */
89
+ finish(key) {
90
+ this.done += 1;
91
+ this.running.delete(key);
92
+ }
93
+ /** Counts one finished entry that never appeared in the block. */
94
+ skip() {
95
+ this.done += 1;
96
+ this.redraw();
97
+ }
98
+ /** Clears the block so permanent log lines can be appended. */
99
+ erase() {
100
+ if (this.raw === undefined || this.statusLines === 0)
101
+ return;
102
+ this.raw(`${ESC}[${this.statusLines}A${ESC}[0J`);
103
+ this.statusLines = 0;
104
+ }
105
+ /**
106
+ * Ends the live block for good: entries still running (an interrupt, a
107
+ * crashed worker) are dropped and the spinner timer is cleared, so nothing
108
+ * repaints after the final summary in a long-lived programmatic caller.
109
+ */
110
+ stop() {
111
+ this.running.clear();
112
+ if (this.timer !== undefined)
113
+ clearInterval(this.timer);
114
+ this.timer = undefined;
115
+ this.erase();
116
+ }
117
+ /**
118
+ * Repaints the block below the permanent log. An entry may span several
119
+ * lines (test header, current step, recent calls); the marker goes on the
120
+ * first line and every line is clamped so the row count stays exact.
121
+ */
122
+ redraw() {
123
+ if (this.raw === undefined)
124
+ return;
125
+ const columns = process.stdout.columns ?? 100;
126
+ let payload = this.statusLines > 0 ? `${ESC}[${this.statusLines}A${ESC}[0J` : '';
127
+ let rows = 0;
128
+ let visible = 0;
129
+ const marker = SPINNER_FRAMES[this.frame] ?? '▸';
130
+ for (const text of this.running.values()) {
131
+ if (text === undefined)
132
+ continue;
133
+ visible += 1;
134
+ const lines = text.split('\n');
135
+ for (const [index, line] of lines.entries()) {
136
+ const prefixed = index === 0 ? `${pc.cyan(marker)} ${line}` : line;
137
+ payload += `${clampToWidth(prefixed, columns)}\n`;
138
+ rows += 1;
139
+ }
140
+ }
141
+ const progress = this.progressLine();
142
+ if (progress !== undefined)
143
+ payload += `${clampToWidth(progress, columns)}\n`;
144
+ this.statusLines = rows + (progress === undefined ? 0 : 1);
145
+ if (payload !== '')
146
+ this.raw(payload);
147
+ this.syncTimer(visible);
148
+ }
149
+ /** One bounded counter line: completed, executing, and waiting entries. */
150
+ progressLine() {
151
+ if (this.total === 0)
152
+ return undefined;
153
+ const waiting = Math.max(0, this.total - this.done - this.running.size);
154
+ const parts = [
155
+ `${this.done}/${this.total} done`,
156
+ `${this.running.size} running`,
157
+ `${waiting} waiting`,
158
+ ];
159
+ return pc.dim(` ${parts.join(' · ')}`);
160
+ }
161
+ }
162
+ //# sourceMappingURL=live-status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"live-status.js","sourceRoot":"","sources":["../../src/report/live-status.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ /** Atomic report persistence (spec 13-reporting.md). */
2
+ /** Atomically writes a wire JSON document with two-space indent and trailing newline. */
3
+ export declare function writeJsonReport(filePath: string, document: unknown): Promise<void>;
4
+ //# sourceMappingURL=write.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../src/report/write.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAOxD,yFAAyF;AACzF,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAKxF"}
@@ -0,0 +1,13 @@
1
+ /** Atomic report persistence (spec 13-reporting.md). */
2
+ import { randomBytes } from 'node:crypto';
3
+ import { mkdirSync } from 'node:fs';
4
+ import { rename, writeFile } from 'node:fs/promises';
5
+ import path from 'node:path';
6
+ /** Atomically writes a wire JSON document with two-space indent and trailing newline. */
7
+ export async function writeJsonReport(filePath, document) {
8
+ mkdirSync(path.dirname(filePath), { recursive: true });
9
+ const temporary = `${filePath}.tmp-${randomBytes(4).toString('hex')}`;
10
+ await writeFile(temporary, `${JSON.stringify(document, null, 2)}\n`, 'utf8');
11
+ await rename(temporary, filePath);
12
+ }
13
+ //# sourceMappingURL=write.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.js","sourceRoot":"","sources":["../../src/report/write.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAExD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,yFAAyF;AACzF,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,QAAgB,EAAE,QAAiB;IACvE,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,MAAM,SAAS,GAAG,GAAG,QAAQ,QAAQ,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;IACtE,MAAM,SAAS,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC7E,MAAM,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AACpC,CAAC"}
@@ -0,0 +1,18 @@
1
+ /** Structured app process management (spec 05-config.md). */
2
+ import type { CommandConfig } from '../types.ts';
3
+ export declare class AppProcess {
4
+ private readonly command;
5
+ private readonly projectRoot;
6
+ private readonly readyUrl;
7
+ private child;
8
+ constructor(command: CommandConfig, projectRoot: string, readyUrl: string);
9
+ /**
10
+ * Spawns the process group and waits for the ready URL. An aborted `signal`
11
+ * ends the wait early and takes the process down with it; the caller reads
12
+ * the signal to learn the run was interrupted.
13
+ */
14
+ start(signal?: AbortSignal): Promise<void>;
15
+ /** Gracefully terminates the whole process group, then force-kills. */
16
+ stop(): Promise<void>;
17
+ }
18
+ //# sourceMappingURL=app-process.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-process.d.ts","sourceRoot":"","sources":["../../src/run/app-process.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAM7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAUjD,qBAAa,UAAU;IAInB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAL3B,OAAO,CAAC,KAAK,CAA6B;IAE1C,YACmB,OAAO,EAAE,aAAa,EACtB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAC/B;IAEJ;;;;OAIG;IACG,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA4D/C;IAED,uEAAuE;IACjE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAyB1B;CACF"}
@@ -0,0 +1,112 @@
1
+ /** Structured app process management (spec 05-config.md). */
2
+ import { spawn } from 'node:child_process';
3
+ import path from 'node:path';
4
+ import { InfrastructureError } from '../internal/errors.js';
5
+ import { sleep } from '../internal/time.js';
6
+ const INHERITED_ENV = ['PATH', 'HOME', 'TMPDIR', 'TMP', 'TEMP', 'SystemRoot', 'COMSPEC'];
7
+ /** Readiness polling starts fast and backs off; a booting server answers late, not on a schedule. */
8
+ const READY_POLL_MIN_MS = 25;
9
+ const READY_POLL_MAX_MS = 250;
10
+ /** One readiness probe never outlives this, so a half-open server cannot stall the deadline check. */
11
+ const READY_PROBE_TIMEOUT_MS = 2_000;
12
+ export class AppProcess {
13
+ command;
14
+ projectRoot;
15
+ readyUrl;
16
+ child = null;
17
+ constructor(command, projectRoot, readyUrl) {
18
+ this.command = command;
19
+ this.projectRoot = projectRoot;
20
+ this.readyUrl = readyUrl;
21
+ }
22
+ /**
23
+ * Spawns the process group and waits for the ready URL. An aborted `signal`
24
+ * ends the wait early and takes the process down with it; the caller reads
25
+ * the signal to learn the run was interrupted.
26
+ */
27
+ async start(signal) {
28
+ const env = {};
29
+ for (const key of INHERITED_ENV) {
30
+ const value = process.env[key];
31
+ if (value !== undefined)
32
+ env[key] = value;
33
+ }
34
+ Object.assign(env, this.command.env ?? {});
35
+ this.child = spawn(this.command.executable, [...(this.command.args ?? [])], {
36
+ cwd: path.resolve(this.projectRoot, this.command.cwd ?? '.'),
37
+ env,
38
+ detached: process.platform !== 'win32',
39
+ stdio: 'ignore',
40
+ });
41
+ let spawnError;
42
+ this.child.on('error', (error) => {
43
+ spawnError = error;
44
+ });
45
+ const startupTimeout = this.command.startupTimeout ?? 60_000;
46
+ const deadline = Date.now() + startupTimeout;
47
+ let pollMs = READY_POLL_MIN_MS;
48
+ for (;;) {
49
+ if (signal?.aborted === true) {
50
+ await this.stop();
51
+ return;
52
+ }
53
+ if (spawnError !== undefined) {
54
+ throw new InfrastructureError('APP_UNREACHABLE', `app command failed to start: ${spawnError.message}`, { cause: spawnError });
55
+ }
56
+ if (this.child.exitCode !== null) {
57
+ throw new InfrastructureError('APP_UNREACHABLE', `app command exited with code ${this.child.exitCode} before becoming ready`);
58
+ }
59
+ const remaining = deadline - Date.now();
60
+ if (remaining <= 0) {
61
+ await this.stop();
62
+ throw new InfrastructureError('APP_UNREACHABLE', `app was not reachable at ${this.readyUrl} within ${startupTimeout} ms`);
63
+ }
64
+ try {
65
+ const response = await fetch(this.readyUrl, {
66
+ redirect: 'manual',
67
+ signal: AbortSignal.timeout(Math.min(READY_PROBE_TIMEOUT_MS, remaining)),
68
+ });
69
+ if (response.status >= 200 && response.status <= 499)
70
+ return;
71
+ }
72
+ catch {
73
+ // not ready yet
74
+ }
75
+ await sleep(Math.min(pollMs, Math.max(0, deadline - Date.now())), signal).catch(() => undefined);
76
+ pollMs = Math.min(pollMs * 2, READY_POLL_MAX_MS);
77
+ }
78
+ }
79
+ /** Gracefully terminates the whole process group, then force-kills. */
80
+ async stop() {
81
+ const child = this.child;
82
+ this.child = null;
83
+ if (child === null || child.exitCode !== null)
84
+ return;
85
+ const shutdownTimeout = this.command.shutdownTimeout ?? 10_000;
86
+ const pid = child.pid;
87
+ const signalGroup = (signal) => {
88
+ if (pid === undefined)
89
+ return;
90
+ try {
91
+ if (process.platform !== 'win32')
92
+ process.kill(-pid, signal);
93
+ else
94
+ child.kill(signal);
95
+ }
96
+ catch {
97
+ // process already gone
98
+ }
99
+ };
100
+ signalGroup('SIGTERM');
101
+ const exited = new Promise((resolve) => {
102
+ child.once('exit', () => resolve());
103
+ });
104
+ const timer = sleep(shutdownTimeout).then(() => 'timeout');
105
+ const winner = await Promise.race([exited.then(() => 'exited'), timer]);
106
+ if (winner === 'timeout') {
107
+ signalGroup('SIGKILL');
108
+ await exited.catch(() => undefined);
109
+ }
110
+ }
111
+ }
112
+ //# sourceMappingURL=app-process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-process.js","sourceRoot":"","sources":["../../src/run/app-process.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /** Attempt-scoped artifact directory and report registration. */
2
+ import type { ArtifactStore } from '../types.ts';
3
+ import type { ArtifactSink } from './fixtures.ts';
4
+ import type { ArtifactRecord } from './records.ts';
5
+ export interface AttemptArtifacts {
6
+ /** Absolute attempt artifact directory, created eagerly. */
7
+ readonly dir: string;
8
+ /** Report-order artifact records; the sink appends to this array. */
9
+ readonly records: ArtifactRecord[];
10
+ readonly sink: ArtifactSink;
11
+ /**
12
+ * Resolves once every registered file has been measured and hashed.
13
+ * Registration itself is synchronous and cheap; the size and digest of a
14
+ * file (a trace zip can be tens of megabytes) are filled in off the event
15
+ * loop, so awaiting this before the record is read is what makes them
16
+ * complete.
17
+ */
18
+ settle(): Promise<void>;
19
+ }
20
+ /**
21
+ * Creates the artifact directory for one attempt and a sink that registers
22
+ * produced files under report-relative paths, measuring and hashing them
23
+ * asynchronously. With a `store`, each complete artifact is also handed to it
24
+ * right then — as produced, not at run end — and the store's reference lands
25
+ * on the record as `ref`.
26
+ */
27
+ export declare function createAttemptArtifacts(options: {
28
+ artifactsRoot: string;
29
+ /** Report path segments, e.g. [targetName, sanitizedTestId, attempt-N]. */
30
+ segments: readonly string[];
31
+ attemptId: string;
32
+ /** When provided, artifacts are attributed to the currently running step. */
33
+ currentStepId?: () => string | undefined;
34
+ /** Host store every artifact is handed to once complete; undefined keeps files local only. */
35
+ store?: ArtifactStore;
36
+ /**
37
+ * Report identity handed to the store with each artifact. `attemptId` is
38
+ * the attempt the REPORT files the artifact under; a serial member's
39
+ * artifacts land on the group attempt's record, so its store identity is the
40
+ * group's id while its own ids (`<attemptId>:artifact:N`) still mint from
41
+ * the member. Defaults to `attemptId`.
42
+ */
43
+ identity?: {
44
+ readonly runId: string;
45
+ readonly testId: string;
46
+ readonly attemptId?: string;
47
+ };
48
+ }): AttemptArtifacts;
49
+ /** Restricts a report path segment to a safe filename alphabet. */
50
+ export declare function sanitizePathSegment(value: string): string;
51
+ //# sourceMappingURL=artifacts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../../src/run/artifacts.ts"],"names":[],"mappings":"AAAA,iEAAiE;AAOjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,WAAW,gBAAgB;IAC/B,4DAA4D;IAC5D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC;IACnC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B;;;;;;OAMG;IACH,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE;IAC9C,aAAa,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IACzC,8FAA8F;IAC9F,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7F,GAAG,gBAAgB,CAwEnB;AAcD,mEAAmE;AACnE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEzD"}
@@ -0,0 +1,114 @@
1
+ /** Attempt-scoped artifact directory and report registration. */
2
+ import { createHash } from 'node:crypto';
3
+ import { createReadStream, mkdirSync } from 'node:fs';
4
+ import { readFile, stat } from 'node:fs/promises';
5
+ import path from 'node:path';
6
+ import { pipeline } from 'node:stream/promises';
7
+ /**
8
+ * Creates the artifact directory for one attempt and a sink that registers
9
+ * produced files under report-relative paths, measuring and hashing them
10
+ * asynchronously. With a `store`, each complete artifact is also handed to it
11
+ * right then — as produced, not at run end — and the store's reference lands
12
+ * on the record as `ref`.
13
+ */
14
+ export function createAttemptArtifacts(options) {
15
+ const dir = path.join(options.artifactsRoot, ...options.segments);
16
+ mkdirSync(dir, { recursive: true });
17
+ const records = [];
18
+ const pending = [];
19
+ const sink = {
20
+ dir,
21
+ register: (kind, relativePath) => {
22
+ const id = `${options.attemptId}:artifact:${records.length}`;
23
+ const absolute = path.join(dir, relativePath);
24
+ const stepId = options.currentStepId?.();
25
+ const record = {
26
+ id,
27
+ kind,
28
+ mediaType: mediaTypeFor(relativePath),
29
+ redaction: 'complete',
30
+ producer: stepId === undefined ? { kind: 'attempt' } : { kind: 'step', stepId },
31
+ };
32
+ records.push(record);
33
+ const reportPath = path.posix.join(...options.segments, relativePath);
34
+ pending.push((async () => {
35
+ // Without a store the file is streamed for its size and digest only;
36
+ // with one it is read whole, since the store needs the bytes anyway,
37
+ // and hashed from that same buffer. A file that never appeared is
38
+ // recorded without size or digest either way.
39
+ if (options.store === undefined) {
40
+ const measured = await measure(absolute);
41
+ if (measured === undefined)
42
+ return;
43
+ record.path = reportPath;
44
+ record.size = measured.size;
45
+ record.sha256 = measured.sha256;
46
+ return;
47
+ }
48
+ const bytes = await readFile(absolute).catch(() => undefined);
49
+ if (bytes === undefined)
50
+ return;
51
+ record.path = reportPath;
52
+ record.size = bytes.byteLength;
53
+ record.sha256 = createHash('sha256').update(bytes).digest('hex');
54
+ // The store's failure is its own: the record keeps its local path and
55
+ // simply carries no ref, and the run is never failed by evidence
56
+ // that did not upload.
57
+ try {
58
+ const { ref } = await options.store.put({
59
+ kind,
60
+ mediaType: record.mediaType,
61
+ bytes,
62
+ size: record.size,
63
+ sha256: record.sha256,
64
+ path: reportPath,
65
+ runId: options.identity?.runId ?? '',
66
+ testId: options.identity?.testId ?? '',
67
+ attemptId: options.identity?.attemptId ?? options.attemptId,
68
+ ...(stepId === undefined ? {} : { stepId }),
69
+ });
70
+ if (typeof ref === 'string' && ref !== '')
71
+ record.ref = ref;
72
+ }
73
+ catch {
74
+ // best-effort by contract
75
+ }
76
+ })());
77
+ return id;
78
+ },
79
+ };
80
+ const settle = async () => {
81
+ // Registrations may land while earlier ones are still hashing.
82
+ while (pending.length > 0)
83
+ await Promise.all(pending.splice(0));
84
+ };
85
+ return { dir, records, sink, settle };
86
+ }
87
+ /** Size and SHA-256 of one file, streamed; undefined when it cannot be read. */
88
+ async function measure(absolute) {
89
+ try {
90
+ const { size } = await stat(absolute);
91
+ const hash = createHash('sha256');
92
+ await pipeline(createReadStream(absolute), hash);
93
+ return { size, sha256: hash.digest('hex') };
94
+ }
95
+ catch {
96
+ return undefined;
97
+ }
98
+ }
99
+ /** Restricts a report path segment to a safe filename alphabet. */
100
+ export function sanitizePathSegment(value) {
101
+ return value.replaceAll(/[^A-Za-z0-9._-]/g, '_').slice(0, 120);
102
+ }
103
+ function mediaTypeFor(relativePath) {
104
+ if (relativePath.endsWith('.png'))
105
+ return 'image/png';
106
+ if (relativePath.endsWith('.zip'))
107
+ return 'application/zip';
108
+ if (relativePath.endsWith('.webm'))
109
+ return 'video/webm';
110
+ if (relativePath.endsWith('.txt'))
111
+ return 'text/plain';
112
+ return 'application/octet-stream';
113
+ }
114
+ //# sourceMappingURL=artifacts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../../src/run/artifacts.ts"],"names":[],"mappings":"AAAA,iEAAiE;AAEjE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAqBhD;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAiBtC;IACC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAClE,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpC,MAAM,OAAO,GAAqB,EAAE,CAAC;IACrC,MAAM,OAAO,GAAoB,EAAE,CAAC;IAEpC,MAAM,IAAI,GAAiB;QACzB,GAAG;QACH,QAAQ,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE;YAC/B,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,SAAS,aAAa,OAAO,CAAC,MAAM,EAAE,CAAC;YAC7D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;YAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC;YACzC,MAAM,MAAM,GAAmB;gBAC7B,EAAE;gBACF,IAAI;gBACJ,SAAS,EAAE,YAAY,CAAC,YAAY,CAAC;gBACrC,SAAS,EAAE,UAAU;gBACrB,QAAQ,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE;aAChF,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACrB,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;YACtE,OAAO,CAAC,IAAI,CACV,CAAC,KAAK,IAAI,EAAE;gBACV,qEAAqE;gBACrE,qEAAqE;gBACrE,kEAAkE;gBAClE,8CAA8C;gBAC9C,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;oBAChC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;oBACzC,IAAI,QAAQ,KAAK,SAAS;wBAAE,OAAO;oBACnC,MAAM,CAAC,IAAI,GAAG,UAAU,CAAC;oBACzB,MAAM,CAAC,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;oBAC5B,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;oBAChC,OAAO;gBACT,CAAC;gBACD,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;gBAC9D,IAAI,KAAK,KAAK,SAAS;oBAAE,OAAO;gBAChC,MAAM,CAAC,IAAI,GAAG,UAAU,CAAC;gBACzB,MAAM,CAAC,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC;gBAC/B,MAAM,CAAC,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACjE,sEAAsE;gBACtE,iEAAiE;gBACjE,uBAAuB;gBACvB,IAAI,CAAC;oBACH,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC;wBACtC,IAAI;wBACJ,SAAS,EAAE,MAAM,CAAC,SAAS;wBAC3B,KAAK;wBACL,IAAI,EAAE,MAAM,CAAC,IAAI;wBACjB,MAAM,EAAE,MAAM,CAAC,MAAM;wBACrB,IAAI,EAAE,UAAU;wBAChB,KAAK,EAAE,OAAO,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;wBACpC,MAAM,EAAE,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE;wBACtC,SAAS,EAAE,OAAO,CAAC,QAAQ,EAAE,SAAS,IAAI,OAAO,CAAC,SAAS;wBAC3D,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;qBAC5C,CAAC,CAAC;oBACH,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,EAAE;wBAAE,MAAM,CAAC,GAAG,GAAG,GAAG,CAAC;gBAC9D,CAAC;gBAAC,MAAM,CAAC;oBACP,0BAA0B;gBAC5B,CAAC;YACH,CAAC,CAAC,EAAE,CACL,CAAC;YACF,OAAO,EAAE,CAAC;QACZ,CAAC;KACF,CAAC;IAEF,MAAM,MAAM,GAAG,KAAK,IAAmB,EAAE;QACvC,+DAA+D;QAC/D,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAClE,CAAC,CAAC;IAEF,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AACxC,CAAC;AAED,gFAAgF;AAChF,KAAK,UAAU,OAAO,CAAC,QAAgB;IACrC,IAAI,CAAC;QACH,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;QACtC,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QAClC,MAAM,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC;QACjD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,OAAO,KAAK,CAAC,UAAU,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACjE,CAAC;AAED,SAAS,YAAY,CAAC,YAAoB;IACxC,IAAI,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,WAAW,CAAC;IACtD,IAAI,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,iBAAiB,CAAC;IAC5D,IAAI,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,YAAY,CAAC;IACxD,IAAI,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,YAAY,CAAC;IACvD,OAAO,0BAA0B,CAAC;AACpC,CAAC"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Structured run events (`@e2edev/e2e/run`): the run's single event spine.
3
+ *
4
+ * Every event is plain JSON data — the same records the report persists and
5
+ * the worker IPC already carries, so a host can stream them over any wire
6
+ * without touching live handles. The list reporter consumes exactly this
7
+ * stream (`ListReporter.handle`, report/list.ts), so the CLI's rendering and a
8
+ * host's dashboard can never drift: there is one dispatch, not two. The
9
+ * report stays the canonical record; the stream exists so consumers can
10
+ * render progress while the run is still going.
11
+ *
12
+ * The emitter is the single writer: it stamps `seq` and `at`, so ordering
13
+ * survives any transport that preserves per-connection order. A sink that
14
+ * throws is quarantined for the rest of the run; a sink whose returned
15
+ * promise rejects is quarantined once the rejection settles, so events
16
+ * emitted before then may still reach it. Either way a broken consumer can
17
+ * never fail the run.
18
+ */
19
+ import type { SerializedError } from '../internal/errors.ts';
20
+ import type { ResultRecord, SerialGroupRecord } from './records.ts';
21
+ import type { StepProgress } from './steps.ts';
22
+ import { type WireResultRecord } from './worker/protocol.ts';
23
+ /** The closed set of process exit codes a run can end with. */
24
+ export type RunExitCode = 0 | 1 | 2 | 3 | 4 | 130;
25
+ /**
26
+ * `ResultRecord` minus the live resolved target (the wire encoding the worker
27
+ * IPC already uses), plus the target's stable identity so a consumer can
28
+ * attribute the result without a side lookup.
29
+ */
30
+ export interface RunEventResult extends WireResultRecord {
31
+ readonly target: {
32
+ readonly name: string;
33
+ readonly platform: string;
34
+ };
35
+ }
36
+ /** One fact about the run; the emitter wraps it in the envelope. */
37
+ export type RunEventFact = {
38
+ readonly type: 'run-started';
39
+ readonly runId: string;
40
+ readonly projectId: string;
41
+ readonly projectRoot: string;
42
+ readonly ci: boolean;
43
+ readonly targets: readonly string[];
44
+ } | {
45
+ readonly type: 'plan';
46
+ readonly total: number;
47
+ } | {
48
+ readonly type: 'test-started';
49
+ readonly testId: string;
50
+ readonly title: string;
51
+ readonly target: string;
52
+ } | {
53
+ readonly type: 'step';
54
+ readonly testId: string;
55
+ readonly target: string;
56
+ readonly progress: StepProgress;
57
+ } | {
58
+ readonly type: 'test-finished';
59
+ readonly result: RunEventResult;
60
+ } | {
61
+ readonly type: 'serial-group';
62
+ readonly group: SerialGroupRecord;
63
+ } | {
64
+ readonly type: 'run-error';
65
+ readonly error: SerializedError;
66
+ } | {
67
+ readonly type: 'run-finished';
68
+ readonly status: 'passed' | 'failed' | 'error' | 'interrupted';
69
+ readonly exitCode: RunExitCode;
70
+ readonly reportPath?: string;
71
+ /** Where `--ai-trace` wrote the run's model calls, once the file exists. */
72
+ readonly aiTracePath?: string;
73
+ };
74
+ /** Envelope stamped by the emitter: monotonic order and wall-clock time. */
75
+ export interface RunEventHeader {
76
+ readonly seq: number;
77
+ readonly at: string;
78
+ }
79
+ export type RunEvent = RunEventHeader & RunEventFact;
80
+ /** The fact of one event type, for a consumer that handles types one at a time. */
81
+ export type RunEventOf<Type extends RunEventFact['type']> = Extract<RunEventFact, {
82
+ type: Type;
83
+ }>;
84
+ /**
85
+ * An event consumer. Must not block; a throw — or, when it returns a
86
+ * promise, a rejection — quarantines the sink. An async sink observes events
87
+ * in emit order but cannot delay them; ordering between its own pending
88
+ * handlers is its own responsibility.
89
+ */
90
+ export type RunEventSink = (event: RunEvent) => void | Promise<void>;
91
+ /** Strips the live target from a result, keeping its stable identity. */
92
+ export declare function toEventResult(record: ResultRecord): RunEventResult;
93
+ /**
94
+ * Builds the run's single event writer over every configured sink. Sinks are
95
+ * quarantined independently, so a broken host callback cannot silence the
96
+ * list reporter or vice versa. Returns a no-op when no sink is configured,
97
+ * so call sites never branch.
98
+ */
99
+ export declare function createRunEventEmitter(sinks: readonly (RunEventSink | undefined)[]): (fact: RunEventFact) => void;
100
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/run/events.ts"],"names":[],"mappings":"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"}