@kirrosh/zond 0.26.1 → 0.27.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 (261) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +39 -22
  3. package/bin/zond.mjs +23 -0
  4. package/package.json +36 -23
  5. package/scripts/npm/postinstall.mjs +76 -0
  6. package/src/CLAUDE.md +0 -112
  7. package/src/bun-types.d.ts +0 -5
  8. package/src/cli/argv.ts +0 -122
  9. package/src/cli/commands/add-api.ts +0 -146
  10. package/src/cli/commands/api/annotate/idempotency.ts +0 -59
  11. package/src/cli/commands/api/annotate/index.ts +0 -880
  12. package/src/cli/commands/api/annotate/lifecycle.ts +0 -74
  13. package/src/cli/commands/api/annotate/overlay.ts +0 -206
  14. package/src/cli/commands/api/annotate/pagination.ts +0 -64
  15. package/src/cli/commands/api/annotate/prompts.ts +0 -220
  16. package/src/cli/commands/api/annotate/readback.ts +0 -58
  17. package/src/cli/commands/api/annotate/resources.ts +0 -91
  18. package/src/cli/commands/api/annotate/seed-bodies.ts +0 -61
  19. package/src/cli/commands/audit.ts +0 -786
  20. package/src/cli/commands/catalog.ts +0 -97
  21. package/src/cli/commands/check.ts +0 -361
  22. package/src/cli/commands/checks.ts +0 -1072
  23. package/src/cli/commands/ci-init.ts +0 -223
  24. package/src/cli/commands/clean.ts +0 -212
  25. package/src/cli/commands/cleanup.ts +0 -236
  26. package/src/cli/commands/completions.ts +0 -192
  27. package/src/cli/commands/coverage.ts +0 -872
  28. package/src/cli/commands/db.ts +0 -611
  29. package/src/cli/commands/describe.ts +0 -120
  30. package/src/cli/commands/discover.ts +0 -1356
  31. package/src/cli/commands/doctor.ts +0 -661
  32. package/src/cli/commands/fixtures.ts +0 -402
  33. package/src/cli/commands/generate.ts +0 -577
  34. package/src/cli/commands/init/agents-md.ts +0 -61
  35. package/src/cli/commands/init/bootstrap.ts +0 -111
  36. package/src/cli/commands/init/index.ts +0 -244
  37. package/src/cli/commands/init/skills.ts +0 -141
  38. package/src/cli/commands/init/templates/agents.md +0 -86
  39. package/src/cli/commands/init/templates/markdown.d.ts +0 -4
  40. package/src/cli/commands/init/templates/skills/warm-up-target.md +0 -122
  41. package/src/cli/commands/init/templates/skills/zond-checks.md +0 -621
  42. package/src/cli/commands/init/templates/skills/zond-seed.md +0 -114
  43. package/src/cli/commands/init/templates/skills/zond-triage.md +0 -272
  44. package/src/cli/commands/init/templates/skills/zond.md +0 -861
  45. package/src/cli/commands/init/templates/zond-config.yml +0 -14
  46. package/src/cli/commands/prepare-fixtures.ts +0 -97
  47. package/src/cli/commands/probe/_seed-bodies.ts +0 -52
  48. package/src/cli/commands/probe/mass-assignment.ts +0 -594
  49. package/src/cli/commands/probe/security.ts +0 -537
  50. package/src/cli/commands/probe/static.ts +0 -255
  51. package/src/cli/commands/probe/webhooks.ts +0 -163
  52. package/src/cli/commands/probe.ts +0 -535
  53. package/src/cli/commands/reference.ts +0 -87
  54. package/src/cli/commands/refresh-api.ts +0 -227
  55. package/src/cli/commands/remove-api.ts +0 -150
  56. package/src/cli/commands/report-bundle.ts +0 -310
  57. package/src/cli/commands/report.ts +0 -241
  58. package/src/cli/commands/request.ts +0 -548
  59. package/src/cli/commands/run.ts +0 -1162
  60. package/src/cli/commands/schema-from-runs.ts +0 -128
  61. package/src/cli/commands/secrets.ts +0 -133
  62. package/src/cli/commands/session.ts +0 -244
  63. package/src/cli/commands/use.ts +0 -74
  64. package/src/cli/index.ts +0 -55
  65. package/src/cli/json-envelope.ts +0 -108
  66. package/src/cli/json-schemas.ts +0 -314
  67. package/src/cli/output.ts +0 -40
  68. package/src/cli/program.ts +0 -219
  69. package/src/cli/resolve.ts +0 -105
  70. package/src/cli/runtime.ts +0 -7
  71. package/src/cli/safe-live.ts +0 -24
  72. package/src/cli/status-filter.ts +0 -114
  73. package/src/cli/util/api-context.ts +0 -85
  74. package/src/cli/version.ts +0 -8
  75. package/src/core/audit/persist.ts +0 -183
  76. package/src/core/checks/budget.ts +0 -59
  77. package/src/core/checks/checks/_crud-helpers.ts +0 -133
  78. package/src/core/checks/checks/_negative_mutator.ts +0 -133
  79. package/src/core/checks/checks/_readback-helpers.ts +0 -133
  80. package/src/core/checks/checks/content_type_conformance.ts +0 -39
  81. package/src/core/checks/checks/cross_call_references.ts +0 -147
  82. package/src/core/checks/checks/cursor_boundary_fuzzing.ts +0 -219
  83. package/src/core/checks/checks/ensure_resource_availability.ts +0 -62
  84. package/src/core/checks/checks/idempotency_replay.ts +0 -242
  85. package/src/core/checks/checks/ignored_auth.ts +0 -254
  86. package/src/core/checks/checks/index.ts +0 -68
  87. package/src/core/checks/checks/lifecycle_transitions.ts +0 -416
  88. package/src/core/checks/checks/missing_required_header.ts +0 -40
  89. package/src/core/checks/checks/negative_data_rejection.ts +0 -148
  90. package/src/core/checks/checks/not_a_server_error.ts +0 -35
  91. package/src/core/checks/checks/open_cors_on_sensitive.ts +0 -160
  92. package/src/core/checks/checks/pagination_invariants.ts +0 -419
  93. package/src/core/checks/checks/positive_data_acceptance.ts +0 -33
  94. package/src/core/checks/checks/rate_limit_headers_absent.ts +0 -77
  95. package/src/core/checks/checks/response_headers_conformance.ts +0 -74
  96. package/src/core/checks/checks/response_schema_conformance.ts +0 -30
  97. package/src/core/checks/checks/status_code_conformance.ts +0 -132
  98. package/src/core/checks/checks/unsupported_method.ts +0 -63
  99. package/src/core/checks/checks/use_after_free.ts +0 -78
  100. package/src/core/checks/index.ts +0 -30
  101. package/src/core/checks/mode.ts +0 -82
  102. package/src/core/checks/recommended-action.ts +0 -68
  103. package/src/core/checks/registry.ts +0 -78
  104. package/src/core/checks/runner.ts +0 -1461
  105. package/src/core/checks/sarif.ts +0 -230
  106. package/src/core/checks/spec-findings.ts +0 -308
  107. package/src/core/checks/stateful.ts +0 -121
  108. package/src/core/checks/types.ts +0 -305
  109. package/src/core/checks/zond-extensions.ts +0 -73
  110. package/src/core/classifier/recommended-action.ts +0 -251
  111. package/src/core/context/current.ts +0 -51
  112. package/src/core/context/session.ts +0 -78
  113. package/src/core/coverage/loader.ts +0 -216
  114. package/src/core/coverage/reasons.ts +0 -300
  115. package/src/core/diagnostics/db-analysis.ts +0 -666
  116. package/src/core/diagnostics/failure-class.ts +0 -140
  117. package/src/core/diagnostics/failure-hints.ts +0 -112
  118. package/src/core/diagnostics/spec-pointer.ts +0 -99
  119. package/src/core/diagnostics/suggested-fixes.ts +0 -155
  120. package/src/core/exporter/case-study/index.ts +0 -270
  121. package/src/core/exporter/curl.ts +0 -40
  122. package/src/core/exporter/exporter.ts +0 -48
  123. package/src/core/exporter/html-report/escape.ts +0 -24
  124. package/src/core/exporter/html-report/index.ts +0 -479
  125. package/src/core/exporter/html-report/script.ts +0 -100
  126. package/src/core/exporter/html-report/styles.ts +0 -408
  127. package/src/core/generator/catalog-builder.ts +0 -179
  128. package/src/core/generator/chunker.ts +0 -78
  129. package/src/core/generator/coverage-phase.ts +0 -0
  130. package/src/core/generator/coverage-scanner.ts +0 -87
  131. package/src/core/generator/data-factory.ts +0 -759
  132. package/src/core/generator/describe.ts +0 -302
  133. package/src/core/generator/endpoint-warnings.ts +0 -52
  134. package/src/core/generator/fixtures-builder.ts +0 -332
  135. package/src/core/generator/index.ts +0 -14
  136. package/src/core/generator/openapi-reader.ts +0 -297
  137. package/src/core/generator/path-param-disambig.ts +0 -140
  138. package/src/core/generator/resources-builder.ts +0 -898
  139. package/src/core/generator/schema-utils.ts +0 -112
  140. package/src/core/generator/serializer.ts +0 -343
  141. package/src/core/generator/suite-generator.ts +0 -1301
  142. package/src/core/generator/types.ts +0 -63
  143. package/src/core/identity/identity-file.ts +0 -0
  144. package/src/core/lint/affects.ts +0 -28
  145. package/src/core/lint/config.ts +0 -96
  146. package/src/core/lint/format.ts +0 -42
  147. package/src/core/lint/index.ts +0 -94
  148. package/src/core/lint/reporter.ts +0 -128
  149. package/src/core/lint/rules/consistency.ts +0 -158
  150. package/src/core/lint/rules/heuristics.ts +0 -97
  151. package/src/core/lint/rules/strictness.ts +0 -109
  152. package/src/core/lint/types.ts +0 -96
  153. package/src/core/lint/walker.ts +0 -248
  154. package/src/core/meta/meta-store.ts +0 -11
  155. package/src/core/output/README.md +0 -73
  156. package/src/core/output/index.ts +0 -13
  157. package/src/core/output/run.ts +0 -91
  158. package/src/core/output/types.ts +0 -122
  159. package/src/core/parser/dynamic-values.ts +0 -160
  160. package/src/core/parser/env-interpolation.ts +0 -104
  161. package/src/core/parser/filter.ts +0 -97
  162. package/src/core/parser/schema.ts +0 -359
  163. package/src/core/parser/types.ts +0 -117
  164. package/src/core/parser/variables.ts +0 -0
  165. package/src/core/parser/yaml-parser.ts +0 -181
  166. package/src/core/probe/bootstrap.ts +0 -34
  167. package/src/core/probe/dry-run-envelope.ts +0 -61
  168. package/src/core/probe/mass-assignment/classify.ts +0 -175
  169. package/src/core/probe/mass-assignment/cleanup.ts +0 -52
  170. package/src/core/probe/mass-assignment/digest.ts +0 -114
  171. package/src/core/probe/mass-assignment/orchestrator.ts +0 -459
  172. package/src/core/probe/mass-assignment/regression.ts +0 -141
  173. package/src/core/probe/mass-assignment/suspects.ts +0 -92
  174. package/src/core/probe/mass-assignment/types.ts +0 -135
  175. package/src/core/probe/mass-assignment-probe-class.ts +0 -198
  176. package/src/core/probe/mass-assignment-probe.ts +0 -27
  177. package/src/core/probe/mass-assignment-template.ts +0 -240
  178. package/src/core/probe/method-probe.ts +0 -164
  179. package/src/core/probe/method-shared.ts +0 -69
  180. package/src/core/probe/negative-probe.ts +0 -691
  181. package/src/core/probe/orphan-tracker.ts +0 -188
  182. package/src/core/probe/path-discovery.ts +0 -439
  183. package/src/core/probe/probe-harness.ts +0 -119
  184. package/src/core/probe/registry.ts +0 -89
  185. package/src/core/probe/runner.ts +0 -136
  186. package/src/core/probe/security/baseline.ts +0 -174
  187. package/src/core/probe/security/classify.ts +0 -341
  188. package/src/core/probe/security/cleanup.ts +0 -125
  189. package/src/core/probe/security/detectors.ts +0 -71
  190. package/src/core/probe/security/digest.ts +0 -104
  191. package/src/core/probe/security/orchestrator.ts +0 -398
  192. package/src/core/probe/security/regression.ts +0 -103
  193. package/src/core/probe/security/types.ts +0 -151
  194. package/src/core/probe/security-probe-class.ts +0 -207
  195. package/src/core/probe/security-probe.ts +0 -32
  196. package/src/core/probe/shared.ts +0 -531
  197. package/src/core/probe/static-probe-class.ts +0 -125
  198. package/src/core/probe/types.ts +0 -165
  199. package/src/core/probe/verdict-aggregator.ts +0 -33
  200. package/src/core/probe/webhooks-probe.ts +0 -282
  201. package/src/core/reporter/console.ts +0 -240
  202. package/src/core/reporter/index.ts +0 -22
  203. package/src/core/reporter/json.ts +0 -22
  204. package/src/core/reporter/junit.ts +0 -93
  205. package/src/core/reporter/ndjson.ts +0 -37
  206. package/src/core/reporter/types.ts +0 -15
  207. package/src/core/runner/assertions.ts +0 -383
  208. package/src/core/runner/async-pool.ts +0 -108
  209. package/src/core/runner/auth-path.ts +0 -8
  210. package/src/core/runner/ci-context.ts +0 -72
  211. package/src/core/runner/executor.ts +0 -652
  212. package/src/core/runner/expr-eval.ts +0 -41
  213. package/src/core/runner/form-encode.ts +0 -41
  214. package/src/core/runner/http-client.ts +0 -224
  215. package/src/core/runner/learn-drift.ts +0 -293
  216. package/src/core/runner/preflight-vars.ts +0 -153
  217. package/src/core/runner/progress-tracker.ts +0 -73
  218. package/src/core/runner/rate-limiter.ts +0 -185
  219. package/src/core/runner/run-kind.ts +0 -45
  220. package/src/core/runner/schema-validator.ts +0 -308
  221. package/src/core/runner/send-request.ts +0 -232
  222. package/src/core/runner/transforms.ts +0 -65
  223. package/src/core/runner/types.ts +0 -94
  224. package/src/core/secrets/registry.ts +0 -164
  225. package/src/core/secrets/secrets-file.ts +0 -115
  226. package/src/core/selectors/operation-filter.ts +0 -144
  227. package/src/core/setup-api.ts +0 -590
  228. package/src/core/severity/category.ts +0 -94
  229. package/src/core/severity/index.ts +0 -58
  230. package/src/core/spec/infer-schema.ts +0 -102
  231. package/src/core/spec/layers.ts +0 -154
  232. package/src/core/spec/merge-specs.ts +0 -156
  233. package/src/core/spec/schema-from-runs.ts +0 -117
  234. package/src/core/spec/schema-overlay.ts +0 -130
  235. package/src/core/util/ajv.ts +0 -13
  236. package/src/core/util/format-eta.ts +0 -21
  237. package/src/core/util/headers.ts +0 -9
  238. package/src/core/util/url.ts +0 -24
  239. package/src/core/utils.ts +0 -13
  240. package/src/core/workspace/config.ts +0 -129
  241. package/src/core/workspace/fixture-gap-report.ts +0 -84
  242. package/src/core/workspace/fixture-gaps.ts +0 -71
  243. package/src/core/workspace/manifest.ts +0 -283
  244. package/src/core/workspace/output-rotation.ts +0 -62
  245. package/src/core/workspace/root.ts +0 -96
  246. package/src/core/workspace/triage-path.ts +0 -87
  247. package/src/db/lint-runs.ts +0 -47
  248. package/src/db/migrate.ts +0 -128
  249. package/src/db/migrations/0001_run_kind.sql +0 -25
  250. package/src/db/migrations/0002_run_kind_request.sql +0 -59
  251. package/src/db/migrations/sql.d.ts +0 -4
  252. package/src/db/queries/collections.ts +0 -133
  253. package/src/db/queries/coverage.ts +0 -9
  254. package/src/db/queries/dashboard.ts +0 -59
  255. package/src/db/queries/results.ts +0 -216
  256. package/src/db/queries/runs.ts +0 -289
  257. package/src/db/queries/sessions.ts +0 -42
  258. package/src/db/queries/settings.ts +0 -28
  259. package/src/db/queries/types.ts +0 -172
  260. package/src/db/queries.ts +0 -75
  261. package/src/db/schema.ts +0 -298
package/CHANGELOG.md CHANGED
@@ -4,6 +4,71 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.27.1] — 2026-07-09
8
+
9
+ m-27 Bucket E (agentic discoverability): metadata release — no engine changes.
10
+
11
+ ### Added
12
+ - **Claude Code plugin / self-serve marketplace (ARV-395):**
13
+ `.claude-plugin/{plugin,marketplace}.json` — install skills via
14
+ `/plugin marketplace add kirrosh/zond`; skills mirrored to root
15
+ `skills/<name>/SKILL.md` (synced from init templates, drift-checked in
16
+ `bun run check`), also picked up by SkillsMP / `npx skills add kirrosh/zond`.
17
+ - **Agent discovery files (ARV-394):** `llms.txt` (machine index of docs) and
18
+ `context7.json` (Context7 config with agent rules) at the repo root.
19
+
20
+ ### Changed
21
+ - **Agentic metadata (ARV-393, ARV-400):** canonical tagline in npm
22
+ `description`, task-shaped `keywords`, `repository`/`homepage`/`bugs` links;
23
+ agent-first README top (what → when → install → minimal example).
24
+
25
+ ## [0.27.0] — 2026-07-09
26
+
27
+ m-27 start-distribution: every install channel works on a clean machine,
28
+ one-run release pipeline, cold-start onboarding pass. Engine untouched.
29
+
30
+ ### Added
31
+ - **npm channel for node-only users (ARV-386):** the published package is a
32
+ thin node launcher (`bin/zond.mjs`) + `postinstall` that downloads the
33
+ platform binary from the matching GitHub release and verifies its sha256
34
+ against `checksums.txt` (`ZOND_DOWNLOAD_BASE` override for mirrors/E2E).
35
+ Zero runtime deps, no `src/` TS execution, no bun required.
36
+ - **Full platform matrix (ARV-388):** releases now ship `darwin-x64` and
37
+ `linux-arm64` on top of `darwin-arm64`/`linux-x64`/`win-x64`, cross-compiled
38
+ via `bun build --compile --target`. `install.sh` fails with a clear message
39
+ + releases link instead of a raw 404; `install.ps1` falls back ARM64 → x64
40
+ with an emulation note.
41
+ - **One-run release pipeline (ARV-389):** tag push cross-compiles all 5
42
+ targets, adhoc-codesigns darwin, emits `checksums.txt`, attaches everything
43
+ to the GH release and publishes npm — documented in `docs/ci.md`. A brew
44
+ formula generator (`scripts/release/generate-brew-formula.mjs`) is in-repo
45
+ but the channel is deferred until first users (ARV-387); its pipeline step
46
+ self-skips while `TAP_GITHUB_TOKEN` is unset.
47
+ - **`zond audit --safe` (ARV-390):** explicit alias of the default safe mode
48
+ (docs and skills always said `--safe`, but the flag didn't exist — a
49
+ stranger's first command died on `unknown option`). Conflicts with `--live`.
50
+ - **Strict-TLS opt-in (ARV-367):** runtime HTTP keeps lax TLS by default
51
+ (internal/dev targets behind corp CAs); `ZOND_STRICT_TLS=1` turns
52
+ certificate verification on for public-API audits. Documented as a TLS
53
+ policy section in ZOND.md + the zond-checks skill.
54
+ - **Raw-apiKey hint (ARV-367):** `doctor` warns when a securityScheme is
55
+ `apiKey` in the `Authorization` header — set `auth_token` to the RAW key,
56
+ no `Bearer ` prefix.
57
+
58
+ ### Fixed
59
+ - **Serializer dropped OPTIONS/HEAD/TRACE method keys (ARV-390):** emitted
60
+ probe-methods suites failed the runner's own validation and were skipped
61
+ with a scary warning on a stranger's first run. Empty `json: {}` bodies no
62
+ longer degrade to a bare `json:` (null). Round-trip regression tests added.
63
+ - **Cold-start dead-ends (ARV-390):** `init` next-steps now end at
64
+ `zond audit --api <name> --safe`; `doctor`'s all-set output points at the
65
+ same next action instead of stopping silently.
66
+
67
+ ### Docs
68
+ - **README distribution pass (ARV-391):** install matrix (curl/npm/win/manual),
69
+ safe-by-default trust note above the fold, animated SVG terminal demo
70
+ (`docs/demo.svg`), one-liner positioning for strangers.
71
+
7
72
  ## [0.26.1] — 2026-07-09
8
73
 
9
74
  Follow-up from the v0.26.0 petstore verify audit (report-zond findings).
package/README.md CHANGED
@@ -1,19 +1,47 @@
1
1
  # zond
2
2
 
3
- AI-powered API testing for Claude Code, Cursor, and CI/CD.
3
+ API hygiene scanner for small teams and their coding agents — test REST API endpoints against the OpenAPI spec, catch contract drift, track coverage.
4
4
 
5
- Say "test my API" get working tests, coverage dashboard, and CI config in minutes.
5
+ **Use it when you need to:** test REST API endpoints from an OpenAPI spec,
6
+ verify an API contract after a deploy, debug a failing HTTP request with
7
+ stored run history, or raise endpoint coverage. Works standalone or through
8
+ a coding agent (Claude Code, Cursor) — say "test my API" and the agent
9
+ drives zond for you.
10
+
11
+ ```bash
12
+ curl -fsSL https://raw.githubusercontent.com/kirrosh/zond/master/install.sh | sh # or: npm i -g @kirrosh/zond
13
+ zond init && zond add api my-api --spec ./openapi.json
14
+ zond audit --api my-api --safe # read-only pass: spec lint → probes → tests → coverage → HTML report
15
+ ```
16
+
17
+ > **Safe by default.** The first run (`zond audit --safe`) sends read-only
18
+ > GET traffic — no writes, no deletes, nothing destructive. Mutating
19
+ > probes require an explicit `--live` opt-in, meant for throwaway/sandbox
20
+ > accounts only. Running zond against your API won't break it.
21
+
22
+ ![zond demo — install to first audit](docs/demo.svg)
6
23
 
7
24
  Zond reads your OpenAPI spec and gives your AI agent everything it needs to test your API: a focused CLI, safety guardrails, coverage tracking, and run history. You don't need to learn anything new — just describe what you want and the agent runs `zond` commands.
8
25
 
9
- ## Quick Start
26
+ ## Install
10
27
 
11
- Install the binary (no Node.js required):
28
+ | Channel | Command |
29
+ |---|---|
30
+ | **curl** (macOS/Linux) | `curl -fsSL https://raw.githubusercontent.com/kirrosh/zond/master/install.sh \| sh` |
31
+ | **npm** (needs Node 20+) | `npm install -g @kirrosh/zond` |
32
+ | **Windows** | `iwr https://raw.githubusercontent.com/kirrosh/zond/master/install.ps1 \| iex` |
33
+ | **Manual** | grab a binary from [releases](https://github.com/kirrosh/zond/releases/latest) (darwin arm64/x64, linux x64/arm64, win x64) |
34
+ | **Claude Code plugin** | `/plugin marketplace add kirrosh/zond` then `/plugin install zond@zond` — the agent skills without `zond init` |
35
+ | **Agent skills** ([skills.sh](https://www.skills.sh)) | `npx skills add kirrosh/zond` |
12
36
 
13
- ```bash
14
- curl -fsSL https://raw.githubusercontent.com/kirrosh/zond/master/install.sh | sh # macOS/Linux
15
- iwr https://raw.githubusercontent.com/kirrosh/zond/master/install.ps1 | iex # Windows
16
- ```
37
+ The plugin/skills channels ship the [five zond skills](skills/) (audit
38
+ pipeline, depth checks, fixture seeding, triage, target warm-up); the zond
39
+ binary itself still comes from any of the channels above.
40
+
41
+ Every channel ships the same self-contained binary — no Bun or Node
42
+ required at runtime (npm uses Node only as a thin launcher).
43
+
44
+ ## Quick Start
17
45
 
18
46
  Bootstrap a workspace, register your first API, then fill its fixtures:
19
47
 
@@ -56,21 +84,13 @@ upstream spec changes.
56
84
 
57
85
  Then say to your agent: _"Safely cover the API from openapi.json with tests."_
58
86
 
59
- Want the whole pipeline at once? `zond audit --api my-api` runs
87
+ Want the whole pipeline at once? `zond audit --api my-api --safe` runs
60
88
  prepare-fixtures → generate → probes → run → coverage → HTML report in a
61
- single shot.
62
-
63
- <details>
64
- <summary>Other installation methods (npx)</summary>
65
-
66
- ```bash
67
- npx -y @kirrosh/zond --version
68
- ```
89
+ single read-only shot (safe is the default even without the flag). Add
90
+ `--live` for the mutating stages once you're pointed at a sandbox.
69
91
 
70
92
  See [ZOND.md](ZOND.md) for the full CLI reference.
71
93
 
72
- </details>
73
-
74
94
  ## What Happens
75
95
 
76
96
  1. **Point** — you give the agent an OpenAPI spec
@@ -117,9 +137,6 @@ curl -fsSL https://raw.githubusercontent.com/kirrosh/zond/master/install.sh | sh
117
137
 
118
138
  # npm
119
139
  npm install -g @kirrosh/zond@latest
120
-
121
- # bun
122
- bun install -g @kirrosh/zond@latest
123
140
  ```
124
141
 
125
142
  ## Shell completions
package/bin/zond.mjs ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ // Thin launcher: exec the platform binary fetched by scripts/npm/postinstall.mjs.
3
+ import { spawnSync } from 'node:child_process'
4
+ import { existsSync } from 'node:fs'
5
+ import { dirname, join } from 'node:path'
6
+ import { fileURLToPath } from 'node:url'
7
+
8
+ const bin = join(
9
+ dirname(fileURLToPath(import.meta.url)),
10
+ process.platform === 'win32' ? 'zond-bin.exe' : 'zond-bin',
11
+ )
12
+
13
+ if (!existsSync(bin)) {
14
+ console.error(
15
+ 'zond binary is missing — the postinstall download likely failed.\n' +
16
+ 'Re-run: npm rebuild @kirrosh/zond\n' +
17
+ 'Or install another way: https://github.com/kirrosh/zond#install',
18
+ )
19
+ process.exit(1)
20
+ }
21
+
22
+ const result = spawnSync(bin, process.argv.slice(2), { stdio: 'inherit' })
23
+ process.exit(result.status ?? 1)
package/package.json CHANGED
@@ -1,60 +1,73 @@
1
1
  {
2
2
  "name": "@kirrosh/zond",
3
- "version": "0.26.1",
4
- "description": "API testing platformdefine tests in YAML, run from CLI, generate from OpenAPI specs",
3
+ "version": "0.27.1",
4
+ "description": "API hygiene scanner for small teams and their coding agents test REST API endpoints against the OpenAPI spec, catch contract drift, track coverage.",
5
5
  "license": "MIT",
6
- "module": "index.ts",
7
6
  "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/kirrosh/zond.git"
10
+ },
11
+ "homepage": "https://github.com/kirrosh/zond#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/kirrosh/zond/issues"
14
+ },
8
15
  "keywords": [
9
- "api",
10
- "testing",
16
+ "api-testing",
17
+ "rest-api",
11
18
  "openapi",
12
- "yaml",
19
+ "contract-testing",
20
+ "contract-drift",
21
+ "schema-validation",
22
+ "api-coverage",
23
+ "api-hygiene",
24
+ "coding-agents",
25
+ "ai-agents",
26
+ "claude-code",
27
+ "sarif",
13
28
  "cli",
14
- "rest",
15
29
  "http"
16
30
  ],
17
31
  "files": [
18
- "src/",
32
+ "bin/zond.mjs",
33
+ "scripts/npm/postinstall.mjs",
19
34
  "README.md",
20
35
  "LICENSE",
21
36
  "CHANGELOG.md"
22
37
  ],
23
38
  "bin": {
24
- "zond": "src/cli/index.ts"
39
+ "zond": "bin/zond.mjs"
25
40
  },
26
41
  "scripts": {
27
42
  "zond": "bun run src/cli/index.ts",
43
+ "postinstall": "node scripts/npm/postinstall.mjs",
28
44
  "backlog": "bunx backlog",
29
45
  "board": "bunx backlog board",
30
46
  "test": "bun run test:unit && bun run test:mocked",
31
47
  "test:unit": "bun test tests/",
32
48
  "test:mocked": "bun run scripts/run-mocked-tests.ts",
33
- "check": "tsc --noEmit --project tsconfig.json",
49
+ "check": "tsc --noEmit --project tsconfig.json && bun run scripts/sync-plugin-skills.ts --check",
34
50
  "schemas": "bun run scripts/emit-json-schemas.ts",
35
51
  "schemas:check": "bun run scripts/emit-json-schemas.ts --check",
36
52
  "lint:dead": "knip --reporter compact",
37
- "build": "bun build --compile src/cli/index.ts --outfile dist/zond && bun run scripts/codesign-darwin.ts ./dist/zond"
53
+ "build": "bun build --compile src/cli/index.ts --outfile dist/zond && bun run scripts/codesign-darwin.ts ./dist/zond",
54
+ "sync:plugin": "bun run scripts/sync-plugin-skills.ts"
38
55
  },
39
56
  "devDependencies": {
40
- "@types/bun": "latest",
41
- "ajv-draft-04": "^1.0.0",
42
- "backlog.md": "^1.44.0",
43
- "knip": "^6.7.0"
44
- },
45
- "engines": {
46
- "bun": ">=1.1.0"
47
- },
48
- "peerDependencies": {
49
- "typescript": "^5"
50
- },
51
- "dependencies": {
52
57
  "@readme/openapi-parser": "^5.5.0",
58
+ "@types/bun": "latest",
53
59
  "ajv": "^8.20.0",
60
+ "ajv-draft-04": "^1.0.0",
54
61
  "ajv-formats": "^3.0.1",
62
+ "backlog.md": "^1.44.0",
55
63
  "commander": "^14.0.0",
64
+ "knip": "^6.7.0",
56
65
  "openapi-types": "^12.1.3",
66
+ "typescript": "^5",
57
67
  "yaml": "^2.8.3",
58
68
  "zod": "^4.3.6"
69
+ },
70
+ "engines": {
71
+ "node": ">=20"
59
72
  }
60
73
  }
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env node
2
+ // npm postinstall: download the platform binary from the GitHub release
3
+ // matching this package version, verify its sha256, drop it next to the
4
+ // bin launcher. Pure node stdlib — the published package has zero deps.
5
+ import { createHash } from 'node:crypto'
6
+ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
7
+ import { dirname, join } from 'node:path'
8
+ import { fileURLToPath, pathToFileURL } from 'node:url'
9
+
10
+ const REPO = 'kirrosh/zond'
11
+ const PKG_DIR = join(dirname(fileURLToPath(import.meta.url)), '..', '..')
12
+
13
+ export function resolveTarget(platform = process.platform, arch = process.arch) {
14
+ const os = { darwin: 'darwin', linux: 'linux', win32: 'win' }[platform]
15
+ const cpu = { x64: 'x64', arm64: 'arm64' }[arch]
16
+ if (!os || !cpu || (os === 'win' && cpu !== 'x64')) {
17
+ throw new Error(
18
+ `Unsupported platform: ${platform}-${arch}. Prebuilt binaries: https://github.com/${REPO}/releases`,
19
+ )
20
+ }
21
+ return `${os}-${cpu}`
22
+ }
23
+
24
+ export function sha256(buf) {
25
+ return createHash('sha256').update(buf).digest('hex')
26
+ }
27
+
28
+ export function expectedChecksum(checksumsText, artifact) {
29
+ for (const line of checksumsText.split('\n')) {
30
+ const [hash, name] = line.trim().split(/\s+/)
31
+ if (name === artifact) return hash
32
+ }
33
+ throw new Error(`No entry for ${artifact} in checksums.txt`)
34
+ }
35
+
36
+ async function download(url) {
37
+ const res = await fetch(url)
38
+ if (!res.ok) throw new Error(`Download failed (${res.status}): ${url}`)
39
+ return Buffer.from(await res.arrayBuffer())
40
+ }
41
+
42
+ async function main() {
43
+ if (process.env.ZOND_SKIP_DOWNLOAD) return
44
+ // Dev checkout (src/ is not shipped in the npm package) — nothing to download.
45
+ if (existsSync(join(PKG_DIR, 'src'))) return
46
+
47
+ const { version } = JSON.parse(readFileSync(join(PKG_DIR, 'package.json'), 'utf8'))
48
+ const target = resolveTarget()
49
+ const artifact = target === 'win-x64' ? `zond-${target}.exe` : `zond-${target}`
50
+ // ZOND_DOWNLOAD_BASE: mirror / clean-machine E2E override.
51
+ const base =
52
+ process.env.ZOND_DOWNLOAD_BASE || `https://github.com/${REPO}/releases/download/v${version}`
53
+ const [binary, checksums] = await Promise.all([
54
+ download(`${base}/${artifact}`),
55
+ download(`${base}/checksums.txt`),
56
+ ])
57
+
58
+ const expected = expectedChecksum(checksums.toString('utf8'), artifact)
59
+ const actual = sha256(binary)
60
+ if (actual !== expected) {
61
+ throw new Error(`Checksum mismatch for ${artifact}: expected ${expected}, got ${actual}`)
62
+ }
63
+
64
+ const dest = join(PKG_DIR, 'bin', target === 'win-x64' ? 'zond-bin.exe' : 'zond-bin')
65
+ mkdirSync(dirname(dest), { recursive: true })
66
+ writeFileSync(dest, binary)
67
+ chmodSync(dest, 0o755)
68
+ console.log(`zond ${version} (${target}) ready`)
69
+ }
70
+
71
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
72
+ main().catch((err) => {
73
+ console.error(`zond postinstall failed: ${err.message}`)
74
+ process.exit(1)
75
+ })
76
+ }
package/src/CLAUDE.md DELETED
@@ -1,112 +0,0 @@
1
- # src/ — architecture map
2
-
3
- Этот файл — точка входа в **код** zond. Workspace-контракт (`.api-fixtures.yaml` vs `.env.yaml`) — в [`../AGENTS.md`](../AGENTS.md), пользовательский CLI-референс — в [`../ZOND.md`](../ZOND.md).
4
-
5
- zond — API hygiene scanner. **Dumb-tool**: умеет дёргать API, собирать evidence, проверять конформность спеки. Не зовёт LLM, не делает решения — это работа агента, который скармливает zond'у YAML и читает обратно отчёты.
6
-
7
- ### Litmus-тест: что кладём в zond, а что оставляем агенту
8
-
9
- m-24 срезал эвристический слой (discovery/seed/cascade, severity-калибраторы, anti-FP-гейты, annotate-auto). Чтобы он не наползал обратно по одному «разумному» фиксу за раз — **каждое изменение zond проходит один тест: детерминировано ли оно?** Один и тот же вход → один и тот же выход, без догадок про намерение / вину / серьёзность / «баг ли это на самом деле».
10
-
11
- - ✅ **В zond** (детерминированное, быстрое, скучное): слать запросы, валидировать схемы, считать диффы, эмитить evidence + closed-enum хинты, **корректно ограничивать scope чека** (какие case-kinds / методы он оценивает), плюс plumbing/скорость/артефакты. zond — удобный работающий быстрый инструмент.
12
- - ❌ **Агенту** (суждение): severity, приоритет, атрибуция вины (spec vs backend), «это false-positive?», «это эксплуатируемо?», выдумывание фикстур, многопроходный discovery. Сложное решает умный агент, читая сырой evidence.
13
-
14
- Практическое следствие для «zond что-то пропустил/зашумил» из аудита: **чини scope чека детерминированно** (напр. ARV-340 — чек смотрел не те case-kinds) — но **не добавляй suppression/down-rank «это FP»** (это ровно тот anti-FP-гейт, что убрал ARV-337; FP отсекает агент в триаже). `recommended_action` — самая размытая из выживших поверхностей: это тонкий routing-хинт, держи его тупым, не выращивай в мини-классификатор.
15
-
16
- **Evidence-over-inference (ARV-376):** статические инференсеры по форме URL (какой `/list` владеет `{id}`, какой ресурс за `{code}`) имеют бесконечный хвост идиосинкразий спеков. **Не** закрывай хвост растущими хардкод-списками маркеров/глаголов (`LIST_VERB_SUFFIXES`, `ACCESSOR_MARKER_SEGS`, версии) — это ровно тот creep «по одному разумному за раз». Правило: **новый маркер/глагол добавляется только под реальный спек, который его упражняет**, никогда «на всякий случай» (так был убран спекулятивный `bycode`). А когда вывод неуверенный — не тупик (`miss-no-list`), а **`item.candidates`**: правдоподобные эндпоинты, ранжированные по структурной близости, БЕЗ выбора значения. Граф — детерминизм zond'а; выбор — суждение агента. Выравнивание двух слоёв, где один уже что-то делает (напр. version-strip, ARV-381), — это консистентность, не новое знание, и ок.
17
-
18
- ## Top-level layout
19
-
20
- | Каталог | Назначение |
21
- |---|---|
22
- | `cli/` | CLI surface: парсинг argv, регистрация команд, форматирование вывода. |
23
- | `core/` | Бизнес-логика: probes, checks, generators, runner, reporters. **Никакой зависимости от commander/process.argv** — это переиспользуемое ядро. |
24
- | `db/` | SQLite-слой: schema, migrations, queries для истории runs/results. |
25
-
26
- **Правило**: `cli/commands/<cmd>.ts` парсит флаги и зовёт функции из `core/`. Бизнес-логика в `cli/` — code-smell (см. ARV-257 про `bootstrap.ts`/`discover.ts`, которые лежат в `cli/commands/` но не регистрируют команды).
27
-
28
- ## cli/
29
-
30
- | Файл | Что |
31
- |---|---|
32
- | `index.ts` | Entry point бинаря. |
33
- | `program.ts` | Commander root: регистрирует все top-level команды. |
34
- | `argv.ts`, `resolve.ts` | Аргумент-парсинг и `--api` chain resolution (per-command > global > `ZOND_API` > `.zond/current-api`). |
35
- | `runtime.ts` | Глобальный runtime context (`api`, workspace paths, http auditor). |
36
- | `output.ts`, `json-envelope.ts`, `json-schemas.ts` | Forматирование результатов: текстовый/JSON envelope + Ajv-валидация envelope-схем. |
37
- | `status-filter.ts` | Общий парсер `--status` фильтров. |
38
- | `commands/` | Один файл на команду; `commands/init/`, `commands/api/`, `commands/probe/` — кластеры с subcommands. |
39
-
40
- Контракт команды: парсит флаги, валидирует через зависимости из `core/`, эмиттит результат через `output.ts` (envelope) или streaming-репортер.
41
-
42
- ## core/ — подсистемы
43
-
44
- | Подкаталог | Роль |
45
- |---|---|
46
- | `parser/`, `spec/` | Парсинг OpenAPI, dereferenced spec.json, extraction схем. |
47
- | `generator/` | Синтез тестовых YAML-сьютов из spec: `suite-generator.ts`, `data-factory.ts`, `resources-builder.ts`. |
48
- | `runner/` | HTTP-исполнение: `executor.ts`, retry, assertions, schema validation. |
49
- | `checks/` | Schemathesis-style depth checks (status_code_conformance, response_schema_conformance, idempotency, …). Орк — `checks/runner.ts`. Один файл = один check class. |
50
- | `probe/` | Активные security/mass-assignment probes. Сейчас два монолита (`security-probe.ts`, `mass-assignment-probe.ts`) — кандидаты на split (ARV-295, ARV-296). |
51
- | `lint/` | Static spec-lint (`check spec`). Не путать с `checks/` — это разные команды и разные envelope'ы. |
52
- | `diagnostics/` | Triage финдингов: классификация ошибок, hints, `recommended_action` mapping. |
53
- | `severity/` | Per-finding severity calibration (ARV-283–288 актуализирует). |
54
- | `anti-fp/` | Anti-false-positive guard'ы; registry-pattern частично (см. ARV-259). |
55
- | `coverage/` | Покрытие endpoint'ов: test-runs + audit-runs, dual-metric. |
56
- | `reporter/` | Форматирование `Run`/`Check`/`Probe` результатов в текст/JSON/NDJSON/SARIF/JUnit/HTML. |
57
- | `exporter/` | HTML-отчёты, case studies. |
58
- | `audit/` | Высокоуровневая `audit` команда — wraps checks + probe + report для CI-smoke. |
59
- | `workspace/` | Layout API-папки: `apis/<name>/{spec,catalog,resources,fixtures,env,secrets}`. |
60
- | `context/`, `identity/`, `secrets/` | Загрузка контекста запуска, identity tokens, секреты. |
61
- | `util/`, `utils.ts` | Общие хелперы. URL/headers/schema-валидация частично дублируются с `probe/shared.ts` — кандидат на консолидацию (ARV-297). |
62
- | `selectors/`, `meta/`, `classifier/` | Endpoint-classification и meta-аттрибуты для дискавери и группировки. |
63
- | `setup-api.ts` | Регистрация нового API в workspace (used by `add api`/`refresh-api`). |
64
-
65
- ## db/
66
-
67
- SQLite на bun. `migrations/` — versioned migrations, `schema.ts` — текущий schema, `queries/` — типизированные SQL. Используется reporter'ами и `coverage` для исторических runs. Retention (ARV-266): `zond db stats` — счётчики строк per `run_kind`; `zond db prune` — opt-in удаление (per-kind defaults: check/probe/request/fixture старше 7d, `regular` — forever; `--older-than 30d` для uniform-cutoff), VACUUM после delete.
68
-
69
- ## Data-flow по фазам
70
-
71
- zond работает по 5 фазам — это ментальная модель CLI и skills:
72
-
73
- ```
74
- Setup ──► Generate ──► Run ──► Analyze ──► Report
75
- ```
76
-
77
- | Фаза | Команды | Что происходит | Артефакты |
78
- |---|---|---|---|
79
- | **Setup** | `init`, `add api`, `refresh-api`, `use`, `doctor`, `prepare-fixtures` | Регистрируем API, тянем spec, заполняем `.env.yaml`. | `apis/<name>/{spec.json, .api-catalog.yaml, .api-resources.yaml, .api-fixtures.yaml, .env.yaml}` |
80
- | **Generate** | `generate`, `api annotate`, `prepare-fixtures` | Синтез тестовых YAML из spec + аннотации (seed-bodies, idempotency, pagination, lifecycle). | `apis/<name>/tests/*.yaml`, annotations |
81
- | **Run** | `run`, `session`, `request`, `audit`, `checks run`, `probe <class>` | Дергаем API: тесты, depth-checks, probes. Пишем runs/results в SQLite. | `runs/` (DB), HTTP-аудит |
82
- | **Analyze** | `coverage`, `db {runs,run,collections,diagnose,compare}`, `check spec`, `describe`, `catalog` | Триаж результатов, coverage-гэпы, lint спеки. Финдинги через `recommended_action`. | Envelope JSON, finding-streams |
83
- | **Report** | `report`, `report-bundle`, `--report {json,ndjson,sarif,junit,html,markdown}` | Конвертация runs/findings в shareable форматы. | NDJSON-stream, SARIF, HTML, JUnit |
84
-
85
- Полная картина с iron-rules и pre-flight checklist'ами — в skill-документах (`src/cli/commands/init/templates/skills/*.md`), которые ставятся через `zond init`.
86
-
87
- ## Extension points
88
-
89
- Когда добавляешь новую функциональность — это **типичные точки расширения**:
90
-
91
- | Хочу добавить | Куда |
92
- |---|---|
93
- | Новый depth-check (schemathesis-style) | `core/checks/checks/<name>.ts` + регистрация в `core/checks/runner.ts`. |
94
- | Новый probe-class (security/mass-assignment-like) | `core/probe/<class>/` подмодуль; subcommand в `cli/commands/probe/`. Не клади всё в один монолит — см. ARV-295/ARV-296. |
95
- | Новый reporter format | `core/reporter/<format>.ts`; зарегистрировать в `cli/output.ts` или per-command `--report`. |
96
- | Новый CLI флаг с envelope-выводом | `cli/commands/<cmd>.ts` + Ajv-схема в `cli/json-schemas.ts`. Envelope shape — единый, не плоди вариации. |
97
- | Анти-FP guard для check/probe | `core/anti-fp/rules/<rule>.ts` (registry-pattern в развитии — ARV-259). |
98
- | Новая diagnostic-hint | `core/diagnostics/` — closed-enum `recommended_action`, не магические строки. |
99
- | Новая DB-сущность | `src/db/migrations/<NNNN>-<name>.sql` + tiped query в `db/queries/`. |
100
-
101
- ## Workspace contract (ссылка)
102
-
103
- Артефакты в `apis/<name>/` (`spec.json` / `.api-catalog.yaml` / `.api-resources.yaml` / **`.api-fixtures.yaml` manifest** / **`.env.yaml` values**) — описаны в [`../AGENTS.md`](../AGENTS.md). Главное правило: **manifest — source of truth о списке переменных, env — только values**.
104
-
105
- ## Conventions
106
-
107
- - TypeScript strict mode, `bun run check` = `tsc --noEmit` без warnings (`noUnusedLocals`/`noUnusedParameters` включены).
108
- - Тесты — `bun test` (unit + integration), `bun run test:mocked` (HTTP-mocked).
109
- - Сборка — `bun run build` → single-file `dist/zond` бинарь.
110
- - Никаких `console.log` в `core/` — выводи через `cli/output.ts` или reporter slot.
111
- - Никаких SDK Anthropic/Ollama/прочих LLM-провайдеров внутри zond — это dumb-tool (см. `feedback_zond_no_llm_calls`).
112
- - Финдинги — через closed-enum `recommended_action`, агент триажит по enum, не по тексту message.
@@ -1,5 +0,0 @@
1
- declare module "*.css" {
2
- const path: string;
3
- export default path;
4
- }
5
-
package/src/cli/argv.ts DELETED
@@ -1,122 +0,0 @@
1
- /**
2
- * Pre-commander argv handling and shared argument parsers used across the
3
- * commander tree. Pulled out of program.ts (TASK-190, m-11) so program.ts
4
- * shrinks toward "just command registration" and these helpers can be
5
- * unit-tested in isolation.
6
- */
7
-
8
- import { InvalidArgumentError } from "commander";
9
- import type { ReporterName } from "../core/reporter/types.ts";
10
-
11
- // ── MSYS path preprocessing ──
12
- //
13
- // Git Bash on Windows converts API paths like "/users" → "C:/Program Files/Git/users".
14
- // We reverse that for flags whose values are API paths, not filesystem paths.
15
-
16
- const MSYS_PREFIX_RE = /^[A-Z]:[\\/](?:Program Files[\\/]Git|msys64|usr)[\\/]/i;
17
-
18
- const API_PATH_FLAGS = new Set(["--path", "--json-path"]);
19
-
20
- function stripMsysPath(value: string): string {
21
- if (!MSYS_PREFIX_RE.test(value)) return value;
22
- return value.replace(MSYS_PREFIX_RE, "/");
23
- }
24
-
25
- /**
26
- * Pre-process argv before commander sees it: undo Git Bash's MSYS path conversion
27
- * for `--path` and `--json-path` values (both `--path X` and `--path=X` forms).
28
- */
29
- export function preprocessArgv(argv: string[]): string[] {
30
- const out = [...argv];
31
- for (let i = 0; i < out.length; i++) {
32
- const arg = out[i]!;
33
-
34
- // --flag=value form
35
- const eqIdx = arg.indexOf("=");
36
- if (arg.startsWith("--") && eqIdx !== -1) {
37
- const flag = arg.slice(0, eqIdx);
38
- if (API_PATH_FLAGS.has(flag)) {
39
- out[i] = `${flag}=${stripMsysPath(arg.slice(eqIdx + 1))}`;
40
- }
41
- continue;
42
- }
43
-
44
- // --flag value form
45
- if (API_PATH_FLAGS.has(arg)) {
46
- const next = out[i + 1];
47
- if (next !== undefined && !next.startsWith("-")) {
48
- out[i + 1] = stripMsysPath(next);
49
- }
50
- }
51
- }
52
- return out;
53
- }
54
-
55
- // ── Argument parsers ──
56
-
57
- export function parsePositiveInt(name: string): (raw: string) => number {
58
- return (raw: string) => {
59
- const n = Number.parseInt(raw, 10);
60
- if (Number.isNaN(n) || n <= 0) {
61
- throw new InvalidArgumentError(`Invalid ${name} value: ${raw}`);
62
- }
63
- return n;
64
- };
65
- }
66
-
67
- /** `--rate-limit` accepts a positive integer (req/sec cap) or the literal
68
- * string `auto` (no static cap; throttle adaptively from ratelimit-* headers). */
69
- export function parseRateLimit(raw: string): number | "auto" {
70
- if (raw.toLowerCase() === "auto") return "auto";
71
- const n = Number.parseInt(raw, 10);
72
- if (Number.isNaN(n) || n <= 0) {
73
- throw new InvalidArgumentError(`Invalid --rate-limit value: ${raw} (expected a positive integer or "auto")`);
74
- }
75
- return n;
76
- }
77
-
78
- export function parseNonNegativeInt(name: string): (raw: string) => number {
79
- return (raw: string) => {
80
- const n = Number.parseInt(raw, 10);
81
- if (Number.isNaN(n) || n < 0 || String(n) !== raw.trim()) {
82
- throw new InvalidArgumentError(`Invalid ${name} value: ${raw} (expected a non-negative integer)`);
83
- }
84
- return n;
85
- };
86
- }
87
-
88
- export function parseInteger(name: string): (raw: string) => number {
89
- return (raw: string) => {
90
- const n = Number.parseInt(raw, 10);
91
- if (Number.isNaN(n)) {
92
- throw new InvalidArgumentError(`Invalid ${name} value: ${raw}`);
93
- }
94
- return n;
95
- };
96
- }
97
-
98
- export function parsePercentage(raw: string): number {
99
- const n = Number.parseInt(raw, 10);
100
- if (Number.isNaN(n) || n < 0 || n > 100) {
101
- throw new InvalidArgumentError(`Invalid --fail-on-coverage value: ${raw} (must be 0–100)`);
102
- }
103
- return n;
104
- }
105
-
106
- export const collect = (val: string, prev: string[]): string[] => [...prev, val];
107
-
108
- const VALID_REPORTERS = new Set<string>(["console", "json", "junit"]);
109
-
110
- export function parseReporter(raw: string): ReporterName {
111
- if (!VALID_REPORTERS.has(raw)) {
112
- throw new InvalidArgumentError(`Unknown reporter: ${raw}. Available: console, json, junit`);
113
- }
114
- return raw as ReporterName;
115
- }
116
-
117
- /** Helper: split repeatable values like ["a,b", "c"] → ["a", "b", "c"] */
118
- export function flatSplit(values: string[] | undefined): string[] | undefined {
119
- if (!values || values.length === 0) return undefined;
120
- const out = values.flatMap((v) => v.split(",")).filter(Boolean);
121
- return out.length > 0 ? out : undefined;
122
- }