@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,352 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth command for managing LLM provider API keys.
|
|
3
|
+
*
|
|
4
|
+
* Provides secure storage via system keychain with fallback to file-based storage.
|
|
5
|
+
* Separate from the `login` command which handles Bellwether Cloud authentication.
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
import * as readline from 'readline';
|
|
9
|
+
import { DEFAULT_MODELS } from '../../llm/client.js';
|
|
10
|
+
import { getKeychainService } from '../../auth/keychain.js';
|
|
11
|
+
import { getAuthStatus } from '../../auth/credentials.js';
|
|
12
|
+
import { EXIT_CODES } from '../../constants.js';
|
|
13
|
+
import * as output from '../output.js';
|
|
14
|
+
/**
|
|
15
|
+
* Provider display names and info.
|
|
16
|
+
*/
|
|
17
|
+
const PROVIDER_INFO = {
|
|
18
|
+
openai: {
|
|
19
|
+
name: 'OpenAI',
|
|
20
|
+
url: 'https://platform.openai.com/api-keys',
|
|
21
|
+
envVar: 'OPENAI_API_KEY',
|
|
22
|
+
},
|
|
23
|
+
anthropic: {
|
|
24
|
+
name: 'Anthropic',
|
|
25
|
+
url: 'https://console.anthropic.com/settings/keys',
|
|
26
|
+
envVar: 'ANTHROPIC_API_KEY',
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Create a readline interface for prompts.
|
|
31
|
+
*/
|
|
32
|
+
function createPrompt() {
|
|
33
|
+
return readline.createInterface({
|
|
34
|
+
input: process.stdin,
|
|
35
|
+
output: process.stdout,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Prompt user for input (hidden for passwords).
|
|
40
|
+
*/
|
|
41
|
+
async function prompt(rl, question, hidden = false) {
|
|
42
|
+
return new Promise((resolve) => {
|
|
43
|
+
if (hidden && process.stdin.isTTY) {
|
|
44
|
+
// Hide input for sensitive data
|
|
45
|
+
const stdin = process.stdin;
|
|
46
|
+
const stdout = process.stdout;
|
|
47
|
+
stdout.write(question);
|
|
48
|
+
stdin.setRawMode(true);
|
|
49
|
+
stdin.resume();
|
|
50
|
+
stdin.setEncoding('utf8');
|
|
51
|
+
let input = '';
|
|
52
|
+
const onData = (char) => {
|
|
53
|
+
const charCode = char.charCodeAt(0);
|
|
54
|
+
if (charCode === 13 || charCode === 10) {
|
|
55
|
+
// Enter
|
|
56
|
+
stdin.setRawMode(false);
|
|
57
|
+
stdin.removeListener('data', onData);
|
|
58
|
+
stdout.write('\n');
|
|
59
|
+
resolve(input);
|
|
60
|
+
}
|
|
61
|
+
else if (charCode === 127 || charCode === 8) {
|
|
62
|
+
// Backspace
|
|
63
|
+
if (input.length > 0) {
|
|
64
|
+
input = input.slice(0, -1);
|
|
65
|
+
stdout.clearLine(0);
|
|
66
|
+
stdout.cursorTo(0);
|
|
67
|
+
stdout.write(question + '*'.repeat(input.length));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else if (charCode === 3) {
|
|
71
|
+
// Ctrl+C
|
|
72
|
+
process.exit(EXIT_CODES.CLEAN);
|
|
73
|
+
}
|
|
74
|
+
else if (charCode >= 32) {
|
|
75
|
+
// Printable character
|
|
76
|
+
input += char;
|
|
77
|
+
stdout.write('*');
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
stdin.on('data', onData);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
// Fallback for non-TTY
|
|
84
|
+
rl.question(question, resolve);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Prompt user to select from a list.
|
|
90
|
+
*/
|
|
91
|
+
async function promptSelect(rl, question, options) {
|
|
92
|
+
output.info(question);
|
|
93
|
+
options.forEach((opt, i) => {
|
|
94
|
+
output.info(` ${i + 1}. ${opt.label}`);
|
|
95
|
+
});
|
|
96
|
+
const answer = await prompt(rl, `Enter choice (1-${options.length}): `);
|
|
97
|
+
const index = parseInt(answer, 10) - 1;
|
|
98
|
+
if (index >= 0 && index < options.length) {
|
|
99
|
+
return options[index].value;
|
|
100
|
+
}
|
|
101
|
+
output.info('Invalid choice. Please try again.\n');
|
|
102
|
+
return promptSelect(rl, question, options);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Validate API key format.
|
|
106
|
+
*/
|
|
107
|
+
function validateApiKey(provider, key) {
|
|
108
|
+
if (!key || key.trim().length === 0) {
|
|
109
|
+
return { valid: false, error: 'API key cannot be empty' };
|
|
110
|
+
}
|
|
111
|
+
if (provider === 'openai') {
|
|
112
|
+
// OpenAI keys start with sk-
|
|
113
|
+
if (!key.startsWith('sk-')) {
|
|
114
|
+
return { valid: false, error: 'OpenAI API keys should start with "sk-"' };
|
|
115
|
+
}
|
|
116
|
+
if (key.length < 20) {
|
|
117
|
+
return { valid: false, error: 'API key appears too short' };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (provider === 'anthropic') {
|
|
121
|
+
// Anthropic keys start with sk-ant-
|
|
122
|
+
if (!key.startsWith('sk-ant-')) {
|
|
123
|
+
return { valid: false, error: 'Anthropic API keys should start with "sk-ant-"' };
|
|
124
|
+
}
|
|
125
|
+
if (key.length < 20) {
|
|
126
|
+
return { valid: false, error: 'API key appears too short' };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return { valid: true };
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Interactive auth setup.
|
|
133
|
+
*/
|
|
134
|
+
async function interactiveSetup() {
|
|
135
|
+
const rl = createPrompt();
|
|
136
|
+
output.info('Bellwether Authentication Setup');
|
|
137
|
+
output.info('================================\n');
|
|
138
|
+
// Check current status
|
|
139
|
+
const status = await getAuthStatus();
|
|
140
|
+
const configuredProviders = status.filter(s => s.provider !== 'ollama' && s.configured);
|
|
141
|
+
if (configuredProviders.length > 0) {
|
|
142
|
+
output.info('Currently configured:');
|
|
143
|
+
for (const s of configuredProviders) {
|
|
144
|
+
const source = s.source === 'keychain' ? 'keychain' :
|
|
145
|
+
s.source === 'env' ? `env (${s.envVar})` : s.source;
|
|
146
|
+
output.info(` - ${PROVIDER_INFO[s.provider]?.name ?? s.provider}: ${source}`);
|
|
147
|
+
}
|
|
148
|
+
output.newline();
|
|
149
|
+
}
|
|
150
|
+
// Select provider
|
|
151
|
+
const provider = await promptSelect(rl, 'Which LLM provider would you like to configure?', [
|
|
152
|
+
{ value: 'anthropic', label: 'Anthropic Claude (recommended)' },
|
|
153
|
+
{ value: 'openai', label: 'OpenAI' },
|
|
154
|
+
]);
|
|
155
|
+
const info = PROVIDER_INFO[provider];
|
|
156
|
+
output.info(`\nGet your ${info.name} API key from:`);
|
|
157
|
+
output.info(` ${info.url}\n`);
|
|
158
|
+
// Get API key
|
|
159
|
+
const apiKey = await prompt(rl, `Enter your ${info.name} API key: `, true);
|
|
160
|
+
// Validate
|
|
161
|
+
const validation = validateApiKey(provider, apiKey);
|
|
162
|
+
if (!validation.valid) {
|
|
163
|
+
output.error(`\nError: ${validation.error}`);
|
|
164
|
+
rl.close();
|
|
165
|
+
process.exit(EXIT_CODES.ERROR);
|
|
166
|
+
}
|
|
167
|
+
// Check keychain availability
|
|
168
|
+
const keychain = getKeychainService();
|
|
169
|
+
const hasSecureKeychain = await keychain.isSecureKeychainAvailable();
|
|
170
|
+
let storageChoice;
|
|
171
|
+
if (hasSecureKeychain) {
|
|
172
|
+
storageChoice = await promptSelect(rl, '\nWhere would you like to store the API key?', [
|
|
173
|
+
{ value: 'keychain', label: 'System keychain (recommended - most secure)' },
|
|
174
|
+
{ value: 'env', label: `Environment file (~/.bellwether/.env)` },
|
|
175
|
+
]);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
output.info('\nNote: System keychain not available. Using file-based storage.');
|
|
179
|
+
storageChoice = 'env';
|
|
180
|
+
}
|
|
181
|
+
// Store the key
|
|
182
|
+
try {
|
|
183
|
+
if (storageChoice === 'keychain') {
|
|
184
|
+
await keychain.setApiKey(provider, apiKey);
|
|
185
|
+
output.success(`\n\u2713 API key stored in system keychain`);
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
// Store in ~/.bellwether/.env
|
|
189
|
+
const fs = await import('fs');
|
|
190
|
+
const path = await import('path');
|
|
191
|
+
const os = await import('os');
|
|
192
|
+
const envDir = path.join(os.homedir(), '.bellwether');
|
|
193
|
+
const envPath = path.join(envDir, '.env');
|
|
194
|
+
if (!fs.existsSync(envDir)) {
|
|
195
|
+
fs.mkdirSync(envDir, { recursive: true, mode: 0o700 });
|
|
196
|
+
}
|
|
197
|
+
// Read existing .env or create new
|
|
198
|
+
let envContent = '';
|
|
199
|
+
if (fs.existsSync(envPath)) {
|
|
200
|
+
envContent = fs.readFileSync(envPath, 'utf-8');
|
|
201
|
+
}
|
|
202
|
+
// Update or add the key
|
|
203
|
+
const envVar = info.envVar;
|
|
204
|
+
const lines = envContent.split('\n').filter(line => !line.startsWith(`${envVar}=`));
|
|
205
|
+
lines.push(`${envVar}=${apiKey}`);
|
|
206
|
+
fs.writeFileSync(envPath, lines.filter(l => l).join('\n') + '\n', { mode: 0o600 });
|
|
207
|
+
output.success(`\n\u2713 API key stored in ~/.bellwether/.env`);
|
|
208
|
+
}
|
|
209
|
+
output.info(`\nYou're all set! Bellwether will now use ${info.name} for tests.`);
|
|
210
|
+
output.info(`\nDefault model: ${DEFAULT_MODELS[provider]}`);
|
|
211
|
+
output.info('\nTry it out:');
|
|
212
|
+
output.info(' bellwether explore npx @modelcontextprotocol/server-memory');
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
output.error(`\nFailed to store API key: ${error instanceof Error ? error.message : error}`);
|
|
216
|
+
process.exit(EXIT_CODES.ERROR);
|
|
217
|
+
}
|
|
218
|
+
rl.close();
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Show auth status.
|
|
222
|
+
*/
|
|
223
|
+
async function showStatus() {
|
|
224
|
+
output.info('Bellwether Authentication Status');
|
|
225
|
+
output.info('=================================\n');
|
|
226
|
+
const status = await getAuthStatus();
|
|
227
|
+
for (const s of status) {
|
|
228
|
+
if (s.provider === 'ollama') {
|
|
229
|
+
output.info('Ollama:');
|
|
230
|
+
output.info(' Status: No API key required (local)');
|
|
231
|
+
output.info(' Model: qwen3:8b');
|
|
232
|
+
output.newline();
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
const info = PROVIDER_INFO[s.provider];
|
|
236
|
+
if (!info)
|
|
237
|
+
continue;
|
|
238
|
+
output.info(`${info.name}:`);
|
|
239
|
+
if (s.configured) {
|
|
240
|
+
let sourceDesc;
|
|
241
|
+
switch (s.source) {
|
|
242
|
+
case 'keychain':
|
|
243
|
+
sourceDesc = 'System keychain';
|
|
244
|
+
break;
|
|
245
|
+
case 'env':
|
|
246
|
+
sourceDesc = `Environment variable (${s.envVar})`;
|
|
247
|
+
break;
|
|
248
|
+
case 'project-env':
|
|
249
|
+
sourceDesc = `Project .env file (${s.envVar})`;
|
|
250
|
+
break;
|
|
251
|
+
case 'global-env':
|
|
252
|
+
sourceDesc = `Global .env file (~/.bellwether/.env)`;
|
|
253
|
+
break;
|
|
254
|
+
default:
|
|
255
|
+
sourceDesc = s.source;
|
|
256
|
+
}
|
|
257
|
+
output.info(` Status: \u2713 Configured`);
|
|
258
|
+
output.info(` Source: ${sourceDesc}`);
|
|
259
|
+
output.info(` Model: ${DEFAULT_MODELS[s.provider]}`);
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
output.info(` Status: \u2717 Not configured`);
|
|
263
|
+
output.info(` Setup: Run \`bellwether auth\` or set ${info.envVar}`);
|
|
264
|
+
}
|
|
265
|
+
output.newline();
|
|
266
|
+
}
|
|
267
|
+
// Show priority order
|
|
268
|
+
output.info('Credential resolution order:');
|
|
269
|
+
output.info(' 1. Environment variables (highest priority)');
|
|
270
|
+
output.info(' 2. System keychain');
|
|
271
|
+
output.info(' 3. ~/.bellwether/.env file');
|
|
272
|
+
output.info(' 4. Project .env file');
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Add a provider.
|
|
276
|
+
*/
|
|
277
|
+
async function addProvider(providerArg) {
|
|
278
|
+
const rl = createPrompt();
|
|
279
|
+
let provider;
|
|
280
|
+
if (providerArg && (providerArg === 'openai' || providerArg === 'anthropic')) {
|
|
281
|
+
provider = providerArg;
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
provider = await promptSelect(rl, 'Which provider?', [
|
|
285
|
+
{ value: 'anthropic', label: 'Anthropic (recommended)' },
|
|
286
|
+
{ value: 'openai', label: 'OpenAI' },
|
|
287
|
+
]);
|
|
288
|
+
}
|
|
289
|
+
const info = PROVIDER_INFO[provider];
|
|
290
|
+
output.info(`\nGet your API key from: ${info.url}\n`);
|
|
291
|
+
const apiKey = await prompt(rl, `Enter ${info.name} API key: `, true);
|
|
292
|
+
const validation = validateApiKey(provider, apiKey);
|
|
293
|
+
if (!validation.valid) {
|
|
294
|
+
output.error(`\nError: ${validation.error}`);
|
|
295
|
+
rl.close();
|
|
296
|
+
process.exit(EXIT_CODES.ERROR);
|
|
297
|
+
}
|
|
298
|
+
const keychain = getKeychainService();
|
|
299
|
+
await keychain.setApiKey(provider, apiKey);
|
|
300
|
+
output.success(`\n\u2713 ${info.name} API key stored in keychain`);
|
|
301
|
+
rl.close();
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Remove a provider.
|
|
305
|
+
*/
|
|
306
|
+
async function removeProvider(providerArg) {
|
|
307
|
+
const rl = createPrompt();
|
|
308
|
+
let provider;
|
|
309
|
+
if (providerArg && (providerArg === 'openai' || providerArg === 'anthropic')) {
|
|
310
|
+
provider = providerArg;
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
provider = await promptSelect(rl, 'Which provider to remove?', [
|
|
314
|
+
{ value: 'anthropic', label: 'Anthropic' },
|
|
315
|
+
{ value: 'openai', label: 'OpenAI' },
|
|
316
|
+
]);
|
|
317
|
+
}
|
|
318
|
+
const keychain = getKeychainService();
|
|
319
|
+
const deleted = await keychain.deleteApiKey(provider);
|
|
320
|
+
if (deleted) {
|
|
321
|
+
output.success(`\n\u2713 ${PROVIDER_INFO[provider].name} API key removed from keychain`);
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
output.info(`\nNo ${PROVIDER_INFO[provider].name} API key found in keychain`);
|
|
325
|
+
}
|
|
326
|
+
rl.close();
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* The auth command.
|
|
330
|
+
*/
|
|
331
|
+
export const authCommand = new Command('auth')
|
|
332
|
+
.description('Manage LLM provider API keys')
|
|
333
|
+
.addCommand(new Command('status')
|
|
334
|
+
.description('Show authentication status for all providers')
|
|
335
|
+
.action(showStatus))
|
|
336
|
+
.addCommand(new Command('add')
|
|
337
|
+
.description('Add or update an API key')
|
|
338
|
+
.argument('[provider]', 'Provider name (openai, anthropic)')
|
|
339
|
+
.action(addProvider))
|
|
340
|
+
.addCommand(new Command('remove')
|
|
341
|
+
.description('Remove an API key from keychain')
|
|
342
|
+
.argument('[provider]', 'Provider name (openai, anthropic)')
|
|
343
|
+
.action(removeProvider))
|
|
344
|
+
.addCommand(new Command('clear')
|
|
345
|
+
.description('Remove all stored API keys')
|
|
346
|
+
.action(async () => {
|
|
347
|
+
const keychain = getKeychainService();
|
|
348
|
+
await keychain.clearAll();
|
|
349
|
+
output.success('All API keys removed from keychain.');
|
|
350
|
+
}))
|
|
351
|
+
.action(interactiveSetup); // Default action is interactive setup
|
|
352
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { createCloudClient } from '../../cloud/client.js';
|
|
3
|
+
import { getLinkedProject } from '../../cloud/auth.js';
|
|
4
|
+
import { EXIT_CODES } from '../../constants.js';
|
|
5
|
+
import * as output from '../output.js';
|
|
6
|
+
export const badgeCommand = new Command('badge')
|
|
7
|
+
.description('Get embeddable badge for your project')
|
|
8
|
+
.option('-p, --project <id>', 'Project ID (uses linked project if not specified)')
|
|
9
|
+
.option('--json', 'Output as JSON')
|
|
10
|
+
.option('--markdown', 'Output markdown only')
|
|
11
|
+
.option('--url', 'Output badge URL only')
|
|
12
|
+
.action(async (options) => {
|
|
13
|
+
// Get project ID
|
|
14
|
+
let projectId = options.project;
|
|
15
|
+
if (!projectId) {
|
|
16
|
+
const link = getLinkedProject();
|
|
17
|
+
if (!link) {
|
|
18
|
+
output.error('No project specified and no linked project found.');
|
|
19
|
+
output.error('Run `bellwether link <project>` first or use --project <id>');
|
|
20
|
+
process.exit(EXIT_CODES.ERROR);
|
|
21
|
+
}
|
|
22
|
+
projectId = link.projectId;
|
|
23
|
+
}
|
|
24
|
+
// Create cloud client
|
|
25
|
+
const client = createCloudClient();
|
|
26
|
+
try {
|
|
27
|
+
const badge = await client.getBadgeInfo(projectId);
|
|
28
|
+
if (!badge) {
|
|
29
|
+
output.error(`Project not found: ${projectId}`);
|
|
30
|
+
process.exit(EXIT_CODES.ERROR);
|
|
31
|
+
}
|
|
32
|
+
if (options.json) {
|
|
33
|
+
output.info(JSON.stringify(badge, null, 2));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (options.markdown) {
|
|
37
|
+
output.info(badge.markdown);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (options.url) {
|
|
41
|
+
output.info(badge.badgeUrl);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
// Default: formatted output
|
|
45
|
+
output.info('');
|
|
46
|
+
output.info(`Project: ${badge.projectName}`);
|
|
47
|
+
output.info(`Status: ${badge.statusText}`);
|
|
48
|
+
if (badge.lastVerified) {
|
|
49
|
+
output.info(`Verified: ${new Date(badge.lastVerified).toLocaleString()}`);
|
|
50
|
+
}
|
|
51
|
+
if (badge.latestVersion) {
|
|
52
|
+
output.info(`Version: v${badge.latestVersion}`);
|
|
53
|
+
}
|
|
54
|
+
output.info('');
|
|
55
|
+
output.info('Badge URL:');
|
|
56
|
+
output.info(` ${badge.badgeUrl}`);
|
|
57
|
+
output.info('');
|
|
58
|
+
output.info('Add to your README.md:');
|
|
59
|
+
output.info('');
|
|
60
|
+
output.info(` ${badge.markdown}`);
|
|
61
|
+
output.info('');
|
|
62
|
+
output.info('Or with HTML:');
|
|
63
|
+
output.info('');
|
|
64
|
+
// Extract base URL from badge URL for consistency
|
|
65
|
+
const baseUrl = badge.badgeUrl.replace(/\/badge\/.*$/, '');
|
|
66
|
+
output.info(` <a href="${baseUrl}/projects/${projectId}"><img src="${badge.badgeUrl}" alt="Bellwether"></a>`);
|
|
67
|
+
output.info('');
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
output.error('Failed to get badge info: ' + (error instanceof Error ? error.message : String(error)));
|
|
71
|
+
process.exit(EXIT_CODES.ERROR);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
//# sourceMappingURL=badge.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* baseline accept command - accept detected drift as intentional changes.
|
|
3
|
+
*
|
|
4
|
+
* This command allows users to acknowledge that detected drift was intentional
|
|
5
|
+
* (e.g., when adding new features, updating tool behavior, etc.) and update
|
|
6
|
+
* the baseline to reflect the new expected state.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* bellwether baseline accept # Accept drift and update baseline
|
|
10
|
+
* bellwether baseline accept --reason "Added new delete tool"
|
|
11
|
+
* bellwether baseline accept --dry-run # Show what would be accepted
|
|
12
|
+
*/
|
|
13
|
+
import { Command } from 'commander';
|
|
14
|
+
export declare const acceptCommand: Command;
|
|
15
|
+
//# sourceMappingURL=baseline-accept.d.ts.map
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* baseline accept command - accept detected drift as intentional changes.
|
|
3
|
+
*
|
|
4
|
+
* This command allows users to acknowledge that detected drift was intentional
|
|
5
|
+
* (e.g., when adding new features, updating tool behavior, etc.) and update
|
|
6
|
+
* the baseline to reflect the new expected state.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* bellwether baseline accept # Accept drift and update baseline
|
|
10
|
+
* bellwether baseline accept --reason "Added new delete tool"
|
|
11
|
+
* bellwether baseline accept --dry-run # Show what would be accepted
|
|
12
|
+
*/
|
|
13
|
+
import { Command } from 'commander';
|
|
14
|
+
import { existsSync, readFileSync } from 'fs';
|
|
15
|
+
import { join } from 'path';
|
|
16
|
+
import { createBaseline, saveBaseline, loadBaseline, compareBaselines, acceptDrift, formatDiffText, } from '../../baseline/index.js';
|
|
17
|
+
import { loadConfig, ConfigNotFoundError } from '../../config/loader.js';
|
|
18
|
+
import { EXIT_CODES } from '../../constants.js';
|
|
19
|
+
import * as output from '../output.js';
|
|
20
|
+
function loadConfigOrExit(configPath) {
|
|
21
|
+
try {
|
|
22
|
+
return loadConfig(configPath);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
if (error instanceof ConfigNotFoundError) {
|
|
26
|
+
output.error(error.message);
|
|
27
|
+
process.exit(EXIT_CODES.ERROR);
|
|
28
|
+
}
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Load interview result from JSON report.
|
|
34
|
+
*/
|
|
35
|
+
function loadInterviewResult(reportPath) {
|
|
36
|
+
if (!existsSync(reportPath)) {
|
|
37
|
+
throw new Error(`Test report not found: ${reportPath}\n\n` +
|
|
38
|
+
'Run `bellwether check` first to generate a report.\n' +
|
|
39
|
+
'Configure in bellwether.yaml:\n' +
|
|
40
|
+
' output:\n' +
|
|
41
|
+
' format: json # or "both" for JSON + markdown');
|
|
42
|
+
}
|
|
43
|
+
const content = readFileSync(reportPath, 'utf-8');
|
|
44
|
+
let result;
|
|
45
|
+
try {
|
|
46
|
+
result = JSON.parse(content);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
throw new Error(`Invalid JSON in report file ${reportPath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
50
|
+
}
|
|
51
|
+
// Validate that this is a check mode result
|
|
52
|
+
if (result.metadata.model && result.metadata.model !== 'check') {
|
|
53
|
+
throw new Error(`Baseline operations only work with check mode results.\n\n` +
|
|
54
|
+
`The report at ${reportPath} was created with explore mode.\n` +
|
|
55
|
+
'Run `bellwether check` to generate a check mode report first.');
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
export const acceptCommand = new Command('accept')
|
|
60
|
+
.description('Accept detected drift as intentional and update the baseline')
|
|
61
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
62
|
+
.option('--report <path>', 'Path to test report JSON file')
|
|
63
|
+
.option('--baseline <path>', 'Path to baseline file')
|
|
64
|
+
.option('--reason <reason>', 'Reason for accepting the drift')
|
|
65
|
+
.option('--accepted-by <name>', 'Who is accepting the drift (for audit trail)')
|
|
66
|
+
.option('--dry-run', 'Show what would be accepted without making changes')
|
|
67
|
+
.option('-f, --force', 'Skip confirmation prompt')
|
|
68
|
+
.action(async (options) => {
|
|
69
|
+
const config = loadConfigOrExit(options.config);
|
|
70
|
+
const outputDir = config.output.dir;
|
|
71
|
+
const resolvedBaselinePath = options.baseline ?? config.baseline.comparePath ?? config.baseline.path;
|
|
72
|
+
if (!resolvedBaselinePath) {
|
|
73
|
+
output.error('No baseline path provided. Set baseline.path or baseline.comparePath in config, or pass --baseline.');
|
|
74
|
+
process.exit(EXIT_CODES.ERROR);
|
|
75
|
+
}
|
|
76
|
+
// Determine paths
|
|
77
|
+
const baselinePath = resolvedBaselinePath.startsWith('/')
|
|
78
|
+
? resolvedBaselinePath
|
|
79
|
+
: join(outputDir, resolvedBaselinePath);
|
|
80
|
+
const reportPath = options.report || join(outputDir, config.output.files.checkReport);
|
|
81
|
+
// Load the existing baseline
|
|
82
|
+
if (!existsSync(baselinePath)) {
|
|
83
|
+
output.error(`Baseline not found: ${baselinePath}`);
|
|
84
|
+
output.error('\nNo baseline exists to compare against.');
|
|
85
|
+
output.error('Run `bellwether check` followed by `bellwether baseline save` first.');
|
|
86
|
+
process.exit(EXIT_CODES.ERROR);
|
|
87
|
+
}
|
|
88
|
+
let previousBaseline;
|
|
89
|
+
try {
|
|
90
|
+
previousBaseline = loadBaseline(baselinePath);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
output.error(`Failed to load baseline: ${error instanceof Error ? error.message : error}`);
|
|
94
|
+
process.exit(EXIT_CODES.ERROR);
|
|
95
|
+
}
|
|
96
|
+
// Load the current test results
|
|
97
|
+
let result;
|
|
98
|
+
try {
|
|
99
|
+
result = loadInterviewResult(reportPath);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
output.error(error instanceof Error ? error.message : String(error));
|
|
103
|
+
process.exit(EXIT_CODES.ERROR);
|
|
104
|
+
}
|
|
105
|
+
// Create current baseline from test results
|
|
106
|
+
const serverCommand = result.metadata.serverCommand || 'unknown';
|
|
107
|
+
const currentBaseline = createBaseline(result, serverCommand);
|
|
108
|
+
// Compare baselines
|
|
109
|
+
const diff = compareBaselines(previousBaseline, currentBaseline);
|
|
110
|
+
// Check if there's any drift to accept
|
|
111
|
+
if (diff.severity === 'none') {
|
|
112
|
+
output.success('No drift detected. Baseline is already up to date.');
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// Show the drift that will be accepted
|
|
116
|
+
output.info('=== Drift to Accept ===');
|
|
117
|
+
output.newline();
|
|
118
|
+
output.info(formatDiffText(diff));
|
|
119
|
+
output.newline();
|
|
120
|
+
// Show summary
|
|
121
|
+
output.info('--- Summary ---');
|
|
122
|
+
output.info(`Severity: ${diff.severity}`);
|
|
123
|
+
if (diff.toolsAdded.length > 0) {
|
|
124
|
+
output.info(`Tools added: ${diff.toolsAdded.join(', ')}`);
|
|
125
|
+
}
|
|
126
|
+
if (diff.toolsRemoved.length > 0) {
|
|
127
|
+
output.warn(`Tools removed: ${diff.toolsRemoved.join(', ')}`);
|
|
128
|
+
}
|
|
129
|
+
if (diff.toolsModified.length > 0) {
|
|
130
|
+
output.info(`Tools modified: ${diff.toolsModified.map((t) => t.tool).join(', ')}`);
|
|
131
|
+
}
|
|
132
|
+
output.info(`Breaking changes: ${diff.breakingCount}`);
|
|
133
|
+
output.info(`Warnings: ${diff.warningCount}`);
|
|
134
|
+
output.info(`Info: ${diff.infoCount}`);
|
|
135
|
+
output.newline();
|
|
136
|
+
// Dry run mode - just show what would happen
|
|
137
|
+
if (options.dryRun) {
|
|
138
|
+
output.info('--- Dry Run Mode ---');
|
|
139
|
+
output.info('Would update baseline with acceptance metadata:');
|
|
140
|
+
output.info(` Accepted by: ${options.acceptedBy || '(not specified)'}`);
|
|
141
|
+
output.info(` Reason: ${options.reason || '(not specified)'}`);
|
|
142
|
+
output.info(` Baseline path: ${baselinePath}`);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// For breaking changes without --force, show a warning
|
|
146
|
+
if (diff.severity === 'breaking' && !options.force) {
|
|
147
|
+
output.warn('');
|
|
148
|
+
output.warn('⚠️ This will accept BREAKING changes!');
|
|
149
|
+
output.warn('');
|
|
150
|
+
output.warn('Breaking changes may affect downstream consumers of this MCP server.');
|
|
151
|
+
output.warn('Make sure you have updated any dependent systems accordingly.');
|
|
152
|
+
output.warn('');
|
|
153
|
+
output.warn('To proceed, run again with --force');
|
|
154
|
+
process.exit(EXIT_CODES.ERROR);
|
|
155
|
+
}
|
|
156
|
+
// Accept the drift
|
|
157
|
+
const acceptedBaseline = acceptDrift(currentBaseline, diff, {
|
|
158
|
+
acceptedBy: options.acceptedBy,
|
|
159
|
+
reason: options.reason,
|
|
160
|
+
});
|
|
161
|
+
// Save the updated baseline
|
|
162
|
+
saveBaseline(acceptedBaseline, baselinePath);
|
|
163
|
+
output.success(`Drift accepted and baseline updated: ${baselinePath}`);
|
|
164
|
+
output.newline();
|
|
165
|
+
// Show acceptance details
|
|
166
|
+
output.info('Acceptance recorded:');
|
|
167
|
+
output.info(` Accepted at: ${acceptedBaseline.acceptance?.acceptedAt}`);
|
|
168
|
+
if (options.acceptedBy) {
|
|
169
|
+
output.info(` Accepted by: ${options.acceptedBy}`);
|
|
170
|
+
}
|
|
171
|
+
if (options.reason) {
|
|
172
|
+
output.info(` Reason: ${options.reason}`);
|
|
173
|
+
}
|
|
174
|
+
output.newline();
|
|
175
|
+
output.info('The baseline now reflects the current server state.');
|
|
176
|
+
output.info('Future `bellwether check` runs will compare against this new baseline.');
|
|
177
|
+
});
|
|
178
|
+
//# sourceMappingURL=baseline-accept.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
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
|
+
/**
|
|
9
|
+
* Create the migrate command.
|
|
10
|
+
*/
|
|
11
|
+
export declare const migrateCommand: Command;
|
|
12
|
+
//# sourceMappingURL=baseline-migrate.d.ts.map
|