@dotsetlabs/bellwether 0.10.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 +291 -0
- package/LICENSE +21 -0
- package/README.md +739 -0
- package/dist/auth/credentials.d.ts +64 -0
- package/dist/auth/credentials.js +218 -0
- package/dist/auth/index.d.ts +6 -0
- package/dist/auth/index.js +6 -0
- package/dist/auth/keychain.d.ts +64 -0
- package/dist/auth/keychain.js +268 -0
- package/dist/baseline/ab-testing.d.ts +80 -0
- package/dist/baseline/ab-testing.js +236 -0
- package/dist/baseline/ai-compatibility-scorer.d.ts +95 -0
- package/dist/baseline/ai-compatibility-scorer.js +606 -0
- package/dist/baseline/calibration.d.ts +77 -0
- package/dist/baseline/calibration.js +136 -0
- package/dist/baseline/category-matching.d.ts +85 -0
- package/dist/baseline/category-matching.js +289 -0
- package/dist/baseline/change-impact-analyzer.d.ts +98 -0
- package/dist/baseline/change-impact-analyzer.js +592 -0
- package/dist/baseline/comparator.d.ts +64 -0
- package/dist/baseline/comparator.js +916 -0
- package/dist/baseline/confidence.d.ts +55 -0
- package/dist/baseline/confidence.js +122 -0
- package/dist/baseline/converter.d.ts +61 -0
- package/dist/baseline/converter.js +585 -0
- package/dist/baseline/dependency-analyzer.d.ts +89 -0
- package/dist/baseline/dependency-analyzer.js +567 -0
- package/dist/baseline/deprecation-tracker.d.ts +133 -0
- package/dist/baseline/deprecation-tracker.js +322 -0
- package/dist/baseline/diff.d.ts +55 -0
- package/dist/baseline/diff.js +1584 -0
- package/dist/baseline/documentation-scorer.d.ts +205 -0
- package/dist/baseline/documentation-scorer.js +466 -0
- package/dist/baseline/embeddings.d.ts +118 -0
- package/dist/baseline/embeddings.js +251 -0
- package/dist/baseline/error-analyzer.d.ts +198 -0
- package/dist/baseline/error-analyzer.js +721 -0
- package/dist/baseline/evaluation/evaluator.d.ts +42 -0
- package/dist/baseline/evaluation/evaluator.js +323 -0
- package/dist/baseline/evaluation/expanded-dataset.d.ts +45 -0
- package/dist/baseline/evaluation/expanded-dataset.js +1164 -0
- package/dist/baseline/evaluation/golden-dataset.d.ts +58 -0
- package/dist/baseline/evaluation/golden-dataset.js +717 -0
- package/dist/baseline/evaluation/index.d.ts +15 -0
- package/dist/baseline/evaluation/index.js +15 -0
- package/dist/baseline/evaluation/types.d.ts +186 -0
- package/dist/baseline/evaluation/types.js +8 -0
- package/dist/baseline/external-dependency-detector.d.ts +181 -0
- package/dist/baseline/external-dependency-detector.js +524 -0
- package/dist/baseline/golden-output.d.ts +162 -0
- package/dist/baseline/golden-output.js +636 -0
- package/dist/baseline/health-scorer.d.ts +174 -0
- package/dist/baseline/health-scorer.js +451 -0
- package/dist/baseline/incremental-checker.d.ts +97 -0
- package/dist/baseline/incremental-checker.js +174 -0
- package/dist/baseline/index.d.ts +31 -0
- package/dist/baseline/index.js +42 -0
- package/dist/baseline/migration-generator.d.ts +137 -0
- package/dist/baseline/migration-generator.js +554 -0
- package/dist/baseline/migrations.d.ts +60 -0
- package/dist/baseline/migrations.js +197 -0
- package/dist/baseline/performance-tracker.d.ts +214 -0
- package/dist/baseline/performance-tracker.js +577 -0
- package/dist/baseline/pr-comment-generator.d.ts +117 -0
- package/dist/baseline/pr-comment-generator.js +546 -0
- package/dist/baseline/response-fingerprint.d.ts +127 -0
- package/dist/baseline/response-fingerprint.js +728 -0
- package/dist/baseline/response-schema-tracker.d.ts +129 -0
- package/dist/baseline/response-schema-tracker.js +420 -0
- package/dist/baseline/risk-scorer.d.ts +54 -0
- package/dist/baseline/risk-scorer.js +434 -0
- package/dist/baseline/saver.d.ts +89 -0
- package/dist/baseline/saver.js +554 -0
- package/dist/baseline/scenario-generator.d.ts +151 -0
- package/dist/baseline/scenario-generator.js +905 -0
- package/dist/baseline/schema-compare.d.ts +86 -0
- package/dist/baseline/schema-compare.js +557 -0
- package/dist/baseline/schema-evolution.d.ts +189 -0
- package/dist/baseline/schema-evolution.js +467 -0
- package/dist/baseline/semantic.d.ts +203 -0
- package/dist/baseline/semantic.js +908 -0
- package/dist/baseline/synonyms.d.ts +60 -0
- package/dist/baseline/synonyms.js +386 -0
- package/dist/baseline/telemetry.d.ts +165 -0
- package/dist/baseline/telemetry.js +294 -0
- package/dist/baseline/test-pruner.d.ts +120 -0
- package/dist/baseline/test-pruner.js +387 -0
- package/dist/baseline/types.d.ts +449 -0
- package/dist/baseline/types.js +5 -0
- package/dist/baseline/version.d.ts +138 -0
- package/dist/baseline/version.js +206 -0
- package/dist/cache/index.d.ts +5 -0
- package/dist/cache/index.js +5 -0
- package/dist/cache/response-cache.d.ts +151 -0
- package/dist/cache/response-cache.js +287 -0
- package/dist/ci/index.d.ts +60 -0
- package/dist/ci/index.js +342 -0
- package/dist/cli/commands/auth.d.ts +12 -0
- package/dist/cli/commands/auth.js +352 -0
- package/dist/cli/commands/badge.d.ts +3 -0
- package/dist/cli/commands/badge.js +74 -0
- package/dist/cli/commands/baseline-accept.d.ts +15 -0
- package/dist/cli/commands/baseline-accept.js +178 -0
- package/dist/cli/commands/baseline-migrate.d.ts +12 -0
- package/dist/cli/commands/baseline-migrate.js +164 -0
- package/dist/cli/commands/baseline.d.ts +14 -0
- package/dist/cli/commands/baseline.js +449 -0
- package/dist/cli/commands/beta.d.ts +10 -0
- package/dist/cli/commands/beta.js +231 -0
- package/dist/cli/commands/check.d.ts +11 -0
- package/dist/cli/commands/check.js +820 -0
- package/dist/cli/commands/cloud/badge.d.ts +3 -0
- package/dist/cli/commands/cloud/badge.js +74 -0
- package/dist/cli/commands/cloud/diff.d.ts +6 -0
- package/dist/cli/commands/cloud/diff.js +79 -0
- package/dist/cli/commands/cloud/history.d.ts +6 -0
- package/dist/cli/commands/cloud/history.js +102 -0
- package/dist/cli/commands/cloud/link.d.ts +9 -0
- package/dist/cli/commands/cloud/link.js +119 -0
- package/dist/cli/commands/cloud/login.d.ts +7 -0
- package/dist/cli/commands/cloud/login.js +499 -0
- package/dist/cli/commands/cloud/projects.d.ts +6 -0
- package/dist/cli/commands/cloud/projects.js +44 -0
- package/dist/cli/commands/cloud/shared.d.ts +7 -0
- package/dist/cli/commands/cloud/shared.js +42 -0
- package/dist/cli/commands/cloud/teams.d.ts +8 -0
- package/dist/cli/commands/cloud/teams.js +169 -0
- package/dist/cli/commands/cloud/upload.d.ts +8 -0
- package/dist/cli/commands/cloud/upload.js +181 -0
- package/dist/cli/commands/contract.d.ts +11 -0
- package/dist/cli/commands/contract.js +280 -0
- package/dist/cli/commands/discover.d.ts +3 -0
- package/dist/cli/commands/discover.js +82 -0
- package/dist/cli/commands/eval.d.ts +9 -0
- package/dist/cli/commands/eval.js +187 -0
- package/dist/cli/commands/explore.d.ts +11 -0
- package/dist/cli/commands/explore.js +437 -0
- package/dist/cli/commands/feedback.d.ts +9 -0
- package/dist/cli/commands/feedback.js +174 -0
- package/dist/cli/commands/golden.d.ts +12 -0
- package/dist/cli/commands/golden.js +407 -0
- package/dist/cli/commands/history.d.ts +10 -0
- package/dist/cli/commands/history.js +202 -0
- package/dist/cli/commands/init.d.ts +9 -0
- package/dist/cli/commands/init.js +219 -0
- package/dist/cli/commands/interview.d.ts +3 -0
- package/dist/cli/commands/interview.js +903 -0
- package/dist/cli/commands/link.d.ts +10 -0
- package/dist/cli/commands/link.js +169 -0
- package/dist/cli/commands/login.d.ts +7 -0
- package/dist/cli/commands/login.js +499 -0
- package/dist/cli/commands/preset.d.ts +33 -0
- package/dist/cli/commands/preset.js +297 -0
- package/dist/cli/commands/profile.d.ts +33 -0
- package/dist/cli/commands/profile.js +286 -0
- package/dist/cli/commands/registry.d.ts +11 -0
- package/dist/cli/commands/registry.js +146 -0
- package/dist/cli/commands/shared.d.ts +79 -0
- package/dist/cli/commands/shared.js +196 -0
- package/dist/cli/commands/teams.d.ts +8 -0
- package/dist/cli/commands/teams.js +169 -0
- package/dist/cli/commands/test.d.ts +9 -0
- package/dist/cli/commands/test.js +500 -0
- package/dist/cli/commands/upload.d.ts +8 -0
- package/dist/cli/commands/upload.js +223 -0
- package/dist/cli/commands/validate-config.d.ts +6 -0
- package/dist/cli/commands/validate-config.js +35 -0
- package/dist/cli/commands/verify.d.ts +11 -0
- package/dist/cli/commands/verify.js +283 -0
- package/dist/cli/commands/watch.d.ts +12 -0
- package/dist/cli/commands/watch.js +253 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +178 -0
- package/dist/cli/interactive.d.ts +47 -0
- package/dist/cli/interactive.js +216 -0
- package/dist/cli/output/terminal-reporter.d.ts +19 -0
- package/dist/cli/output/terminal-reporter.js +104 -0
- package/dist/cli/output.d.ts +226 -0
- package/dist/cli/output.js +438 -0
- package/dist/cli/utils/env.d.ts +5 -0
- package/dist/cli/utils/env.js +14 -0
- package/dist/cli/utils/progress.d.ts +59 -0
- package/dist/cli/utils/progress.js +206 -0
- package/dist/cli/utils/server-context.d.ts +10 -0
- package/dist/cli/utils/server-context.js +36 -0
- package/dist/cloud/auth.d.ts +144 -0
- package/dist/cloud/auth.js +374 -0
- package/dist/cloud/client.d.ts +24 -0
- package/dist/cloud/client.js +65 -0
- package/dist/cloud/http-client.d.ts +38 -0
- package/dist/cloud/http-client.js +215 -0
- package/dist/cloud/index.d.ts +23 -0
- package/dist/cloud/index.js +25 -0
- package/dist/cloud/mock-client.d.ts +107 -0
- package/dist/cloud/mock-client.js +545 -0
- package/dist/cloud/types.d.ts +515 -0
- package/dist/cloud/types.js +15 -0
- package/dist/config/defaults.d.ts +160 -0
- package/dist/config/defaults.js +169 -0
- package/dist/config/loader.d.ts +24 -0
- package/dist/config/loader.js +122 -0
- package/dist/config/template.d.ts +42 -0
- package/dist/config/template.js +647 -0
- package/dist/config/validator.d.ts +2112 -0
- package/dist/config/validator.js +658 -0
- package/dist/constants/cloud.d.ts +107 -0
- package/dist/constants/cloud.js +110 -0
- package/dist/constants/core.d.ts +521 -0
- package/dist/constants/core.js +556 -0
- package/dist/constants/testing.d.ts +1283 -0
- package/dist/constants/testing.js +1568 -0
- package/dist/constants.d.ts +10 -0
- package/dist/constants.js +10 -0
- package/dist/contract/index.d.ts +6 -0
- package/dist/contract/index.js +5 -0
- package/dist/contract/validator.d.ts +177 -0
- package/dist/contract/validator.js +574 -0
- package/dist/cost/index.d.ts +6 -0
- package/dist/cost/index.js +5 -0
- package/dist/cost/tracker.d.ts +134 -0
- package/dist/cost/tracker.js +313 -0
- package/dist/discovery/discovery.d.ts +16 -0
- package/dist/discovery/discovery.js +173 -0
- package/dist/discovery/types.d.ts +51 -0
- package/dist/discovery/types.js +2 -0
- package/dist/docs/agents.d.ts +3 -0
- package/dist/docs/agents.js +995 -0
- package/dist/docs/contract.d.ts +51 -0
- package/dist/docs/contract.js +1681 -0
- package/dist/docs/generator.d.ts +4 -0
- package/dist/docs/generator.js +4 -0
- package/dist/docs/html-reporter.d.ts +9 -0
- package/dist/docs/html-reporter.js +757 -0
- package/dist/docs/index.d.ts +10 -0
- package/dist/docs/index.js +11 -0
- package/dist/docs/junit-reporter.d.ts +18 -0
- package/dist/docs/junit-reporter.js +210 -0
- package/dist/docs/report.d.ts +14 -0
- package/dist/docs/report.js +44 -0
- package/dist/docs/sarif-reporter.d.ts +19 -0
- package/dist/docs/sarif-reporter.js +335 -0
- package/dist/docs/shared.d.ts +35 -0
- package/dist/docs/shared.js +162 -0
- package/dist/docs/templates.d.ts +12 -0
- package/dist/docs/templates.js +76 -0
- package/dist/errors/index.d.ts +6 -0
- package/dist/errors/index.js +6 -0
- package/dist/errors/retry.d.ts +92 -0
- package/dist/errors/retry.js +323 -0
- package/dist/errors/types.d.ts +321 -0
- package/dist/errors/types.js +584 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +32 -0
- package/dist/interview/dependency-resolver.d.ts +11 -0
- package/dist/interview/dependency-resolver.js +32 -0
- package/dist/interview/interviewer.d.ts +232 -0
- package/dist/interview/interviewer.js +1939 -0
- package/dist/interview/mock-response-generator.d.ts +7 -0
- package/dist/interview/mock-response-generator.js +102 -0
- package/dist/interview/orchestrator.d.ts +237 -0
- package/dist/interview/orchestrator.js +1296 -0
- package/dist/interview/rate-limiter.d.ts +15 -0
- package/dist/interview/rate-limiter.js +55 -0
- package/dist/interview/response-validator.d.ts +10 -0
- package/dist/interview/response-validator.js +132 -0
- package/dist/interview/schema-inferrer.d.ts +8 -0
- package/dist/interview/schema-inferrer.js +71 -0
- package/dist/interview/schema-test-generator.d.ts +71 -0
- package/dist/interview/schema-test-generator.js +834 -0
- package/dist/interview/smart-value-generator.d.ts +155 -0
- package/dist/interview/smart-value-generator.js +554 -0
- package/dist/interview/stateful-test-runner.d.ts +19 -0
- package/dist/interview/stateful-test-runner.js +106 -0
- package/dist/interview/types.d.ts +561 -0
- package/dist/interview/types.js +2 -0
- package/dist/llm/anthropic.d.ts +41 -0
- package/dist/llm/anthropic.js +355 -0
- package/dist/llm/client.d.ts +123 -0
- package/dist/llm/client.js +42 -0
- package/dist/llm/factory.d.ts +38 -0
- package/dist/llm/factory.js +145 -0
- package/dist/llm/fallback.d.ts +140 -0
- package/dist/llm/fallback.js +379 -0
- package/dist/llm/index.d.ts +18 -0
- package/dist/llm/index.js +15 -0
- package/dist/llm/ollama.d.ts +37 -0
- package/dist/llm/ollama.js +330 -0
- package/dist/llm/openai.d.ts +25 -0
- package/dist/llm/openai.js +320 -0
- package/dist/llm/token-budget.d.ts +161 -0
- package/dist/llm/token-budget.js +395 -0
- package/dist/logging/logger.d.ts +70 -0
- package/dist/logging/logger.js +130 -0
- package/dist/metrics/collector.d.ts +106 -0
- package/dist/metrics/collector.js +547 -0
- package/dist/metrics/index.d.ts +7 -0
- package/dist/metrics/index.js +7 -0
- package/dist/metrics/prometheus.d.ts +20 -0
- package/dist/metrics/prometheus.js +241 -0
- package/dist/metrics/types.d.ts +209 -0
- package/dist/metrics/types.js +5 -0
- package/dist/persona/builtins.d.ts +54 -0
- package/dist/persona/builtins.js +219 -0
- package/dist/persona/index.d.ts +8 -0
- package/dist/persona/index.js +8 -0
- package/dist/persona/loader.d.ts +30 -0
- package/dist/persona/loader.js +190 -0
- package/dist/persona/types.d.ts +144 -0
- package/dist/persona/types.js +5 -0
- package/dist/persona/validation.d.ts +94 -0
- package/dist/persona/validation.js +332 -0
- package/dist/prompts/index.d.ts +5 -0
- package/dist/prompts/index.js +5 -0
- package/dist/prompts/templates.d.ts +180 -0
- package/dist/prompts/templates.js +431 -0
- package/dist/registry/client.d.ts +49 -0
- package/dist/registry/client.js +191 -0
- package/dist/registry/index.d.ts +7 -0
- package/dist/registry/index.js +6 -0
- package/dist/registry/types.d.ts +140 -0
- package/dist/registry/types.js +6 -0
- package/dist/scenarios/evaluator.d.ts +43 -0
- package/dist/scenarios/evaluator.js +206 -0
- package/dist/scenarios/index.d.ts +10 -0
- package/dist/scenarios/index.js +9 -0
- package/dist/scenarios/loader.d.ts +20 -0
- package/dist/scenarios/loader.js +285 -0
- package/dist/scenarios/types.d.ts +153 -0
- package/dist/scenarios/types.js +8 -0
- package/dist/security/index.d.ts +17 -0
- package/dist/security/index.js +18 -0
- package/dist/security/payloads.d.ts +61 -0
- package/dist/security/payloads.js +268 -0
- package/dist/security/security-tester.d.ts +42 -0
- package/dist/security/security-tester.js +582 -0
- package/dist/security/types.d.ts +166 -0
- package/dist/security/types.js +8 -0
- package/dist/transport/base-transport.d.ts +59 -0
- package/dist/transport/base-transport.js +38 -0
- package/dist/transport/http-transport.d.ts +67 -0
- package/dist/transport/http-transport.js +238 -0
- package/dist/transport/mcp-client.d.ts +141 -0
- package/dist/transport/mcp-client.js +496 -0
- package/dist/transport/sse-transport.d.ts +88 -0
- package/dist/transport/sse-transport.js +316 -0
- package/dist/transport/stdio-transport.d.ts +43 -0
- package/dist/transport/stdio-transport.js +238 -0
- package/dist/transport/types.d.ts +125 -0
- package/dist/transport/types.js +16 -0
- package/dist/utils/concurrency.d.ts +123 -0
- package/dist/utils/concurrency.js +213 -0
- package/dist/utils/formatters.d.ts +16 -0
- package/dist/utils/formatters.js +37 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/jsonpath.d.ts +87 -0
- package/dist/utils/jsonpath.js +326 -0
- package/dist/utils/markdown.d.ts +113 -0
- package/dist/utils/markdown.js +265 -0
- package/dist/utils/network.d.ts +14 -0
- package/dist/utils/network.js +17 -0
- package/dist/utils/sanitize.d.ts +92 -0
- package/dist/utils/sanitize.js +191 -0
- package/dist/utils/semantic.d.ts +194 -0
- package/dist/utils/semantic.js +1051 -0
- package/dist/utils/smart-truncate.d.ts +94 -0
- package/dist/utils/smart-truncate.js +361 -0
- package/dist/utils/timeout.d.ts +153 -0
- package/dist/utils/timeout.js +205 -0
- package/dist/utils/yaml-parser.d.ts +58 -0
- package/dist/utils/yaml-parser.js +86 -0
- package/dist/validation/index.d.ts +32 -0
- package/dist/validation/index.js +32 -0
- package/dist/validation/semantic-test-generator.d.ts +50 -0
- package/dist/validation/semantic-test-generator.js +176 -0
- package/dist/validation/semantic-types.d.ts +66 -0
- package/dist/validation/semantic-types.js +94 -0
- package/dist/validation/semantic-validator.d.ts +38 -0
- package/dist/validation/semantic-validator.js +340 -0
- package/dist/verification/index.d.ts +6 -0
- package/dist/verification/index.js +5 -0
- package/dist/verification/types.d.ts +133 -0
- package/dist/verification/types.js +5 -0
- package/dist/verification/verifier.d.ts +30 -0
- package/dist/verification/verifier.js +309 -0
- package/dist/version.d.ts +19 -0
- package/dist/version.js +48 -0
- package/dist/workflow/auto-generator.d.ts +27 -0
- package/dist/workflow/auto-generator.js +513 -0
- package/dist/workflow/discovery.d.ts +40 -0
- package/dist/workflow/discovery.js +195 -0
- package/dist/workflow/executor.d.ts +82 -0
- package/dist/workflow/executor.js +611 -0
- package/dist/workflow/index.d.ts +10 -0
- package/dist/workflow/index.js +10 -0
- package/dist/workflow/loader.d.ts +24 -0
- package/dist/workflow/loader.js +194 -0
- package/dist/workflow/state-tracker.d.ts +98 -0
- package/dist/workflow/state-tracker.js +424 -0
- package/dist/workflow/types.d.ts +337 -0
- package/dist/workflow/types.js +5 -0
- package/package.json +94 -0
- package/schemas/bellwether-check.schema.json +651 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Baseline migrate command - upgrade baselines to current format version.
|
|
3
|
+
*
|
|
4
|
+
* This command migrates baseline files from older format versions to the
|
|
5
|
+
* current format version, ensuring compatibility with the latest CLI features.
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
import { EXIT_CODES } from '../../constants.js';
|
|
11
|
+
import * as output from '../output.js';
|
|
12
|
+
import { formatVersion } from '../../baseline/version.js';
|
|
13
|
+
import { migrateBaseline, getMigrationInfo, } from '../../baseline/migrations.js';
|
|
14
|
+
import { recalculateIntegrityHash } from '../../baseline/saver.js';
|
|
15
|
+
import { loadConfig, ConfigNotFoundError } from '../../config/loader.js';
|
|
16
|
+
function loadConfigOrExit(configPath) {
|
|
17
|
+
try {
|
|
18
|
+
return loadConfig(configPath);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
if (error instanceof ConfigNotFoundError) {
|
|
22
|
+
output.error(error.message);
|
|
23
|
+
process.exit(EXIT_CODES.ERROR);
|
|
24
|
+
}
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Load raw baseline JSON without full validation.
|
|
30
|
+
* Used for migration where the format might not match current schema.
|
|
31
|
+
*/
|
|
32
|
+
function loadRawBaseline(path) {
|
|
33
|
+
if (!existsSync(path)) {
|
|
34
|
+
throw new Error(`Baseline file not found: ${path}`);
|
|
35
|
+
}
|
|
36
|
+
const content = readFileSync(path, 'utf-8');
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(content);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
throw new Error(`Invalid JSON in baseline file ${path}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Save migrated baseline to file.
|
|
46
|
+
*/
|
|
47
|
+
function saveMigratedBaseline(baseline, path) {
|
|
48
|
+
const serialized = JSON.stringify(baseline, null, 2);
|
|
49
|
+
writeFileSync(path, serialized, 'utf-8');
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Create the migrate command.
|
|
53
|
+
*/
|
|
54
|
+
export const migrateCommand = new Command('migrate')
|
|
55
|
+
.description('Migrate baseline to current format version')
|
|
56
|
+
.argument('[path]', 'Path to baseline file')
|
|
57
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
58
|
+
.option('--dry-run', 'Show what would change without writing')
|
|
59
|
+
.option('-o, --output <path>', 'Output path (default: overwrite input)')
|
|
60
|
+
.option('-f, --force', 'Overwrite output file without prompting')
|
|
61
|
+
.option('--info', 'Show migration info without performing migration')
|
|
62
|
+
.action(async (baselinePath, options) => {
|
|
63
|
+
try {
|
|
64
|
+
const config = loadConfigOrExit(options.config);
|
|
65
|
+
const outputDir = config.output.dir;
|
|
66
|
+
const resolvedBaselinePath = baselinePath ?? config.baseline.path;
|
|
67
|
+
if (!resolvedBaselinePath) {
|
|
68
|
+
output.error('No baseline path provided. Set baseline.path in config or pass a path argument.');
|
|
69
|
+
process.exit(EXIT_CODES.ERROR);
|
|
70
|
+
}
|
|
71
|
+
// Resolve path
|
|
72
|
+
const baseDir = baselinePath ? process.cwd() : outputDir;
|
|
73
|
+
const fullPath = resolvedBaselinePath.startsWith('/')
|
|
74
|
+
? resolvedBaselinePath
|
|
75
|
+
: join(baseDir, resolvedBaselinePath);
|
|
76
|
+
// Load raw baseline
|
|
77
|
+
const rawBaseline = loadRawBaseline(fullPath);
|
|
78
|
+
// Get migration info
|
|
79
|
+
const info = getMigrationInfo(rawBaseline);
|
|
80
|
+
// Info mode - just show status
|
|
81
|
+
if (options.info) {
|
|
82
|
+
output.info('=== Baseline Migration Info ===');
|
|
83
|
+
output.info(`File: ${fullPath}`);
|
|
84
|
+
output.info(`Current format version: ${formatVersion(info.currentVersion)}`);
|
|
85
|
+
output.info(`Target format version: ${formatVersion(info.targetVersion)}`);
|
|
86
|
+
output.info(`Needs migration: ${info.needsMigration ? 'Yes' : 'No'}`);
|
|
87
|
+
if (info.needsMigration) {
|
|
88
|
+
output.info(`Migrations to apply: ${info.migrationsToApply.join(' -> ')}`);
|
|
89
|
+
output.info(`Can migrate: ${info.canMigrate ? 'Yes' : 'No'}`);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
output.success('Baseline is already at the current format version.');
|
|
93
|
+
}
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
// Check if migration is needed
|
|
97
|
+
if (!info.needsMigration) {
|
|
98
|
+
output.success(`Baseline is already at the current format version (${formatVersion(info.currentVersion)}).`);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// Check if migration is possible
|
|
102
|
+
if (!info.canMigrate) {
|
|
103
|
+
output.error(`Cannot migrate baseline from ${formatVersion(info.currentVersion)} ` +
|
|
104
|
+
`to ${formatVersion(info.targetVersion)}. ` +
|
|
105
|
+
`This may require a newer version of the CLI.`);
|
|
106
|
+
process.exit(EXIT_CODES.ERROR);
|
|
107
|
+
}
|
|
108
|
+
// Dry run mode
|
|
109
|
+
if (options.dryRun) {
|
|
110
|
+
output.info('=== Dry Run - No changes will be made ===');
|
|
111
|
+
output.info(`File: ${fullPath}`);
|
|
112
|
+
output.info(`Would migrate from ${formatVersion(info.currentVersion)} to ${formatVersion(info.targetVersion)}`);
|
|
113
|
+
output.info(`Migrations to apply: ${info.migrationsToApply.join(' -> ')}`);
|
|
114
|
+
// Show what would change
|
|
115
|
+
const migrated = migrateBaseline(rawBaseline);
|
|
116
|
+
output.info('\nMigrated baseline preview:');
|
|
117
|
+
output.info(` Version: ${migrated.version}`);
|
|
118
|
+
output.info(` Tools: ${migrated.tools.length}`);
|
|
119
|
+
output.info(` Assertions: ${migrated.assertions.length}`);
|
|
120
|
+
if (migrated.workflowSignatures && migrated.workflowSignatures.length > 0) {
|
|
121
|
+
output.info(` Workflows: ${migrated.workflowSignatures.length}`);
|
|
122
|
+
}
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
// Determine output path
|
|
126
|
+
const outputPath = options.output
|
|
127
|
+
? options.output.startsWith('/')
|
|
128
|
+
? options.output
|
|
129
|
+
: join(process.cwd(), options.output)
|
|
130
|
+
: fullPath;
|
|
131
|
+
// Check if output file exists (when different from input)
|
|
132
|
+
if (outputPath !== fullPath && existsSync(outputPath) && !options.force) {
|
|
133
|
+
output.error(`Output file already exists: ${outputPath}`);
|
|
134
|
+
output.error('Use --force to overwrite or specify a different --output path.');
|
|
135
|
+
process.exit(EXIT_CODES.ERROR);
|
|
136
|
+
}
|
|
137
|
+
// Perform migration
|
|
138
|
+
output.info(`Migrating baseline from ${formatVersion(info.currentVersion)} to ${formatVersion(info.targetVersion)}...`);
|
|
139
|
+
const migrated = migrateBaseline(rawBaseline);
|
|
140
|
+
// Recalculate integrity hash after migration
|
|
141
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
142
|
+
const { integrityHash: _, ...baselineWithoutHash } = migrated;
|
|
143
|
+
const finalBaseline = recalculateIntegrityHash(baselineWithoutHash);
|
|
144
|
+
// Save migrated baseline
|
|
145
|
+
saveMigratedBaseline(finalBaseline, outputPath);
|
|
146
|
+
output.success(`Baseline migrated successfully!`);
|
|
147
|
+
output.info(` From: ${formatVersion(info.currentVersion)}`);
|
|
148
|
+
output.info(` To: ${formatVersion(info.targetVersion)}`);
|
|
149
|
+
output.info(` Output: ${outputPath}`);
|
|
150
|
+
// Show summary
|
|
151
|
+
output.info('\nBaseline summary:');
|
|
152
|
+
output.info(` Server: ${finalBaseline.server.name} v${finalBaseline.server.version}`);
|
|
153
|
+
output.info(` Tools: ${finalBaseline.tools.length}`);
|
|
154
|
+
output.info(` Assertions: ${finalBaseline.assertions.length}`);
|
|
155
|
+
if (finalBaseline.workflowSignatures && finalBaseline.workflowSignatures.length > 0) {
|
|
156
|
+
output.info(` Workflows: ${finalBaseline.workflowSignatures.length}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
output.error(error instanceof Error ? error.message : String(error));
|
|
161
|
+
process.exit(EXIT_CODES.ERROR);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
//# sourceMappingURL=baseline-migrate.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Baseline commands - manage baselines for drift detection.
|
|
3
|
+
*
|
|
4
|
+
* Subcommands:
|
|
5
|
+
* - save [path] Save test results as baseline
|
|
6
|
+
* - compare <path> Compare test results against baseline
|
|
7
|
+
* - show [path] Display baseline contents
|
|
8
|
+
* - diff <path1> <path2> Compare two baseline files
|
|
9
|
+
* - accept Accept detected drift as intentional
|
|
10
|
+
* - migrate Upgrade baseline to current format
|
|
11
|
+
*/
|
|
12
|
+
import { Command } from 'commander';
|
|
13
|
+
export declare const baselineCommand: Command;
|
|
14
|
+
//# sourceMappingURL=baseline.d.ts.map
|
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Baseline commands - manage baselines for drift detection.
|
|
3
|
+
*
|
|
4
|
+
* Subcommands:
|
|
5
|
+
* - save [path] Save test results as baseline
|
|
6
|
+
* - compare <path> Compare test results against baseline
|
|
7
|
+
* - show [path] Display baseline contents
|
|
8
|
+
* - diff <path1> <path2> Compare two baseline files
|
|
9
|
+
* - accept Accept detected drift as intentional
|
|
10
|
+
* - migrate Upgrade baseline to current format
|
|
11
|
+
*/
|
|
12
|
+
import { Command } from 'commander';
|
|
13
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
14
|
+
import { join, basename } from 'path';
|
|
15
|
+
import { createBaseline, saveBaseline, loadBaseline, compareBaselines, formatDiffText, formatDiffJson, formatDiffMarkdown, formatDiffCompact, verifyIntegrity, } from '../../baseline/index.js';
|
|
16
|
+
import { createCloudBaseline } from '../../baseline/converter.js';
|
|
17
|
+
import { BaselineVersionError } from '../../baseline/version.js';
|
|
18
|
+
import { EXIT_CODES } from '../../constants.js';
|
|
19
|
+
import { migrateCommand } from './baseline-migrate.js';
|
|
20
|
+
import { acceptCommand } from './baseline-accept.js';
|
|
21
|
+
import { loadConfig, ConfigNotFoundError } from '../../config/loader.js';
|
|
22
|
+
import * as output from '../output.js';
|
|
23
|
+
/**
|
|
24
|
+
* Load interview result from JSON report.
|
|
25
|
+
* Only accepts check mode results - explore results are for documentation only.
|
|
26
|
+
*/
|
|
27
|
+
function loadInterviewResult(reportPath) {
|
|
28
|
+
if (!existsSync(reportPath)) {
|
|
29
|
+
throw new Error(`Test report not found: ${reportPath}\n\n` +
|
|
30
|
+
'Run `bellwether check` first with JSON output enabled.\n' +
|
|
31
|
+
'Configure in bellwether.yaml:\n' +
|
|
32
|
+
' output:\n' +
|
|
33
|
+
' format: json # or "both" for JSON + markdown');
|
|
34
|
+
}
|
|
35
|
+
const content = readFileSync(reportPath, 'utf-8');
|
|
36
|
+
let result;
|
|
37
|
+
try {
|
|
38
|
+
result = JSON.parse(content);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
throw new Error(`Invalid JSON in report file ${reportPath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
42
|
+
}
|
|
43
|
+
// Validate that this is a check mode result, not explore
|
|
44
|
+
if (result.metadata.model && result.metadata.model !== 'check') {
|
|
45
|
+
throw new Error(`Baseline operations only work with check mode results.\n\n` +
|
|
46
|
+
`The report at ${reportPath} was created with explore mode (model: ${result.metadata.model}).\n` +
|
|
47
|
+
`Explore results are for documentation only and cannot be used for baselines.\n\n` +
|
|
48
|
+
'To create a baseline:\n' +
|
|
49
|
+
' 1. Run `bellwether check` to generate a check mode report\n' +
|
|
50
|
+
' 2. Run `bellwether baseline save` to create the baseline');
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get the output directory from config or use current directory.
|
|
56
|
+
*/
|
|
57
|
+
function loadConfigOrExit(configPath) {
|
|
58
|
+
try {
|
|
59
|
+
return loadConfig(configPath);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (error instanceof ConfigNotFoundError) {
|
|
63
|
+
output.error(error.message);
|
|
64
|
+
process.exit(EXIT_CODES.ERROR);
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export const baselineCommand = new Command('baseline')
|
|
70
|
+
.description('Manage baselines for drift detection')
|
|
71
|
+
.addHelpText('after', `
|
|
72
|
+
Examples:
|
|
73
|
+
$ bellwether baseline save # Save baseline from last test
|
|
74
|
+
$ bellwether baseline save ./my-baseline.json # Save to specific path
|
|
75
|
+
$ bellwether baseline compare ./baseline.json # Compare test against baseline
|
|
76
|
+
$ bellwether baseline show # Show current baseline
|
|
77
|
+
$ bellwether baseline diff v1.json v2.json # Compare two baselines
|
|
78
|
+
$ bellwether baseline accept # Accept drift as intentional
|
|
79
|
+
$ bellwether baseline accept --reason "Added new feature"
|
|
80
|
+
$ bellwether baseline migrate # Upgrade baseline to current format
|
|
81
|
+
$ bellwether baseline migrate --info # Check if migration is needed
|
|
82
|
+
`);
|
|
83
|
+
baselineCommand.addCommand(migrateCommand);
|
|
84
|
+
baselineCommand.addCommand(acceptCommand);
|
|
85
|
+
// baseline save
|
|
86
|
+
baselineCommand
|
|
87
|
+
.command('save')
|
|
88
|
+
.description('Save test results as a baseline for drift detection')
|
|
89
|
+
.argument('[path]', 'Output path for baseline file')
|
|
90
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
91
|
+
.option('--report <path>', 'Path to test report JSON file')
|
|
92
|
+
.option('--cloud', 'Save in cloud-compatible format')
|
|
93
|
+
.option('-f, --force', 'Overwrite existing baseline without prompting')
|
|
94
|
+
.action(async (baselinePath, options) => {
|
|
95
|
+
const config = loadConfigOrExit(options.config);
|
|
96
|
+
const outputDir = config.output.dir;
|
|
97
|
+
const defaultBaselinePath = config.baseline.savePath ?? config.baseline.path;
|
|
98
|
+
const resolvedBaselinePath = baselinePath ?? defaultBaselinePath;
|
|
99
|
+
if (!resolvedBaselinePath) {
|
|
100
|
+
output.error('No baseline path provided. Set baseline.path or baseline.savePath in config, or pass a path argument.');
|
|
101
|
+
process.exit(EXIT_CODES.ERROR);
|
|
102
|
+
}
|
|
103
|
+
// Find the report file
|
|
104
|
+
const reportPath = options.report || join(outputDir, config.output.files.checkReport);
|
|
105
|
+
// Load interview result
|
|
106
|
+
let result;
|
|
107
|
+
try {
|
|
108
|
+
result = loadInterviewResult(reportPath);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
output.error(error instanceof Error ? error.message : String(error));
|
|
112
|
+
process.exit(EXIT_CODES.ERROR);
|
|
113
|
+
}
|
|
114
|
+
// Determine baseline path (relative to output dir if not absolute)
|
|
115
|
+
const finalPath = resolvedBaselinePath.startsWith('/')
|
|
116
|
+
? resolvedBaselinePath
|
|
117
|
+
: join(outputDir, resolvedBaselinePath);
|
|
118
|
+
// Check for existing baseline
|
|
119
|
+
if (existsSync(finalPath) && !options.force) {
|
|
120
|
+
output.error(`Baseline already exists: ${finalPath}`);
|
|
121
|
+
output.error('Use --force to overwrite.');
|
|
122
|
+
process.exit(EXIT_CODES.ERROR);
|
|
123
|
+
}
|
|
124
|
+
// Extract server command from result metadata
|
|
125
|
+
const serverCommand = result.metadata.serverCommand || 'unknown';
|
|
126
|
+
// Create and save baseline
|
|
127
|
+
if (options.cloud) {
|
|
128
|
+
const cloudBaseline = createCloudBaseline(result, serverCommand);
|
|
129
|
+
writeFileSync(finalPath, JSON.stringify(cloudBaseline, null, 2));
|
|
130
|
+
output.success(`Cloud baseline saved: ${finalPath}`);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
const baseline = createBaseline(result, serverCommand);
|
|
134
|
+
saveBaseline(baseline, finalPath);
|
|
135
|
+
output.success(`Baseline saved: ${finalPath}`);
|
|
136
|
+
}
|
|
137
|
+
// Show summary
|
|
138
|
+
const assertionCount = result.toolProfiles.reduce((sum, p) => sum + p.behavioralNotes.length + p.limitations.length + p.securityNotes.length, 0);
|
|
139
|
+
output.info(` Server: ${result.discovery.serverInfo.name} v${result.discovery.serverInfo.version}`);
|
|
140
|
+
output.info(` Tools: ${result.toolProfiles.length}`);
|
|
141
|
+
output.info(` Assertions: ${assertionCount}`);
|
|
142
|
+
});
|
|
143
|
+
// baseline compare
|
|
144
|
+
baselineCommand
|
|
145
|
+
.command('compare')
|
|
146
|
+
.description('Compare test results against a baseline')
|
|
147
|
+
.argument('[baseline-path]', 'Path to baseline file to compare against')
|
|
148
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
149
|
+
.option('--report <path>', 'Path to test report JSON file')
|
|
150
|
+
.option('--format <format>', 'Output format: text, json, markdown, compact')
|
|
151
|
+
.option('--fail-on-drift', 'Exit with error if drift is detected')
|
|
152
|
+
.option('--ignore-version-mismatch', 'Force comparison even if format versions are incompatible')
|
|
153
|
+
.action(async (baselinePath, options) => {
|
|
154
|
+
const config = loadConfigOrExit(options.config);
|
|
155
|
+
const outputDir = config.output.dir;
|
|
156
|
+
const format = options.format ?? config.baseline.outputFormat;
|
|
157
|
+
const failOnDrift = options.failOnDrift ? true : config.baseline.failOnDrift;
|
|
158
|
+
const resolvedBaselinePath = baselinePath ?? config.baseline.comparePath ?? config.baseline.path;
|
|
159
|
+
// Load baseline
|
|
160
|
+
if (!resolvedBaselinePath) {
|
|
161
|
+
output.error('No baseline path provided. Set baseline.path or baseline.comparePath in config, or pass a path argument.');
|
|
162
|
+
process.exit(EXIT_CODES.ERROR);
|
|
163
|
+
}
|
|
164
|
+
const baselineBaseDir = baselinePath ? process.cwd() : outputDir;
|
|
165
|
+
const fullBaselinePath = resolvedBaselinePath.startsWith('/')
|
|
166
|
+
? resolvedBaselinePath
|
|
167
|
+
: join(baselineBaseDir, resolvedBaselinePath);
|
|
168
|
+
if (!existsSync(fullBaselinePath)) {
|
|
169
|
+
output.error(`Baseline not found: ${fullBaselinePath}`);
|
|
170
|
+
process.exit(EXIT_CODES.ERROR);
|
|
171
|
+
}
|
|
172
|
+
let previousBaseline;
|
|
173
|
+
try {
|
|
174
|
+
previousBaseline = loadBaseline(fullBaselinePath);
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
output.error(`Failed to load baseline: ${error instanceof Error ? error.message : error}`);
|
|
178
|
+
process.exit(EXIT_CODES.ERROR);
|
|
179
|
+
}
|
|
180
|
+
// Find and load the report file
|
|
181
|
+
const reportPath = options.report || join(outputDir, config.output.files.checkReport);
|
|
182
|
+
let result;
|
|
183
|
+
try {
|
|
184
|
+
result = loadInterviewResult(reportPath);
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
output.error(error instanceof Error ? error.message : String(error));
|
|
188
|
+
process.exit(EXIT_CODES.ERROR);
|
|
189
|
+
}
|
|
190
|
+
// Create current baseline for comparison
|
|
191
|
+
const serverCommand = result.metadata.serverCommand || 'unknown';
|
|
192
|
+
const currentBaseline = createBaseline(result, serverCommand);
|
|
193
|
+
// Compare baselines
|
|
194
|
+
let diff;
|
|
195
|
+
try {
|
|
196
|
+
diff = compareBaselines(previousBaseline, currentBaseline, {
|
|
197
|
+
ignoreVersionMismatch: options.ignoreVersionMismatch,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
if (error instanceof BaselineVersionError) {
|
|
202
|
+
output.error('Version Compatibility Error:');
|
|
203
|
+
output.error(error.message);
|
|
204
|
+
output.error(`\nBaseline version: ${error.sourceVersion}`);
|
|
205
|
+
output.error(`Current version: ${error.targetVersion}`);
|
|
206
|
+
output.error('\nTo fix this, either:');
|
|
207
|
+
output.error(' 1. Run: bellwether baseline migrate <baseline-path>');
|
|
208
|
+
output.error(' 2. Use: --ignore-version-mismatch (results may be incorrect)');
|
|
209
|
+
process.exit(EXIT_CODES.ERROR);
|
|
210
|
+
}
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
// Show version compatibility warning if applicable
|
|
214
|
+
if (diff.versionCompatibility?.warning) {
|
|
215
|
+
output.warn(`Version Warning: ${diff.versionCompatibility.warning}`);
|
|
216
|
+
output.newline();
|
|
217
|
+
}
|
|
218
|
+
// Format and output
|
|
219
|
+
switch (format) {
|
|
220
|
+
case 'json':
|
|
221
|
+
console.log(formatDiffJson(diff));
|
|
222
|
+
break;
|
|
223
|
+
case 'markdown':
|
|
224
|
+
console.log(formatDiffMarkdown(diff));
|
|
225
|
+
break;
|
|
226
|
+
case 'compact':
|
|
227
|
+
console.log(formatDiffCompact(diff));
|
|
228
|
+
break;
|
|
229
|
+
default:
|
|
230
|
+
output.info('--- Drift Report ---');
|
|
231
|
+
output.info(formatDiffText(diff));
|
|
232
|
+
}
|
|
233
|
+
// Show summary
|
|
234
|
+
const totalChanges = diff.toolsAdded.length + diff.toolsRemoved.length + diff.toolsModified.length;
|
|
235
|
+
output.newline();
|
|
236
|
+
output.info(`Changes: ${totalChanges} tools affected`);
|
|
237
|
+
output.info(`Severity: ${diff.severity}`);
|
|
238
|
+
if (diff.versionCompatibility) {
|
|
239
|
+
output.info(`Format versions: ${diff.versionCompatibility.sourceVersion} -> ${diff.versionCompatibility.targetVersion}`);
|
|
240
|
+
}
|
|
241
|
+
// Exit with error if drift detected and --fail-on-drift
|
|
242
|
+
if (failOnDrift) {
|
|
243
|
+
if (diff.severity === 'breaking') {
|
|
244
|
+
output.error('\nBreaking changes detected!');
|
|
245
|
+
process.exit(EXIT_CODES.ERROR);
|
|
246
|
+
}
|
|
247
|
+
else if (diff.severity === 'warning') {
|
|
248
|
+
output.warn('\nWarning-level changes detected.');
|
|
249
|
+
process.exit(EXIT_CODES.ERROR);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
// baseline show
|
|
254
|
+
baselineCommand
|
|
255
|
+
.command('show')
|
|
256
|
+
.description('Display baseline contents')
|
|
257
|
+
.argument('[path]', 'Path to baseline file')
|
|
258
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
259
|
+
.option('--json', 'Output raw JSON')
|
|
260
|
+
.option('--tools', 'Show only tool fingerprints')
|
|
261
|
+
.option('--assertions', 'Show only assertions')
|
|
262
|
+
.action(async (baselinePath, options) => {
|
|
263
|
+
const config = loadConfigOrExit(options.config);
|
|
264
|
+
const outputDir = config.output.dir;
|
|
265
|
+
const resolvedBaselinePath = baselinePath ?? config.baseline.comparePath ?? config.baseline.path;
|
|
266
|
+
if (!resolvedBaselinePath) {
|
|
267
|
+
output.error('No baseline path provided. Set baseline.path or baseline.comparePath in config, or pass a path argument.');
|
|
268
|
+
process.exit(EXIT_CODES.ERROR);
|
|
269
|
+
}
|
|
270
|
+
// Determine full path
|
|
271
|
+
const fullPath = resolvedBaselinePath.startsWith('/')
|
|
272
|
+
? resolvedBaselinePath
|
|
273
|
+
: join(outputDir, resolvedBaselinePath);
|
|
274
|
+
if (!existsSync(fullPath)) {
|
|
275
|
+
output.error(`Baseline not found: ${fullPath}`);
|
|
276
|
+
output.error('\nRun `bellwether baseline save` to create a baseline.');
|
|
277
|
+
process.exit(EXIT_CODES.ERROR);
|
|
278
|
+
}
|
|
279
|
+
let baseline;
|
|
280
|
+
try {
|
|
281
|
+
baseline = loadBaseline(fullPath);
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
output.error(`Failed to load baseline: ${error instanceof Error ? error.message : error}`);
|
|
285
|
+
process.exit(EXIT_CODES.ERROR);
|
|
286
|
+
}
|
|
287
|
+
// Raw JSON output
|
|
288
|
+
if (options.json) {
|
|
289
|
+
console.log(JSON.stringify(baseline, null, 2));
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
// Formatted output
|
|
293
|
+
output.info('=== Baseline ===');
|
|
294
|
+
output.info(`File: ${fullPath}`);
|
|
295
|
+
output.info(`Format Version: ${baseline.version}`);
|
|
296
|
+
output.info(`Created: ${baseline.createdAt instanceof Date ? baseline.createdAt.toISOString() : baseline.createdAt}`);
|
|
297
|
+
output.info(`Mode: ${baseline.mode || 'check'}`);
|
|
298
|
+
output.info(`Server Command: ${baseline.serverCommand}`);
|
|
299
|
+
output.newline();
|
|
300
|
+
output.info('--- Server ---');
|
|
301
|
+
output.info(`Name: ${baseline.server.name}`);
|
|
302
|
+
output.info(`Version: ${baseline.server.version}`);
|
|
303
|
+
output.info(`Protocol: ${baseline.server.protocolVersion}`);
|
|
304
|
+
output.info(`Capabilities: ${baseline.server.capabilities.join(', ')}`);
|
|
305
|
+
output.newline();
|
|
306
|
+
// Tools
|
|
307
|
+
if (!options.assertions) {
|
|
308
|
+
output.info(`--- Tools (${baseline.tools.length}) ---`);
|
|
309
|
+
for (const tool of baseline.tools) {
|
|
310
|
+
output.info(`\n ${tool.name}`);
|
|
311
|
+
output.info(` Description: ${tool.description.slice(0, 80)}${tool.description.length > 80 ? '...' : ''}`);
|
|
312
|
+
output.info(` Schema Hash: ${tool.schemaHash}`);
|
|
313
|
+
if (tool.securityNotes.length > 0) {
|
|
314
|
+
output.info(` Security Notes: ${tool.securityNotes.length}`);
|
|
315
|
+
}
|
|
316
|
+
if (tool.limitations.length > 0) {
|
|
317
|
+
output.info(` Limitations: ${tool.limitations.length}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
output.newline();
|
|
321
|
+
}
|
|
322
|
+
// Assertions
|
|
323
|
+
if (!options.tools) {
|
|
324
|
+
output.info(`--- Assertions (${baseline.assertions.length}) ---`);
|
|
325
|
+
const byTool = new Map();
|
|
326
|
+
for (const assertion of baseline.assertions) {
|
|
327
|
+
const existing = byTool.get(assertion.tool) || [];
|
|
328
|
+
existing.push(assertion);
|
|
329
|
+
byTool.set(assertion.tool, existing);
|
|
330
|
+
}
|
|
331
|
+
for (const [tool, assertions] of byTool) {
|
|
332
|
+
output.info(`\n ${tool}:`);
|
|
333
|
+
for (const a of assertions.slice(0, 5)) {
|
|
334
|
+
const icon = a.isPositive ? '+' : '-';
|
|
335
|
+
output.info(` [${icon}] ${a.aspect}: ${a.assertion.slice(0, 60)}${a.assertion.length > 60 ? '...' : ''}`);
|
|
336
|
+
}
|
|
337
|
+
if (assertions.length > 5) {
|
|
338
|
+
output.info(` ... and ${assertions.length - 5} more`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
output.newline();
|
|
342
|
+
}
|
|
343
|
+
// Workflows
|
|
344
|
+
if (baseline.workflowSignatures && baseline.workflowSignatures.length > 0) {
|
|
345
|
+
output.info(`--- Workflows (${baseline.workflowSignatures.length}) ---`);
|
|
346
|
+
for (const wf of baseline.workflowSignatures) {
|
|
347
|
+
const icon = wf.succeeded ? '\u2713' : '\u2717';
|
|
348
|
+
output.info(` ${icon} ${wf.name}: ${wf.toolSequence.join(' -> ')}`);
|
|
349
|
+
}
|
|
350
|
+
output.newline();
|
|
351
|
+
}
|
|
352
|
+
// Summary
|
|
353
|
+
output.info('--- Summary ---');
|
|
354
|
+
output.info(baseline.summary.slice(0, 500) + (baseline.summary.length > 500 ? '...' : ''));
|
|
355
|
+
// Integrity check
|
|
356
|
+
output.newline();
|
|
357
|
+
const isValid = verifyIntegrity(baseline);
|
|
358
|
+
if (isValid) {
|
|
359
|
+
output.success('Integrity: Valid');
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
output.warn('Integrity: INVALID - file may have been modified');
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
// baseline diff
|
|
366
|
+
baselineCommand
|
|
367
|
+
.command('diff')
|
|
368
|
+
.description('Compare two baseline files')
|
|
369
|
+
.argument('<path1>', 'Path to first baseline file')
|
|
370
|
+
.argument('<path2>', 'Path to second baseline file')
|
|
371
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
372
|
+
.option('--format <format>', 'Output format: text, json, markdown, compact')
|
|
373
|
+
.option('--ignore-version-mismatch', 'Force comparison even if format versions are incompatible')
|
|
374
|
+
.action(async (path1, path2, options) => {
|
|
375
|
+
const config = loadConfigOrExit(options.config);
|
|
376
|
+
const format = options.format ?? config.baseline.outputFormat;
|
|
377
|
+
// Load both baselines
|
|
378
|
+
if (!existsSync(path1)) {
|
|
379
|
+
output.error(`Baseline not found: ${path1}`);
|
|
380
|
+
process.exit(EXIT_CODES.ERROR);
|
|
381
|
+
}
|
|
382
|
+
if (!existsSync(path2)) {
|
|
383
|
+
output.error(`Baseline not found: ${path2}`);
|
|
384
|
+
process.exit(EXIT_CODES.ERROR);
|
|
385
|
+
}
|
|
386
|
+
let baseline1, baseline2;
|
|
387
|
+
try {
|
|
388
|
+
baseline1 = loadBaseline(path1);
|
|
389
|
+
baseline2 = loadBaseline(path2);
|
|
390
|
+
}
|
|
391
|
+
catch (error) {
|
|
392
|
+
output.error(`Failed to load baseline: ${error instanceof Error ? error.message : error}`);
|
|
393
|
+
process.exit(EXIT_CODES.ERROR);
|
|
394
|
+
}
|
|
395
|
+
// Compare
|
|
396
|
+
let diff;
|
|
397
|
+
try {
|
|
398
|
+
diff = compareBaselines(baseline1, baseline2, {
|
|
399
|
+
ignoreVersionMismatch: options.ignoreVersionMismatch,
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
catch (error) {
|
|
403
|
+
if (error instanceof BaselineVersionError) {
|
|
404
|
+
output.error('Version Compatibility Error:');
|
|
405
|
+
output.error(error.message);
|
|
406
|
+
output.error(`\nBaseline 1 version: ${error.sourceVersion}`);
|
|
407
|
+
output.error(`Baseline 2 version: ${error.targetVersion}`);
|
|
408
|
+
output.error('\nTo fix this, either:');
|
|
409
|
+
output.error(' 1. Run: bellwether baseline migrate <baseline-path>');
|
|
410
|
+
output.error(' 2. Use: --ignore-version-mismatch (results may be incorrect)');
|
|
411
|
+
process.exit(EXIT_CODES.ERROR);
|
|
412
|
+
}
|
|
413
|
+
throw error;
|
|
414
|
+
}
|
|
415
|
+
// Header
|
|
416
|
+
output.info(`Comparing baselines:`);
|
|
417
|
+
output.info(` Old: ${basename(path1)} (${baseline1.createdAt instanceof Date ? baseline1.createdAt.toISOString().split('T')[0] : 'unknown'}) [${baseline1.version}]`);
|
|
418
|
+
output.info(` New: ${basename(path2)} (${baseline2.createdAt instanceof Date ? baseline2.createdAt.toISOString().split('T')[0] : 'unknown'}) [${baseline2.version}]`);
|
|
419
|
+
output.newline();
|
|
420
|
+
// Show version compatibility warning if applicable
|
|
421
|
+
if (diff.versionCompatibility?.warning) {
|
|
422
|
+
output.warn(`Version Warning: ${diff.versionCompatibility.warning}`);
|
|
423
|
+
output.newline();
|
|
424
|
+
}
|
|
425
|
+
// Format and output
|
|
426
|
+
switch (format) {
|
|
427
|
+
case 'json':
|
|
428
|
+
console.log(formatDiffJson(diff));
|
|
429
|
+
break;
|
|
430
|
+
case 'markdown':
|
|
431
|
+
console.log(formatDiffMarkdown(diff));
|
|
432
|
+
break;
|
|
433
|
+
case 'compact':
|
|
434
|
+
console.log(formatDiffCompact(diff));
|
|
435
|
+
break;
|
|
436
|
+
default:
|
|
437
|
+
output.info(formatDiffText(diff));
|
|
438
|
+
}
|
|
439
|
+
// Summary
|
|
440
|
+
output.newline();
|
|
441
|
+
output.info(`Severity: ${diff.severity}`);
|
|
442
|
+
output.info(`Tools added: ${diff.toolsAdded.length}`);
|
|
443
|
+
output.info(`Tools removed: ${diff.toolsRemoved.length}`);
|
|
444
|
+
output.info(`Tools modified: ${diff.toolsModified.length}`);
|
|
445
|
+
if (diff.versionCompatibility) {
|
|
446
|
+
output.info(`Format versions: ${diff.versionCompatibility.sourceVersion} -> ${diff.versionCompatibility.targetVersion}`);
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
//# sourceMappingURL=baseline.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Beta command for Bellwether Cloud beta access management.
|
|
3
|
+
*
|
|
4
|
+
* Subcommands:
|
|
5
|
+
* - status: Check beta status and user's access
|
|
6
|
+
* - invite <code>: Enter and verify an invitation code
|
|
7
|
+
*/
|
|
8
|
+
import { Command } from 'commander';
|
|
9
|
+
export declare const betaCommand: Command;
|
|
10
|
+
//# sourceMappingURL=beta.d.ts.map
|