@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,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Teams command for managing team selection in Bellwether Cloud.
|
|
3
|
+
*
|
|
4
|
+
* Allows users to list their teams and switch the active team for API requests.
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
import { getStoredSession, getSessionTeams, setActiveTeam, getTeamId, TEAM_ID_ENV_VAR, } from '../../../cloud/auth.js';
|
|
8
|
+
import { EXIT_CODES } from '../../../constants.js';
|
|
9
|
+
import * as output from '../../output.js';
|
|
10
|
+
export const teamsCommand = new Command('teams')
|
|
11
|
+
.description('Manage team selection for cloud operations')
|
|
12
|
+
.option('--json', 'Output as JSON')
|
|
13
|
+
.action(async (options) => {
|
|
14
|
+
// List teams by default
|
|
15
|
+
const session = getStoredSession();
|
|
16
|
+
if (!session) {
|
|
17
|
+
output.error('Not logged in.');
|
|
18
|
+
output.error('Run `bellwether login` first.');
|
|
19
|
+
process.exit(EXIT_CODES.ERROR);
|
|
20
|
+
}
|
|
21
|
+
const teams = getSessionTeams();
|
|
22
|
+
if (teams.length === 0) {
|
|
23
|
+
output.warn('No teams found in session.');
|
|
24
|
+
output.info('Try logging out and back in: `bellwether login --logout && bellwether login`');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
// Check for env var override
|
|
28
|
+
const envTeamId = process.env[TEAM_ID_ENV_VAR];
|
|
29
|
+
const effectiveTeamId = getTeamId();
|
|
30
|
+
if (options.json) {
|
|
31
|
+
output.info(JSON.stringify({
|
|
32
|
+
teams,
|
|
33
|
+
activeTeamId: session.activeTeamId,
|
|
34
|
+
effectiveTeamId,
|
|
35
|
+
envOverride: envTeamId || null,
|
|
36
|
+
}, null, 2));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
output.info('Your Teams');
|
|
40
|
+
output.info('-----------');
|
|
41
|
+
for (const team of teams) {
|
|
42
|
+
const isActive = team.id === session.activeTeamId;
|
|
43
|
+
const isEffective = team.id === effectiveTeamId;
|
|
44
|
+
const markers = [];
|
|
45
|
+
if (isActive)
|
|
46
|
+
markers.push('active');
|
|
47
|
+
if (envTeamId && isEffective)
|
|
48
|
+
markers.push('env override');
|
|
49
|
+
const suffix = markers.length > 0 ? ` (${markers.join(', ')})` : '';
|
|
50
|
+
const roleStr = `[${team.role}]`;
|
|
51
|
+
output.info(` ${isEffective ? '>' : ' '} ${team.name} ${roleStr} - ${team.plan}${suffix}`);
|
|
52
|
+
output.info(` ID: ${team.id}`);
|
|
53
|
+
}
|
|
54
|
+
if (envTeamId) {
|
|
55
|
+
output.info(`\nNote: ${TEAM_ID_ENV_VAR} is set, overriding session team.`);
|
|
56
|
+
}
|
|
57
|
+
if (teams.length > 1) {
|
|
58
|
+
output.info('\nUse `bellwether teams switch <team-id>` to change active team.');
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
// Subcommand: switch
|
|
62
|
+
teamsCommand
|
|
63
|
+
.command('switch [team-id]')
|
|
64
|
+
.description('Switch to a different team')
|
|
65
|
+
.action(async (teamIdArg) => {
|
|
66
|
+
const session = getStoredSession();
|
|
67
|
+
if (!session) {
|
|
68
|
+
output.error('Not logged in.');
|
|
69
|
+
output.error('Run `bellwether login` first.');
|
|
70
|
+
process.exit(EXIT_CODES.ERROR);
|
|
71
|
+
}
|
|
72
|
+
const teams = getSessionTeams();
|
|
73
|
+
if (teams.length === 0) {
|
|
74
|
+
output.warn('No teams found in session.');
|
|
75
|
+
output.info('Try logging out and back in: `bellwether login --logout && bellwether login`');
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (teams.length === 1) {
|
|
79
|
+
output.info(`You only have access to one team: ${teams[0].name}`);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const targetTeamId = teamIdArg;
|
|
83
|
+
// If no team ID provided, show interactive selection
|
|
84
|
+
if (!targetTeamId) {
|
|
85
|
+
output.info('Select a team:\n');
|
|
86
|
+
for (let i = 0; i < teams.length; i++) {
|
|
87
|
+
const team = teams[i];
|
|
88
|
+
const isActive = team.id === session.activeTeamId;
|
|
89
|
+
const marker = isActive ? ' (current)' : '';
|
|
90
|
+
output.info(` ${i + 1}. ${team.name} [${team.role}]${marker}`);
|
|
91
|
+
output.info(` ID: ${team.id}`);
|
|
92
|
+
}
|
|
93
|
+
output.info('\nRun `bellwether teams switch <team-id>` with a team ID from above.');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
// Find the target team
|
|
97
|
+
const targetTeam = teams.find(t => t.id === targetTeamId || t.name.toLowerCase() === targetTeamId.toLowerCase());
|
|
98
|
+
if (!targetTeam) {
|
|
99
|
+
output.error(`Team not found: ${targetTeamId}`);
|
|
100
|
+
output.error('\nAvailable teams:');
|
|
101
|
+
for (const team of teams) {
|
|
102
|
+
output.error(` - ${team.name} (${team.id})`);
|
|
103
|
+
}
|
|
104
|
+
process.exit(EXIT_CODES.ERROR);
|
|
105
|
+
}
|
|
106
|
+
// Check if already active
|
|
107
|
+
if (targetTeam.id === session.activeTeamId) {
|
|
108
|
+
output.info(`Already using team: ${targetTeam.name}`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
// Switch team
|
|
112
|
+
const success = setActiveTeam(targetTeam.id);
|
|
113
|
+
if (success) {
|
|
114
|
+
output.info(`Switched to team: ${targetTeam.name}`);
|
|
115
|
+
output.info(`\nAll cloud commands will now use this team context.`);
|
|
116
|
+
// Warn about env var override
|
|
117
|
+
const envTeamId = process.env[TEAM_ID_ENV_VAR];
|
|
118
|
+
if (envTeamId && envTeamId !== targetTeam.id) {
|
|
119
|
+
output.warn(`\nNote: ${TEAM_ID_ENV_VAR} is set and will override this selection.`);
|
|
120
|
+
output.warn(`Unset it with: unset ${TEAM_ID_ENV_VAR}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
output.error('Failed to switch team. Please try logging in again.');
|
|
125
|
+
process.exit(EXIT_CODES.ERROR);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
// Subcommand: current
|
|
129
|
+
teamsCommand
|
|
130
|
+
.command('current')
|
|
131
|
+
.description('Show the current active team')
|
|
132
|
+
.option('--json', 'Output as JSON')
|
|
133
|
+
.action(async (options) => {
|
|
134
|
+
const session = getStoredSession();
|
|
135
|
+
if (!session) {
|
|
136
|
+
output.error('Not logged in.');
|
|
137
|
+
process.exit(EXIT_CODES.ERROR);
|
|
138
|
+
}
|
|
139
|
+
const effectiveTeamId = getTeamId();
|
|
140
|
+
const envTeamId = process.env[TEAM_ID_ENV_VAR];
|
|
141
|
+
const teams = getSessionTeams();
|
|
142
|
+
const activeTeam = teams.find(t => t.id === effectiveTeamId);
|
|
143
|
+
if (options.json) {
|
|
144
|
+
output.info(JSON.stringify({
|
|
145
|
+
team: activeTeam || null,
|
|
146
|
+
source: envTeamId ? 'environment' : 'session',
|
|
147
|
+
envVar: envTeamId || null,
|
|
148
|
+
}, null, 2));
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (!activeTeam) {
|
|
152
|
+
output.warn('No active team.');
|
|
153
|
+
if (teams.length > 0) {
|
|
154
|
+
output.info('Run `bellwether teams switch` to select a team.');
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
output.info('Try logging out and back in: `bellwether login --logout && bellwether login`');
|
|
158
|
+
}
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
output.info(`Current team: ${activeTeam.name}`);
|
|
162
|
+
output.info(` ID: ${activeTeam.id}`);
|
|
163
|
+
output.info(` Role: ${activeTeam.role}`);
|
|
164
|
+
output.info(` Plan: ${activeTeam.plan}`);
|
|
165
|
+
if (envTeamId) {
|
|
166
|
+
output.info(`\nSource: ${TEAM_ID_ENV_VAR} environment variable`);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
//# sourceMappingURL=teams.js.map
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upload command for uploading baselines to Bellwether Cloud.
|
|
3
|
+
*
|
|
4
|
+
* Can read baseline path from bellwether.yaml config.
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
import { existsSync, readFileSync } from 'fs';
|
|
8
|
+
import { join } from 'path';
|
|
9
|
+
import { getLinkedProject } from '../../../cloud/auth.js';
|
|
10
|
+
import { loadBaseline } from '../../../baseline/saver.js';
|
|
11
|
+
import { convertToCloudBaseline } from '../../../baseline/converter.js';
|
|
12
|
+
import { EXIT_CODES } from '../../../constants.js';
|
|
13
|
+
import * as output from '../../output.js';
|
|
14
|
+
import { getSeverityIcon } from '../../output.js';
|
|
15
|
+
import { loadConfigOrExit, getSessionTokenOrExit, createAuthenticatedClient } from './shared.js';
|
|
16
|
+
function resolveBaselinePath(pathValue, outputDir) {
|
|
17
|
+
return pathValue.startsWith('/') ? pathValue : join(outputDir, pathValue);
|
|
18
|
+
}
|
|
19
|
+
export const uploadCommand = new Command('upload')
|
|
20
|
+
.description('Upload a baseline to Bellwether Cloud')
|
|
21
|
+
.argument('[baseline]', 'Path to baseline JSON file (defaults to baseline.path in config)')
|
|
22
|
+
.option('-b, --baseline <path>', 'Path to baseline JSON file')
|
|
23
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
24
|
+
.option('-p, --project <id>', 'Project ID to upload to (uses linked project if not specified)')
|
|
25
|
+
.option('--ci', 'CI mode - output URL only, exit 1 on breaking drift')
|
|
26
|
+
.option('--session <session>', 'Session token (overrides stored/env session)')
|
|
27
|
+
.option('--fail-on-drift', 'Exit with error if any behavioral drift detected')
|
|
28
|
+
.action(async (baselineArg, options) => {
|
|
29
|
+
// Get config settings
|
|
30
|
+
const config = loadConfigOrExit(options.config);
|
|
31
|
+
const outputDir = config.output.dir;
|
|
32
|
+
// Determine baseline path with priority:
|
|
33
|
+
// 1. Positional argument
|
|
34
|
+
// 2. --baseline flag
|
|
35
|
+
// 3. baseline.path from config
|
|
36
|
+
let baselinePath = baselineArg ?? options.baseline;
|
|
37
|
+
if (!baselinePath) {
|
|
38
|
+
baselinePath = resolveBaselinePath(config.baseline.path, outputDir);
|
|
39
|
+
}
|
|
40
|
+
const isCiMode = options.ci;
|
|
41
|
+
// Get session
|
|
42
|
+
const sessionToken = getSessionTokenOrExit(options.session, isCiMode ? 'BELLWETHER_SESSION not set' : 'Not authenticated. Run `bellwether login` first or set BELLWETHER_SESSION.');
|
|
43
|
+
// Check baseline file exists
|
|
44
|
+
if (!existsSync(baselinePath)) {
|
|
45
|
+
if (isCiMode) {
|
|
46
|
+
output.error(`Baseline not found: ${baselinePath}`);
|
|
47
|
+
process.exit(EXIT_CODES.ERROR);
|
|
48
|
+
}
|
|
49
|
+
output.error(`Baseline file not found: ${baselinePath}`);
|
|
50
|
+
output.error('\nCreate a baseline first:');
|
|
51
|
+
output.error(' 1. Run `bellwether check` (with output.format: json in config)');
|
|
52
|
+
output.error(' 2. Run `bellwether baseline save`');
|
|
53
|
+
process.exit(EXIT_CODES.ERROR);
|
|
54
|
+
}
|
|
55
|
+
// Determine project ID
|
|
56
|
+
let projectId = options.project;
|
|
57
|
+
if (!projectId) {
|
|
58
|
+
const link = getLinkedProject();
|
|
59
|
+
if (link) {
|
|
60
|
+
projectId = link.projectId;
|
|
61
|
+
if (!isCiMode) {
|
|
62
|
+
output.info(`Using linked project: ${link.projectName}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (!projectId) {
|
|
67
|
+
if (isCiMode) {
|
|
68
|
+
output.error('No project specified');
|
|
69
|
+
process.exit(EXIT_CODES.ERROR);
|
|
70
|
+
}
|
|
71
|
+
output.error('No project specified.');
|
|
72
|
+
output.error('\nEither:');
|
|
73
|
+
output.error(' - Use --project <id> to specify a project');
|
|
74
|
+
output.error(' - Run `bellwether link` to link this directory to a project');
|
|
75
|
+
process.exit(EXIT_CODES.ERROR);
|
|
76
|
+
}
|
|
77
|
+
// Load and convert baseline
|
|
78
|
+
let cloudBaseline;
|
|
79
|
+
try {
|
|
80
|
+
// Try loading as cloud baseline first
|
|
81
|
+
const content = readFileSync(baselinePath, 'utf-8');
|
|
82
|
+
const parsed = JSON.parse(content);
|
|
83
|
+
if (parsed.version === '1.0' && parsed.metadata?.formatVersion === '1.0') {
|
|
84
|
+
// Already in cloud format
|
|
85
|
+
cloudBaseline = parsed;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
// Convert from local format
|
|
89
|
+
const localBaseline = loadBaseline(baselinePath);
|
|
90
|
+
cloudBaseline = convertToCloudBaseline(localBaseline);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
if (isCiMode) {
|
|
95
|
+
output.error(`Failed to load baseline: ${error instanceof Error ? error.message : error}`);
|
|
96
|
+
process.exit(EXIT_CODES.ERROR);
|
|
97
|
+
}
|
|
98
|
+
output.error('Failed to load baseline: ' + (error instanceof Error ? error.message : String(error)));
|
|
99
|
+
process.exit(EXIT_CODES.ERROR);
|
|
100
|
+
}
|
|
101
|
+
// Create client and upload
|
|
102
|
+
const client = createAuthenticatedClient(sessionToken, isCiMode ? 'Authentication failed' : 'Authentication failed. Run `bellwether login` to re-authenticate.');
|
|
103
|
+
if (!isCiMode) {
|
|
104
|
+
output.info(`Uploading baseline to project ${projectId}...`);
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
const result = await client.uploadBaseline(projectId, cloudBaseline);
|
|
108
|
+
if (isCiMode) {
|
|
109
|
+
// CI mode - minimal output
|
|
110
|
+
output.info(result.viewUrl);
|
|
111
|
+
// Check for drift
|
|
112
|
+
if (result.version > 1) {
|
|
113
|
+
const diff = await client.getLatestDiff(projectId);
|
|
114
|
+
if (diff) {
|
|
115
|
+
if (diff.severity === 'breaking') {
|
|
116
|
+
output.error('Breaking changes detected');
|
|
117
|
+
process.exit(EXIT_CODES.ERROR);
|
|
118
|
+
}
|
|
119
|
+
if (options.failOnDrift && diff.severity !== 'none') {
|
|
120
|
+
output.error(`Behavioral drift detected: ${diff.severity}`);
|
|
121
|
+
process.exit(EXIT_CODES.ERROR);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
// Interactive mode - detailed output
|
|
128
|
+
output.info(`\nUpload successful!`);
|
|
129
|
+
output.info(`Version: ${result.version}`);
|
|
130
|
+
output.info(`View: ${result.viewUrl}`);
|
|
131
|
+
if (result.diffUrl) {
|
|
132
|
+
output.info(`Diff: ${result.diffUrl}`);
|
|
133
|
+
// Show diff summary
|
|
134
|
+
const diff = await client.getLatestDiff(projectId);
|
|
135
|
+
if (diff) {
|
|
136
|
+
output.info('\nChanges from previous version:');
|
|
137
|
+
printDiffSummary(diff);
|
|
138
|
+
if (diff.severity === 'breaking') {
|
|
139
|
+
output.info('\n⚠️ Breaking changes detected!');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
output.info('\nThis is the first baseline for this project.');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
if (isCiMode) {
|
|
150
|
+
output.error(`Upload failed: ${error instanceof Error ? error.message : error}`);
|
|
151
|
+
process.exit(EXIT_CODES.ERROR);
|
|
152
|
+
}
|
|
153
|
+
output.error('Upload failed: ' + (error instanceof Error ? error.message : String(error)));
|
|
154
|
+
process.exit(EXIT_CODES.ERROR);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
/**
|
|
158
|
+
* Print a diff summary in human-readable format (verbose format).
|
|
159
|
+
*/
|
|
160
|
+
function printDiffSummary(diff) {
|
|
161
|
+
output.info(` Severity: ${getSeverityIcon(diff.severity)} ${diff.severity}`);
|
|
162
|
+
if (diff.toolsAdded > 0) {
|
|
163
|
+
output.info(` Tools added: +${diff.toolsAdded}`);
|
|
164
|
+
}
|
|
165
|
+
if (diff.toolsRemoved > 0) {
|
|
166
|
+
output.info(` Tools removed: -${diff.toolsRemoved}`);
|
|
167
|
+
}
|
|
168
|
+
if (diff.toolsModified > 0) {
|
|
169
|
+
output.info(` Tools modified: ~${diff.toolsModified}`);
|
|
170
|
+
}
|
|
171
|
+
if (diff.behaviorChanges > 0) {
|
|
172
|
+
output.info(` Behavior changes: ${diff.behaviorChanges}`);
|
|
173
|
+
}
|
|
174
|
+
if (diff.toolsAdded === 0 &&
|
|
175
|
+
diff.toolsRemoved === 0 &&
|
|
176
|
+
diff.toolsModified === 0 &&
|
|
177
|
+
diff.behaviorChanges === 0) {
|
|
178
|
+
output.info(' No changes detected');
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=upload.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract commands - validate MCP servers against contracts.
|
|
3
|
+
*
|
|
4
|
+
* Subcommands:
|
|
5
|
+
* - validate [path] Validate server against a contract file
|
|
6
|
+
* - generate [path] Generate a contract from current server state
|
|
7
|
+
* - show [path] Display contract contents
|
|
8
|
+
*/
|
|
9
|
+
import { Command } from 'commander';
|
|
10
|
+
export declare const contractCommand: Command;
|
|
11
|
+
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract commands - validate MCP servers against contracts.
|
|
3
|
+
*
|
|
4
|
+
* Subcommands:
|
|
5
|
+
* - validate [path] Validate server against a contract file
|
|
6
|
+
* - generate [path] Generate a contract from current server state
|
|
7
|
+
* - show [path] Display contract contents
|
|
8
|
+
*/
|
|
9
|
+
import { Command } from 'commander';
|
|
10
|
+
import { existsSync, writeFileSync, readFileSync } from 'fs';
|
|
11
|
+
import { join } from 'path';
|
|
12
|
+
import { loadContract, findContractFile, validateContract, generateContract, generateContractYaml, generateContractValidationMarkdown, } from '../../contract/index.js';
|
|
13
|
+
import { MCPClient } from '../../transport/mcp-client.js';
|
|
14
|
+
import { discover } from '../../discovery/discovery.js';
|
|
15
|
+
import { EXIT_CODES, CONTRACT_TESTING } from '../../constants.js';
|
|
16
|
+
import { loadConfig, ConfigNotFoundError } from '../../config/loader.js';
|
|
17
|
+
import * as output from '../output.js';
|
|
18
|
+
/**
|
|
19
|
+
* Default paths for contract files.
|
|
20
|
+
*/
|
|
21
|
+
const DEFAULT_CONTRACT_FILENAMES = CONTRACT_TESTING.CONTRACT_FILENAMES;
|
|
22
|
+
function loadConfigOrExit(configPath) {
|
|
23
|
+
try {
|
|
24
|
+
return loadConfig(configPath);
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
if (error instanceof ConfigNotFoundError) {
|
|
28
|
+
output.error(error.message);
|
|
29
|
+
process.exit(EXIT_CODES.ERROR);
|
|
30
|
+
}
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Find or use provided contract path.
|
|
36
|
+
*/
|
|
37
|
+
function resolveContractPath(providedPath, baseDir) {
|
|
38
|
+
if (providedPath) {
|
|
39
|
+
const fullPath = providedPath.startsWith('/')
|
|
40
|
+
? providedPath
|
|
41
|
+
: join(baseDir || '.', providedPath);
|
|
42
|
+
if (existsSync(fullPath)) {
|
|
43
|
+
return fullPath;
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
// Try default filenames
|
|
48
|
+
return findContractFile(baseDir || '.');
|
|
49
|
+
}
|
|
50
|
+
export const contractCommand = new Command('contract')
|
|
51
|
+
.description('Validate MCP servers against contract definitions')
|
|
52
|
+
.addHelpText('after', `
|
|
53
|
+
Examples:
|
|
54
|
+
$ bellwether contract validate npx @mcp/server # Validate against contract
|
|
55
|
+
$ bellwether contract generate npx @mcp/server # Generate contract from server
|
|
56
|
+
$ bellwether contract show # Show current contract
|
|
57
|
+
$ bellwether contract validate npx @mcp/server --contract ./my-contract.yaml
|
|
58
|
+
`);
|
|
59
|
+
// contract validate
|
|
60
|
+
contractCommand
|
|
61
|
+
.command('validate')
|
|
62
|
+
.description('Validate an MCP server against a contract file')
|
|
63
|
+
.argument('<server-command...>', 'MCP server command and arguments')
|
|
64
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
65
|
+
.option('--contract <path>', 'Path to contract file (default: bellwether-contract.yaml)')
|
|
66
|
+
.option('--mode <mode>', 'Validation mode: strict, lenient, report')
|
|
67
|
+
.option('--fail-on-violation', 'Exit with error if violations detected (default in CI)')
|
|
68
|
+
.option('--format <format>', 'Output format: text, json, markdown')
|
|
69
|
+
.option('--timeout <ms>', 'Server startup timeout in milliseconds')
|
|
70
|
+
.action(async (serverCmd, options) => {
|
|
71
|
+
const config = loadConfigOrExit(options.config);
|
|
72
|
+
const outputDir = config.output.dir;
|
|
73
|
+
const defaultContractPath = config.contract.path;
|
|
74
|
+
const mode = (options.mode ?? config.contract.mode);
|
|
75
|
+
const format = options.format ?? config.contract.format;
|
|
76
|
+
const timeout = parseInt(options.timeout ?? String(config.contract.timeout), 10);
|
|
77
|
+
// Find contract file
|
|
78
|
+
const contractPath = resolveContractPath(options.contract ?? defaultContractPath, outputDir);
|
|
79
|
+
if (!contractPath) {
|
|
80
|
+
output.error('Contract file not found.');
|
|
81
|
+
output.error('');
|
|
82
|
+
output.error('Create a contract file by running:');
|
|
83
|
+
output.error(' bellwether contract generate <server-command>');
|
|
84
|
+
output.error('');
|
|
85
|
+
output.error(`Expected filenames: ${DEFAULT_CONTRACT_FILENAMES.join(', ')}`);
|
|
86
|
+
process.exit(EXIT_CODES.ERROR);
|
|
87
|
+
}
|
|
88
|
+
// Load contract
|
|
89
|
+
let contract;
|
|
90
|
+
try {
|
|
91
|
+
contract = loadContract(contractPath);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
output.error(`Failed to load contract: ${error instanceof Error ? error.message : error}`);
|
|
95
|
+
process.exit(EXIT_CODES.ERROR);
|
|
96
|
+
}
|
|
97
|
+
output.info(`Loading contract: ${contractPath}`);
|
|
98
|
+
output.info(`Validating against: ${serverCmd.join(' ')}`);
|
|
99
|
+
output.newline();
|
|
100
|
+
// Start MCP server
|
|
101
|
+
const mcpClient = new MCPClient({
|
|
102
|
+
timeout,
|
|
103
|
+
debug: false,
|
|
104
|
+
transport: 'stdio',
|
|
105
|
+
});
|
|
106
|
+
try {
|
|
107
|
+
// Parse server command
|
|
108
|
+
const command = serverCmd[0];
|
|
109
|
+
const args = serverCmd.slice(1);
|
|
110
|
+
await mcpClient.connect(command, args);
|
|
111
|
+
// Discover capabilities
|
|
112
|
+
const discovery = await discover(mcpClient, command, args);
|
|
113
|
+
// Validate against contract
|
|
114
|
+
const result = await validateContract(contract, discovery.tools, {
|
|
115
|
+
mode,
|
|
116
|
+
});
|
|
117
|
+
// Output results
|
|
118
|
+
switch (format) {
|
|
119
|
+
case 'json':
|
|
120
|
+
console.log(JSON.stringify(result, null, 2));
|
|
121
|
+
break;
|
|
122
|
+
case 'markdown':
|
|
123
|
+
console.log(generateContractValidationMarkdown(result));
|
|
124
|
+
break;
|
|
125
|
+
default:
|
|
126
|
+
// Text format
|
|
127
|
+
if (result.passed) {
|
|
128
|
+
output.success('Contract validation PASSED');
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
output.error('Contract validation FAILED');
|
|
132
|
+
}
|
|
133
|
+
output.newline();
|
|
134
|
+
output.info(`Mode: ${result.mode}`);
|
|
135
|
+
output.info(`Tools checked: ${result.summary.toolsChecked}`);
|
|
136
|
+
output.info(`Tools passed: ${result.summary.toolsPassed}`);
|
|
137
|
+
output.info(`Violations: ${result.summary.totalViolations}`);
|
|
138
|
+
if (result.violations.length > 0) {
|
|
139
|
+
output.newline();
|
|
140
|
+
output.info('Violations:');
|
|
141
|
+
// Group by severity
|
|
142
|
+
const breaking = result.violations.filter(v => v.severity === 'breaking');
|
|
143
|
+
const warnings = result.violations.filter(v => v.severity === 'warning');
|
|
144
|
+
const infos = result.violations.filter(v => v.severity === 'info');
|
|
145
|
+
for (const v of breaking) {
|
|
146
|
+
output.error(` [BREAKING] ${v.tool || v.type}: ${v.message}`);
|
|
147
|
+
}
|
|
148
|
+
for (const v of warnings) {
|
|
149
|
+
output.warn(` [WARNING] ${v.tool || v.type}: ${v.message}`);
|
|
150
|
+
}
|
|
151
|
+
for (const v of infos.slice(0, 5)) {
|
|
152
|
+
output.info(` [INFO] ${v.tool || v.type}: ${v.message}`);
|
|
153
|
+
}
|
|
154
|
+
if (infos.length > 5) {
|
|
155
|
+
output.info(` ... and ${infos.length - 5} more info violations`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Exit with appropriate code
|
|
160
|
+
const failOnViolation = options.failOnViolation ? true : (config.contract.failOnViolation ?? !!process.env.CI);
|
|
161
|
+
if (!result.passed && failOnViolation) {
|
|
162
|
+
process.exit(EXIT_CODES.BREAKING);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
output.error(`Validation failed: ${error instanceof Error ? error.message : error}`);
|
|
167
|
+
process.exit(EXIT_CODES.ERROR);
|
|
168
|
+
}
|
|
169
|
+
finally {
|
|
170
|
+
await mcpClient.disconnect();
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
// contract generate
|
|
174
|
+
contractCommand
|
|
175
|
+
.command('generate')
|
|
176
|
+
.description('Generate a contract file from current server state')
|
|
177
|
+
.argument('<server-command...>', 'MCP server command and arguments')
|
|
178
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
179
|
+
.option('-o, --output <path>', 'Output path for contract file')
|
|
180
|
+
.option('--timeout <ms>', 'Server startup timeout in milliseconds')
|
|
181
|
+
.option('-f, --force', 'Overwrite existing contract file')
|
|
182
|
+
.action(async (serverCmd, options) => {
|
|
183
|
+
const config = loadConfigOrExit(options.config);
|
|
184
|
+
const outputDir = config.output.dir;
|
|
185
|
+
const defaultOutput = config.contract.path ?? DEFAULT_CONTRACT_FILENAMES[0];
|
|
186
|
+
const outputOption = options.output ?? defaultOutput;
|
|
187
|
+
const outputPath = outputOption.startsWith('/')
|
|
188
|
+
? outputOption
|
|
189
|
+
: join(outputDir, outputOption);
|
|
190
|
+
const timeout = parseInt(options.timeout ?? String(config.contract.timeout), 10);
|
|
191
|
+
// Check for existing file
|
|
192
|
+
if (existsSync(outputPath) && !options.force) {
|
|
193
|
+
output.error(`Contract file already exists: ${outputPath}`);
|
|
194
|
+
output.error('Use --force to overwrite.');
|
|
195
|
+
process.exit(EXIT_CODES.ERROR);
|
|
196
|
+
}
|
|
197
|
+
output.info(`Generating contract from: ${serverCmd.join(' ')}`);
|
|
198
|
+
// Start MCP server
|
|
199
|
+
const mcpClient = new MCPClient({
|
|
200
|
+
timeout,
|
|
201
|
+
debug: false,
|
|
202
|
+
transport: 'stdio',
|
|
203
|
+
});
|
|
204
|
+
try {
|
|
205
|
+
// Parse server command
|
|
206
|
+
const command = serverCmd[0];
|
|
207
|
+
const args = serverCmd.slice(1);
|
|
208
|
+
await mcpClient.connect(command, args);
|
|
209
|
+
// Discover capabilities
|
|
210
|
+
const discovery = await discover(mcpClient, command, args);
|
|
211
|
+
// Generate contract
|
|
212
|
+
const serverName = discovery.serverInfo?.name || 'MCP Server';
|
|
213
|
+
const contract = generateContract(discovery.tools, serverName);
|
|
214
|
+
const yaml = generateContractYaml(contract);
|
|
215
|
+
// Write to file
|
|
216
|
+
writeFileSync(outputPath, yaml);
|
|
217
|
+
output.success(`Contract generated: ${outputPath}`);
|
|
218
|
+
output.newline();
|
|
219
|
+
output.info(` Server: ${serverName}`);
|
|
220
|
+
output.info(` Tools: ${discovery.tools.length}`);
|
|
221
|
+
output.info(` Parameters: ${countParameters(contract)}`);
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
output.error(`Generation failed: ${error instanceof Error ? error.message : error}`);
|
|
225
|
+
process.exit(EXIT_CODES.ERROR);
|
|
226
|
+
}
|
|
227
|
+
finally {
|
|
228
|
+
await mcpClient.disconnect();
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
// contract show
|
|
232
|
+
contractCommand
|
|
233
|
+
.command('show')
|
|
234
|
+
.description('Display contract file contents')
|
|
235
|
+
.argument('[path]', 'Path to contract file')
|
|
236
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
237
|
+
.option('--json', 'Output as JSON instead of YAML')
|
|
238
|
+
.action(async (contractPath, options) => {
|
|
239
|
+
const config = loadConfigOrExit(options.config);
|
|
240
|
+
const outputDir = config.output.dir;
|
|
241
|
+
const path = resolveContractPath(contractPath ?? config.contract.path, outputDir);
|
|
242
|
+
if (!path) {
|
|
243
|
+
output.error('Contract file not found.');
|
|
244
|
+
output.error(`Expected filenames: ${DEFAULT_CONTRACT_FILENAMES.join(', ')}`);
|
|
245
|
+
process.exit(EXIT_CODES.ERROR);
|
|
246
|
+
}
|
|
247
|
+
try {
|
|
248
|
+
const contract = loadContract(path);
|
|
249
|
+
output.info(`Contract: ${path}`);
|
|
250
|
+
output.info(`Version: ${contract.version}`);
|
|
251
|
+
if (contract.server?.name) {
|
|
252
|
+
output.info(`Server: ${contract.server.name}`);
|
|
253
|
+
}
|
|
254
|
+
output.newline();
|
|
255
|
+
if (options.json) {
|
|
256
|
+
console.log(JSON.stringify(contract, null, 2));
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
const content = readFileSync(path, 'utf-8');
|
|
260
|
+
console.log(content);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
output.error(`Failed to load contract: ${error instanceof Error ? error.message : error}`);
|
|
265
|
+
process.exit(EXIT_CODES.ERROR);
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
/**
|
|
269
|
+
* Count total parameters in a contract.
|
|
270
|
+
*/
|
|
271
|
+
function countParameters(contract) {
|
|
272
|
+
let count = 0;
|
|
273
|
+
for (const toolContract of Object.values(contract.tools)) {
|
|
274
|
+
if (toolContract.input) {
|
|
275
|
+
count += Object.keys(toolContract.input).length;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return count;
|
|
279
|
+
}
|
|
280
|
+
//# sourceMappingURL=contract.js.map
|