|
| 1 | +import type { Command } from '../commands.js' |
| 2 | +import type { LocalCommandCall } from '../types/command.js' |
| 3 | +import { getAPIProvider } from '../utils/model/providers.js' |
| 4 | +import { updateSettingsForSource } from '../utils/settings/settings.js' |
| 5 | +import { getSettings_DEPRECATED } from '../utils/settings/settings.js' |
| 6 | +import { applyConfigEnvironmentVariables } from '../utils/managedEnv.js' |
| 7 | + |
| 8 | +function getEnvVarForProvider(provider: string): string { |
| 9 | + switch (provider) { |
| 10 | + case 'bedrock': |
| 11 | + return 'CLAUDE_CODE_USE_BEDROCK' |
| 12 | + case 'vertex': |
| 13 | + return 'CLAUDE_CODE_USE_VERTEX' |
| 14 | + case 'foundry': |
| 15 | + return 'CLAUDE_CODE_USE_FOUNDRY' |
| 16 | + default: |
| 17 | + throw new Error(`Unknown provider: ${provider}`) |
| 18 | + } |
| 19 | +} |
| 20 | + |
| 21 | +// Get merged env: process.env + settings.env (from userSettings) |
| 22 | +function getMergedEnv(): Record<string, string> { |
| 23 | + const settings = getSettings_DEPRECATED() |
| 24 | + const merged = { ...process.env } |
| 25 | + if (settings?.env) { |
| 26 | + Object.assign(merged, settings.env) |
| 27 | + } |
| 28 | + return merged |
| 29 | +} |
| 30 | + |
| 31 | +const call: LocalCommandCall = async (args, context) => { |
| 32 | + const arg = args.trim().toLowerCase() |
| 33 | + |
| 34 | + // No argument: show current provider |
| 35 | + if (!arg) { |
| 36 | + const current = getAPIProvider() |
| 37 | + return { type: 'text', value: `Current API provider: ${current}` } |
| 38 | + } |
| 39 | + |
| 40 | + // unset - clear settings, fallback to env vars |
| 41 | + if (arg === 'unset') { |
| 42 | + updateSettingsForSource('userSettings', { modelType: undefined }) |
| 43 | + // Also clear all provider-specific env vars to prevent conflicts |
| 44 | + delete process.env.CLAUDE_CODE_USE_BEDROCK |
| 45 | + delete process.env.CLAUDE_CODE_USE_VERTEX |
| 46 | + delete process.env.CLAUDE_CODE_USE_FOUNDRY |
| 47 | + delete process.env.CLAUDE_CODE_USE_OPENAI |
| 48 | + return { type: 'text', value: 'API provider cleared (will use environment variables).' } |
| 49 | + } |
| 50 | + |
| 51 | + // Validate provider |
| 52 | + const validProviders = ['anthropic', 'openai', 'bedrock', 'vertex', 'foundry'] |
| 53 | + if (!validProviders.includes(arg)) { |
| 54 | + return { type: 'text', value: `Invalid provider: ${arg}\nValid: ${validProviders.join(', ')}` } |
| 55 | + } |
| 56 | + |
| 57 | + // Check env vars when switching to openai (including settings.env) |
| 58 | + if (arg === 'openai') { |
| 59 | + const mergedEnv = getMergedEnv() |
| 60 | + const hasKey = !!mergedEnv.OPENAI_API_KEY |
| 61 | + const hasUrl = !!mergedEnv.OPENAI_BASE_URL |
| 62 | + if (!hasKey || !hasUrl) { |
| 63 | + updateSettingsForSource('userSettings', { modelType: 'openai' }) |
| 64 | + const missing = [] |
| 65 | + if (!hasKey) missing.push('OPENAI_API_KEY') |
| 66 | + if (!hasUrl) missing.push('OPENAI_BASE_URL') |
| 67 | + return { |
| 68 | + type: 'text', |
| 69 | + value: `Switched to OpenAI provider.\nWarning: Missing env vars: ${missing.join(', ')}\nConfigure them via /login or set manually.`, |
| 70 | + } |
| 71 | + } |
| 72 | + } |
| 73 | + |
| 74 | + // Handle different provider types |
| 75 | + // - 'anthropic' and 'openai' are stored in settings.json (persistent) |
| 76 | + // - 'bedrock', 'vertex', 'foundry' are env-only (do NOT touch settings.json) |
| 77 | + if (arg === 'anthropic' || arg === 'openai') { |
| 78 | + // Clear any cloud provider env vars to avoid conflicts |
| 79 | + delete process.env.CLAUDE_CODE_USE_BEDROCK |
| 80 | + delete process.env.CLAUDE_CODE_USE_VERTEX |
| 81 | + delete process.env.CLAUDE_CODE_USE_FOUNDRY |
| 82 | + // Update settings.json |
| 83 | + updateSettingsForSource('userSettings', { modelType: arg }) |
| 84 | + // Ensure settings.env gets applied to process.env |
| 85 | + applyConfigEnvironmentVariables() |
| 86 | + return { type: 'text', value: `API provider set to ${arg}.` } |
| 87 | + } else { |
| 88 | + // Cloud providers: set env vars only, clear settings.modelType |
| 89 | + delete process.env.CLAUDE_CODE_USE_OPENAI |
| 90 | + delete process.env.OPENAI_API_KEY |
| 91 | + delete process.env.OPENAI_BASE_URL |
| 92 | + process.env[getEnvVarForProvider(arg)] = '1' |
| 93 | + // Clear settings.json to 'anthropic' to avoid validation errors |
| 94 | + updateSettingsForSource('userSettings', { modelType: 'anthropic' }) |
| 95 | + // Ensure settings.env gets applied to process.env (in case it contains provider-specific creds) |
| 96 | + applyConfigEnvironmentVariables() |
| 97 | + return { type: 'text', value: `API provider set to ${arg} (via environment variable).` } |
| 98 | + } |
| 99 | +} |
| 100 | + |
| 101 | +const provider = { |
| 102 | + type: 'local', |
| 103 | + name: 'provider', |
| 104 | + description: 'Switch API provider (anthropic/openai/bedrock/vertex/foundry)', |
| 105 | + aliases: ['api'], |
| 106 | + argumentHint: '[anthropic|openai|bedrock|vertex|foundry|unset]', |
| 107 | + supportsNonInteractive: true, |
| 108 | + load: () => Promise.resolve({ call }), |
| 109 | +} satisfies Command |
| 110 | + |
| 111 | +export default provider |
0 commit comments