flakelab 0.1.0 → 0.1.1

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 (316) hide show
  1. package/README.md +224 -37
  2. package/dist/analysis/blob-report.d.ts +15 -0
  3. package/dist/analysis/blob-report.js +137 -0
  4. package/dist/analysis/blob-report.js.map +1 -0
  5. package/dist/analysis/cli.d.ts +7 -0
  6. package/dist/analysis/cli.js +28 -0
  7. package/dist/analysis/cli.js.map +1 -0
  8. package/dist/analysis/schema.d.ts +175 -0
  9. package/dist/analysis/schema.js +34 -0
  10. package/dist/analysis/schema.js.map +1 -0
  11. package/dist/analysis/zip-archive.d.ts +6 -0
  12. package/dist/analysis/zip-archive.js +193 -0
  13. package/dist/analysis/zip-archive.js.map +1 -0
  14. package/dist/artifacts/paths.d.ts +3 -0
  15. package/dist/artifacts/paths.js +34 -0
  16. package/dist/artifacts/paths.js.map +1 -0
  17. package/dist/artifacts/retention.d.ts +1 -0
  18. package/dist/artifacts/retention.js +80 -0
  19. package/dist/artifacts/retention.js.map +1 -0
  20. package/dist/bisect/engine.js +0 -1
  21. package/dist/bisect/engine.js.map +1 -1
  22. package/dist/bisect/schema.d.ts +5 -6
  23. package/dist/bisect/schema.js +1 -2
  24. package/dist/bisect/schema.js.map +1 -1
  25. package/dist/bisect/solari-evaluator.js +9 -1
  26. package/dist/bisect/solari-evaluator.js.map +1 -1
  27. package/dist/ci/job-summary.d.ts +2 -0
  28. package/dist/ci/job-summary.js +120 -2
  29. package/dist/ci/job-summary.js.map +1 -1
  30. package/dist/ci/select-command.js +1 -1
  31. package/dist/ci/select-command.js.map +1 -1
  32. package/dist/ci/summary-command.js +12 -7
  33. package/dist/ci/summary-command.js.map +1 -1
  34. package/dist/cli-arguments.d.ts +44 -0
  35. package/dist/cli-arguments.js +356 -0
  36. package/dist/cli-arguments.js.map +1 -0
  37. package/dist/cli-command-help.d.ts +11 -0
  38. package/dist/cli-command-help.js +211 -0
  39. package/dist/cli-command-help.js.map +1 -0
  40. package/dist/cli-help.d.ts +5 -2
  41. package/dist/cli-help.js +74 -45
  42. package/dist/cli-help.js.map +1 -1
  43. package/dist/cli-option-definitions.d.ts +202 -0
  44. package/dist/cli-option-definitions.js +59 -0
  45. package/dist/cli-option-definitions.js.map +1 -0
  46. package/dist/cli.js +100 -100
  47. package/dist/cli.js.map +1 -1
  48. package/dist/commands/analyze-summary.d.ts +5 -0
  49. package/dist/commands/analyze-summary.js +89 -0
  50. package/dist/commands/analyze-summary.js.map +1 -0
  51. package/dist/commands/analyze.d.ts +6 -0
  52. package/dist/commands/analyze.js +128 -0
  53. package/dist/commands/analyze.js.map +1 -0
  54. package/dist/commands/bisect.d.ts +2 -2
  55. package/dist/commands/bisect.js +32 -3
  56. package/dist/commands/bisect.js.map +1 -1
  57. package/dist/commands/diagnose.d.ts +3 -2
  58. package/dist/commands/diagnose.js +331 -23
  59. package/dist/commands/diagnose.js.map +1 -1
  60. package/dist/commands/discover.d.ts +18 -2
  61. package/dist/commands/discover.js +215 -25
  62. package/dist/commands/discover.js.map +1 -1
  63. package/dist/commands/doctor.d.ts +2 -0
  64. package/dist/commands/doctor.js +66 -25
  65. package/dist/commands/doctor.js.map +1 -1
  66. package/dist/commands/investigate.d.ts +3 -2
  67. package/dist/commands/investigate.js +22 -3
  68. package/dist/commands/investigate.js.map +1 -1
  69. package/dist/commands/options.d.ts +123 -16
  70. package/dist/commands/options.js +24 -2
  71. package/dist/commands/options.js.map +1 -1
  72. package/dist/commands/prove.d.ts +2 -2
  73. package/dist/commands/prove.js +36 -1
  74. package/dist/commands/prove.js.map +1 -1
  75. package/dist/commands/repair.d.ts +8 -2
  76. package/dist/commands/repair.js +32 -9
  77. package/dist/commands/repair.js.map +1 -1
  78. package/dist/commands/replay.d.ts +2 -2
  79. package/dist/commands/replay.js +15 -2
  80. package/dist/commands/replay.js.map +1 -1
  81. package/dist/commands/report.d.ts +2 -2
  82. package/dist/commands/report.js +35 -10
  83. package/dist/commands/report.js.map +1 -1
  84. package/dist/commands/scan-summary.d.ts +7 -0
  85. package/dist/commands/scan-summary.js +175 -0
  86. package/dist/commands/scan-summary.js.map +1 -0
  87. package/dist/commands/scan.d.ts +9 -12
  88. package/dist/commands/scan.js +66 -62
  89. package/dist/commands/scan.js.map +1 -1
  90. package/dist/core/plan.js +1 -1
  91. package/dist/core/plan.js.map +1 -1
  92. package/dist/diagnosis/checkpoint.d.ts +6 -0
  93. package/dist/diagnosis/checkpoint.js +46 -0
  94. package/dist/diagnosis/checkpoint.js.map +1 -0
  95. package/dist/diagnosis/cli.d.ts +7 -0
  96. package/dist/diagnosis/cli.js +84 -0
  97. package/dist/diagnosis/cli.js.map +1 -0
  98. package/dist/diagnosis/recommendation.d.ts +13 -0
  99. package/dist/diagnosis/recommendation.js +95 -0
  100. package/dist/diagnosis/recommendation.js.map +1 -0
  101. package/dist/diagnosis/run-state.d.ts +52 -0
  102. package/dist/diagnosis/run-state.js +173 -0
  103. package/dist/diagnosis/run-state.js.map +1 -0
  104. package/dist/diagnosis/schema.d.ts +207 -0
  105. package/dist/diagnosis/schema.js +108 -0
  106. package/dist/diagnosis/schema.js.map +1 -0
  107. package/dist/diagnosis/summary.d.ts +3 -0
  108. package/dist/diagnosis/summary.js +115 -0
  109. package/dist/diagnosis/summary.js.map +1 -0
  110. package/dist/discovery/auth-cookie.d.ts +19 -0
  111. package/dist/discovery/auth-cookie.js +26 -0
  112. package/dist/discovery/auth-cookie.js.map +1 -0
  113. package/dist/discovery/evaluate.d.ts +35 -2
  114. package/dist/discovery/evaluate.js +136 -30
  115. package/dist/discovery/evaluate.js.map +1 -1
  116. package/dist/discovery/event-loop.d.ts +25 -0
  117. package/dist/discovery/event-loop.js +84 -0
  118. package/dist/discovery/event-loop.js.map +1 -0
  119. package/dist/discovery/minimize.d.ts +61 -1
  120. package/dist/discovery/minimize.js +179 -7
  121. package/dist/discovery/minimize.js.map +1 -1
  122. package/dist/discovery/resource-loading.d.ts +25 -0
  123. package/dist/discovery/resource-loading.js +84 -0
  124. package/dist/discovery/resource-loading.js.map +1 -0
  125. package/dist/discovery/runner-environment.d.ts +26 -0
  126. package/dist/discovery/runner-environment.js +68 -0
  127. package/dist/discovery/runner-environment.js.map +1 -0
  128. package/dist/discovery/startup-event.d.ts +25 -0
  129. package/dist/discovery/startup-event.js +84 -0
  130. package/dist/discovery/startup-event.js.map +1 -0
  131. package/dist/discovery/storage-state.d.ts +26 -0
  132. package/dist/discovery/storage-state.js +86 -0
  133. package/dist/discovery/storage-state.js.map +1 -0
  134. package/dist/discovery/temporal-environment.d.ts +31 -0
  135. package/dist/discovery/temporal-environment.js +43 -0
  136. package/dist/discovery/temporal-environment.js.map +1 -0
  137. package/dist/discovery/visual-environment.d.ts +28 -0
  138. package/dist/discovery/visual-environment.js +42 -0
  139. package/dist/discovery/visual-environment.js.map +1 -0
  140. package/dist/domain/schema.d.ts +840 -20
  141. package/dist/domain/schema.js +216 -6
  142. package/dist/domain/schema.js.map +1 -1
  143. package/dist/faults/browser-context.d.ts +16 -0
  144. package/dist/faults/browser-context.js +60 -0
  145. package/dist/faults/browser-context.js.map +1 -0
  146. package/dist/faults/cookie-header.d.ts +5 -0
  147. package/dist/faults/cookie-header.js +25 -0
  148. package/dist/faults/cookie-header.js.map +1 -0
  149. package/dist/faults/document-bootstrap.d.ts +5 -0
  150. package/dist/faults/document-bootstrap.js +141 -0
  151. package/dist/faults/document-bootstrap.js.map +1 -0
  152. package/dist/faults/install.d.ts +1 -1
  153. package/dist/faults/install.js +199 -6
  154. package/dist/faults/install.js.map +1 -1
  155. package/dist/faults/pattern.d.ts +1 -0
  156. package/dist/faults/pattern.js +24 -0
  157. package/dist/faults/pattern.js.map +1 -0
  158. package/dist/investigator/agent.js +39 -17
  159. package/dist/investigator/agent.js.map +1 -1
  160. package/dist/investigator/ledger.d.ts +1 -1
  161. package/dist/investigator/ledger.js +2 -2
  162. package/dist/investigator/ledger.js.map +1 -1
  163. package/dist/investigator/safe-source.d.ts +1 -0
  164. package/dist/investigator/safe-source.js +12 -3
  165. package/dist/investigator/safe-source.js.map +1 -1
  166. package/dist/investigator/schema.d.ts +293 -2
  167. package/dist/investigator/schema.js +100 -2
  168. package/dist/investigator/schema.js.map +1 -1
  169. package/dist/proof/cli.d.ts +7 -0
  170. package/dist/proof/cli.js +108 -0
  171. package/dist/proof/cli.js.map +1 -0
  172. package/dist/repair/generator.d.ts +12 -1
  173. package/dist/repair/generator.js +30 -6
  174. package/dist/repair/generator.js.map +1 -1
  175. package/dist/repair/remote-proof-runner.js +6 -10
  176. package/dist/repair/remote-proof-runner.js.map +1 -1
  177. package/dist/repair/schema.d.ts +77 -2
  178. package/dist/repair/schema.js +23 -2
  179. package/dist/repair/schema.js.map +1 -1
  180. package/dist/repair/solari-validator.d.ts +2 -0
  181. package/dist/repair/solari-validator.js +4 -6
  182. package/dist/repair/solari-validator.js.map +1 -1
  183. package/dist/repair/summary.d.ts +10 -0
  184. package/dist/repair/summary.js +58 -0
  185. package/dist/repair/summary.js.map +1 -0
  186. package/dist/repair/validator.d.ts +1 -0
  187. package/dist/repair/validator.js +53 -12
  188. package/dist/repair/validator.js.map +1 -1
  189. package/dist/report/bundle.js +16 -37
  190. package/dist/report/bundle.js.map +1 -1
  191. package/dist/report/model.js +131 -12
  192. package/dist/report/model.js.map +1 -1
  193. package/dist/report/open.d.ts +9 -0
  194. package/dist/report/open.js +23 -0
  195. package/dist/report/open.js.map +1 -1
  196. package/dist/report/report-client.css +2 -0
  197. package/dist/report/report-client.js +58 -0
  198. package/dist/report/schema.d.ts +115 -5
  199. package/dist/report/schema.js +24 -5
  200. package/dist/report/schema.js.map +1 -1
  201. package/dist/reproducer/schema.d.ts +89 -4
  202. package/dist/reproducer/schema.js +3 -4
  203. package/dist/reproducer/schema.js.map +1 -1
  204. package/dist/runner/execution-fault.d.ts +10 -0
  205. package/dist/runner/execution-fault.js +24 -0
  206. package/dist/runner/execution-fault.js.map +1 -0
  207. package/dist/runner/fault-proxy.d.ts +7 -0
  208. package/dist/runner/fault-proxy.js +366 -0
  209. package/dist/runner/fault-proxy.js.map +1 -0
  210. package/dist/runner/local.js +2 -2
  211. package/dist/runner/local.js.map +1 -1
  212. package/dist/runner/native-scan.d.ts +25 -0
  213. package/dist/runner/native-scan.js +306 -0
  214. package/dist/runner/native-scan.js.map +1 -0
  215. package/dist/runner/playwright-executor.d.ts +3 -0
  216. package/dist/runner/playwright-executor.js +125 -66
  217. package/dist/runner/playwright-executor.js.map +1 -1
  218. package/dist/runner/process-tree.d.ts +7 -0
  219. package/dist/runner/process-tree.js +62 -0
  220. package/dist/runner/process-tree.js.map +1 -0
  221. package/dist/runner/project-bridge.d.ts +7 -0
  222. package/dist/runner/project-bridge.js +83 -0
  223. package/dist/runner/project-bridge.js.map +1 -0
  224. package/dist/runner/trial-reporter.d.ts +7 -0
  225. package/dist/runner/trial-reporter.js +47 -0
  226. package/dist/runner/trial-reporter.js.map +1 -0
  227. package/dist/scan/failure-clusters.d.ts +24 -0
  228. package/dist/scan/failure-clusters.js +74 -0
  229. package/dist/scan/failure-clusters.js.map +1 -0
  230. package/dist/scan/schema.d.ts +170 -0
  231. package/dist/scan/schema.js +71 -0
  232. package/dist/scan/schema.js.map +1 -0
  233. package/dist/solari/executor.d.ts +5 -1
  234. package/dist/solari/executor.js +25 -17
  235. package/dist/solari/executor.js.map +1 -1
  236. package/dist/solari/run-demo.d.ts +13 -1
  237. package/dist/solari/run-demo.js +35 -16
  238. package/dist/solari/run-demo.js.map +1 -1
  239. package/dist/solari/usage.d.ts +8 -2
  240. package/dist/solari/usage.js +14 -4
  241. package/dist/solari/usage.js.map +1 -1
  242. package/dist/ui/boundary.d.ts +17 -0
  243. package/dist/ui/boundary.js +18 -0
  244. package/dist/ui/boundary.js.map +1 -0
  245. package/dist/ui/console.d.ts +4 -0
  246. package/dist/ui/console.js +11 -0
  247. package/dist/ui/console.js.map +1 -0
  248. package/dist/ui/document.d.ts +40 -0
  249. package/dist/ui/document.js +120 -0
  250. package/dist/ui/document.js.map +1 -0
  251. package/dist/ui/format.d.ts +8 -0
  252. package/dist/ui/format.js +32 -0
  253. package/dist/ui/format.js.map +1 -0
  254. package/dist/ui/progress.d.ts +17 -1
  255. package/dist/ui/progress.js +51 -8
  256. package/dist/ui/progress.js.map +1 -1
  257. package/dist/ui/status.d.ts +7 -0
  258. package/dist/ui/status.js +36 -0
  259. package/dist/ui/status.js.map +1 -0
  260. package/dist/ui/style.d.ts +8 -0
  261. package/dist/ui/style.js +22 -0
  262. package/dist/ui/style.js.map +1 -0
  263. package/dist/ui/text.d.ts +17 -0
  264. package/dist/ui/text.js +123 -0
  265. package/dist/ui/text.js.map +1 -0
  266. package/dist/ui/theme.d.ts +23 -0
  267. package/dist/ui/theme.js +77 -0
  268. package/dist/ui/theme.js.map +1 -0
  269. package/dist/ui/trial-progress.d.ts +13 -0
  270. package/dist/ui/trial-progress.js +42 -0
  271. package/dist/ui/trial-progress.js.map +1 -0
  272. package/package.json +14 -19
  273. package/dist/faults/network-delay.d.ts +0 -5
  274. package/dist/faults/network-delay.js +0 -34
  275. package/dist/faults/network-delay.js.map +0 -1
  276. package/dist/faults/request-failure.d.ts +0 -5
  277. package/dist/faults/request-failure.js +0 -24
  278. package/dist/faults/request-failure.js.map +0 -1
  279. package/dist/playwright.d.ts +0 -3
  280. package/dist/playwright.js +0 -21
  281. package/dist/playwright.js.map +0 -1
  282. package/dist/report/browser-entry.d.ts +0 -2
  283. package/dist/report/browser-entry.js +0 -15
  284. package/dist/report/browser-entry.js.map +0 -1
  285. package/dist/report/components/artifacts.d.ts +0 -4
  286. package/dist/report/components/artifacts.js +0 -6
  287. package/dist/report/components/artifacts.js.map +0 -1
  288. package/dist/report/components/experiments.d.ts +0 -4
  289. package/dist/report/components/experiments.js +0 -7
  290. package/dist/report/components/experiments.js.map +0 -1
  291. package/dist/report/components/hypotheses.d.ts +0 -4
  292. package/dist/report/components/hypotheses.js +0 -26
  293. package/dist/report/components/hypotheses.js.map +0 -1
  294. package/dist/report/components/indicators.d.ts +0 -32
  295. package/dist/report/components/indicators.js +0 -42
  296. package/dist/report/components/indicators.js.map +0 -1
  297. package/dist/report/components/layout.d.ts +0 -29
  298. package/dist/report/components/layout.js +0 -20
  299. package/dist/report/components/layout.js.map +0 -1
  300. package/dist/report/components/proof.d.ts +0 -4
  301. package/dist/report/components/proof.js +0 -34
  302. package/dist/report/components/proof.js.map +0 -1
  303. package/dist/report/components/reproduction.d.ts +0 -4
  304. package/dist/report/components/reproduction.js +0 -19
  305. package/dist/report/components/reproduction.js.map +0 -1
  306. package/dist/report/components/summary.d.ts +0 -4
  307. package/dist/report/components/summary.js +0 -12
  308. package/dist/report/components/summary.js.map +0 -1
  309. package/dist/report/components/verdict.d.ts +0 -4
  310. package/dist/report/components/verdict.js +0 -27
  311. package/dist/report/components/verdict.js.map +0 -1
  312. package/dist/report/report-app.d.ts +0 -6
  313. package/dist/report/report-app.js +0 -16
  314. package/dist/report/report-app.js.map +0 -1
  315. package/dist/report/report-details.css +0 -182
  316. package/dist/report/report.css +0 -404
package/README.md CHANGED
@@ -18,34 +18,93 @@ failure is rare:
18
18
  ```bash
19
19
  npx flakelab@latest tests/checkout.spec.ts --runs 20
20
20
  npx flakelab@latest tests/checkout.spec.ts --runs 20 --verbose
21
+ npx flakelab@latest scan tests/checkout.spec.ts --runs 20 --json
21
22
  ```
22
23
 
23
- The default command repeatedly executes the selected target in isolated Playwright processes,
24
- distinguishes intermittent failures from consistent test failures, prints a concise result, and
25
- writes machine-readable evidence to `.flakelab/runs/scan.json`. It does not call Groq or Solari.
24
+ The default command starts one Playwright process and uses Playwright's native `--repeat-each`
25
+ and JSON reporter to classify every selected test as `no-failure-observed`, `mixed-outcomes`,
26
+ `failed-every-run`, `skipped`, or `errored`. These labels describe only the bounded sample; they
27
+ do not claim that a test is permanently stable. The human summary includes the 80% Wilson
28
+ confidence interval—or the upper bound when no failure was observed—and writes machine-readable evidence to
29
+ `.flakelab/runs/scan.json`. The artifact contains direct run totals, per-test identities,
30
+ confidence bounds, and bounded failure-signature clusters. Each cluster records its observed
31
+ rate, first and last attempt, a redacted representative reason, and up to three representative
32
+ trace, screenshot, or video paths. Multiple independent failure modes are called out explicitly.
33
+ The scan does not call
34
+ Groq or Solari. Use `--json` when another tool needs the scan artifact on stdout; progress remains
35
+ on stderr so the JSON stream stays parseable. A scan exits `0` when it observes no failure, `1`
36
+ when it observes mixed outcomes or failure in every run, and `2` when infrastructure makes the
37
+ result inconclusive.
38
+
39
+ For a large target, the terminal summary lists problematic tests first and collapses clean
40
+ observations into a count. The saved artifact, `--json`, and `--verbose` retain every test record.
41
+
42
+ Paths stored in scan evidence are relative to the scanned project. Outside-project paths are
43
+ reduced to a non-identifying placeholder and basename, and the JSON does not record its own output
44
+ location. Playwright directories containing only `.last-run.json` are removed after the scan.
45
+ Evidence-bearing directories receive a FlakeLab ownership marker and only the five newest are
46
+ retained; unmarked directories are always treated as user-owned and are never removed.
47
+
48
+ Every scan target is deliberate: `flakelab .` is the shortest whole-project form, while the
49
+ spelled-out `flakelab scan` command requires its target and never silently substitutes `.`.
50
+
51
+ ## Analyze an existing CI failure
52
+
53
+ Point FlakeLab at a Playwright blob-report archive or a directory of shard archives:
26
54
 
27
- For the complete causal workflow, use FlakeLab's Playwright fixture in tests that should accept
28
- controlled browser faults:
29
-
30
- ```ts
31
- import { expect, test } from "flakelab/playwright"
55
+ ```bash
56
+ npx flakelab@latest analyze ./blob-report
57
+ npx flakelab@latest analyze ./blob-report --baseline .flakelab/runs/analyze.json --json
32
58
  ```
33
59
 
60
+ Analysis does not rerun tests and needs no provider credentials. FlakeLab copies and validates
61
+ the archives inside an owned working directory, asks Playwright's supported report merger for
62
+ the canonical JSON report, and leaves the source reports untouched. Unsafe paths, links,
63
+ malformed ZIP structures, excessive expansion, and unbounded archive sets are rejected before
64
+ the merger runs.
65
+
66
+ The terminal summary ranks mixed outcomes, recurring failures, diagnostic attachments, and
67
+ failure modes not present in the optional baseline. The complete result is written to
68
+ `.flakelab/runs/analyze.json`; `--json` emits the same validated artifact on stdout. Referenced
69
+ traces, screenshots, and videos are retained with the owned analysis evidence under bounded
70
+ retention, while temporary archive copies and conversion files are removed.
71
+
72
+ The scan explicitly disables Playwright retries so a retry is never mistaken for an independent
73
+ statistical repetition. It uses one Playwright worker by default because arbitrary test suites may
74
+ share ports, databases, caches, or workspace files. Use `--concurrency` greater than `1` only when
75
+ those resources are safe for normal Playwright worker parallelism. Traces, screenshots, videos,
76
+ and ordinary test output stay in a unique directory beneath the selected evidence directory;
77
+ FlakeLab removes only its temporary JSON reporter file.
78
+
79
+ The native JSON reporter contract is tested against `@playwright/test` 1.62.1, which is the current
80
+ peer requirement. The integration fixture covers repetitions, multiple projects, expected
81
+ failures, skips, timeouts, file-backed attachments, and paths containing spaces.
82
+
83
+ The causal workflow works with ordinary Playwright tests. Keep importing `test` and `expect` from
84
+ `@playwright/test`; no per-test FlakeLab fixture or source edit is required. For each experiment,
85
+ FlakeLab creates a temporary config beside the existing Playwright config, retains its projects,
86
+ dependencies, web server, authentication settings, application environment, and reporters, and
87
+ adds a local fault proxy through Playwright's public configuration. The generated config and proxy
88
+ are removed after the trial, including after failure or interruption. The current project-level
89
+ bridge injects matching HTTP requests; it reports an explicit unsupported-path error instead of
90
+ claiming success when a selected route is HTTPS or no request matches.
91
+
34
92
  Then explicitly opt into AI investigation and isolated proof:
35
93
 
36
94
  ```bash
37
- npx flakelab@latest tests/checkout.spec.ts \
38
- --prove \
95
+ npx flakelab@latest prove tests/checkout.spec.ts \
96
+ --fault network-delay \
39
97
  --pattern "**/api/checkout" \
40
98
  --open
41
99
  ```
42
100
 
43
- `--prove` requires `GROQ_API_KEY` and `SOLARI_API_KEY`. It chains trigger discovery, reproducer
101
+ The equivalent target shortcut is `npx flakelab@latest tests/checkout.spec.ts --prove`.
102
+ Proof mode requires `GROQ_API_KEY` and `SOLARI_API_KEY`. It chains trigger discovery, reproducer
44
103
  verification, bounded AI investigation, candidate generation, proof in a disposable Solari
45
104
  microVM, and the offline evidence report. The candidate remains a reviewable diff and is never
46
105
  applied automatically.
47
106
 
48
- See every command and default without running tests:
107
+ See every command and its accepted options without running tests:
49
108
 
50
109
  ```bash
51
110
  npx flakelab@latest --help
@@ -61,18 +120,56 @@ The doctor checks Node.js, Playwright configuration, Chromium, ignored evidence
61
120
  credential availability, and test-process credential isolation. It reports only whether a
62
121
  credential is configured and where it came from; it never prints the value or a fingerprint.
63
122
 
64
- The local diagnostic core can already run controlled baseline and fault-injected trials:
123
+ Use `diagnose` as the adaptive product journey. A target begins with the bounded native scan;
124
+ an existing CI report begins with read-only analysis:
65
125
 
66
126
  ```bash
67
- pnpm flakelab diagnose tests/fixtures/flaky-checkout.spec.ts \
68
- --runs 4 \
69
- --seed 42 \
70
- --delay-ms 250
127
+ npx flakelab@latest diagnose tests/checkout.spec.ts
128
+ npx flakelab@latest diagnose --report ./blob-report
129
+ ```
130
+
131
+ The command explains what was observed and writes `.flakelab/runs/diagnose.json` with the cheapest
132
+ useful next experiment, its trial bound, an expected duration based on the local scan when
133
+ available, required credentials, and the Solari estimate—or an explicit statement that no
134
+ reliable estimate is available. Local diagnosis never requests provider credentials.
135
+
136
+ Continue only as far as the evidence justifies:
137
+
138
+ ```bash
139
+ npx flakelab@latest diagnose tests/checkout.spec.ts --discover
140
+ npx flakelab@latest diagnose tests/checkout.spec.ts --investigate
141
+ npx flakelab@latest diagnose tests/checkout.spec.ts --repair
142
+ ```
143
+
144
+ `--discover` remains local and creates a minimized reproducer. `--investigate` explicitly enables
145
+ bounded Groq usage and retains both the reproducer and investigation evidence. `--repair`
146
+ explicitly enables candidate generation and a disposable Solari proof; a rejected candidate still
147
+ produces the portable evidence report. When starting from `--report`, supply a test target before
148
+ requesting new experiments so FlakeLab never guesses which test to execute.
149
+
150
+ Every diagnosis writes `diagnose.json` atomically after each completed phase and when work fails
151
+ or is interrupted. Continue the exact saved workflow with:
152
+
153
+ ```bash
154
+ npx flakelab@latest resume .flakelab/runs/diagnose.json
71
155
  ```
72
156
 
73
- The command runs alternating control and network-delay trials, prints their failure rates,
74
- and writes a validated NDJSON event stream under `.flakelab/runs/`. The included checkout
75
- fixture passes normally and fails reliably when its checkout request is delayed.
157
+ Resume validates the checkpoint and its original input hash, confines saved paths to the current
158
+ project, and starts at the next safe phase. It does not repeat a confirmed scan, discovery, or
159
+ investigation. Completed checkpoints are a no-op apart from printing their summary. Partial
160
+ checkpoints remain readable and are labelled `running`, `interrupted`, or `failed`; they never
161
+ masquerade as a completed result.
162
+
163
+ The checkpoint shows planned and actual trial counts, wall time, AI token usage and estimated
164
+ cost, Solari sandbox creation and release counts, snapshot-cache decisions, and cleanup status.
165
+ It stores normalized options and project-relative artifact paths, not credentials. `Ctrl+C` stops
166
+ new trials; local Playwright process trees and disposable remote resources are cleaned up by the
167
+ same bounded execution paths used outside diagnosis.
168
+
169
+ Prepared Solari demo snapshots are reused only when a key covering the commit, lockfile, fixture,
170
+ template, runtime command, application path, port, and timeout matches. The metrics artifact gives
171
+ the key and a reason for every hit or miss. Candidate repair workspaces are intentionally not
172
+ cached because each contains a unique patch; their checkpoint explains that decision.
76
173
 
77
174
  ## Discover and replay a minimal reproducer
78
175
 
@@ -81,25 +178,95 @@ failure-rate and confidence thresholds:
81
178
 
82
179
  ```bash
83
180
  pnpm flakelab discover tests/fixtures/flaky-checkout.spec.ts \
181
+ --fault network-delay \
84
182
  --trials 4 \
85
- --concurrency 4 \
183
+ --concurrency 1 \
86
184
  --seed 42 \
87
185
  --max-delay 125 \
186
+ --max-seconds 300 \
88
187
  --min-rate 0.7 \
89
188
  --output flakelab.repro.yaml
90
189
  ```
91
190
 
92
191
  The command writes a strict portable YAML reproducer and a JSON discovery sidecar containing
93
- the evidence for every candidate. It normalizes Playwright failures into stable signatures,
94
- uses an 80% Wilson lower confidence bound, and accepts a trigger only when both the observed
95
- rate and lower bound meet the requested minimum. Replay the result with:
192
+ the evidence for every candidate. Every candidate alternates matched-seed control and intervention
193
+ trials. A pre-existing failure is allowed, but the intervention is causal only when the same
194
+ normalized failure signature reaches the requested rate and its 80% lower confidence bound exceeds
195
+ the control's upper bound. Discovery prints each completed trial to stderr
196
+ and stops at the five-minute default elapsed-time ceiling; set `--max-seconds` explicitly when a
197
+ suite needs a different local budget. Replay the result with:
96
198
 
97
199
  ```bash
98
- pnpm flakelab replay flakelab.repro.yaml --concurrency 4
200
+ pnpm flakelab replay flakelab.repro.yaml --concurrency 1
99
201
  ```
100
202
 
101
- The current browser-fault matrix supports deterministic network delay and injected HTTP
102
- failures. Fault routes are removed after every trial, including failed tests.
203
+ The current project-level fault matrix supports deterministic network delay, type-aware resource
204
+ loading delay, injected HTTP failures, response truncation, response-tail duplication, and response
205
+ reordering. Minimize
206
+ malformed payload triggers with `--fault response-truncation --max-remove-bytes 1024` or
207
+ `--fault response-duplication --max-duplicate-bytes 1024`. Find request races with
208
+ `--fault response-reordering --max-hold-ms 250`; the first response in each adjacent matching pair
209
+ is held while the second proceeds. Response mutations run in the order stored in the bounded fault
210
+ set, so a reproducer can express and replay an exact composition without introducing another schema.
211
+ Isolate startup dependencies with
212
+ `--fault resource-loading-delay --resource-type script --pattern "**/assets/*" --max-delay 250`.
213
+ Resource loading can target `document`, `script`, `stylesheet`, `image`, or `font` without slowing
214
+ unrelated API traffic. Discovery reports the minimum observed timing boundary and saves a
215
+ stability-margin trigger between that boundary and the configured maximum, so the portable
216
+ reproducer is less sensitive to ordinary machine jitter.
217
+ Delay application lifecycle listeners independently with
218
+ `--fault startup-event-delay --startup-event dom-content-loaded --pattern "**/checkout"`.
219
+ The fault supports `dom-content-loaded` and `load`, injects a temporary same-origin bootstrap into
220
+ matching HTML, preserves an existing script nonce, and verifies that the bootstrap actually ran.
221
+ It delays application listeners rather than the browser lifecycle event itself, network traffic,
222
+ or the event loop.
223
+ Probe missed main-thread deadlines with
224
+ `--fault event-loop-stall --max-stall-ms 500 --stall-after-ms 0 --pattern "**/checkout"`.
225
+ The offset is measured from the native `DOMContentLoaded` event and the stall duration is bounded
226
+ at two seconds. Discovery finds the minimum observed duration, confirms it with repeated trials,
227
+ and saves a stability-margin reproducer. The fault blocks only the matching page's main thread;
228
+ it does not simulate system-wide CPU saturation.
229
+ Test expired session handling without recording credentials with
230
+ `--fault auth-cookie-expiry --cookie-name session-id --pattern "**/api/session"`.
231
+ Only the named cookie is withheld from matched requests, and an absent cookie is reported as an
232
+ unapplied fault rather than false causal evidence. Reproducers contain the cookie name but never
233
+ its value. The direct browser adapter restores the original cookie in `finally`.
234
+ Test partially initialized browser state with
235
+ `--fault storage-state-delay --storage local-storage --storage-key auth-token --max-delay 500`
236
+ and a document URL pattern. The bootstrap temporarily makes `getItem` return `null` only for that
237
+ key; it does not delete or record the stored value. Both local and session storage are supported,
238
+ and discovery minimizes the visibility delay before saving a stability-margin trigger.
239
+ Expose wall-clock assumptions with
240
+ `--fault clock-jump --clock-offset-ms 3600000 --jump-after-ms 25 --pattern "**/app"`.
241
+ The fault changes `Date` wall time after a deterministic offset while leaving `performance.now`,
242
+ timeouts, and interval scheduling monotonic. Use `--clock-offset-ms=-3600000` for a backward jump.
243
+ Exercise internationalization assumptions with
244
+ `--fault locale --locale fr-FR --pattern "**/app"` or
245
+ `--fault timezone --timezone America/New_York --pattern "**/app"`. Locale tags and IANA timezone
246
+ names are validated before trials. These two settings are applied through Playwright's browser
247
+ context configuration; the document pattern verifies that the intended target was reached. The
248
+ direct page adapter rejects them explicitly because an existing Playwright context cannot be
249
+ reconfigured faithfully after creation.
250
+ Probe responsive and accessibility-specific behavior with
251
+ `--fault viewport --viewport-width 375 --viewport-height 667` and
252
+ `--fault reduced-motion`. Viewports are bounded from 200 to 7680 pixels in each dimension, and
253
+ reduced motion uses the browser's native media emulation. Test animation races with
254
+ `--fault animation-speed --animation-rate 5`; rates are bounded from 0.1x to 10x and alter Web
255
+ Animations and CSS animation/transition playback without accelerating application timers. Each
256
+ visual fault is reversible, uses a paired control, and requires a matched document before FlakeLab
257
+ accepts its causal evidence.
258
+ Expose suite-level isolation bugs with
259
+ `--fault worker-pressure --max-workers 4`. FlakeLab raises Playwright's supported worker limit,
260
+ enables full parallel scheduling for the selected target, and saves the smallest worker count that
261
+ reproduces the failure. Probe shared accounts, records, ports, and other cross-test state with
262
+ `--fault shared-state-interference --max-copies 4`; this overlaps repeated copies of the selected
263
+ test through Playwright's supported `repeatEach` and worker controls. Both faults use matched-seed
264
+ single-worker controls, are bounded to 16 workers or copies, require an independent 12-trial
265
+ confirmation, and store the selected test path—not a URL glob—as their artifact target.
266
+ The local proxy and generated Playwright config are removed after every trial, including failed and
267
+ interrupted tests. Solari repair proof consumes the same validated fault set as local replay.
268
+ Revision bisect still rejects unsupported fault combinations explicitly rather than approximating
269
+ remote behavior.
103
270
 
104
271
  ## AI investigator
105
272
 
@@ -109,7 +276,7 @@ terminal requests it through hidden input and keeps it in memory for this run on
109
276
  ```bash
110
277
  pnpm flakelab investigate tests/fixtures/flaky-checkout.spec.ts \
111
278
  --trials 4 \
112
- --concurrency 4 \
279
+ --concurrency 1 \
113
280
  --max-delay 125
114
281
  ```
115
282
 
@@ -168,17 +335,24 @@ tree:
168
335
  ```bash
169
336
  pnpm flakelab repair flakelab.investigation.json \
170
337
  --reproducer flakelab.repro.yaml \
338
+ --source src/checkout.ts \
339
+ --max-cost 0.25 \
171
340
  --patch candidate.diff \
172
341
  --proof flakelab.proof.json
173
342
  ```
174
343
 
175
- This command requires both `GROQ_API_KEY` and `SOLARI_API_KEY`. The model can propose only exact,
176
- bounded edits to application source it previously received. FlakeLab rejects test changes,
344
+ This command requires both `GROQ_API_KEY` and `SOLARI_API_KEY`. `--max-cost` bounds candidate
345
+ generation, and the result reports observed model tokens and estimated cost. Repeat `--source` to
346
+ approve up to seven application source files that a black-box Playwright test does not import.
347
+ Every approved file remains project-confined, JavaScript/TypeScript-only, credential-blocked, and
348
+ inside the shared 64 KiB context limit. The model can propose only exact, bounded edits to source it
349
+ received. FlakeLab rejects test changes,
177
350
  assertion weakening, lint suppressions, credential-like additions, path escapes, and numeric-only
178
351
  timeout increases before execution.
179
352
 
180
353
  The candidate is copied into a disposable Solari microVM. Typecheck, lint, hostile trials, clean
181
- controls, and nearby regression tests all run there. The machine is destroyed afterward, and the
354
+ controls, and up to 50 nested or co-located `.spec.*` and `.test.*` regression files all run there.
355
+ The machine is destroyed afterward, and the
182
356
  candidate is returned as a reviewable diff; FlakeLab never applies it to the local checkout.
183
357
  Cold validation installs the pinned Node/pnpm toolchain and Chromium, so it is intentionally
184
358
  slower than the future snapshot-backed warm path.
@@ -199,12 +373,19 @@ pnpm flakelab report flakelab.investigation.json \
199
373
  The React/Recharts interface is bundled by Vite into a single HTML file. It explains the root
200
374
  cause, deterministic ownership classification, experiment timeline, competing hypotheses,
201
375
  minimal trigger, before/after proof matrix, static checks, model usage, and reviewable artifacts.
376
+ Each causal conclusion links to both its intervention and clean control. Investigation mode retains
377
+ project-relative Playwright traces, compares one representative passing control with one failing
378
+ intervention, and links each recording from its experiment. The report also gives an exact replay
379
+ command, the bounded source files shown to the investigator, and the line of every candidate edit.
202
380
  The evidence is schema-validated and credential-redacted before rendering. A restrictive content
203
- security policy blocks runtime network access, so the report remains usable offline.
381
+ security policy blocks runtime network access, unsafe evidence links are rejected, and the report
382
+ remains usable offline.
204
383
 
205
- `--open` launches only the local file. `--publish` is optional and always asks for interactive
206
- confirmation immediately before creating a public Solari preview. Published reports expire and
207
- their hosting sandbox is automatically killed after at most 60 minutes.
384
+ Interactive report generation offers to open the local file in the default browser. `--open`
385
+ accepts that choice without prompting, while CI and redirected sessions never prompt or open a
386
+ browser implicitly. `--publish` is optional and always asks for interactive confirmation
387
+ immediately before creating a public Solari preview. Published reports expire and their hosting
388
+ sandbox is automatically killed after at most 60 minutes.
208
389
 
209
390
  ## Statistical Git bisect
210
391
 
@@ -264,8 +445,14 @@ Playwright configuration, manifest, or lockfile behavior changes, it falls back
264
445
  to keep time and cost predictable; `.flakelab/changed-tests.json` preserves the complete selection.
265
446
 
266
447
  The uploaded artifact is retained for seven days and includes the offline HTML report,
267
- investigation, reproducer, discovery evidence, proof, candidate diff, and selection manifest. A
268
- rejected repair still uploads its evidence before the job reports failure.
448
+ investigation traces, reproducer, discovery evidence, proof, candidate diff, and selection manifest.
449
+ A rejected repair still uploads its evidence before the job reports failure.
450
+
451
+ The same composite action can analyze an existing Playwright blob report without Groq or Solari
452
+ credentials. Pass `blob-report` and, optionally, an artifact downloaded from an earlier run as
453
+ `baseline`. The action does not rerun tests in this mode; it merges shards, ranks failures, marks
454
+ signatures absent from the baseline, uploads the validated analysis, and writes a concise Markdown
455
+ job summary. No hosted database is required.
269
456
 
270
457
  ## Parallel Solari runner
271
458
 
@@ -0,0 +1,15 @@
1
+ import type { ScanTestResult } from "../scan/schema.js";
2
+ import type { AnalysisSourceKind } from "./schema.js";
3
+ export interface BlobReportAnalysisOptions {
4
+ artifactDirectory: string;
5
+ playwrightCliPath?: string;
6
+ signal?: AbortSignal;
7
+ }
8
+ export interface BlobReportAnalysisResult {
9
+ archiveCount: number;
10
+ artifactDirectory: string | null;
11
+ runnerErrors: string[];
12
+ sourceKind: AnalysisSourceKind;
13
+ tests: ScanTestResult[];
14
+ }
15
+ export declare function analyzeBlobReports(projectRoot: string, source: string, options: BlobReportAnalysisOptions): Promise<BlobReportAnalysisResult>;
@@ -0,0 +1,137 @@
1
+ import { spawn } from "node:child_process";
2
+ import { copyFile, lstat, mkdir, mkdtemp, readFile, readdir, rm } from "node:fs/promises";
3
+ import { basename, extname, isAbsolute, join, relative, resolve } from "node:path";
4
+ import { retainOwnedArtifacts } from "../artifacts/retention.js";
5
+ import { redactText } from "../report/redaction.js";
6
+ import { parseNativeScanReport } from "../runner/native-scan.js";
7
+ import { createPlaywrightEnvironment, resolvePlaywrightCliPath, } from "../runner/playwright-executor.js";
8
+ import { waitForProcessTree } from "../runner/process-tree.js";
9
+ import { inspectZipArchive } from "./zip-archive.js";
10
+ const MAX_ARCHIVES = 100;
11
+ const MAX_TOTAL_EXPANDED_BYTES = 1_024 * 1_024 * 1_024;
12
+ async function discoverInput(sourcePath) {
13
+ const information = await lstat(sourcePath);
14
+ if (information.isSymbolicLink()) {
15
+ throw new Error("Blob report input cannot be a symbolic link");
16
+ }
17
+ if (information.isFile()) {
18
+ if (extname(sourcePath).toLowerCase() !== ".zip") {
19
+ throw new Error("Blob report file must use the .zip extension");
20
+ }
21
+ return { archives: [sourcePath], kind: "blob-archive" };
22
+ }
23
+ if (!information.isDirectory()) {
24
+ throw new Error("Blob report input must be a ZIP archive or directory");
25
+ }
26
+ const entries = await readdir(sourcePath, { withFileTypes: true });
27
+ const archiveEntries = entries.filter((entry) => extname(entry.name).toLowerCase() === ".zip");
28
+ if (archiveEntries.some((entry) => entry.isSymbolicLink() || !entry.isFile())) {
29
+ throw new Error("Blob report directory contains a non-regular ZIP entry");
30
+ }
31
+ const archives = archiveEntries
32
+ .map((entry) => resolve(sourcePath, entry.name))
33
+ .sort((left, right) => left.localeCompare(right));
34
+ if (archives.length === 0) {
35
+ throw new Error("Blob report directory does not contain any ZIP archives");
36
+ }
37
+ return { archives, kind: "blob-directory" };
38
+ }
39
+ async function validateArchives(archives) {
40
+ if (archives.length > MAX_ARCHIVES) {
41
+ throw new Error(`Blob report input exceeds the ${MAX_ARCHIVES}-archive safety limit`);
42
+ }
43
+ let expandedBytes = 0;
44
+ for (const archive of archives) {
45
+ const summary = await inspectZipArchive(archive);
46
+ expandedBytes += summary.expandedBytes;
47
+ if (expandedBytes > MAX_TOTAL_EXPANDED_BYTES) {
48
+ throw new Error("Blob reports expand beyond the 1 GiB combined safety limit");
49
+ }
50
+ }
51
+ }
52
+ function mergeArguments(playwrightCliPath, inputDirectory) {
53
+ return [playwrightCliPath, "merge-reports", "--reporter=json", inputDirectory];
54
+ }
55
+ async function copyArchives(archives, directory) {
56
+ for (const [index, archive] of archives.entries()) {
57
+ const destination = join(directory, `${String(index + 1).padStart(3, "0")}-${basename(archive)}`);
58
+ await copyFile(archive, destination);
59
+ }
60
+ }
61
+ function assertDirectChild(parent, directory) {
62
+ const child = relative(resolve(parent), resolve(directory));
63
+ if (!child || child.startsWith("..") || isAbsolute(child) || child.includes("/") || child.includes("\\")) {
64
+ throw new Error("Refusing to clean an analysis path outside its owned parent");
65
+ }
66
+ }
67
+ async function removeWorkDirectory(parent, directory) {
68
+ assertDirectChild(parent, directory);
69
+ await rm(directory, { force: true, recursive: true });
70
+ }
71
+ function mergeFailure(diagnostic) {
72
+ const safeDiagnostic = redactText(diagnostic).trim().slice(-2_000);
73
+ return new Error(safeDiagnostic || "Playwright could not merge the blob reports");
74
+ }
75
+ async function removeConversionFiles(directory) {
76
+ const entries = await readdir(directory, { withFileTypes: true });
77
+ const files = entries.filter((entry) => entry.isFile() || entry.isSymbolicLink());
78
+ await Promise.all(files.map(async (entry) => rm(join(directory, entry.name), { force: true })));
79
+ }
80
+ export async function analyzeBlobReports(projectRoot, source, options) {
81
+ const sourcePath = resolve(projectRoot, source);
82
+ const input = await discoverInput(sourcePath);
83
+ const workParent = resolve(projectRoot, options.artifactDirectory, "playwright-analysis");
84
+ await mkdir(workParent, { recursive: true });
85
+ const workDirectory = await mkdtemp(join(workParent, "run-"));
86
+ let cleanupComplete = false;
87
+ try {
88
+ await copyArchives(input.archives, workDirectory);
89
+ const archiveCopies = (await readdir(workDirectory, { withFileTypes: true }))
90
+ .filter((entry) => entry.isFile() && extname(entry.name).toLowerCase() === ".zip")
91
+ .map((entry) => resolve(workDirectory, entry.name))
92
+ .sort((left, right) => left.localeCompare(right));
93
+ await validateArchives(archiveCopies);
94
+ const reportPath = join(workDirectory, "flakelab-merged.json");
95
+ const playwrightCliPath = options.playwrightCliPath ?? resolvePlaywrightCliPath(projectRoot);
96
+ const child = spawn(process.execPath, mergeArguments(playwrightCliPath, workDirectory), {
97
+ cwd: projectRoot,
98
+ detached: process.platform !== "win32",
99
+ env: {
100
+ ...createPlaywrightEnvironment(),
101
+ PLAYWRIGHT_JSON_OUTPUT_FILE: reportPath,
102
+ },
103
+ shell: false,
104
+ windowsHide: true,
105
+ });
106
+ const execution = await waitForProcessTree(child, options.signal);
107
+ if (options.signal?.aborted) {
108
+ throw new Error("Playwright blob report analysis was interrupted");
109
+ }
110
+ if (execution.spawnError || execution.exitCode !== 0) {
111
+ throw mergeFailure(execution.spawnError ?? execution.diagnostic);
112
+ }
113
+ let parsed;
114
+ try {
115
+ parsed = parseNativeScanReport(await readFile(reportPath, "utf8"), projectRoot);
116
+ }
117
+ catch {
118
+ throw new Error("Playwright produced a missing or malformed merged JSON report");
119
+ }
120
+ await removeConversionFiles(workDirectory);
121
+ const artifactDirectory = await retainOwnedArtifacts(projectRoot, workDirectory);
122
+ cleanupComplete = true;
123
+ return {
124
+ archiveCount: input.archives.length,
125
+ artifactDirectory,
126
+ runnerErrors: parsed.runnerErrors,
127
+ sourceKind: input.kind,
128
+ tests: parsed.tests,
129
+ };
130
+ }
131
+ finally {
132
+ if (!cleanupComplete) {
133
+ await removeWorkDirectory(workParent, workDirectory);
134
+ }
135
+ }
136
+ }
137
+ //# sourceMappingURL=blob-report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blob-report.js","sourceRoot":"","sources":["../../src/analysis/blob-report.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { AnalyzeOptions } from "../commands/options.js";
2
+ export interface AnalyzeInvocation {
3
+ command: "analyze";
4
+ options: AnalyzeOptions;
5
+ target: string;
6
+ }
7
+ export declare function parseAnalyzeArguments(args: string[]): AnalyzeInvocation;
@@ -0,0 +1,28 @@
1
+ import { parseArgs } from "node:util";
2
+ export function parseAnalyzeArguments(args) {
3
+ const parsed = parseArgs({
4
+ args,
5
+ allowPositionals: true,
6
+ strict: true,
7
+ options: {
8
+ artifacts: { type: "string", default: ".flakelab/runs" },
9
+ baseline: { type: "string" },
10
+ json: { type: "boolean", default: false },
11
+ verbose: { type: "boolean", default: false },
12
+ },
13
+ });
14
+ if (parsed.positionals.length !== 1) {
15
+ throw new Error("analyze requires exactly one blob report path");
16
+ }
17
+ return {
18
+ command: "analyze",
19
+ options: {
20
+ artifacts: parsed.values.artifacts ?? ".flakelab/runs",
21
+ baseline: parsed.values.baseline,
22
+ json: parsed.values.json ?? false,
23
+ verbose: parsed.values.verbose ?? false,
24
+ },
25
+ target: parsed.positionals[0],
26
+ };
27
+ }
28
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/analysis/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAUrC,MAAM,UAAU,qBAAqB,CAAC,IAAc;IAClD,MAAM,MAAM,GAAG,SAAS,CAAC;QACvB,IAAI;QACJ,gBAAgB,EAAE,IAAI;QACtB,MAAM,EAAE,IAAI;QACZ,OAAO,EAAE;YACP,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,gBAAgB,EAAE;YACxD,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC5B,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;YACzC,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;SAC7C;KACF,CAAC,CAAA;IACF,IAAI,MAAM,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAA;IAClE,CAAC;IACD,OAAO;QACL,OAAO,EAAE,SAAS;QAClB,OAAO,EAAE;YACP,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,IAAI,gBAAgB;YACtD,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ;YAChC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,KAAK;YACjC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,IAAI,KAAK;SACxC;QACD,MAAM,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;KAC9B,CAAA;AACH,CAAC"}