@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,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential resolution - unified API key retrieval from multiple sources.
|
|
3
|
+
*
|
|
4
|
+
* Resolution order (highest to lowest priority):
|
|
5
|
+
* 1. Direct config (apiKey in config)
|
|
6
|
+
* 2. Custom environment variable (apiKeyEnvVar in config)
|
|
7
|
+
* 3. Standard environment variable (OPENAI_API_KEY, ANTHROPIC_API_KEY)
|
|
8
|
+
* 4. Project .env file (./env in current working directory)
|
|
9
|
+
* 5. Global .env file (~/.bellwether/.env)
|
|
10
|
+
* 6. System keychain (via bellwether auth)
|
|
11
|
+
*/
|
|
12
|
+
import type { LLMProviderId, LLMConfig } from '../llm/client.js';
|
|
13
|
+
/**
|
|
14
|
+
* Default environment variable names for each provider.
|
|
15
|
+
*/
|
|
16
|
+
export declare const DEFAULT_ENV_VARS: Record<LLMProviderId, string>;
|
|
17
|
+
/**
|
|
18
|
+
* Result of credential resolution.
|
|
19
|
+
*/
|
|
20
|
+
export interface CredentialResult {
|
|
21
|
+
apiKey: string | undefined;
|
|
22
|
+
source: 'config' | 'env' | 'project-env' | 'global-env' | 'keychain' | 'none';
|
|
23
|
+
envVar?: string;
|
|
24
|
+
envFile?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Resolve API key from all available sources.
|
|
28
|
+
*
|
|
29
|
+
* Resolution order (highest to lowest priority):
|
|
30
|
+
* 1. Direct config (apiKey in config)
|
|
31
|
+
* 2. Custom environment variable (apiKeyEnvVar in config)
|
|
32
|
+
* 3. Standard environment variable (OPENAI_API_KEY, ANTHROPIC_API_KEY)
|
|
33
|
+
* 4. Project .env file (./env in current working directory)
|
|
34
|
+
* 5. Global .env file (~/.bellwether/.env)
|
|
35
|
+
* 6. System keychain (via bellwether auth)
|
|
36
|
+
*
|
|
37
|
+
* @param config - LLM configuration
|
|
38
|
+
* @returns The resolved API key and its source
|
|
39
|
+
*/
|
|
40
|
+
export declare function resolveCredentials(config: Pick<LLMConfig, 'provider' | 'apiKey' | 'apiKeyEnvVar'>): Promise<CredentialResult>;
|
|
41
|
+
/**
|
|
42
|
+
* Synchronous API key resolution (for backward compatibility).
|
|
43
|
+
* Does NOT check keychain - use resolveCredentials for full resolution.
|
|
44
|
+
*/
|
|
45
|
+
export declare function resolveApiKeySync(config: Pick<LLMConfig, 'provider' | 'apiKey' | 'apiKeyEnvVar'>): string | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* Check if credentials are available for a provider.
|
|
48
|
+
*/
|
|
49
|
+
export declare function hasCredentials(provider: LLMProviderId): Promise<boolean>;
|
|
50
|
+
/**
|
|
51
|
+
* Get a description of where credentials are configured.
|
|
52
|
+
*/
|
|
53
|
+
export declare function describeCredentialSource(provider: LLMProviderId): Promise<string>;
|
|
54
|
+
/**
|
|
55
|
+
* Get authentication status for all providers.
|
|
56
|
+
*/
|
|
57
|
+
export interface AuthStatus {
|
|
58
|
+
provider: LLMProviderId;
|
|
59
|
+
configured: boolean;
|
|
60
|
+
source: CredentialResult['source'];
|
|
61
|
+
envVar?: string;
|
|
62
|
+
}
|
|
63
|
+
export declare function getAuthStatus(): Promise<AuthStatus[]>;
|
|
64
|
+
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential resolution - unified API key retrieval from multiple sources.
|
|
3
|
+
*
|
|
4
|
+
* Resolution order (highest to lowest priority):
|
|
5
|
+
* 1. Direct config (apiKey in config)
|
|
6
|
+
* 2. Custom environment variable (apiKeyEnvVar in config)
|
|
7
|
+
* 3. Standard environment variable (OPENAI_API_KEY, ANTHROPIC_API_KEY)
|
|
8
|
+
* 4. Project .env file (./env in current working directory)
|
|
9
|
+
* 5. Global .env file (~/.bellwether/.env)
|
|
10
|
+
* 6. System keychain (via bellwether auth)
|
|
11
|
+
*/
|
|
12
|
+
import { existsSync, readFileSync } from 'fs';
|
|
13
|
+
import { join } from 'path';
|
|
14
|
+
import { homedir } from 'os';
|
|
15
|
+
import { getKeychainService } from './keychain.js';
|
|
16
|
+
/**
|
|
17
|
+
* Default environment variable names for each provider.
|
|
18
|
+
*/
|
|
19
|
+
export const DEFAULT_ENV_VARS = {
|
|
20
|
+
openai: 'OPENAI_API_KEY',
|
|
21
|
+
anthropic: 'ANTHROPIC_API_KEY',
|
|
22
|
+
ollama: '', // Ollama doesn't need an API key
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Read a specific environment variable from a .env file.
|
|
26
|
+
* Returns undefined if the file doesn't exist or the variable isn't found.
|
|
27
|
+
*/
|
|
28
|
+
function readEnvFile(filePath, envVar) {
|
|
29
|
+
try {
|
|
30
|
+
if (!existsSync(filePath)) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
34
|
+
const lines = content.split('\n');
|
|
35
|
+
for (const line of lines) {
|
|
36
|
+
const trimmed = line.trim();
|
|
37
|
+
// Skip empty lines and comments
|
|
38
|
+
if (!trimmed || trimmed.startsWith('#')) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
// Parse KEY=VALUE format
|
|
42
|
+
const eqIndex = trimmed.indexOf('=');
|
|
43
|
+
if (eqIndex === -1) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const key = trimmed.substring(0, eqIndex).trim();
|
|
47
|
+
if (key !== envVar) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
let value = trimmed.substring(eqIndex + 1).trim();
|
|
51
|
+
// Remove surrounding quotes if present
|
|
52
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
53
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
54
|
+
value = value.slice(1, -1);
|
|
55
|
+
}
|
|
56
|
+
if (value) {
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// File read error - return undefined
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get the path to the global Bellwether .env file.
|
|
69
|
+
*/
|
|
70
|
+
function getGlobalEnvPath() {
|
|
71
|
+
return join(homedir(), '.bellwether', '.env');
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get the path to the project .env file.
|
|
75
|
+
*/
|
|
76
|
+
function getProjectEnvPath() {
|
|
77
|
+
return join(process.cwd(), '.env');
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Resolve API key from all available sources.
|
|
81
|
+
*
|
|
82
|
+
* Resolution order (highest to lowest priority):
|
|
83
|
+
* 1. Direct config (apiKey in config)
|
|
84
|
+
* 2. Custom environment variable (apiKeyEnvVar in config)
|
|
85
|
+
* 3. Standard environment variable (OPENAI_API_KEY, ANTHROPIC_API_KEY)
|
|
86
|
+
* 4. Project .env file (./env in current working directory)
|
|
87
|
+
* 5. Global .env file (~/.bellwether/.env)
|
|
88
|
+
* 6. System keychain (via bellwether auth)
|
|
89
|
+
*
|
|
90
|
+
* @param config - LLM configuration
|
|
91
|
+
* @returns The resolved API key and its source
|
|
92
|
+
*/
|
|
93
|
+
export async function resolveCredentials(config) {
|
|
94
|
+
// 1. Direct config
|
|
95
|
+
if (config.apiKey) {
|
|
96
|
+
return { apiKey: config.apiKey, source: 'config' };
|
|
97
|
+
}
|
|
98
|
+
// 2. Custom environment variable
|
|
99
|
+
if (config.apiKeyEnvVar) {
|
|
100
|
+
const key = process.env[config.apiKeyEnvVar];
|
|
101
|
+
if (key) {
|
|
102
|
+
return { apiKey: key, source: 'env', envVar: config.apiKeyEnvVar };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// 3. Standard environment variable (already in process.env)
|
|
106
|
+
const defaultEnvVar = DEFAULT_ENV_VARS[config.provider];
|
|
107
|
+
if (defaultEnvVar) {
|
|
108
|
+
const key = process.env[defaultEnvVar];
|
|
109
|
+
if (key) {
|
|
110
|
+
return { apiKey: key, source: 'env', envVar: defaultEnvVar };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// 4. Project .env file
|
|
114
|
+
if (defaultEnvVar) {
|
|
115
|
+
const projectEnvPath = getProjectEnvPath();
|
|
116
|
+
const key = readEnvFile(projectEnvPath, defaultEnvVar);
|
|
117
|
+
if (key) {
|
|
118
|
+
return { apiKey: key, source: 'project-env', envVar: defaultEnvVar, envFile: projectEnvPath };
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// 5. Global .env file (~/.bellwether/.env)
|
|
122
|
+
if (defaultEnvVar) {
|
|
123
|
+
const globalEnvPath = getGlobalEnvPath();
|
|
124
|
+
const key = readEnvFile(globalEnvPath, defaultEnvVar);
|
|
125
|
+
if (key) {
|
|
126
|
+
return { apiKey: key, source: 'global-env', envVar: defaultEnvVar, envFile: globalEnvPath };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// 6. System keychain
|
|
130
|
+
if (config.provider !== 'ollama') {
|
|
131
|
+
try {
|
|
132
|
+
const keychain = getKeychainService();
|
|
133
|
+
const key = await keychain.getApiKey(config.provider);
|
|
134
|
+
if (key) {
|
|
135
|
+
return { apiKey: key, source: 'keychain' };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// Keychain not available
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return { apiKey: undefined, source: 'none' };
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Synchronous API key resolution (for backward compatibility).
|
|
146
|
+
* Does NOT check keychain - use resolveCredentials for full resolution.
|
|
147
|
+
*/
|
|
148
|
+
export function resolveApiKeySync(config) {
|
|
149
|
+
if (config.apiKey) {
|
|
150
|
+
return config.apiKey;
|
|
151
|
+
}
|
|
152
|
+
if (config.apiKeyEnvVar) {
|
|
153
|
+
const key = process.env[config.apiKeyEnvVar];
|
|
154
|
+
if (key)
|
|
155
|
+
return key;
|
|
156
|
+
}
|
|
157
|
+
const defaultEnvVar = DEFAULT_ENV_VARS[config.provider];
|
|
158
|
+
if (defaultEnvVar) {
|
|
159
|
+
return process.env[defaultEnvVar];
|
|
160
|
+
}
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Check if credentials are available for a provider.
|
|
165
|
+
*/
|
|
166
|
+
export async function hasCredentials(provider) {
|
|
167
|
+
if (provider === 'ollama') {
|
|
168
|
+
return true; // Ollama doesn't need credentials
|
|
169
|
+
}
|
|
170
|
+
const result = await resolveCredentials({ provider });
|
|
171
|
+
return result.source !== 'none';
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Get a description of where credentials are configured.
|
|
175
|
+
*/
|
|
176
|
+
export async function describeCredentialSource(provider) {
|
|
177
|
+
if (provider === 'ollama') {
|
|
178
|
+
return 'Ollama (no API key required)';
|
|
179
|
+
}
|
|
180
|
+
const result = await resolveCredentials({ provider });
|
|
181
|
+
switch (result.source) {
|
|
182
|
+
case 'config':
|
|
183
|
+
return 'Provided in configuration';
|
|
184
|
+
case 'env':
|
|
185
|
+
return `Environment variable: ${result.envVar}`;
|
|
186
|
+
case 'project-env':
|
|
187
|
+
return `Project .env file: ${result.envFile}`;
|
|
188
|
+
case 'global-env':
|
|
189
|
+
return `Global .env file: ${result.envFile}`;
|
|
190
|
+
case 'keychain':
|
|
191
|
+
return 'System keychain';
|
|
192
|
+
case 'none':
|
|
193
|
+
return 'Not configured';
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
export async function getAuthStatus() {
|
|
197
|
+
const providers = ['openai', 'anthropic', 'ollama'];
|
|
198
|
+
const results = [];
|
|
199
|
+
for (const provider of providers) {
|
|
200
|
+
if (provider === 'ollama') {
|
|
201
|
+
results.push({
|
|
202
|
+
provider,
|
|
203
|
+
configured: true,
|
|
204
|
+
source: 'none', // Ollama doesn't use credentials
|
|
205
|
+
});
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
const creds = await resolveCredentials({ provider });
|
|
209
|
+
results.push({
|
|
210
|
+
provider,
|
|
211
|
+
configured: creds.source !== 'none',
|
|
212
|
+
source: creds.source,
|
|
213
|
+
envVar: creds.envVar,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return results;
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=credentials.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication module - secure credential management.
|
|
3
|
+
*/
|
|
4
|
+
export { KeychainService, getKeychainService, type KeychainBackend, } from './keychain.js';
|
|
5
|
+
export { resolveCredentials, resolveApiKeySync, hasCredentials, describeCredentialSource, getAuthStatus, DEFAULT_ENV_VARS, type CredentialResult, type AuthStatus, } from './credentials.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication module - secure credential management.
|
|
3
|
+
*/
|
|
4
|
+
export { KeychainService, getKeychainService, } from './keychain.js';
|
|
5
|
+
export { resolveCredentials, resolveApiKeySync, hasCredentials, describeCredentialSource, getAuthStatus, DEFAULT_ENV_VARS, } from './credentials.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform keychain service for secure credential storage.
|
|
3
|
+
*
|
|
4
|
+
* Uses the system keychain:
|
|
5
|
+
* - macOS: Keychain
|
|
6
|
+
* - Windows: Credential Manager
|
|
7
|
+
* - Linux: Secret Service (libsecret)
|
|
8
|
+
*
|
|
9
|
+
* Gracefully degrades if keychain is unavailable (e.g., CI environments).
|
|
10
|
+
*/
|
|
11
|
+
import type { LLMProviderId } from '../llm/client.js';
|
|
12
|
+
/**
|
|
13
|
+
* Keychain interface - can be implemented by different backends.
|
|
14
|
+
*/
|
|
15
|
+
export interface KeychainBackend {
|
|
16
|
+
getPassword(service: string, account: string): Promise<string | null>;
|
|
17
|
+
setPassword(service: string, account: string, password: string): Promise<void>;
|
|
18
|
+
deletePassword(service: string, account: string): Promise<boolean>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Keychain service - manages API key storage.
|
|
22
|
+
*/
|
|
23
|
+
export declare class KeychainService {
|
|
24
|
+
private backend;
|
|
25
|
+
private useFileBackend;
|
|
26
|
+
constructor();
|
|
27
|
+
/**
|
|
28
|
+
* Check if secure keychain (keytar) is available.
|
|
29
|
+
*/
|
|
30
|
+
isSecureKeychainAvailable(): Promise<boolean>;
|
|
31
|
+
/**
|
|
32
|
+
* Get the backend being used.
|
|
33
|
+
*/
|
|
34
|
+
getBackendType(): Promise<'keychain' | 'file'>;
|
|
35
|
+
/**
|
|
36
|
+
* Enable file-based fallback explicitly.
|
|
37
|
+
*/
|
|
38
|
+
enableFileBackend(): void;
|
|
39
|
+
/**
|
|
40
|
+
* Get API key for a provider from keychain.
|
|
41
|
+
*/
|
|
42
|
+
getApiKey(provider: LLMProviderId): Promise<string | null>;
|
|
43
|
+
/**
|
|
44
|
+
* Store API key for a provider in keychain.
|
|
45
|
+
*/
|
|
46
|
+
setApiKey(provider: LLMProviderId, apiKey: string): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Delete API key for a provider from keychain.
|
|
49
|
+
*/
|
|
50
|
+
deleteApiKey(provider: LLMProviderId): Promise<boolean>;
|
|
51
|
+
/**
|
|
52
|
+
* Get all stored credentials (without values, just which providers have keys).
|
|
53
|
+
*/
|
|
54
|
+
getStoredProviders(): Promise<LLMProviderId[]>;
|
|
55
|
+
/**
|
|
56
|
+
* Clear all stored credentials.
|
|
57
|
+
*/
|
|
58
|
+
clearAll(): Promise<void>;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get the keychain service instance.
|
|
62
|
+
*/
|
|
63
|
+
export declare function getKeychainService(): KeychainService;
|
|
64
|
+
//# sourceMappingURL=keychain.d.ts.map
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform keychain service for secure credential storage.
|
|
3
|
+
*
|
|
4
|
+
* Uses the system keychain:
|
|
5
|
+
* - macOS: Keychain
|
|
6
|
+
* - Windows: Credential Manager
|
|
7
|
+
* - Linux: Secret Service (libsecret)
|
|
8
|
+
*
|
|
9
|
+
* Gracefully degrades if keychain is unavailable (e.g., CI environments).
|
|
10
|
+
*/
|
|
11
|
+
import { homedir } from 'os';
|
|
12
|
+
import { join } from 'path';
|
|
13
|
+
import { createRequire } from 'module';
|
|
14
|
+
// Create require function for loading CommonJS optional dependencies in ESM
|
|
15
|
+
const require = createRequire(import.meta.url);
|
|
16
|
+
// Service name for keychain entries
|
|
17
|
+
const SERVICE_NAME = 'bellwether';
|
|
18
|
+
// Account names for each provider
|
|
19
|
+
const PROVIDER_ACCOUNTS = {
|
|
20
|
+
openai: 'openai-api-key',
|
|
21
|
+
anthropic: 'anthropic-api-key',
|
|
22
|
+
ollama: 'ollama', // Ollama doesn't use API keys, but included for completeness
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Keytar-based keychain backend (requires keytar package).
|
|
26
|
+
*/
|
|
27
|
+
class KeytarBackend {
|
|
28
|
+
keytar = null;
|
|
29
|
+
initPromise = null;
|
|
30
|
+
async init() {
|
|
31
|
+
if (this.initPromise) {
|
|
32
|
+
return this.initPromise;
|
|
33
|
+
}
|
|
34
|
+
this.initPromise = (async () => {
|
|
35
|
+
try {
|
|
36
|
+
// Dynamic import to avoid requiring keytar if not installed
|
|
37
|
+
// Using require() for optional dependency
|
|
38
|
+
this.keytar = require('keytar');
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// keytar not available - will use fallback
|
|
42
|
+
this.keytar = null;
|
|
43
|
+
}
|
|
44
|
+
})();
|
|
45
|
+
return this.initPromise;
|
|
46
|
+
}
|
|
47
|
+
async getPassword(service, account) {
|
|
48
|
+
await this.init();
|
|
49
|
+
if (!this.keytar)
|
|
50
|
+
return null;
|
|
51
|
+
return this.keytar.getPassword(service, account);
|
|
52
|
+
}
|
|
53
|
+
async setPassword(service, account, password) {
|
|
54
|
+
await this.init();
|
|
55
|
+
if (!this.keytar) {
|
|
56
|
+
throw new Error('Keychain not available. Install keytar: npm install keytar\n' +
|
|
57
|
+
'Or use environment variables instead.');
|
|
58
|
+
}
|
|
59
|
+
await this.keytar.setPassword(service, account, password);
|
|
60
|
+
}
|
|
61
|
+
async deletePassword(service, account) {
|
|
62
|
+
await this.init();
|
|
63
|
+
if (!this.keytar)
|
|
64
|
+
return false;
|
|
65
|
+
return this.keytar.deletePassword(service, account);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* File-based fallback for environments without keychain access.
|
|
70
|
+
* Stores credentials in ~/.bellwether/credentials (with restrictive permissions).
|
|
71
|
+
*
|
|
72
|
+
* NOTE: This is less secure than system keychain but better than nothing.
|
|
73
|
+
* Credentials are stored in plain text but with 0600 permissions.
|
|
74
|
+
*/
|
|
75
|
+
class FileBackend {
|
|
76
|
+
credentialsPath;
|
|
77
|
+
credentials = null;
|
|
78
|
+
constructor() {
|
|
79
|
+
this.credentialsPath = join(homedir(), '.bellwether', 'credentials.json');
|
|
80
|
+
}
|
|
81
|
+
async load() {
|
|
82
|
+
if (this.credentials)
|
|
83
|
+
return this.credentials;
|
|
84
|
+
const fs = await import('fs');
|
|
85
|
+
try {
|
|
86
|
+
if (fs.existsSync(this.credentialsPath)) {
|
|
87
|
+
const content = fs.readFileSync(this.credentialsPath, 'utf-8');
|
|
88
|
+
this.credentials = JSON.parse(content);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
this.credentials = {};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
this.credentials = {};
|
|
96
|
+
}
|
|
97
|
+
return this.credentials;
|
|
98
|
+
}
|
|
99
|
+
async save() {
|
|
100
|
+
const fs = await import('fs');
|
|
101
|
+
const path = await import('path');
|
|
102
|
+
const os = await import('os');
|
|
103
|
+
const dir = path.join(os.homedir(), '.bellwether');
|
|
104
|
+
if (!fs.existsSync(dir)) {
|
|
105
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
106
|
+
}
|
|
107
|
+
fs.writeFileSync(this.credentialsPath, JSON.stringify(this.credentials, null, 2), { mode: 0o600 });
|
|
108
|
+
}
|
|
109
|
+
async getPassword(service, account) {
|
|
110
|
+
const creds = await this.load();
|
|
111
|
+
return creds[service]?.[account] ?? null;
|
|
112
|
+
}
|
|
113
|
+
async setPassword(service, account, password) {
|
|
114
|
+
const creds = await this.load();
|
|
115
|
+
if (!creds[service]) {
|
|
116
|
+
creds[service] = {};
|
|
117
|
+
}
|
|
118
|
+
creds[service][account] = password;
|
|
119
|
+
await this.save();
|
|
120
|
+
}
|
|
121
|
+
async deletePassword(service, account) {
|
|
122
|
+
const creds = await this.load();
|
|
123
|
+
if (creds[service]?.[account]) {
|
|
124
|
+
delete creds[service][account];
|
|
125
|
+
if (Object.keys(creds[service]).length === 0) {
|
|
126
|
+
delete creds[service];
|
|
127
|
+
}
|
|
128
|
+
await this.save();
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Keychain service - manages API key storage.
|
|
136
|
+
*/
|
|
137
|
+
export class KeychainService {
|
|
138
|
+
backend;
|
|
139
|
+
useFileBackend = false;
|
|
140
|
+
constructor() {
|
|
141
|
+
// Try keytar first, fall back to file-based storage
|
|
142
|
+
this.backend = new KeytarBackend();
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Check if secure keychain (keytar) is available.
|
|
146
|
+
*/
|
|
147
|
+
async isSecureKeychainAvailable() {
|
|
148
|
+
try {
|
|
149
|
+
// Try to access keytar using require
|
|
150
|
+
require('keytar');
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get the backend being used.
|
|
159
|
+
*/
|
|
160
|
+
async getBackendType() {
|
|
161
|
+
if (await this.isSecureKeychainAvailable()) {
|
|
162
|
+
return 'keychain';
|
|
163
|
+
}
|
|
164
|
+
return 'file';
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Enable file-based fallback explicitly.
|
|
168
|
+
*/
|
|
169
|
+
enableFileBackend() {
|
|
170
|
+
this.backend = new FileBackend();
|
|
171
|
+
this.useFileBackend = true;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Get API key for a provider from keychain.
|
|
175
|
+
*/
|
|
176
|
+
async getApiKey(provider) {
|
|
177
|
+
const account = PROVIDER_ACCOUNTS[provider];
|
|
178
|
+
if (!account || provider === 'ollama') {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
return await this.backend.getPassword(SERVICE_NAME, account);
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
// If keytar fails, try file backend
|
|
186
|
+
if (!this.useFileBackend) {
|
|
187
|
+
this.enableFileBackend();
|
|
188
|
+
return await this.backend.getPassword(SERVICE_NAME, account);
|
|
189
|
+
}
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Store API key for a provider in keychain.
|
|
195
|
+
*/
|
|
196
|
+
async setApiKey(provider, apiKey) {
|
|
197
|
+
const account = PROVIDER_ACCOUNTS[provider];
|
|
198
|
+
if (!account || provider === 'ollama') {
|
|
199
|
+
throw new Error(`Provider ${provider} does not use API keys`);
|
|
200
|
+
}
|
|
201
|
+
try {
|
|
202
|
+
await this.backend.setPassword(SERVICE_NAME, account, apiKey);
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
// If keytar fails, try file backend
|
|
206
|
+
if (!this.useFileBackend) {
|
|
207
|
+
this.enableFileBackend();
|
|
208
|
+
await this.backend.setPassword(SERVICE_NAME, account, apiKey);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Delete API key for a provider from keychain.
|
|
217
|
+
*/
|
|
218
|
+
async deleteApiKey(provider) {
|
|
219
|
+
const account = PROVIDER_ACCOUNTS[provider];
|
|
220
|
+
if (!account || provider === 'ollama') {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
return await this.backend.deletePassword(SERVICE_NAME, account);
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
// If keytar fails, try file backend
|
|
228
|
+
if (!this.useFileBackend) {
|
|
229
|
+
this.enableFileBackend();
|
|
230
|
+
return await this.backend.deletePassword(SERVICE_NAME, account);
|
|
231
|
+
}
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Get all stored credentials (without values, just which providers have keys).
|
|
237
|
+
*/
|
|
238
|
+
async getStoredProviders() {
|
|
239
|
+
const providers = [];
|
|
240
|
+
for (const provider of ['openai', 'anthropic']) {
|
|
241
|
+
const key = await this.getApiKey(provider);
|
|
242
|
+
if (key) {
|
|
243
|
+
providers.push(provider);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return providers;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Clear all stored credentials.
|
|
250
|
+
*/
|
|
251
|
+
async clearAll() {
|
|
252
|
+
for (const provider of ['openai', 'anthropic']) {
|
|
253
|
+
await this.deleteApiKey(provider);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// Singleton instance
|
|
258
|
+
let keychainService = null;
|
|
259
|
+
/**
|
|
260
|
+
* Get the keychain service instance.
|
|
261
|
+
*/
|
|
262
|
+
export function getKeychainService() {
|
|
263
|
+
if (!keychainService) {
|
|
264
|
+
keychainService = new KeychainService();
|
|
265
|
+
}
|
|
266
|
+
return keychainService;
|
|
267
|
+
}
|
|
268
|
+
//# sourceMappingURL=keychain.js.map
|