@namzu/cli 0.0.3 → 0.1.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/dist/bin.js +4 -32
- package/dist/bin.js.map +1 -1
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +124 -0
- package/dist/cli.js.map +1 -0
- package/dist/cli.test.d.ts +2 -0
- package/dist/cli.test.d.ts.map +1 -0
- package/dist/cli.test.js +94 -0
- package/dist/cli.test.js.map +1 -0
- package/dist/commands/doctor.d.ts +6 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +10 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/providers.d.ts +17 -0
- package/dist/commands/providers.d.ts.map +1 -0
- package/dist/commands/providers.js +274 -0
- package/dist/commands/providers.js.map +1 -0
- package/dist/commands/registry.d.ts +11 -0
- package/dist/commands/registry.d.ts.map +1 -0
- package/dist/commands/registry.js +28 -0
- package/dist/commands/registry.js.map +1 -0
- package/dist/commands/run.d.ts +14 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/run.js +73 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/stubs.d.ts +12 -0
- package/dist/commands/stubs.d.ts.map +1 -0
- package/dist/commands/stubs.js +32 -0
- package/dist/commands/stubs.js.map +1 -0
- package/dist/commands/tools.d.ts +16 -0
- package/dist/commands/tools.d.ts.map +1 -0
- package/dist/commands/tools.js +161 -0
- package/dist/commands/tools.js.map +1 -0
- package/dist/commands/types.d.ts +25 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +2 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/config/load.d.ts +25 -0
- package/dist/config/load.d.ts.map +1 -0
- package/dist/config/load.js +92 -0
- package/dist/config/load.js.map +1 -0
- package/dist/config/load.test.d.ts +2 -0
- package/dist/config/load.test.d.ts.map +1 -0
- package/dist/config/load.test.js +57 -0
- package/dist/config/load.test.js.map +1 -0
- package/dist/config/schema.d.ts +29 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +13 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/doctor/registry.d.ts.map +1 -1
- package/dist/doctor/registry.js +3 -1
- package/dist/doctor/registry.js.map +1 -1
- package/dist/doctor/registry.test.js +4 -1
- package/dist/doctor/registry.test.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/integrations/clawtool/agents.d.ts +31 -0
- package/dist/integrations/clawtool/agents.d.ts.map +1 -0
- package/dist/integrations/clawtool/agents.js +30 -0
- package/dist/integrations/clawtool/agents.js.map +1 -0
- package/dist/integrations/clawtool/agents.test.d.ts +2 -0
- package/dist/integrations/clawtool/agents.test.d.ts.map +1 -0
- package/dist/integrations/clawtool/agents.test.js +79 -0
- package/dist/integrations/clawtool/agents.test.js.map +1 -0
- package/dist/integrations/clawtool/auth.d.ts +26 -0
- package/dist/integrations/clawtool/auth.d.ts.map +1 -0
- package/dist/integrations/clawtool/auth.js +56 -0
- package/dist/integrations/clawtool/auth.js.map +1 -0
- package/dist/integrations/clawtool/auth.test.d.ts +2 -0
- package/dist/integrations/clawtool/auth.test.d.ts.map +1 -0
- package/dist/integrations/clawtool/auth.test.js +40 -0
- package/dist/integrations/clawtool/auth.test.js.map +1 -0
- package/dist/integrations/clawtool/binary.d.ts +25 -0
- package/dist/integrations/clawtool/binary.d.ts.map +1 -0
- package/dist/integrations/clawtool/binary.js +50 -0
- package/dist/integrations/clawtool/binary.js.map +1 -0
- package/dist/integrations/clawtool/binary.test.d.ts +2 -0
- package/dist/integrations/clawtool/binary.test.d.ts.map +1 -0
- package/dist/integrations/clawtool/binary.test.js +42 -0
- package/dist/integrations/clawtool/binary.test.js.map +1 -0
- package/dist/integrations/clawtool/daemon.d.ts +37 -0
- package/dist/integrations/clawtool/daemon.d.ts.map +1 -0
- package/dist/integrations/clawtool/daemon.js +109 -0
- package/dist/integrations/clawtool/daemon.js.map +1 -0
- package/dist/integrations/clawtool/daemon.test.d.ts +11 -0
- package/dist/integrations/clawtool/daemon.test.d.ts.map +1 -0
- package/dist/integrations/clawtool/daemon.test.js +118 -0
- package/dist/integrations/clawtool/daemon.test.js.map +1 -0
- package/dist/integrations/clawtool/dispatch.d.ts +33 -0
- package/dist/integrations/clawtool/dispatch.d.ts.map +1 -0
- package/dist/integrations/clawtool/dispatch.js +155 -0
- package/dist/integrations/clawtool/dispatch.js.map +1 -0
- package/dist/integrations/clawtool/dispatch.test.d.ts +2 -0
- package/dist/integrations/clawtool/dispatch.test.d.ts.map +1 -0
- package/dist/integrations/clawtool/dispatch.test.js +138 -0
- package/dist/integrations/clawtool/dispatch.test.js.map +1 -0
- package/dist/integrations/clawtool/index.d.ts +11 -0
- package/dist/integrations/clawtool/index.d.ts.map +1 -0
- package/dist/integrations/clawtool/index.js +10 -0
- package/dist/integrations/clawtool/index.js.map +1 -0
- package/dist/integrations/clawtool/mcp.d.ts +51 -0
- package/dist/integrations/clawtool/mcp.d.ts.map +1 -0
- package/dist/integrations/clawtool/mcp.js +156 -0
- package/dist/integrations/clawtool/mcp.js.map +1 -0
- package/dist/integrations/clawtool/mcp.test.d.ts +2 -0
- package/dist/integrations/clawtool/mcp.test.d.ts.map +1 -0
- package/dist/integrations/clawtool/mcp.test.js +126 -0
- package/dist/integrations/clawtool/mcp.test.js.map +1 -0
- package/dist/integrations/clawtool/paths.d.ts +8 -0
- package/dist/integrations/clawtool/paths.d.ts.map +1 -0
- package/dist/integrations/clawtool/paths.js +19 -0
- package/dist/integrations/clawtool/paths.js.map +1 -0
- package/dist/integrations/clawtool/peers.d.ts +67 -0
- package/dist/integrations/clawtool/peers.d.ts.map +1 -0
- package/dist/integrations/clawtool/peers.js +150 -0
- package/dist/integrations/clawtool/peers.js.map +1 -0
- package/dist/integrations/clawtool/plugin.d.ts +42 -0
- package/dist/integrations/clawtool/plugin.d.ts.map +1 -0
- package/dist/integrations/clawtool/plugin.js +38 -0
- package/dist/integrations/clawtool/plugin.js.map +1 -0
- package/dist/integrations/clawtool/state.d.ts +11 -0
- package/dist/integrations/clawtool/state.d.ts.map +1 -0
- package/dist/integrations/clawtool/state.js +41 -0
- package/dist/integrations/clawtool/state.js.map +1 -0
- package/dist/integrations/clawtool/state.test.d.ts +2 -0
- package/dist/integrations/clawtool/state.test.d.ts.map +1 -0
- package/dist/integrations/clawtool/state.test.js +38 -0
- package/dist/integrations/clawtool/state.test.js.map +1 -0
- package/dist/integrations/clawtool/tooling.d.ts +50 -0
- package/dist/integrations/clawtool/tooling.d.ts.map +1 -0
- package/dist/integrations/clawtool/tooling.js +89 -0
- package/dist/integrations/clawtool/tooling.js.map +1 -0
- package/dist/integrations/clawtool/tooling.test.d.ts +2 -0
- package/dist/integrations/clawtool/tooling.test.d.ts.map +1 -0
- package/dist/integrations/clawtool/tooling.test.js +58 -0
- package/dist/integrations/clawtool/tooling.test.js.map +1 -0
- package/dist/integrations/clawtool/types.d.ts +41 -0
- package/dist/integrations/clawtool/types.d.ts.map +1 -0
- package/dist/integrations/clawtool/types.js +9 -0
- package/dist/integrations/clawtool/types.js.map +1 -0
- package/dist/integrations/clipboard/image.d.ts +18 -0
- package/dist/integrations/clipboard/image.d.ts.map +1 -0
- package/dist/integrations/clipboard/image.js +88 -0
- package/dist/integrations/clipboard/image.js.map +1 -0
- package/dist/integrations/providers/discover.d.ts +68 -0
- package/dist/integrations/providers/discover.d.ts.map +1 -0
- package/dist/integrations/providers/discover.js +108 -0
- package/dist/integrations/providers/discover.js.map +1 -0
- package/dist/integrations/providers/discover.test.d.ts +2 -0
- package/dist/integrations/providers/discover.test.d.ts.map +1 -0
- package/dist/integrations/providers/discover.test.js +120 -0
- package/dist/integrations/providers/discover.test.js.map +1 -0
- package/dist/integrations/providers/index.d.ts +10 -0
- package/dist/integrations/providers/index.d.ts.map +1 -0
- package/dist/integrations/providers/index.js +12 -0
- package/dist/integrations/providers/index.js.map +1 -0
- package/dist/integrations/providers/keychain.d.ts +44 -0
- package/dist/integrations/providers/keychain.d.ts.map +1 -0
- package/dist/integrations/providers/keychain.js +154 -0
- package/dist/integrations/providers/keychain.js.map +1 -0
- package/dist/integrations/providers/keychain.test.d.ts +2 -0
- package/dist/integrations/providers/keychain.test.d.ts.map +1 -0
- package/dist/integrations/providers/keychain.test.js +21 -0
- package/dist/integrations/providers/keychain.test.js.map +1 -0
- package/dist/integrations/providers/mask.d.ts +7 -0
- package/dist/integrations/providers/mask.d.ts.map +1 -0
- package/dist/integrations/providers/mask.js +14 -0
- package/dist/integrations/providers/mask.js.map +1 -0
- package/dist/integrations/providers/mask.test.d.ts +2 -0
- package/dist/integrations/providers/mask.test.d.ts.map +1 -0
- package/dist/integrations/providers/mask.test.js +20 -0
- package/dist/integrations/providers/mask.test.js.map +1 -0
- package/dist/integrations/providers/oauth.d.ts +28 -0
- package/dist/integrations/providers/oauth.d.ts.map +1 -0
- package/dist/integrations/providers/oauth.js +65 -0
- package/dist/integrations/providers/oauth.js.map +1 -0
- package/dist/integrations/providers/oauth.test.d.ts +2 -0
- package/dist/integrations/providers/oauth.test.d.ts.map +1 -0
- package/dist/integrations/providers/oauth.test.js +86 -0
- package/dist/integrations/providers/oauth.test.js.map +1 -0
- package/dist/integrations/providers/preferences.d.ts +43 -0
- package/dist/integrations/providers/preferences.d.ts.map +1 -0
- package/dist/integrations/providers/preferences.js +111 -0
- package/dist/integrations/providers/preferences.js.map +1 -0
- package/dist/integrations/providers/preferences.test.d.ts +2 -0
- package/dist/integrations/providers/preferences.test.d.ts.map +1 -0
- package/dist/integrations/providers/preferences.test.js +68 -0
- package/dist/integrations/providers/preferences.test.js.map +1 -0
- package/dist/integrations/providers/registry.d.ts +31 -0
- package/dist/integrations/providers/registry.d.ts.map +1 -0
- package/dist/integrations/providers/registry.js +79 -0
- package/dist/integrations/providers/registry.js.map +1 -0
- package/dist/integrations/providers/schema.d.ts +73 -0
- package/dist/integrations/providers/schema.d.ts.map +1 -0
- package/dist/integrations/providers/schema.js +70 -0
- package/dist/integrations/providers/schema.js.map +1 -0
- package/dist/integrations/providers/schema.test.d.ts +2 -0
- package/dist/integrations/providers/schema.test.d.ts.map +1 -0
- package/dist/integrations/providers/schema.test.js +43 -0
- package/dist/integrations/providers/schema.test.js.map +1 -0
- package/dist/integrations/providers/secrets.d.ts +37 -0
- package/dist/integrations/providers/secrets.d.ts.map +1 -0
- package/dist/integrations/providers/secrets.js +69 -0
- package/dist/integrations/providers/secrets.js.map +1 -0
- package/dist/integrations/providers/store.d.ts +32 -0
- package/dist/integrations/providers/store.d.ts.map +1 -0
- package/dist/integrations/providers/store.js +133 -0
- package/dist/integrations/providers/store.js.map +1 -0
- package/dist/integrations/providers/store.test.d.ts +2 -0
- package/dist/integrations/providers/store.test.d.ts.map +1 -0
- package/dist/integrations/providers/store.test.js +127 -0
- package/dist/integrations/providers/store.test.js.map +1 -0
- package/dist/integrations/sessions/store.d.ts +40 -0
- package/dist/integrations/sessions/store.d.ts.map +1 -0
- package/dist/integrations/sessions/store.js +90 -0
- package/dist/integrations/sessions/store.js.map +1 -0
- package/dist/integrations/subagents/runtime.d.ts +37 -0
- package/dist/integrations/subagents/runtime.d.ts.map +1 -0
- package/dist/integrations/subagents/runtime.js +183 -0
- package/dist/integrations/subagents/runtime.js.map +1 -0
- package/dist/integrations/trust/store.d.ts +16 -0
- package/dist/integrations/trust/store.d.ts.map +1 -0
- package/dist/integrations/trust/store.js +59 -0
- package/dist/integrations/trust/store.js.map +1 -0
- package/dist/integrations/trust/store.test.d.ts +2 -0
- package/dist/integrations/trust/store.test.d.ts.map +1 -0
- package/dist/integrations/trust/store.test.js +50 -0
- package/dist/integrations/trust/store.test.js.map +1 -0
- package/dist/integrations/updates.d.ts +27 -0
- package/dist/integrations/updates.d.ts.map +1 -0
- package/dist/integrations/updates.js +99 -0
- package/dist/integrations/updates.js.map +1 -0
- package/dist/memory/store.d.ts +29 -0
- package/dist/memory/store.d.ts.map +1 -0
- package/dist/memory/store.js +74 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/memory/store.test.d.ts +2 -0
- package/dist/memory/store.test.d.ts.map +1 -0
- package/dist/memory/store.test.js +63 -0
- package/dist/memory/store.test.js.map +1 -0
- package/dist/output/formatter.d.ts +30 -0
- package/dist/output/formatter.d.ts.map +1 -0
- package/dist/output/formatter.js +16 -0
- package/dist/output/formatter.js.map +1 -0
- package/dist/output/formatter.test.d.ts +2 -0
- package/dist/output/formatter.test.d.ts.map +1 -0
- package/dist/output/formatter.test.js +88 -0
- package/dist/output/formatter.test.js.map +1 -0
- package/dist/output/index.d.ts +5 -0
- package/dist/output/index.d.ts.map +1 -0
- package/dist/output/index.js +22 -0
- package/dist/output/index.js.map +1 -0
- package/dist/output/json.d.ts +13 -0
- package/dist/output/json.d.ts.map +1 -0
- package/dist/output/json.js +51 -0
- package/dist/output/json.js.map +1 -0
- package/dist/output/text.d.ts +13 -0
- package/dist/output/text.d.ts.map +1 -0
- package/dist/output/text.js +36 -0
- package/dist/output/text.js.map +1 -0
- package/dist/output/yaml.d.ts +13 -0
- package/dist/output/yaml.d.ts.map +1 -0
- package/dist/output/yaml.js +26 -0
- package/dist/output/yaml.js.map +1 -0
- package/dist/skills/store.d.ts +49 -0
- package/dist/skills/store.d.ts.map +1 -0
- package/dist/skills/store.js +109 -0
- package/dist/skills/store.js.map +1 -0
- package/dist/skills/store.test.d.ts +2 -0
- package/dist/skills/store.test.d.ts.map +1 -0
- package/dist/skills/store.test.js +80 -0
- package/dist/skills/store.test.js.map +1 -0
- package/dist/tui/App.d.ts +18 -0
- package/dist/tui/App.d.ts.map +1 -0
- package/dist/tui/App.js +742 -0
- package/dist/tui/App.js.map +1 -0
- package/dist/tui/Composer.d.ts +17 -0
- package/dist/tui/Composer.d.ts.map +1 -0
- package/dist/tui/Composer.js +123 -0
- package/dist/tui/Composer.js.map +1 -0
- package/dist/tui/LiveActivity.d.ts +22 -0
- package/dist/tui/LiveActivity.d.ts.map +1 -0
- package/dist/tui/LiveActivity.js +46 -0
- package/dist/tui/LiveActivity.js.map +1 -0
- package/dist/tui/Markdown.d.ts +18 -0
- package/dist/tui/Markdown.d.ts.map +1 -0
- package/dist/tui/Markdown.js +68 -0
- package/dist/tui/Markdown.js.map +1 -0
- package/dist/tui/PermissionOverlay.d.ts +14 -0
- package/dist/tui/PermissionOverlay.d.ts.map +1 -0
- package/dist/tui/PermissionOverlay.js +22 -0
- package/dist/tui/PermissionOverlay.js.map +1 -0
- package/dist/tui/Picker.d.ts +20 -0
- package/dist/tui/Picker.d.ts.map +1 -0
- package/dist/tui/Picker.js +72 -0
- package/dist/tui/Picker.js.map +1 -0
- package/dist/tui/ResumePicker.d.ts +12 -0
- package/dist/tui/ResumePicker.d.ts.map +1 -0
- package/dist/tui/ResumePicker.js +26 -0
- package/dist/tui/ResumePicker.js.map +1 -0
- package/dist/tui/StatusBar.d.ts +22 -0
- package/dist/tui/StatusBar.d.ts.map +1 -0
- package/dist/tui/StatusBar.js +73 -0
- package/dist/tui/StatusBar.js.map +1 -0
- package/dist/tui/Transcript.d.ts +29 -0
- package/dist/tui/Transcript.d.ts.map +1 -0
- package/dist/tui/Transcript.js +105 -0
- package/dist/tui/Transcript.js.map +1 -0
- package/dist/tui/TrustPrompt.d.ts +11 -0
- package/dist/tui/TrustPrompt.d.ts.map +1 -0
- package/dist/tui/TrustPrompt.js +13 -0
- package/dist/tui/TrustPrompt.js.map +1 -0
- package/dist/tui/agent.d.ts +163 -0
- package/dist/tui/agent.d.ts.map +1 -0
- package/dist/tui/agent.js +684 -0
- package/dist/tui/agent.js.map +1 -0
- package/dist/tui/agent.test.d.ts +2 -0
- package/dist/tui/agent.test.d.ts.map +1 -0
- package/dist/tui/agent.test.js +225 -0
- package/dist/tui/agent.test.js.map +1 -0
- package/dist/tui/index.d.ts +7 -0
- package/dist/tui/index.d.ts.map +1 -0
- package/dist/tui/index.js +29 -0
- package/dist/tui/index.js.map +1 -0
- package/dist/tui/logo.d.ts +23 -0
- package/dist/tui/logo.d.ts.map +1 -0
- package/dist/tui/logo.js +35 -0
- package/dist/tui/logo.js.map +1 -0
- package/dist/tui/markdownParser.d.ts +45 -0
- package/dist/tui/markdownParser.d.ts.map +1 -0
- package/dist/tui/markdownParser.js +127 -0
- package/dist/tui/markdownParser.js.map +1 -0
- package/dist/tui/markdownParser.test.d.ts +2 -0
- package/dist/tui/markdownParser.test.d.ts.map +1 -0
- package/dist/tui/markdownParser.test.js +90 -0
- package/dist/tui/markdownParser.test.js.map +1 -0
- package/dist/tui/mentions.d.ts +22 -0
- package/dist/tui/mentions.d.ts.map +1 -0
- package/dist/tui/mentions.js +59 -0
- package/dist/tui/mentions.js.map +1 -0
- package/dist/tui/mentions.test.d.ts +2 -0
- package/dist/tui/mentions.test.d.ts.map +1 -0
- package/dist/tui/mentions.test.js +35 -0
- package/dist/tui/mentions.test.js.map +1 -0
- package/dist/tui/slashCommands.d.ts +64 -0
- package/dist/tui/slashCommands.d.ts.map +1 -0
- package/dist/tui/slashCommands.js +153 -0
- package/dist/tui/slashCommands.js.map +1 -0
- package/dist/tui/slashCommands.test.d.ts +2 -0
- package/dist/tui/slashCommands.test.d.ts.map +1 -0
- package/dist/tui/slashCommands.test.js +114 -0
- package/dist/tui/slashCommands.test.js.map +1 -0
- package/dist/tui/theme.d.ts +34 -0
- package/dist/tui/theme.d.ts.map +1 -0
- package/dist/tui/theme.js +32 -0
- package/dist/tui/theme.js.map +1 -0
- package/dist/tui/types.d.ts +27 -0
- package/dist/tui/types.d.ts.map +1 -0
- package/dist/tui/types.js +7 -0
- package/dist/tui/types.js.map +1 -0
- package/package.json +13 -3
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ProviderProfile` discriminated union.
|
|
3
|
+
*
|
|
4
|
+
* Each provider type mirrors a `@namzu/<type>` package the SDK ships. The
|
|
5
|
+
* shapes are the *minimum* the CLI persists; richer per-type knobs (Bedrock
|
|
6
|
+
* IAM role assumption, OpenRouter routing rules, Anthropic `maxTokens`,
|
|
7
|
+
* etc.) come in as additive optional fields when M3+ surface needs them.
|
|
8
|
+
*
|
|
9
|
+
* Validation is intentionally hand-rolled (no Zod) — we want zero-runtime-dep
|
|
10
|
+
* config IO in M2; a future session can introduce a schema library when
|
|
11
|
+
* provider-package-specific knobs start to collide.
|
|
12
|
+
*/
|
|
13
|
+
export type ProviderType = 'openai' | 'anthropic' | 'openrouter' | 'ollama' | 'bedrock' | 'http' | 'lmstudio';
|
|
14
|
+
export declare const PROVIDER_TYPES: readonly ProviderType[];
|
|
15
|
+
/**
|
|
16
|
+
* Per-type API-key env-var fallback. Order checked: `NAMZU_<NAME>_API_KEY`
|
|
17
|
+
* (per-profile override) → entry below (per-type vendor default) → empty.
|
|
18
|
+
* Mirrors what users already have set in CI / shells for other tools.
|
|
19
|
+
*/
|
|
20
|
+
export declare const TYPE_ENV_FALLBACK: Readonly<Record<ProviderType, string | null>>;
|
|
21
|
+
export interface BaseProfile {
|
|
22
|
+
readonly name: string;
|
|
23
|
+
readonly default?: boolean;
|
|
24
|
+
readonly model?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface OpenAIProfile extends BaseProfile {
|
|
27
|
+
readonly type: 'openai';
|
|
28
|
+
readonly apiKey?: string;
|
|
29
|
+
readonly baseUrl?: string;
|
|
30
|
+
readonly organization?: string;
|
|
31
|
+
readonly project?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface AnthropicProfile extends BaseProfile {
|
|
34
|
+
readonly type: 'anthropic';
|
|
35
|
+
readonly apiKey?: string;
|
|
36
|
+
readonly baseUrl?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface OpenRouterProfile extends BaseProfile {
|
|
39
|
+
readonly type: 'openrouter';
|
|
40
|
+
readonly apiKey?: string;
|
|
41
|
+
readonly baseUrl?: string;
|
|
42
|
+
}
|
|
43
|
+
export interface OllamaProfile extends BaseProfile {
|
|
44
|
+
readonly type: 'ollama';
|
|
45
|
+
readonly host?: string;
|
|
46
|
+
}
|
|
47
|
+
export interface BedrockProfile extends BaseProfile {
|
|
48
|
+
readonly type: 'bedrock';
|
|
49
|
+
readonly region?: string;
|
|
50
|
+
}
|
|
51
|
+
export interface HttpProfile extends BaseProfile {
|
|
52
|
+
readonly type: 'http';
|
|
53
|
+
readonly baseUrl: string;
|
|
54
|
+
readonly apiKey?: string;
|
|
55
|
+
readonly dialect?: 'openai' | 'anthropic';
|
|
56
|
+
}
|
|
57
|
+
export interface LMStudioProfile extends BaseProfile {
|
|
58
|
+
readonly type: 'lmstudio';
|
|
59
|
+
readonly host?: string;
|
|
60
|
+
}
|
|
61
|
+
export type ProviderProfile = OpenAIProfile | AnthropicProfile | OpenRouterProfile | OllamaProfile | BedrockProfile | HttpProfile | LMStudioProfile;
|
|
62
|
+
export interface ProvidersFile {
|
|
63
|
+
readonly version: 1;
|
|
64
|
+
readonly profiles: readonly ProviderProfile[];
|
|
65
|
+
}
|
|
66
|
+
export declare const PROVIDERS_FILE_VERSION: 1;
|
|
67
|
+
export declare class ProfileValidationError extends Error {
|
|
68
|
+
constructor(message: string);
|
|
69
|
+
}
|
|
70
|
+
/** Strict at-the-boundary validator. Throws `ProfileValidationError` on bad input. */
|
|
71
|
+
export declare function validateProfile(value: unknown): ProviderProfile;
|
|
72
|
+
export declare function isProviderType(value: unknown): value is ProviderType;
|
|
73
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/integrations/providers/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,YAAY,GACrB,QAAQ,GACR,WAAW,GACX,YAAY,GACZ,QAAQ,GACR,SAAS,GACT,MAAM,GACN,UAAU,CAAA;AAEb,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAQxC,CAAA;AAEV;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC,CAQ1E,CAAA;AAEF,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,aAAc,SAAQ,WAAW;IACjD,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACpD,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,iBAAkB,SAAQ,WAAW;IACrD,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,aAAc,SAAQ,WAAW;IACjD,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,cAAe,SAAQ,WAAW;IAClD,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAA;CACzC;AAED,MAAM,WAAW,eAAgB,SAAQ,WAAW;IACnD,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,MAAM,eAAe,GACxB,aAAa,GACb,gBAAgB,GAChB,iBAAiB,GACjB,aAAa,GACb,cAAc,GACd,WAAW,GACX,eAAe,CAAA;AAElB,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;IACnB,QAAQ,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAA;CAC7C;AAED,eAAO,MAAM,sBAAsB,EAAG,CAAU,CAAA;AAEhD,qBAAa,sBAAuB,SAAQ,KAAK;gBACpC,OAAO,EAAE,MAAM;CAI3B;AAED,sFAAsF;AACtF,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,CAsB/D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAEpE"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ProviderProfile` discriminated union.
|
|
3
|
+
*
|
|
4
|
+
* Each provider type mirrors a `@namzu/<type>` package the SDK ships. The
|
|
5
|
+
* shapes are the *minimum* the CLI persists; richer per-type knobs (Bedrock
|
|
6
|
+
* IAM role assumption, OpenRouter routing rules, Anthropic `maxTokens`,
|
|
7
|
+
* etc.) come in as additive optional fields when M3+ surface needs them.
|
|
8
|
+
*
|
|
9
|
+
* Validation is intentionally hand-rolled (no Zod) — we want zero-runtime-dep
|
|
10
|
+
* config IO in M2; a future session can introduce a schema library when
|
|
11
|
+
* provider-package-specific knobs start to collide.
|
|
12
|
+
*/
|
|
13
|
+
export const PROVIDER_TYPES = [
|
|
14
|
+
'openai',
|
|
15
|
+
'anthropic',
|
|
16
|
+
'openrouter',
|
|
17
|
+
'ollama',
|
|
18
|
+
'bedrock',
|
|
19
|
+
'http',
|
|
20
|
+
'lmstudio',
|
|
21
|
+
];
|
|
22
|
+
/**
|
|
23
|
+
* Per-type API-key env-var fallback. Order checked: `NAMZU_<NAME>_API_KEY`
|
|
24
|
+
* (per-profile override) → entry below (per-type vendor default) → empty.
|
|
25
|
+
* Mirrors what users already have set in CI / shells for other tools.
|
|
26
|
+
*/
|
|
27
|
+
export const TYPE_ENV_FALLBACK = Object.freeze({
|
|
28
|
+
openai: 'OPENAI_API_KEY',
|
|
29
|
+
anthropic: 'ANTHROPIC_API_KEY',
|
|
30
|
+
openrouter: 'OPENROUTER_API_KEY',
|
|
31
|
+
ollama: null, // local server; no API key concept
|
|
32
|
+
bedrock: null, // AWS SDK credential chain; handled in M3
|
|
33
|
+
http: null, // generic; user supplies via apiKey or per-profile env
|
|
34
|
+
lmstudio: null, // local server
|
|
35
|
+
});
|
|
36
|
+
export const PROVIDERS_FILE_VERSION = 1;
|
|
37
|
+
export class ProfileValidationError extends Error {
|
|
38
|
+
constructor(message) {
|
|
39
|
+
super(message);
|
|
40
|
+
this.name = 'ProfileValidationError';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Strict at-the-boundary validator. Throws `ProfileValidationError` on bad input. */
|
|
44
|
+
export function validateProfile(value) {
|
|
45
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
46
|
+
throw new ProfileValidationError('profile must be a JSON object');
|
|
47
|
+
}
|
|
48
|
+
const v = value;
|
|
49
|
+
if (typeof v.name !== 'string' || v.name.length === 0) {
|
|
50
|
+
throw new ProfileValidationError('profile.name is required');
|
|
51
|
+
}
|
|
52
|
+
if (!isProviderType(v.type)) {
|
|
53
|
+
throw new ProfileValidationError(`profile.type must be one of: ${PROVIDER_TYPES.join(', ')}`);
|
|
54
|
+
}
|
|
55
|
+
if (v.default !== undefined && typeof v.default !== 'boolean') {
|
|
56
|
+
throw new ProfileValidationError('profile.default must be a boolean');
|
|
57
|
+
}
|
|
58
|
+
if (v.model !== undefined && typeof v.model !== 'string') {
|
|
59
|
+
throw new ProfileValidationError('profile.model must be a string');
|
|
60
|
+
}
|
|
61
|
+
// http requires baseUrl; others optional. We trust the discriminator.
|
|
62
|
+
if (v.type === 'http' && (typeof v.baseUrl !== 'string' || v.baseUrl.length === 0)) {
|
|
63
|
+
throw new ProfileValidationError('profile.baseUrl is required when type=http');
|
|
64
|
+
}
|
|
65
|
+
return v;
|
|
66
|
+
}
|
|
67
|
+
export function isProviderType(value) {
|
|
68
|
+
return typeof value === 'string' && PROVIDER_TYPES.includes(value);
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/integrations/providers/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAWH,MAAM,CAAC,MAAM,cAAc,GAA4B;IACtD,QAAQ;IACR,WAAW;IACX,YAAY;IACZ,QAAQ;IACR,SAAS;IACT,MAAM;IACN,UAAU;CACD,CAAA;AAEV;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAkD,MAAM,CAAC,MAAM,CAAC;IAC7F,MAAM,EAAE,gBAAgB;IACxB,SAAS,EAAE,mBAAmB;IAC9B,UAAU,EAAE,oBAAoB;IAChC,MAAM,EAAE,IAAI,EAAE,mCAAmC;IACjD,OAAO,EAAE,IAAI,EAAE,0CAA0C;IACzD,IAAI,EAAE,IAAI,EAAE,uDAAuD;IACnE,QAAQ,EAAE,IAAI,EAAE,eAAe;CAC/B,CAAC,CAAA;AAgEF,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAU,CAAA;AAEhD,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAChD,YAAY,OAAe;QAC1B,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAA;IACrC,CAAC;CACD;AAED,sFAAsF;AACtF,MAAM,UAAU,eAAe,CAAC,KAAc;IAC7C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzE,MAAM,IAAI,sBAAsB,CAAC,+BAA+B,CAAC,CAAA;IAClE,CAAC;IACD,MAAM,CAAC,GAAG,KAAgC,CAAA;IAC1C,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,sBAAsB,CAAC,0BAA0B,CAAC,CAAA;IAC7D,CAAC;IACD,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,sBAAsB,CAAC,gCAAgC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAC9F,CAAC;IACD,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAC/D,MAAM,IAAI,sBAAsB,CAAC,mCAAmC,CAAC,CAAA;IACtE,CAAC;IACD,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC1D,MAAM,IAAI,sBAAsB,CAAC,gCAAgC,CAAC,CAAA;IACnE,CAAC;IACD,sEAAsE;IACtE,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;QACpF,MAAM,IAAI,sBAAsB,CAAC,4CAA4C,CAAC,CAAA;IAC/E,CAAC;IACD,OAAO,CAA+B,CAAA;AACvC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAc;IAC5C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAK,cAAoC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;AAC1F,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.test.d.ts","sourceRoot":"","sources":["../../../src/integrations/providers/schema.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { PROVIDER_TYPES, ProfileValidationError, isProviderType, validateProfile, } from './schema.js';
|
|
3
|
+
describe('isProviderType', () => {
|
|
4
|
+
it('accepts every shipped type', () => {
|
|
5
|
+
for (const t of PROVIDER_TYPES)
|
|
6
|
+
expect(isProviderType(t)).toBe(true);
|
|
7
|
+
});
|
|
8
|
+
it('rejects unknown types', () => {
|
|
9
|
+
expect(isProviderType('gemini')).toBe(false);
|
|
10
|
+
expect(isProviderType(undefined)).toBe(false);
|
|
11
|
+
expect(isProviderType(42)).toBe(false);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
describe('validateProfile', () => {
|
|
15
|
+
it('passes a minimal anthropic profile', () => {
|
|
16
|
+
const p = validateProfile({ name: 'work', type: 'anthropic' });
|
|
17
|
+
expect(p.name).toBe('work');
|
|
18
|
+
expect(p.type).toBe('anthropic');
|
|
19
|
+
});
|
|
20
|
+
it('rejects missing name', () => {
|
|
21
|
+
expect(() => validateProfile({ type: 'anthropic' })).toThrow(ProfileValidationError);
|
|
22
|
+
});
|
|
23
|
+
it('rejects unknown type with a helpful message', () => {
|
|
24
|
+
expect(() => validateProfile({ name: 'x', type: 'gpt-5-pro' })).toThrowError(/must be one of/);
|
|
25
|
+
});
|
|
26
|
+
it('rejects non-boolean default', () => {
|
|
27
|
+
expect(() => validateProfile({ name: 'x', type: 'openai', default: 'yes' })).toThrow(ProfileValidationError);
|
|
28
|
+
});
|
|
29
|
+
it('requires baseUrl when type=http', () => {
|
|
30
|
+
expect(() => validateProfile({ name: 'h', type: 'http' })).toThrowError(/baseUrl is required/);
|
|
31
|
+
const ok = validateProfile({ name: 'h', type: 'http', baseUrl: 'https://example.com' });
|
|
32
|
+
expect(ok.type).toBe('http');
|
|
33
|
+
});
|
|
34
|
+
it('rejects non-string model', () => {
|
|
35
|
+
expect(() => validateProfile({ name: 'x', type: 'openai', model: 42 })).toThrow(ProfileValidationError);
|
|
36
|
+
});
|
|
37
|
+
it('rejects non-object inputs', () => {
|
|
38
|
+
expect(() => validateProfile(null)).toThrow(ProfileValidationError);
|
|
39
|
+
expect(() => validateProfile('string')).toThrow(ProfileValidationError);
|
|
40
|
+
expect(() => validateProfile([])).toThrow(ProfileValidationError);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
//# sourceMappingURL=schema.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.test.js","sourceRoot":"","sources":["../../../src/integrations/providers/schema.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reader for clawtool's `~/.config/clawtool/secrets.toml`.
|
|
3
|
+
*
|
|
4
|
+
* Schema (per clawtool's `internal/config/portals_io.go`):
|
|
5
|
+
*
|
|
6
|
+
* [secrets.work]
|
|
7
|
+
* ANTHROPIC_API_KEY = "sk-ant-..."
|
|
8
|
+
* OPENAI_API_KEY = "sk-..."
|
|
9
|
+
*
|
|
10
|
+
* [secrets.personal]
|
|
11
|
+
* OPENROUTER_API_KEY = "..."
|
|
12
|
+
*
|
|
13
|
+
* Namzu treats each `[secrets.X]` section as a candidate credential
|
|
14
|
+
* bundle — the env-var keys inside are the same names as native env
|
|
15
|
+
* vars (e.g. `ANTHROPIC_API_KEY`). We flatten every section into a
|
|
16
|
+
* single `Map<envVarName, value[]>` so the discoverer can ask "does any
|
|
17
|
+
* source have ANTHROPIC_API_KEY?" without caring which scope it came
|
|
18
|
+
* from. Multiple sections can carry the same key; we keep all values
|
|
19
|
+
* so the picker can present them as distinct candidates if the user
|
|
20
|
+
* has multiple Anthropic accounts.
|
|
21
|
+
*/
|
|
22
|
+
export interface SecretCandidate {
|
|
23
|
+
/** Env-var name as it would appear at runtime (e.g. `ANTHROPIC_API_KEY`). */
|
|
24
|
+
readonly envName: string;
|
|
25
|
+
readonly value: string;
|
|
26
|
+
/** Which `[secrets.X]` section it came from (e.g. `work`, `personal`). */
|
|
27
|
+
readonly scope: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function clawtoolSecretsPath(home?: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Read clawtool's secrets.toml and return a flat array of every
|
|
32
|
+
* env-var-style credential it carries, tagged by scope. Returns `[]`
|
|
33
|
+
* when the file is missing or unparseable — never throws (this is an
|
|
34
|
+
* optional discovery source).
|
|
35
|
+
*/
|
|
36
|
+
export declare function readClawtoolSecrets(home?: string): readonly SecretCandidate[];
|
|
37
|
+
//# sourceMappingURL=secrets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../../../src/integrations/providers/secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAQH,MAAM,WAAW,eAAe;IAC/B,6EAA6E;IAC7E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACtB;AAED,wBAAgB,mBAAmB,CAAC,IAAI,GAAE,MAAkB,GAAG,MAAM,CAEpE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,GAAE,MAAkB,GAAG,SAAS,eAAe,EAAE,CA4BxF"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reader for clawtool's `~/.config/clawtool/secrets.toml`.
|
|
3
|
+
*
|
|
4
|
+
* Schema (per clawtool's `internal/config/portals_io.go`):
|
|
5
|
+
*
|
|
6
|
+
* [secrets.work]
|
|
7
|
+
* ANTHROPIC_API_KEY = "sk-ant-..."
|
|
8
|
+
* OPENAI_API_KEY = "sk-..."
|
|
9
|
+
*
|
|
10
|
+
* [secrets.personal]
|
|
11
|
+
* OPENROUTER_API_KEY = "..."
|
|
12
|
+
*
|
|
13
|
+
* Namzu treats each `[secrets.X]` section as a candidate credential
|
|
14
|
+
* bundle — the env-var keys inside are the same names as native env
|
|
15
|
+
* vars (e.g. `ANTHROPIC_API_KEY`). We flatten every section into a
|
|
16
|
+
* single `Map<envVarName, value[]>` so the discoverer can ask "does any
|
|
17
|
+
* source have ANTHROPIC_API_KEY?" without caring which scope it came
|
|
18
|
+
* from. Multiple sections can carry the same key; we keep all values
|
|
19
|
+
* so the picker can present them as distinct candidates if the user
|
|
20
|
+
* has multiple Anthropic accounts.
|
|
21
|
+
*/
|
|
22
|
+
import { readFileSync } from 'node:fs';
|
|
23
|
+
import { homedir } from 'node:os';
|
|
24
|
+
import { join } from 'node:path';
|
|
25
|
+
import { parse as tomlParse } from 'smol-toml';
|
|
26
|
+
export function clawtoolSecretsPath(home = homedir()) {
|
|
27
|
+
return join(home, '.config', 'clawtool', 'secrets.toml');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Read clawtool's secrets.toml and return a flat array of every
|
|
31
|
+
* env-var-style credential it carries, tagged by scope. Returns `[]`
|
|
32
|
+
* when the file is missing or unparseable — never throws (this is an
|
|
33
|
+
* optional discovery source).
|
|
34
|
+
*/
|
|
35
|
+
export function readClawtoolSecrets(home = homedir()) {
|
|
36
|
+
const path = clawtoolSecretsPath(home);
|
|
37
|
+
let raw;
|
|
38
|
+
try {
|
|
39
|
+
raw = readFileSync(path, 'utf8');
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
let parsed;
|
|
45
|
+
try {
|
|
46
|
+
parsed = tomlParse(raw);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
52
|
+
return [];
|
|
53
|
+
const root = parsed;
|
|
54
|
+
const secrets = root.secrets;
|
|
55
|
+
if (typeof secrets !== 'object' || secrets === null)
|
|
56
|
+
return [];
|
|
57
|
+
const out = [];
|
|
58
|
+
for (const [scope, section] of Object.entries(secrets)) {
|
|
59
|
+
if (typeof section !== 'object' || section === null)
|
|
60
|
+
continue;
|
|
61
|
+
for (const [envName, value] of Object.entries(section)) {
|
|
62
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
63
|
+
out.push({ envName, value, scope });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=secrets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../../src/integrations/providers/secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,WAAW,CAAA;AAU9C,MAAM,UAAU,mBAAmB,CAAC,OAAe,OAAO,EAAE;IAC3D,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,CAAC,CAAA;AACzD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAe,OAAO,EAAE;IAC3D,MAAM,IAAI,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAA;IACtC,IAAI,GAAW,CAAA;IACf,IAAI,CAAC;QACJ,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IACjC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,EAAE,CAAA;IACV,CAAC;IACD,IAAI,MAAe,CAAA;IACnB,IAAI,CAAC;QACJ,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,CAAA;IACxB,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,EAAE,CAAA;IACV,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,EAAE,CAAA;IAC5D,MAAM,IAAI,GAAG,MAAiC,CAAA;IAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAA;IAC5B,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,EAAE,CAAA;IAC9D,MAAM,GAAG,GAAsB,EAAE,CAAA;IACjC,KAAK,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAkC,CAAC,EAAE,CAAC;QACnF,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI;YAAE,SAAQ;QAC7D,KAAK,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAkC,CAAC,EAAE,CAAC;YACnF,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACnD,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;YACpC,CAAC;QACF,CAAC;IACF,CAAC;IACD,OAAO,GAAG,CAAA;AACX,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `~/.namzu/providers.json` store.
|
|
3
|
+
*
|
|
4
|
+
* - Reads return `[]` (not error) when the file is missing — first-run UX.
|
|
5
|
+
* - Writes are atomic: temp file + rename. Mode 0600 enforced on every write.
|
|
6
|
+
* - `resolveApiKey(profile, env)` cascades `NAMZU_<NAME>_API_KEY` (per-
|
|
7
|
+
* profile override) → per-type vendor default (`OPENAI_API_KEY` etc.)
|
|
8
|
+
* → profile.apiKey on-disk → null. Hermes-style ergonomics.
|
|
9
|
+
* - Uniqueness invariants enforced on write: no duplicate `name`; at most
|
|
10
|
+
* one profile with `default: true`.
|
|
11
|
+
*/
|
|
12
|
+
import { type ProviderProfile } from './schema.js';
|
|
13
|
+
export declare class ProvidersStoreError extends Error {
|
|
14
|
+
constructor(message: string);
|
|
15
|
+
}
|
|
16
|
+
export interface StoreLocation {
|
|
17
|
+
readonly path: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function providersPath(home?: string): string;
|
|
20
|
+
export declare function readProfiles(home?: string): ProviderProfile[];
|
|
21
|
+
export declare function writeProfiles(profiles: readonly ProviderProfile[], home?: string): void;
|
|
22
|
+
export declare function assertInvariants(profiles: readonly ProviderProfile[]): void;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the live API key for a profile, honoring overrides:
|
|
25
|
+
* 1. `NAMZU_<UPPERCASE_NAME>_API_KEY` (per-profile override)
|
|
26
|
+
* 2. Per-type vendor default (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, etc.)
|
|
27
|
+
* 3. `profile.apiKey` (on-disk)
|
|
28
|
+
* 4. `null` (no key — caller decides whether that's fatal)
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveApiKey(profile: ProviderProfile, env?: NodeJS.ProcessEnv): string | null;
|
|
31
|
+
export declare function findDefault(profiles: readonly ProviderProfile[]): ProviderProfile | null;
|
|
32
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/integrations/providers/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,OAAO,EAGN,KAAK,eAAe,EAIpB,MAAM,aAAa,CAAA;AAKpB,qBAAa,mBAAoB,SAAQ,KAAK;gBACjC,OAAO,EAAE,MAAM;CAI3B;AAED,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACrB;AAED,wBAAgB,aAAa,CAAC,IAAI,GAAE,MAAkB,GAAG,MAAM,CAE9D;AAED,wBAAgB,YAAY,CAAC,IAAI,GAAE,MAAkB,GAAG,eAAe,EAAE,CA2CxE;AAED,wBAAgB,aAAa,CAC5B,QAAQ,EAAE,SAAS,eAAe,EAAE,EACpC,IAAI,GAAE,MAAkB,GACtB,IAAI,CAmBN;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,GAAG,IAAI,CAa3E;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC5B,OAAO,EAAE,eAAe,EACxB,GAAG,GAAE,MAAM,CAAC,UAAwB,GAClC,MAAM,GAAG,IAAI,CAWf;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,GAAG,eAAe,GAAG,IAAI,CAExF"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `~/.namzu/providers.json` store.
|
|
3
|
+
*
|
|
4
|
+
* - Reads return `[]` (not error) when the file is missing — first-run UX.
|
|
5
|
+
* - Writes are atomic: temp file + rename. Mode 0600 enforced on every write.
|
|
6
|
+
* - `resolveApiKey(profile, env)` cascades `NAMZU_<NAME>_API_KEY` (per-
|
|
7
|
+
* profile override) → per-type vendor default (`OPENAI_API_KEY` etc.)
|
|
8
|
+
* → profile.apiKey on-disk → null. Hermes-style ergonomics.
|
|
9
|
+
* - Uniqueness invariants enforced on write: no duplicate `name`; at most
|
|
10
|
+
* one profile with `default: true`.
|
|
11
|
+
*/
|
|
12
|
+
import { randomBytes } from 'node:crypto';
|
|
13
|
+
import { chmodSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
14
|
+
import { homedir } from 'node:os';
|
|
15
|
+
import { dirname, join } from 'node:path';
|
|
16
|
+
import { PROVIDERS_FILE_VERSION, ProfileValidationError, TYPE_ENV_FALLBACK, validateProfile, } from './schema.js';
|
|
17
|
+
const FILE_MODE = 0o600;
|
|
18
|
+
const DIR_MODE = 0o700;
|
|
19
|
+
export class ProvidersStoreError extends Error {
|
|
20
|
+
constructor(message) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = 'ProvidersStoreError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function providersPath(home = homedir()) {
|
|
26
|
+
return join(home, '.namzu', 'providers.json');
|
|
27
|
+
}
|
|
28
|
+
export function readProfiles(home = homedir()) {
|
|
29
|
+
const path = providersPath(home);
|
|
30
|
+
let raw;
|
|
31
|
+
try {
|
|
32
|
+
raw = readFileSync(path, 'utf8');
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
if (err.code === 'ENOENT')
|
|
36
|
+
return [];
|
|
37
|
+
throw new ProvidersStoreError(`could not read ${path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
38
|
+
}
|
|
39
|
+
let parsed;
|
|
40
|
+
try {
|
|
41
|
+
parsed = JSON.parse(raw);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
throw new ProvidersStoreError(`${path} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
45
|
+
}
|
|
46
|
+
if (typeof parsed !== 'object' || parsed === null) {
|
|
47
|
+
throw new ProvidersStoreError(`${path} top-level must be an object`);
|
|
48
|
+
}
|
|
49
|
+
const file = parsed;
|
|
50
|
+
if (file.version !== PROVIDERS_FILE_VERSION) {
|
|
51
|
+
throw new ProvidersStoreError(`${path} has unsupported version ${String(file.version)}; expected ${PROVIDERS_FILE_VERSION}. Upgrade @namzu/cli, or back up the file and re-create your profiles with \`namzu providers add\`.`);
|
|
52
|
+
}
|
|
53
|
+
if (!Array.isArray(file.profiles)) {
|
|
54
|
+
throw new ProvidersStoreError(`${path}.profiles must be an array`);
|
|
55
|
+
}
|
|
56
|
+
const validated = [];
|
|
57
|
+
for (const entry of file.profiles) {
|
|
58
|
+
try {
|
|
59
|
+
validated.push(validateProfile(entry));
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
if (err instanceof ProfileValidationError) {
|
|
63
|
+
throw new ProvidersStoreError(`${path}: invalid profile — ${err.message}`);
|
|
64
|
+
}
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return validated;
|
|
69
|
+
}
|
|
70
|
+
export function writeProfiles(profiles, home = homedir()) {
|
|
71
|
+
assertInvariants(profiles);
|
|
72
|
+
const path = providersPath(home);
|
|
73
|
+
const dir = dirname(path);
|
|
74
|
+
mkdirSync(dir, { recursive: true, mode: DIR_MODE });
|
|
75
|
+
const payload = { version: PROVIDERS_FILE_VERSION, profiles: [...profiles] };
|
|
76
|
+
const body = `${JSON.stringify(payload, null, 2)}\n`;
|
|
77
|
+
// PID alone is not enough — two concurrent writes in the same process
|
|
78
|
+
// (Promise.all on `providers add` from a script, for example) would
|
|
79
|
+
// collide on the temp file and one mutation could be lost. Append a
|
|
80
|
+
// random suffix so each writer has its own temp path; the final
|
|
81
|
+
// `renameSync` is atomic per-rename so the last successful rename
|
|
82
|
+
// wins (caller's responsibility to serialize semantically distinct
|
|
83
|
+
// mutations — see assertInvariants for the in-memory check).
|
|
84
|
+
const tmp = `${path}.tmp.${process.pid}.${randomBytes(4).toString('hex')}`;
|
|
85
|
+
writeFileSync(tmp, body, { mode: FILE_MODE });
|
|
86
|
+
// Ensure mode even when umask interfered.
|
|
87
|
+
chmodSync(tmp, FILE_MODE);
|
|
88
|
+
renameSync(tmp, path);
|
|
89
|
+
}
|
|
90
|
+
export function assertInvariants(profiles) {
|
|
91
|
+
const seen = new Set();
|
|
92
|
+
let defaults = 0;
|
|
93
|
+
for (const p of profiles) {
|
|
94
|
+
if (seen.has(p.name)) {
|
|
95
|
+
throw new ProvidersStoreError(`duplicate profile name: ${p.name}`);
|
|
96
|
+
}
|
|
97
|
+
seen.add(p.name);
|
|
98
|
+
if (p.default === true)
|
|
99
|
+
defaults += 1;
|
|
100
|
+
}
|
|
101
|
+
if (defaults > 1) {
|
|
102
|
+
throw new ProvidersStoreError(`at most one profile may have default: true (got ${defaults})`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Resolve the live API key for a profile, honoring overrides:
|
|
107
|
+
* 1. `NAMZU_<UPPERCASE_NAME>_API_KEY` (per-profile override)
|
|
108
|
+
* 2. Per-type vendor default (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, etc.)
|
|
109
|
+
* 3. `profile.apiKey` (on-disk)
|
|
110
|
+
* 4. `null` (no key — caller decides whether that's fatal)
|
|
111
|
+
*/
|
|
112
|
+
export function resolveApiKey(profile, env = process.env) {
|
|
113
|
+
const namedEnv = env[`NAMZU_${nameToEnv(profile.name)}_API_KEY`];
|
|
114
|
+
if (namedEnv && namedEnv.length > 0)
|
|
115
|
+
return namedEnv;
|
|
116
|
+
const fallbackName = TYPE_ENV_FALLBACK[profile.type];
|
|
117
|
+
if (fallbackName) {
|
|
118
|
+
const v = env[fallbackName];
|
|
119
|
+
if (v && v.length > 0)
|
|
120
|
+
return v;
|
|
121
|
+
}
|
|
122
|
+
const onDisk = profile.apiKey;
|
|
123
|
+
if (onDisk && onDisk.length > 0)
|
|
124
|
+
return onDisk;
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
export function findDefault(profiles) {
|
|
128
|
+
return profiles.find((p) => p.default === true) ?? null;
|
|
129
|
+
}
|
|
130
|
+
function nameToEnv(name) {
|
|
131
|
+
return name.toUpperCase().replace(/[^A-Z0-9]/g, '_');
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/integrations/providers/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.test.d.ts","sourceRoot":"","sources":["../../../src/integrations/providers/store.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { mkdirSync, mkdtempSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
import { ProvidersStoreError, assertInvariants, findDefault, providersPath, readProfiles, resolveApiKey, writeProfiles, } from './store.js';
|
|
6
|
+
function tmpHome() {
|
|
7
|
+
return mkdtempSync(join(tmpdir(), 'namzu-providers-'));
|
|
8
|
+
}
|
|
9
|
+
const anthropic = (over = {}) => ({
|
|
10
|
+
name: 'a',
|
|
11
|
+
type: 'anthropic',
|
|
12
|
+
apiKey: 'sk-ant-deadbeef',
|
|
13
|
+
...over,
|
|
14
|
+
});
|
|
15
|
+
const openai = (over = {}) => ({
|
|
16
|
+
name: 'o',
|
|
17
|
+
type: 'openai',
|
|
18
|
+
apiKey: 'sk-openai-cafebabe',
|
|
19
|
+
...over,
|
|
20
|
+
});
|
|
21
|
+
describe('readProfiles', () => {
|
|
22
|
+
it('returns an empty array when the file is missing', () => {
|
|
23
|
+
expect(readProfiles(tmpHome())).toEqual([]);
|
|
24
|
+
});
|
|
25
|
+
it('throws ProvidersStoreError on malformed JSON', () => {
|
|
26
|
+
const home = tmpHome();
|
|
27
|
+
mkdirSync(join(home, '.namzu'));
|
|
28
|
+
writeFileSync(join(home, '.namzu', 'providers.json'), '{not json}');
|
|
29
|
+
expect(() => readProfiles(home)).toThrow(ProvidersStoreError);
|
|
30
|
+
});
|
|
31
|
+
it('throws on wrong version', () => {
|
|
32
|
+
const home = tmpHome();
|
|
33
|
+
mkdirSync(join(home, '.namzu'));
|
|
34
|
+
writeFileSync(join(home, '.namzu', 'providers.json'), JSON.stringify({ version: 99, profiles: [] }));
|
|
35
|
+
expect(() => readProfiles(home)).toThrowError(/unsupported version/);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
describe('writeProfiles', () => {
|
|
39
|
+
it('round-trips a profile through disk', () => {
|
|
40
|
+
const home = tmpHome();
|
|
41
|
+
writeProfiles([anthropic()], home);
|
|
42
|
+
expect(readProfiles(home)).toEqual([anthropic()]);
|
|
43
|
+
});
|
|
44
|
+
it('enforces mode 0600 on the file and 0700 on the directory', () => {
|
|
45
|
+
const home = tmpHome();
|
|
46
|
+
writeProfiles([anthropic()], home);
|
|
47
|
+
const fileStat = statSync(providersPath(home));
|
|
48
|
+
const dirStat = statSync(join(home, '.namzu'));
|
|
49
|
+
expect(fileStat.mode & 0o777).toBe(0o600);
|
|
50
|
+
expect(dirStat.mode & 0o777).toBe(0o700);
|
|
51
|
+
});
|
|
52
|
+
it('writes valid JSON that includes the version header', () => {
|
|
53
|
+
const home = tmpHome();
|
|
54
|
+
writeProfiles([anthropic()], home);
|
|
55
|
+
const raw = readFileSync(providersPath(home), 'utf8');
|
|
56
|
+
const parsed = JSON.parse(raw);
|
|
57
|
+
expect(parsed.version).toBe(1);
|
|
58
|
+
expect(Array.isArray(parsed.profiles)).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
it('rejects duplicate names', () => {
|
|
61
|
+
expect(() => writeProfiles([anthropic(), anthropic()], tmpHome())).toThrowError(/duplicate profile name/);
|
|
62
|
+
});
|
|
63
|
+
it('rejects more than one default', () => {
|
|
64
|
+
expect(() => writeProfiles([anthropic({ default: true }), openai({ name: 'o', default: true })], tmpHome())).toThrowError(/at most one/);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe('concurrent writes', () => {
|
|
68
|
+
it('two simultaneous writeProfiles calls do not collide on the temp file', async () => {
|
|
69
|
+
const home = tmpHome();
|
|
70
|
+
// Fire two writes in parallel. Each carries a different profile set
|
|
71
|
+
// so we can tell which one won the final rename — the important
|
|
72
|
+
// guarantee is that NEITHER throws (no temp-path collision) and
|
|
73
|
+
// the final on-disk state is exactly one of the two valid inputs
|
|
74
|
+
// (no half-written, no merged garbage).
|
|
75
|
+
const a = anthropic({ name: 'one' });
|
|
76
|
+
const b = openai({ name: 'two' });
|
|
77
|
+
await Promise.all([
|
|
78
|
+
Promise.resolve().then(() => writeProfiles([a], home)),
|
|
79
|
+
Promise.resolve().then(() => writeProfiles([b], home)),
|
|
80
|
+
]);
|
|
81
|
+
const final = readProfiles(home);
|
|
82
|
+
expect(final.length).toBe(1);
|
|
83
|
+
expect(['one', 'two']).toContain(final[0]?.name);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
describe('assertInvariants', () => {
|
|
87
|
+
it('passes for an empty store', () => {
|
|
88
|
+
expect(() => assertInvariants([])).not.toThrow();
|
|
89
|
+
});
|
|
90
|
+
it('passes for a single profile with default=true', () => {
|
|
91
|
+
expect(() => assertInvariants([anthropic({ default: true })])).not.toThrow();
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
describe('resolveApiKey', () => {
|
|
95
|
+
it('returns null when no key is anywhere', () => {
|
|
96
|
+
const p = { name: 'x', type: 'anthropic' };
|
|
97
|
+
expect(resolveApiKey(p, {})).toBeNull();
|
|
98
|
+
});
|
|
99
|
+
it('reads from profile.apiKey when no env override', () => {
|
|
100
|
+
expect(resolveApiKey(anthropic(), {})).toBe('sk-ant-deadbeef');
|
|
101
|
+
});
|
|
102
|
+
it('per-type env (ANTHROPIC_API_KEY) wins over file', () => {
|
|
103
|
+
expect(resolveApiKey(anthropic(), { ANTHROPIC_API_KEY: 'env-key' })).toBe('env-key');
|
|
104
|
+
});
|
|
105
|
+
it('per-profile env (NAMZU_<NAME>_API_KEY) wins over per-type', () => {
|
|
106
|
+
expect(resolveApiKey(anthropic({ name: 'work' }), {
|
|
107
|
+
ANTHROPIC_API_KEY: 'type-key',
|
|
108
|
+
NAMZU_WORK_API_KEY: 'profile-key',
|
|
109
|
+
})).toBe('profile-key');
|
|
110
|
+
});
|
|
111
|
+
it('normalizes profile names with non-alphanumerics in env lookup', () => {
|
|
112
|
+
expect(resolveApiKey(anthropic({ name: 'work-1' }), { NAMZU_WORK_1_API_KEY: 'normalized' })).toBe('normalized');
|
|
113
|
+
});
|
|
114
|
+
it('returns null for ollama (no api-key concept) when nothing is set', () => {
|
|
115
|
+
expect(resolveApiKey({ name: 'local', type: 'ollama' }, {})).toBeNull();
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
describe('findDefault', () => {
|
|
119
|
+
it('returns the profile with default=true', () => {
|
|
120
|
+
const list = [anthropic(), openai({ default: true })];
|
|
121
|
+
expect(findDefault(list)?.name).toBe('o');
|
|
122
|
+
});
|
|
123
|
+
it('returns null when nothing is marked default', () => {
|
|
124
|
+
expect(findDefault([anthropic(), openai()])).toBeNull();
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
//# sourceMappingURL=store.test.js.map
|