@kirrosh/zond 0.21.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +758 -3
- package/README.md +78 -15
- package/package.json +17 -10
- package/src/cli/argv.ts +122 -0
- package/src/cli/commands/add-api.ts +134 -0
- package/src/cli/commands/api/annotate/idempotency.ts +59 -0
- package/src/cli/commands/api/annotate/index.ts +525 -0
- package/src/cli/commands/api/annotate/lifecycle.ts +74 -0
- package/src/cli/commands/api/annotate/overlay.ts +206 -0
- package/src/cli/commands/api/annotate/pagination.ts +60 -0
- package/src/cli/commands/api/annotate/prompts.ts +183 -0
- package/src/cli/commands/api/annotate/readback.ts +58 -0
- package/src/cli/commands/api/annotate/resources.ts +91 -0
- package/src/cli/commands/api/annotate/seed-bodies.ts +61 -0
- package/src/cli/commands/audit.ts +480 -0
- package/src/cli/commands/bootstrap.ts +710 -0
- package/src/cli/commands/catalog.ts +35 -0
- package/src/cli/commands/check.ts +348 -0
- package/src/cli/commands/checks.ts +756 -0
- package/src/cli/commands/ci-init.ts +55 -6
- package/src/cli/commands/clean.ts +212 -0
- package/src/cli/commands/cleanup.ts +262 -0
- package/src/cli/commands/completions.ts +192 -0
- package/src/cli/commands/coverage.ts +605 -132
- package/src/cli/commands/db.ts +180 -8
- package/src/cli/commands/describe.ts +37 -2
- package/src/cli/commands/discover.ts +1236 -0
- package/src/cli/commands/doctor.ts +607 -0
- package/src/cli/commands/fixtures.ts +402 -0
- package/src/cli/commands/generate.ts +420 -47
- package/src/cli/commands/init/agents-md.ts +61 -0
- package/src/cli/commands/init/bootstrap.ts +108 -0
- package/src/cli/commands/init/index.ts +244 -0
- package/src/cli/commands/init/skills.ts +98 -0
- package/src/cli/commands/init/templates/agents.md +77 -0
- package/src/cli/commands/init/templates/markdown.d.ts +4 -0
- package/src/cli/commands/init/templates/skills/zond-checks.md +397 -0
- package/src/cli/commands/init/templates/skills/zond-triage.md +210 -0
- package/src/cli/commands/init/templates/skills/zond.md +651 -0
- package/src/cli/commands/init/templates/zond-config.yml +14 -0
- package/src/cli/commands/prepare-fixtures.ts +135 -0
- package/src/cli/commands/probe/mass-assignment.ts +503 -0
- package/src/cli/commands/probe/security.ts +454 -0
- package/src/cli/commands/probe/static.ts +255 -0
- package/src/cli/commands/probe/webhooks.ts +161 -0
- package/src/cli/commands/probe.ts +459 -0
- package/src/cli/commands/reference.ts +87 -0
- package/src/cli/commands/refresh-api.ts +169 -0
- package/src/cli/commands/remove-api.ts +150 -0
- package/src/cli/commands/report-bundle.ts +318 -0
- package/src/cli/commands/report.ts +241 -0
- package/src/cli/commands/request.ts +379 -4
- package/src/cli/commands/run.ts +911 -33
- package/src/cli/commands/session.ts +244 -0
- package/src/cli/commands/use.ts +74 -0
- package/src/cli/index.ts +36 -607
- package/src/cli/json-envelope.ts +112 -3
- package/src/cli/json-schemas.ts +263 -0
- package/src/cli/program.ts +218 -0
- package/src/cli/resolve.ts +105 -0
- package/src/cli/status-filter.ts +124 -0
- package/src/cli/util/api-context.ts +85 -0
- package/src/cli/version.ts +8 -0
- package/src/core/anti-fp/bootstrap.ts +34 -0
- package/src/core/anti-fp/index.ts +33 -0
- package/src/core/anti-fp/registry.ts +44 -0
- package/src/core/anti-fp/rules/baseline-echo.ts +74 -0
- package/src/core/anti-fp/rules/schemathesis/body_negation_becomes_valid.ts +52 -0
- package/src/core/anti-fp/rules/schemathesis/coverage_phase_boundary_positive.ts +38 -0
- package/src/core/anti-fp/rules/schemathesis/has_unverifiable_mutations.ts +35 -0
- package/src/core/anti-fp/rules/schemathesis/index.ts +24 -0
- package/src/core/anti-fp/rules/schemathesis/string_type_mutation_becomes_valid.ts +53 -0
- package/src/core/anti-fp/rules/subscription-gated/index.ts +11 -0
- package/src/core/anti-fp/rules/subscription-gated/paid-plan-403.ts +75 -0
- package/src/core/anti-fp/types.ts +68 -0
- package/src/core/checks/checks/_crud-helpers.ts +133 -0
- package/src/core/checks/checks/_negative_mutator.ts +133 -0
- package/src/core/checks/checks/_readback-helpers.ts +133 -0
- package/src/core/checks/checks/content_type_conformance.ts +39 -0
- package/src/core/checks/checks/cross_call_references.ts +134 -0
- package/src/core/checks/checks/ensure_resource_availability.ts +62 -0
- package/src/core/checks/checks/idempotency_replay.ts +246 -0
- package/src/core/checks/checks/ignored_auth.ts +211 -0
- package/src/core/checks/checks/index.ts +65 -0
- package/src/core/checks/checks/lifecycle_transitions.ts +273 -0
- package/src/core/checks/checks/missing_required_header.ts +40 -0
- package/src/core/checks/checks/negative_data_rejection.ts +45 -0
- package/src/core/checks/checks/not_a_server_error.ts +27 -0
- package/src/core/checks/checks/open_cors_on_sensitive.ts +131 -0
- package/src/core/checks/checks/pagination_invariants.ts +238 -0
- package/src/core/checks/checks/positive_data_acceptance.ts +36 -0
- package/src/core/checks/checks/rate_limit_headers_absent.ts +77 -0
- package/src/core/checks/checks/response_headers_conformance.ts +74 -0
- package/src/core/checks/checks/response_schema_conformance.ts +30 -0
- package/src/core/checks/checks/status_code_conformance.ts +61 -0
- package/src/core/checks/checks/unsupported_method.ts +63 -0
- package/src/core/checks/checks/use_after_free.ts +78 -0
- package/src/core/checks/index.ts +30 -0
- package/src/core/checks/mode.ts +79 -0
- package/src/core/checks/recommended-action.ts +64 -0
- package/src/core/checks/registry.ts +78 -0
- package/src/core/checks/runner.ts +874 -0
- package/src/core/checks/sarif.ts +230 -0
- package/src/core/checks/stateful.ts +121 -0
- package/src/core/checks/types.ts +189 -0
- package/src/core/classifier/recommended-action.ts +222 -0
- package/src/core/context/current.ts +51 -0
- package/src/core/context/session.ts +78 -0
- package/src/core/coverage/loader.ts +185 -0
- package/src/core/coverage/reasons.ts +300 -0
- package/src/core/diagnostics/db-analysis.ts +161 -12
- package/src/core/diagnostics/failure-class.ts +120 -0
- package/src/core/diagnostics/failure-hints.ts +212 -9
- package/src/core/diagnostics/spec-pointer.ts +99 -0
- package/src/core/diagnostics/suggested-fixes.ts +156 -0
- package/src/core/exporter/case-study/index.ts +270 -0
- package/src/core/exporter/curl.ts +40 -0
- package/src/core/exporter/exporter.ts +48 -0
- package/src/core/exporter/html-report/escape.ts +24 -0
- package/src/core/exporter/html-report/index.ts +479 -0
- package/src/core/exporter/html-report/script.ts +100 -0
- package/src/core/exporter/html-report/styles.ts +408 -0
- package/src/core/generator/chunker.ts +53 -15
- package/src/core/generator/coverage-phase.ts +0 -0
- package/src/core/generator/create-body.ts +89 -0
- package/src/core/generator/data-factory.ts +490 -33
- package/src/core/generator/describe.ts +1 -1
- package/src/core/generator/fixtures-builder.ts +325 -0
- package/src/core/generator/index.ts +7 -5
- package/src/core/generator/openapi-reader.ts +55 -3
- package/src/core/generator/path-param-disambig.ts +114 -0
- package/src/core/generator/resources-builder.ts +648 -0
- package/src/core/generator/schema-utils.ts +11 -3
- package/src/core/generator/serializer.ts +114 -15
- package/src/core/generator/suite-generator.ts +484 -77
- package/src/core/generator/types.ts +8 -0
- package/src/core/identity/identity-file.ts +129 -0
- package/src/core/lint/affects.ts +28 -0
- package/src/core/lint/config.ts +96 -0
- package/src/core/lint/format.ts +42 -0
- package/src/core/lint/index.ts +94 -0
- package/src/core/lint/reporter.ts +128 -0
- package/src/core/lint/rules/consistency.ts +158 -0
- package/src/core/lint/rules/heuristics.ts +97 -0
- package/src/core/lint/rules/strictness.ts +109 -0
- package/src/core/lint/types.ts +96 -0
- package/src/core/lint/walker.ts +248 -0
- package/src/core/meta/meta-store.ts +6 -73
- package/src/core/output/README.md +91 -0
- package/src/core/output/index.ts +13 -0
- package/src/core/output/run.ts +126 -0
- package/src/core/output/types.ts +129 -0
- package/src/core/parser/env-interpolation.ts +104 -0
- package/src/core/parser/filter.ts +57 -0
- package/src/core/parser/schema.ts +132 -5
- package/src/core/parser/types.ts +29 -2
- package/src/core/parser/variables.ts +0 -0
- package/src/core/parser/yaml-parser.ts +108 -13
- package/src/core/probe/bootstrap.ts +34 -0
- package/src/core/probe/dry-run-envelope.ts +57 -0
- package/src/core/probe/mass-assignment-probe-class.ts +198 -0
- package/src/core/probe/mass-assignment-probe.ts +1122 -0
- package/src/core/probe/mass-assignment-template.ts +212 -0
- package/src/core/probe/method-probe.ts +164 -0
- package/src/core/probe/method-shared.ts +69 -0
- package/src/core/probe/negative-probe.ts +691 -0
- package/src/core/probe/orphan-tracker.ts +188 -0
- package/src/core/probe/path-discovery.ts +440 -0
- package/src/core/probe/probe-harness.ts +120 -0
- package/src/core/probe/registry.ts +89 -0
- package/src/core/probe/runner.ts +136 -0
- package/src/core/probe/security-probe-class.ts +201 -0
- package/src/core/probe/security-probe.ts +1453 -0
- package/src/core/probe/shared.ts +505 -0
- package/src/core/probe/static-probe-class.ts +125 -0
- package/src/core/probe/types.ts +165 -0
- package/src/core/probe/verdict-aggregator.ts +33 -0
- package/src/core/probe/webhooks-probe.ts +284 -0
- package/src/core/reporter/console.ts +69 -4
- package/src/core/reporter/index.ts +2 -3
- package/src/core/reporter/json.ts +15 -2
- package/src/core/reporter/junit.ts +27 -12
- package/src/core/reporter/ndjson.ts +37 -0
- package/src/core/reporter/types.ts +3 -0
- package/src/core/runner/assertions.ts +62 -2
- package/src/core/runner/async-pool.ts +108 -0
- package/src/core/runner/auth-path.ts +8 -0
- package/src/core/runner/ci-context.ts +72 -0
- package/src/core/runner/executor.ts +391 -52
- package/src/core/runner/form-encode.ts +51 -0
- package/src/core/runner/http-client.ts +115 -7
- package/src/core/runner/learn-drift.ts +293 -0
- package/src/core/runner/preflight-vars.ts +149 -0
- package/src/core/runner/progress-tracker.ts +73 -0
- package/src/core/runner/rate-limiter.ts +203 -0
- package/src/core/runner/run-kind.ts +39 -0
- package/src/core/runner/schema-validator.ts +312 -0
- package/src/core/runner/send-request.ts +153 -20
- package/src/core/runner/types.ts +38 -0
- package/src/core/secrets/registry.ts +164 -0
- package/src/core/secrets/secrets-file.ts +115 -0
- package/src/core/selectors/operation-filter.ts +144 -0
- package/src/core/setup-api.ts +419 -17
- package/src/core/severity/category.ts +94 -0
- package/src/core/severity/index.ts +121 -0
- package/src/core/spec/layers.ts +154 -0
- package/src/core/util/format-eta.ts +21 -0
- package/src/core/utils.ts +5 -1
- package/src/core/workspace/config.ts +129 -0
- package/src/core/workspace/manifest.ts +283 -0
- package/src/core/workspace/output-rotation.ts +62 -0
- package/src/core/workspace/root.ts +94 -0
- package/src/core/workspace/triage-path.ts +87 -0
- package/src/db/lint-runs.ts +47 -0
- package/src/db/migrate.ts +126 -0
- package/src/db/migrations/0001_run_kind.sql +25 -0
- package/src/db/migrations/sql.d.ts +4 -0
- package/src/db/queries/collections.ts +133 -0
- package/src/db/queries/coverage.ts +9 -0
- package/src/db/queries/dashboard.ts +59 -0
- package/src/db/queries/results.ts +128 -0
- package/src/db/queries/runs.ts +235 -0
- package/src/db/queries/sessions.ts +42 -0
- package/src/db/queries/settings.ts +28 -0
- package/src/db/queries/types.ts +172 -0
- package/src/db/queries.ts +72 -802
- package/src/db/schema.ts +179 -48
- package/src/cli/commands/export.ts +0 -144
- package/src/cli/commands/guide.ts +0 -127
- package/src/cli/commands/init.ts +0 -57
- package/src/cli/commands/serve.ts +0 -81
- package/src/cli/commands/sync.ts +0 -269
- package/src/cli/commands/update.ts +0 -189
- package/src/cli/commands/validate.ts +0 -34
- package/src/core/exporter/postman.ts +0 -963
- package/src/core/generator/guide-builder.ts +0 -253
- package/src/core/meta/types.ts +0 -21
- package/src/core/parser/index.ts +0 -21
- package/src/core/runner/execute-run.ts +0 -132
- package/src/core/runner/index.ts +0 -12
- package/src/core/sync/spec-differ.ts +0 -38
- package/src/web/data/collection-state.ts +0 -362
- package/src/web/routes/api.ts +0 -314
- package/src/web/routes/dashboard.ts +0 -350
- package/src/web/routes/runs.ts +0 -64
- package/src/web/schemas.ts +0 -121
- package/src/web/server.ts +0 -134
- package/src/web/static/htmx.min.cjs +0 -1
- package/src/web/static/style.css +0 -1148
- package/src/web/views/endpoints-tab.ts +0 -174
- package/src/web/views/explorer-tab.ts +0 -402
- package/src/web/views/health-strip.ts +0 -92
- package/src/web/views/layout.ts +0 -48
- package/src/web/views/results.ts +0 -210
- package/src/web/views/runs-tab.ts +0 -126
- package/src/web/views/suites-tab.ts +0 -181
package/src/cli/index.ts
CHANGED
|
@@ -1,626 +1,55 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { ciInitCommand } from "./commands/ci-init.ts";
|
|
8
|
-
import { initCommand } from "./commands/init.ts";
|
|
9
|
-
import { describeCommand } from "./commands/describe.ts";
|
|
10
|
-
import { dbCommand } from "./commands/db.ts";
|
|
11
|
-
import { requestCommand } from "./commands/request.ts";
|
|
12
|
-
import { guideCommand } from "./commands/guide.ts";
|
|
13
|
-
import { generateCommand } from "./commands/generate.ts";
|
|
14
|
-
import { exportCommand } from "./commands/export.ts";
|
|
15
|
-
import { syncCommand } from "./commands/sync.ts";
|
|
16
|
-
import { updateCommand } from "./commands/update.ts";
|
|
17
|
-
import { catalogCommand } from "./commands/catalog.ts";
|
|
18
|
-
import { printError } from "./output.ts";
|
|
19
|
-
import { getRuntimeInfo } from "./runtime.ts";
|
|
20
|
-
import { getDb } from "../db/schema.ts";
|
|
21
|
-
import { findCollectionByNameOrId } from "../db/queries.ts";
|
|
22
|
-
import type { ReporterName } from "../core/reporter/types.ts";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { CommanderError } from "commander";
|
|
5
|
+
import { buildProgram, preprocessArgv } from "./program.ts";
|
|
6
|
+
import { VERSION } from "./version.ts";
|
|
23
7
|
|
|
24
|
-
|
|
25
|
-
export const VERSION = pkgVersion;
|
|
26
|
-
|
|
27
|
-
export interface ParsedArgs {
|
|
28
|
-
command: string | undefined;
|
|
29
|
-
positional: string[];
|
|
30
|
-
flags: Record<string, string | boolean>;
|
|
31
|
-
}
|
|
8
|
+
export { VERSION };
|
|
32
9
|
|
|
33
10
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
11
|
+
* Anything that reaches this handler is an *unexpected* throw — command
|
|
12
|
+
* implementations are expected to catch their own usage/config errors and
|
|
13
|
+
* return exit 2 themselves. Tag these with `[zond:internal]` so operators
|
|
14
|
+
* can tell them apart from sandbox/SIGKILL/OOM (137/143). See ZOND.md →
|
|
15
|
+
* "Exit codes" for the full taxonomy.
|
|
37
16
|
*/
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/** Flags whose values are API paths, not filesystem paths — subject to MSYS fix */
|
|
48
|
-
const API_PATH_FLAGS = new Set(["path", "json-path"]);
|
|
49
|
-
|
|
50
|
-
export function parseArgs(argv: string[]): ParsedArgs {
|
|
51
|
-
// argv: [bunPath, scriptPath, ...userArgs]
|
|
52
|
-
const args = argv.slice(2);
|
|
53
|
-
let command: string | undefined;
|
|
54
|
-
const positional: string[] = [];
|
|
55
|
-
const flags: Record<string, string | boolean> = {};
|
|
56
|
-
|
|
57
|
-
let i = 0;
|
|
58
|
-
while (i < args.length) {
|
|
59
|
-
const arg = args[i]!;
|
|
60
|
-
|
|
61
|
-
if (arg.startsWith("--")) {
|
|
62
|
-
const eqIndex = arg.indexOf("=");
|
|
63
|
-
if (eqIndex !== -1) {
|
|
64
|
-
// --flag=value
|
|
65
|
-
const key = arg.slice(2, eqIndex);
|
|
66
|
-
let val = arg.slice(eqIndex + 1);
|
|
67
|
-
if (API_PATH_FLAGS.has(key)) val = stripMsysPath(val);
|
|
68
|
-
flags[key] = val;
|
|
69
|
-
} else {
|
|
70
|
-
const key = arg.slice(2);
|
|
71
|
-
const next = args[i + 1];
|
|
72
|
-
if (next !== undefined && !next.startsWith("-")) {
|
|
73
|
-
flags[key] = API_PATH_FLAGS.has(key) ? stripMsysPath(next) : next;
|
|
74
|
-
i++;
|
|
75
|
-
} else {
|
|
76
|
-
flags[key] = true;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
} else if (arg.startsWith("-") && arg.length === 2) {
|
|
80
|
-
// Short flag: -h, -v
|
|
81
|
-
flags[arg.slice(1)] = true;
|
|
82
|
-
} else if (command === undefined) {
|
|
83
|
-
command = arg;
|
|
84
|
-
} else {
|
|
85
|
-
positional.push(arg);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
i++;
|
|
17
|
+
function reportInternalError(err: unknown): void {
|
|
18
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
19
|
+
const stack = err instanceof Error && err.stack ? err.stack : message;
|
|
20
|
+
const stackHash = createHash("sha1").update(stack).digest("hex").slice(0, 8);
|
|
21
|
+
process.stderr.write(`[zond:internal] zond ${VERSION} — uncaught ${message} (stack ${stackHash})\n`);
|
|
22
|
+
if (err instanceof Error && err.stack) {
|
|
23
|
+
process.stderr.write(err.stack + "\n");
|
|
89
24
|
}
|
|
90
|
-
|
|
91
|
-
return { command, positional, flags };
|
|
92
25
|
}
|
|
93
26
|
|
|
94
|
-
function
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
zond catalog <spec> Generate API catalog (compact endpoint reference)
|
|
112
|
-
zond sync <spec> Detect new/removed endpoints and generate tests for new ones
|
|
113
|
-
zond update Check for updates and self-update the binary
|
|
114
|
-
|
|
115
|
-
Options for 'run':
|
|
116
|
-
--dry-run Show requests without sending them (exit code always 0)
|
|
117
|
-
--env-var KEY=VALUE Inject env variable (repeatable, overrides env file)
|
|
118
|
-
--api <name> Use API collection (resolves test path automatically)
|
|
119
|
-
--env <name> Use environment file (.env.<name>.yaml)
|
|
120
|
-
--report <format> Output format: console, json, junit (default: console)
|
|
121
|
-
--timeout <ms> Override request timeout
|
|
122
|
-
--bail Stop on first suite failure
|
|
123
|
-
--no-db Do not save results to zond.db
|
|
124
|
-
--db <path> Path to SQLite database file (default: zond.db)
|
|
125
|
-
--auth-token <token> Auth token injected as {{auth_token}} variable
|
|
126
|
-
--safe Run only GET tests (read-only, safe mode)
|
|
127
|
-
--tag <tag> Filter suites by tag (repeatable, comma-separated, OR logic)
|
|
128
|
-
--exclude-tag <tag> Exclude suites by tag (repeatable, comma-separated)
|
|
129
|
-
--method <method> Filter tests by HTTP method (e.g. GET, POST)
|
|
130
|
-
|
|
131
|
-
Options for 'init':
|
|
132
|
-
--name <name> API name (auto-detected from spec title if omitted)
|
|
133
|
-
--spec <path> Path to OpenAPI spec file
|
|
134
|
-
--base-url <url> Override base URL
|
|
135
|
-
--force Overwrite existing API collection
|
|
136
|
-
|
|
137
|
-
Options for 'describe':
|
|
138
|
-
--compact List all endpoints briefly
|
|
139
|
-
--list-params List all unique parameters across all endpoints
|
|
140
|
-
--method <method> HTTP method for single endpoint detail
|
|
141
|
-
--path <path> Endpoint path for single endpoint detail
|
|
142
|
-
|
|
143
|
-
Options for 'db':
|
|
144
|
-
zond db collections List all API collections
|
|
145
|
-
zond db runs [--limit N] List recent test runs
|
|
146
|
-
zond db run <id> [--verbose] Show run details (--method GET, --status 403 to filter)
|
|
147
|
-
zond db diagnose <id> Diagnose run failures (--limit N examples per group, --verbose for all)
|
|
148
|
-
zond db compare <idA> <idB> Compare two runs
|
|
149
|
-
|
|
150
|
-
Options for 'request':
|
|
151
|
-
--header <H> Request header "Name: Value" (repeatable)
|
|
152
|
-
--body <json> Request body (JSON string)
|
|
153
|
-
--env <name> Environment for variable interpolation
|
|
154
|
-
--api <name> Collection name (loads env from its directory)
|
|
155
|
-
--json-path <path> Extract value from response (dot notation)
|
|
156
|
-
|
|
157
|
-
Options for 'generate':
|
|
158
|
-
--output <dir> Output directory for generated test files (required)
|
|
159
|
-
--tag <tag> Generate only for endpoints with this tag
|
|
160
|
-
--uncovered-only Skip endpoints already covered by existing tests
|
|
161
|
-
|
|
162
|
-
Options for 'guide':
|
|
163
|
-
--tests-dir <dir> Filter to uncovered endpoints only
|
|
164
|
-
--tag <tag> Generate only for endpoints with this tag
|
|
165
|
-
|
|
166
|
-
Options for 'coverage':
|
|
167
|
-
--api <name> Use API collection (auto-resolves spec and tests dir)
|
|
168
|
-
--spec <path> Path to OpenAPI spec (required unless --api used)
|
|
169
|
-
--tests <dir> Path to test files directory (required unless --api used)
|
|
170
|
-
--fail-on-coverage N Exit 1 when coverage percentage is below N (0–100)
|
|
171
|
-
--run-id <number> Cross-reference with a test run for pass/fail/5xx breakdown
|
|
172
|
-
|
|
173
|
-
Options for 'serve' / 'ui':
|
|
174
|
-
--port <port> Server port (default: 8080)
|
|
175
|
-
--host <host> Server host (default: 0.0.0.0)
|
|
176
|
-
--db <path> Path to SQLite database file (default: zond.db)
|
|
177
|
-
--open Open dashboard in browser after starting
|
|
178
|
-
--watch Enable dev mode with hot reload (auto-refresh browser on file changes)
|
|
179
|
-
|
|
180
|
-
Options for 'ci init':
|
|
181
|
-
--github Generate GitHub Actions workflow
|
|
182
|
-
--gitlab Generate GitLab CI config
|
|
183
|
-
--dir <path> Project root directory (default: current directory)
|
|
184
|
-
--force Overwrite existing CI config
|
|
185
|
-
|
|
186
|
-
Options for 'export postman':
|
|
187
|
-
--output <file> Output file path (default: collection.postman.json)
|
|
188
|
-
--env <file> Also export .env.yaml as Postman environment
|
|
189
|
-
--collection-name <name> Collection name (default: derived from path)
|
|
190
|
-
|
|
191
|
-
Options for 'catalog':
|
|
192
|
-
--output <dir> Output directory (default: current directory)
|
|
193
|
-
|
|
194
|
-
Options for 'sync':
|
|
195
|
-
--tests <dir> Path to test files directory (required)
|
|
196
|
-
--dry-run Show what would be generated without writing files
|
|
197
|
-
--tag <tag> Limit sync to endpoints with this tag
|
|
198
|
-
|
|
199
|
-
Options for 'update':
|
|
200
|
-
--check Only check for updates, do not download
|
|
201
|
-
|
|
202
|
-
General:
|
|
203
|
-
--json Output in JSON envelope format (available for all commands)
|
|
204
|
-
--help, -h Show this help
|
|
205
|
-
--version, -v Show version`);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const VALID_REPORTERS = new Set<string>(["console", "json", "junit"]);
|
|
209
|
-
|
|
210
|
-
async function main(): Promise<number> {
|
|
211
|
-
const { command, positional, flags } = parseArgs(process.argv);
|
|
212
|
-
const jsonFlag = flags["json"] === true;
|
|
213
|
-
|
|
214
|
-
// Help
|
|
215
|
-
if (command === "help" || command === "--help" || flags["help"] === true || flags["h"] === true) {
|
|
216
|
-
printUsage();
|
|
217
|
-
return 0;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Version
|
|
221
|
-
if (command === "--version" || flags["version"] === true || flags["v"] === true) {
|
|
222
|
-
console.log(`zond ${VERSION} (${getRuntimeInfo()})`);
|
|
223
|
-
return 0;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (!command) {
|
|
227
|
-
printUsage();
|
|
228
|
-
return 0;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
switch (command) {
|
|
232
|
-
case "run": {
|
|
233
|
-
let path = positional[0];
|
|
234
|
-
const apiFlag = typeof flags["api"] === "string" ? flags["api"] : undefined;
|
|
235
|
-
if (!path && apiFlag) {
|
|
236
|
-
try {
|
|
237
|
-
getDb(typeof flags["db"] === "string" ? flags["db"] : undefined);
|
|
238
|
-
const col = findCollectionByNameOrId(apiFlag);
|
|
239
|
-
if (!col) { printError(`API '${apiFlag}' not found`); return 1; }
|
|
240
|
-
path = col.test_path;
|
|
241
|
-
} catch (err) {
|
|
242
|
-
printError(`Failed to resolve --api: ${(err as Error).message}`);
|
|
243
|
-
return 2;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
if (!path) {
|
|
247
|
-
printError("Missing path argument. Usage: zond run <path> or zond run --api <name>");
|
|
248
|
-
return 2;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const report = (flags["report"] as string) ?? "console";
|
|
252
|
-
if (!VALID_REPORTERS.has(report)) {
|
|
253
|
-
printError(`Unknown reporter: ${report}. Available: console, json, junit`);
|
|
254
|
-
return 2;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const timeoutRaw = flags["timeout"];
|
|
258
|
-
let timeout: number | undefined;
|
|
259
|
-
if (typeof timeoutRaw === "string") {
|
|
260
|
-
timeout = parseInt(timeoutRaw, 10);
|
|
261
|
-
if (isNaN(timeout) || timeout <= 0) {
|
|
262
|
-
printError(`Invalid timeout value: ${timeoutRaw}`);
|
|
263
|
-
return 2;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Collect all --tag, --exclude-tag, and --env-var flags (parseArgs only stores last one, so re-parse)
|
|
268
|
-
const tagValues: string[] = [];
|
|
269
|
-
const excludeTagValues: string[] = [];
|
|
270
|
-
const envVarValues: string[] = [];
|
|
271
|
-
const rawRunArgs = process.argv.slice(2);
|
|
272
|
-
for (let i = 0; i < rawRunArgs.length; i++) {
|
|
273
|
-
const arg = rawRunArgs[i]!;
|
|
274
|
-
if (arg === "--tag" && rawRunArgs[i + 1]) {
|
|
275
|
-
tagValues.push(rawRunArgs[i + 1]!);
|
|
276
|
-
i++;
|
|
277
|
-
} else if (arg.startsWith("--tag=")) {
|
|
278
|
-
tagValues.push(arg.slice("--tag=".length));
|
|
279
|
-
} else if (arg === "--exclude-tag" && rawRunArgs[i + 1]) {
|
|
280
|
-
excludeTagValues.push(rawRunArgs[i + 1]!);
|
|
281
|
-
i++;
|
|
282
|
-
} else if (arg.startsWith("--exclude-tag=")) {
|
|
283
|
-
excludeTagValues.push(arg.slice("--exclude-tag=".length));
|
|
284
|
-
} else if (arg === "--env-var" && rawRunArgs[i + 1]) {
|
|
285
|
-
envVarValues.push(rawRunArgs[i + 1]!);
|
|
286
|
-
i++;
|
|
287
|
-
} else if (arg.startsWith("--env-var=")) {
|
|
288
|
-
envVarValues.push(arg.slice("--env-var=".length));
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
// Support comma-separated: --tag smoke,crud → ["smoke", "crud"]
|
|
292
|
-
const tags = tagValues.flatMap(v => v.split(",")).filter(Boolean);
|
|
293
|
-
const excludeTags = excludeTagValues.flatMap(v => v.split(",")).filter(Boolean);
|
|
294
|
-
|
|
295
|
-
return runCommand({
|
|
296
|
-
path,
|
|
297
|
-
env: typeof flags["env"] === "string" ? flags["env"] : undefined,
|
|
298
|
-
report: report as ReporterName,
|
|
299
|
-
timeout,
|
|
300
|
-
bail: flags["bail"] === true,
|
|
301
|
-
noDb: flags["no-db"] === true,
|
|
302
|
-
dbPath: typeof flags["db"] === "string" ? flags["db"] : undefined,
|
|
303
|
-
authToken: typeof flags["auth-token"] === "string" ? flags["auth-token"] : undefined,
|
|
304
|
-
safe: flags["safe"] === true,
|
|
305
|
-
tag: tags.length > 0 ? tags : undefined,
|
|
306
|
-
excludeTag: excludeTags.length > 0 ? excludeTags : undefined,
|
|
307
|
-
method: typeof flags["method"] === "string" ? flags["method"] : undefined,
|
|
308
|
-
envVars: envVarValues.length > 0 ? envVarValues : undefined,
|
|
309
|
-
dryRun: flags["dry-run"] === true,
|
|
310
|
-
json: jsonFlag,
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
case "validate": {
|
|
315
|
-
const path = positional[0];
|
|
316
|
-
if (!path) {
|
|
317
|
-
printError("Missing path argument. Usage: zond validate <path>");
|
|
318
|
-
return 2;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
return validateCommand({ path, json: jsonFlag });
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
case "ui":
|
|
325
|
-
case "serve": {
|
|
326
|
-
const portRaw = flags["port"];
|
|
327
|
-
let port: number | undefined;
|
|
328
|
-
if (typeof portRaw === "string") {
|
|
329
|
-
port = parseInt(portRaw, 10);
|
|
330
|
-
if (isNaN(port) || port <= 0) {
|
|
331
|
-
printError(`Invalid port value: ${portRaw}`);
|
|
332
|
-
return 2;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
return serveCommand({
|
|
336
|
-
port,
|
|
337
|
-
host: typeof flags["host"] === "string" ? flags["host"] : undefined,
|
|
338
|
-
dbPath: typeof flags["db"] === "string" ? flags["db"] : undefined,
|
|
339
|
-
watch: flags["watch"] === true,
|
|
340
|
-
open: command === "ui" || flags["open"] === true,
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
case "ci": {
|
|
345
|
-
const ciSub = positional[0];
|
|
346
|
-
if (ciSub !== "init") {
|
|
347
|
-
printError("Usage: zond ci init [--github|--gitlab] [--force]");
|
|
348
|
-
return 2;
|
|
349
|
-
}
|
|
350
|
-
let platform: "github" | "gitlab" | undefined;
|
|
351
|
-
if (flags["github"] === true) platform = "github";
|
|
352
|
-
else if (flags["gitlab"] === true) platform = "gitlab";
|
|
353
|
-
return ciInitCommand({
|
|
354
|
-
platform,
|
|
355
|
-
force: flags["force"] === true,
|
|
356
|
-
dir: typeof flags["dir"] === "string" ? flags["dir"] : undefined,
|
|
357
|
-
json: jsonFlag,
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
case "coverage": {
|
|
362
|
-
let spec = flags["spec"] as string | undefined;
|
|
363
|
-
let tests = flags["tests"] as string | undefined;
|
|
364
|
-
const coverageApiFlag = typeof flags["api"] === "string" ? flags["api"] : undefined;
|
|
365
|
-
|
|
366
|
-
if (coverageApiFlag) {
|
|
367
|
-
try {
|
|
368
|
-
getDb(typeof flags["db"] === "string" ? flags["db"] : undefined);
|
|
369
|
-
const col = findCollectionByNameOrId(coverageApiFlag);
|
|
370
|
-
if (!col) { printError(`API '${coverageApiFlag}' not found`); return 1; }
|
|
371
|
-
if (!spec && col.openapi_spec) spec = col.openapi_spec;
|
|
372
|
-
if (!tests && col.test_path) tests = col.test_path;
|
|
373
|
-
} catch (err) {
|
|
374
|
-
printError(`Failed to resolve --api: ${(err as Error).message}`);
|
|
375
|
-
return 2;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
if (typeof spec !== "string") {
|
|
380
|
-
printError("Missing --spec <path>. Usage: zond coverage --spec <path> --tests <dir>");
|
|
381
|
-
return 2;
|
|
382
|
-
}
|
|
383
|
-
if (typeof tests !== "string") {
|
|
384
|
-
printError("Missing --tests <dir>. Usage: zond coverage --spec <path> --tests <dir>");
|
|
385
|
-
return 2;
|
|
386
|
-
}
|
|
387
|
-
const failOnCoverageRaw = flags["fail-on-coverage"];
|
|
388
|
-
let failOnCoverage: number | undefined;
|
|
389
|
-
if (typeof failOnCoverageRaw === "string") {
|
|
390
|
-
failOnCoverage = parseInt(failOnCoverageRaw, 10);
|
|
391
|
-
if (isNaN(failOnCoverage) || failOnCoverage < 0 || failOnCoverage > 100) {
|
|
392
|
-
printError(`Invalid --fail-on-coverage value: ${failOnCoverageRaw} (must be 0–100)`);
|
|
393
|
-
return 2;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
const runIdRaw = flags["run-id"];
|
|
397
|
-
let runId: number | undefined;
|
|
398
|
-
if (typeof runIdRaw === "string") {
|
|
399
|
-
runId = parseInt(runIdRaw, 10);
|
|
400
|
-
if (isNaN(runId)) {
|
|
401
|
-
printError(`Invalid --run-id value: ${runIdRaw} (must be a number)`);
|
|
402
|
-
return 2;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
return coverageCommand({ spec, tests, failOnCoverage, runId, json: jsonFlag });
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
case "init": {
|
|
409
|
-
return initCommand({
|
|
410
|
-
name: typeof flags["name"] === "string" ? flags["name"] : undefined,
|
|
411
|
-
spec: typeof flags["spec"] === "string" ? flags["spec"] : positional[0],
|
|
412
|
-
baseUrl: typeof flags["base-url"] === "string" ? flags["base-url"] : undefined,
|
|
413
|
-
dir: typeof flags["dir"] === "string" ? flags["dir"] : undefined,
|
|
414
|
-
force: flags["force"] === true,
|
|
415
|
-
insecure: flags["insecure"] === true,
|
|
416
|
-
dbPath: typeof flags["db"] === "string" ? flags["db"] : undefined,
|
|
417
|
-
json: jsonFlag,
|
|
418
|
-
});
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
case "describe": {
|
|
422
|
-
const specPath = positional[0];
|
|
423
|
-
if (!specPath) {
|
|
424
|
-
printError("Missing spec path. Usage: zond describe <spec> [--compact | --method <M> --path <P>]");
|
|
425
|
-
return 2;
|
|
426
|
-
}
|
|
427
|
-
return describeCommand({
|
|
428
|
-
specPath,
|
|
429
|
-
compact: flags["compact"] === true,
|
|
430
|
-
listParams: flags["list-params"] === true,
|
|
431
|
-
method: typeof flags["method"] === "string" ? flags["method"] : undefined,
|
|
432
|
-
path: typeof flags["path"] === "string" ? flags["path"] : undefined,
|
|
433
|
-
json: jsonFlag,
|
|
434
|
-
});
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
case "db": {
|
|
438
|
-
const dbSub = positional[0];
|
|
439
|
-
if (!dbSub) {
|
|
440
|
-
printError("Missing subcommand. Usage: zond db <collections|runs|run|diagnose|compare> [args]");
|
|
441
|
-
return 2;
|
|
442
|
-
}
|
|
443
|
-
const limitRaw = flags["limit"];
|
|
444
|
-
let limit: number | undefined;
|
|
445
|
-
if (typeof limitRaw === "string") {
|
|
446
|
-
limit = parseInt(limitRaw, 10);
|
|
447
|
-
if (isNaN(limit) || limit <= 0) limit = undefined;
|
|
448
|
-
}
|
|
449
|
-
const statusRaw = flags["status"];
|
|
450
|
-
let statusFilter: number | undefined;
|
|
451
|
-
if (typeof statusRaw === "string") {
|
|
452
|
-
statusFilter = parseInt(statusRaw, 10);
|
|
453
|
-
if (isNaN(statusFilter)) statusFilter = undefined;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
return dbCommand({
|
|
457
|
-
subcommand: dbSub,
|
|
458
|
-
positional: positional.slice(1),
|
|
459
|
-
limit,
|
|
460
|
-
verbose: flags["verbose"] === true,
|
|
461
|
-
dbPath: typeof flags["db"] === "string" ? flags["db"] : undefined,
|
|
462
|
-
json: jsonFlag,
|
|
463
|
-
method: typeof flags["method"] === "string" ? flags["method"] : undefined,
|
|
464
|
-
status: statusFilter,
|
|
465
|
-
});
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
case "request": {
|
|
469
|
-
const method = positional[0];
|
|
470
|
-
const url = positional[1];
|
|
471
|
-
if (!method || !url) {
|
|
472
|
-
printError("Missing arguments. Usage: zond request <METHOD> <URL> [--header H] [--body JSON]");
|
|
473
|
-
return 2;
|
|
474
|
-
}
|
|
475
|
-
// Collect all --header flags
|
|
476
|
-
const headerValues: string[] = [];
|
|
477
|
-
const rawArgs = process.argv.slice(2);
|
|
478
|
-
for (let i = 0; i < rawArgs.length; i++) {
|
|
479
|
-
const arg = rawArgs[i]!;
|
|
480
|
-
if (arg === "--header" && rawArgs[i + 1]) {
|
|
481
|
-
headerValues.push(rawArgs[i + 1]!);
|
|
482
|
-
i++;
|
|
483
|
-
} else if (arg.startsWith("--header=")) {
|
|
484
|
-
headerValues.push(arg.slice("--header=".length));
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
const timeoutRaw = flags["timeout"];
|
|
489
|
-
let timeout: number | undefined;
|
|
490
|
-
if (typeof timeoutRaw === "string") {
|
|
491
|
-
timeout = parseInt(timeoutRaw, 10);
|
|
492
|
-
if (isNaN(timeout) || timeout <= 0) timeout = undefined;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
return requestCommand({
|
|
496
|
-
method,
|
|
497
|
-
url,
|
|
498
|
-
headers: headerValues.length > 0 ? headerValues : undefined,
|
|
499
|
-
body: typeof flags["body"] === "string" ? flags["body"] : undefined,
|
|
500
|
-
timeout,
|
|
501
|
-
env: typeof flags["env"] === "string" ? flags["env"] : undefined,
|
|
502
|
-
api: typeof flags["api"] === "string" ? flags["api"] : undefined,
|
|
503
|
-
jsonPath: typeof flags["json-path"] === "string" ? flags["json-path"] : undefined,
|
|
504
|
-
dbPath: typeof flags["db"] === "string" ? flags["db"] : undefined,
|
|
505
|
-
json: jsonFlag,
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
case "generate": {
|
|
510
|
-
const specPath = positional[0];
|
|
511
|
-
if (!specPath) {
|
|
512
|
-
printError("Missing spec path. Usage: zond generate <spec> --output <dir> [--tag <tag>] [--uncovered-only] [--json]");
|
|
513
|
-
return 2;
|
|
514
|
-
}
|
|
515
|
-
const output = typeof flags["output"] === "string" ? flags["output"] : undefined;
|
|
516
|
-
if (!output) {
|
|
517
|
-
printError("Missing --output <dir>. Usage: zond generate <spec> --output <dir>");
|
|
518
|
-
return 2;
|
|
519
|
-
}
|
|
520
|
-
return generateCommand({
|
|
521
|
-
specPath,
|
|
522
|
-
output,
|
|
523
|
-
tag: typeof flags["tag"] === "string" ? flags["tag"] : undefined,
|
|
524
|
-
uncoveredOnly: flags["uncovered-only"] === true,
|
|
525
|
-
json: jsonFlag,
|
|
526
|
-
});
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
case "catalog": {
|
|
530
|
-
const specPath = positional[0];
|
|
531
|
-
if (!specPath) {
|
|
532
|
-
printError("Missing spec path. Usage: zond catalog <spec> [--output <dir>]");
|
|
533
|
-
return 2;
|
|
534
|
-
}
|
|
535
|
-
return catalogCommand({
|
|
536
|
-
specPath,
|
|
537
|
-
output: typeof flags["output"] === "string" ? flags["output"] : undefined,
|
|
538
|
-
json: jsonFlag,
|
|
539
|
-
});
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
case "guide": {
|
|
543
|
-
const specPath = positional[0];
|
|
544
|
-
if (!specPath) {
|
|
545
|
-
printError("Missing spec path. Usage: zond guide <spec> [--tests-dir <dir>] [--tag <tag>]");
|
|
546
|
-
return 2;
|
|
547
|
-
}
|
|
548
|
-
return guideCommand({
|
|
549
|
-
specPath,
|
|
550
|
-
testsDir: typeof flags["tests-dir"] === "string" ? flags["tests-dir"] : undefined,
|
|
551
|
-
tag: typeof flags["tag"] === "string" ? flags["tag"] : undefined,
|
|
552
|
-
json: jsonFlag,
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
case "export": {
|
|
557
|
-
const subcommand = positional[0];
|
|
558
|
-
if (subcommand !== "postman") {
|
|
559
|
-
printError(`Unknown export subcommand: ${subcommand ?? "(none)"}. Usage: zond export postman <path>`);
|
|
560
|
-
return 2;
|
|
561
|
-
}
|
|
562
|
-
const testsPath = positional[1];
|
|
563
|
-
if (!testsPath) {
|
|
564
|
-
printError("Missing tests path. Usage: zond export postman <path> [--output <file>]");
|
|
565
|
-
return 2;
|
|
566
|
-
}
|
|
567
|
-
return exportCommand({
|
|
568
|
-
testsPath,
|
|
569
|
-
output: typeof flags["output"] === "string" ? flags["output"] : "collection.postman.json",
|
|
570
|
-
env: typeof flags["env"] === "string" ? flags["env"] : undefined,
|
|
571
|
-
collectionName: typeof flags["collection-name"] === "string" ? flags["collection-name"] : undefined,
|
|
572
|
-
json: jsonFlag,
|
|
573
|
-
});
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
case "update":
|
|
577
|
-
case "self-update": {
|
|
578
|
-
return updateCommand({
|
|
579
|
-
check: flags["check"] === true,
|
|
580
|
-
json: jsonFlag,
|
|
581
|
-
});
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
case "sync": {
|
|
585
|
-
const specPath = positional[0];
|
|
586
|
-
if (!specPath) {
|
|
587
|
-
printError("Missing spec path. Usage: zond sync <spec> --tests <dir> [--dry-run] [--tag <tag>]");
|
|
588
|
-
return 2;
|
|
589
|
-
}
|
|
590
|
-
const testsDir = typeof flags["tests"] === "string" ? flags["tests"] : undefined;
|
|
591
|
-
if (!testsDir) {
|
|
592
|
-
printError("Missing --tests <dir>. Usage: zond sync <spec> --tests <dir>");
|
|
593
|
-
return 2;
|
|
594
|
-
}
|
|
595
|
-
return syncCommand({
|
|
596
|
-
specPath,
|
|
597
|
-
testsDir,
|
|
598
|
-
dryRun: flags["dry-run"] === true,
|
|
599
|
-
tag: typeof flags["tag"] === "string" ? flags["tag"] : undefined,
|
|
600
|
-
json: jsonFlag,
|
|
601
|
-
});
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
default: {
|
|
605
|
-
printError(`Unknown command: ${command}`);
|
|
606
|
-
printUsage();
|
|
607
|
-
return 2;
|
|
608
|
-
}
|
|
27
|
+
async function main(): Promise<void> {
|
|
28
|
+
const program = buildProgram();
|
|
29
|
+
try {
|
|
30
|
+
await program.parseAsync(preprocessArgv(process.argv));
|
|
31
|
+
} catch (err) {
|
|
32
|
+
if (err instanceof CommanderError) {
|
|
33
|
+
// Help/version — commander prints content itself, exit 0
|
|
34
|
+
if (err.code === "commander.helpDisplayed" || err.code === "commander.version" || err.code === "commander.help") {
|
|
35
|
+
process.exitCode = 0;
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
// Unknown command / unknown option / missing argument — exit 2 (already printed by commander/showHelpAfterError)
|
|
39
|
+
process.exitCode = 2;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
reportInternalError(err);
|
|
43
|
+
process.exitCode = 3;
|
|
609
44
|
}
|
|
610
45
|
}
|
|
611
46
|
|
|
612
|
-
// Only run when executed directly, not when imported
|
|
613
47
|
const scriptPath = process.argv[1]?.replaceAll("\\", "/") ?? "";
|
|
614
48
|
const metaFile = import.meta.filename?.replaceAll("\\", "/") ?? "";
|
|
615
49
|
const isMain = scriptPath === metaFile
|
|
616
50
|
|| scriptPath.endsWith("cli/index.ts")
|
|
617
51
|
|| import.meta.main === true;
|
|
52
|
+
|
|
618
53
|
if (isMain) {
|
|
619
|
-
|
|
620
|
-
const code = await main();
|
|
621
|
-
process.exitCode = code;
|
|
622
|
-
} catch (err) {
|
|
623
|
-
printError(err instanceof Error ? err.message : String(err));
|
|
624
|
-
process.exitCode = 2;
|
|
625
|
-
}
|
|
54
|
+
await main();
|
|
626
55
|
}
|