diff --git a/src/commands/auth-choice.apply.anthropic.ts b/src/commands/auth-choice.apply.anthropic.ts index 64678e51c..c5700663c 100644 --- a/src/commands/auth-choice.apply.anthropic.ts +++ b/src/commands/auth-choice.apply.anthropic.ts @@ -198,10 +198,20 @@ export async function applyAuthChoiceAnthropic( } if (params.authChoice === "apiKey") { + if (params.opts?.tokenProvider && params.opts.tokenProvider !== "anthropic") { + return null; + } + let nextConfig = params.config; let hasCredential = false; const envKey = process.env.ANTHROPIC_API_KEY?.trim(); - if (envKey) { + + if (params.opts?.token) { + await setAnthropicApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir); + hasCredential = true; + } + + if (!hasCredential && envKey) { const useExisting = await params.prompter.confirm({ message: `Use existing ANTHROPIC_API_KEY (env, ${formatApiKeyPreview(envKey)})?`, initialValue: true, diff --git a/src/commands/auth-choice.apply.api-providers.ts b/src/commands/auth-choice.apply.api-providers.ts index 763628b49..cddb7f8e0 100644 --- a/src/commands/auth-choice.apply.api-providers.ts +++ b/src/commands/auth-choice.apply.api-providers.ts @@ -56,7 +56,33 @@ export async function applyAuthChoiceApiProviders( ); }; - if (params.authChoice === "openrouter-api-key") { + let authChoice = params.authChoice; + if ( + authChoice === "apiKey" && + params.opts?.tokenProvider && + params.opts.tokenProvider !== "anthropic" && + params.opts.tokenProvider !== "openai" + ) { + if (params.opts.tokenProvider === "openrouter") { + authChoice = "openrouter-api-key"; + } else if (params.opts.tokenProvider === "vercel-ai-gateway") { + authChoice = "ai-gateway-api-key"; + } else if (params.opts.tokenProvider === "moonshot") { + authChoice = "moonshot-api-key"; + } else if (params.opts.tokenProvider === "kimi-code") { + authChoice = "kimi-code-api-key"; + } else if (params.opts.tokenProvider === "google") { + authChoice = "gemini-api-key"; + } else if (params.opts.tokenProvider === "zai") { + authChoice = "zai-api-key"; + } else if (params.opts.tokenProvider === "synthetic") { + authChoice = "synthetic-api-key"; + } else if (params.opts.tokenProvider === "opencode") { + authChoice = "opencode-zen"; + } + } + + if (authChoice === "openrouter-api-key") { const store = ensureAuthProfileStore(params.agentDir, { allowKeychainPrompt: false, }); @@ -82,6 +108,11 @@ export async function applyAuthChoiceApiProviders( hasCredential = true; } + if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "openrouter") { + await setOpenrouterApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir); + hasCredential = true; + } + if (!hasCredential) { const envKey = resolveEnvApiKey("openrouter"); if (envKey) { @@ -129,8 +160,18 @@ export async function applyAuthChoiceApiProviders( return { config: nextConfig, agentModelOverride }; } - if (params.authChoice === "ai-gateway-api-key") { + if (authChoice === "ai-gateway-api-key") { let hasCredential = false; + + if ( + !hasCredential && + params.opts?.token && + params.opts?.tokenProvider === "vercel-ai-gateway" + ) { + await setVercelAiGatewayApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir); + hasCredential = true; + } + const envKey = resolveEnvApiKey("vercel-ai-gateway"); if (envKey) { const useExisting = await params.prompter.confirm({ @@ -171,8 +212,14 @@ export async function applyAuthChoiceApiProviders( return { config: nextConfig, agentModelOverride }; } - if (params.authChoice === "moonshot-api-key") { + if (authChoice === "moonshot-api-key") { let hasCredential = false; + + if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "moonshot") { + await setMoonshotApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir); + hasCredential = true; + } + const envKey = resolveEnvApiKey("moonshot"); if (envKey) { const useExisting = await params.prompter.confirm({ @@ -212,15 +259,22 @@ export async function applyAuthChoiceApiProviders( return { config: nextConfig, agentModelOverride }; } - if (params.authChoice === "kimi-code-api-key") { - await params.prompter.note( - [ - "Kimi Code uses a dedicated endpoint and API key.", - "Get your API key at: https://www.kimi.com/code/en", - ].join("\n"), - "Kimi Code", - ); + if (authChoice === "kimi-code-api-key") { let hasCredential = false; + if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "kimi-code") { + await setKimiCodeApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir); + hasCredential = true; + } + + if (!hasCredential) { + await params.prompter.note( + [ + "Kimi Code uses a dedicated endpoint and API key.", + "Get your API key at: https://www.kimi.com/code/en", + ].join("\n"), + "Kimi Code", + ); + } const envKey = resolveEnvApiKey("kimi-code"); if (envKey) { const useExisting = await params.prompter.confirm({ @@ -261,8 +315,14 @@ export async function applyAuthChoiceApiProviders( return { config: nextConfig, agentModelOverride }; } - if (params.authChoice === "gemini-api-key") { + if (authChoice === "gemini-api-key") { let hasCredential = false; + + if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "google") { + await setGeminiApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir); + hasCredential = true; + } + const envKey = resolveEnvApiKey("google"); if (envKey) { const useExisting = await params.prompter.confirm({ @@ -302,8 +362,14 @@ export async function applyAuthChoiceApiProviders( return { config: nextConfig, agentModelOverride }; } - if (params.authChoice === "zai-api-key") { + if (authChoice === "zai-api-key") { let hasCredential = false; + + if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "zai") { + await setZaiApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir); + hasCredential = true; + } + const envKey = resolveEnvApiKey("zai"); if (envKey) { const useExisting = await params.prompter.confirm({ @@ -359,12 +425,16 @@ export async function applyAuthChoiceApiProviders( return { config: nextConfig, agentModelOverride }; } - if (params.authChoice === "synthetic-api-key") { - const key = await params.prompter.text({ - message: "Enter Synthetic API key", - validate: (value) => (value?.trim() ? undefined : "Required"), - }); - await setSyntheticApiKey(String(key).trim(), params.agentDir); + if (authChoice === "synthetic-api-key") { + if (params.opts?.token && params.opts?.tokenProvider === "synthetic") { + await setSyntheticApiKey(String(params.opts.token).trim(), params.agentDir); + } else { + const key = await params.prompter.text({ + message: "Enter Synthetic API key", + validate: (value) => (value?.trim() ? undefined : "Required"), + }); + await setSyntheticApiKey(String(key).trim(), params.agentDir); + } nextConfig = applyAuthProfileConfig(nextConfig, { profileId: "synthetic:default", provider: "synthetic", @@ -387,16 +457,23 @@ export async function applyAuthChoiceApiProviders( return { config: nextConfig, agentModelOverride }; } - if (params.authChoice === "opencode-zen") { - await params.prompter.note( - [ - "OpenCode Zen provides access to Claude, GPT, Gemini, and more models.", - "Get your API key at: https://opencode.ai/auth", - "Requires an active OpenCode Zen subscription.", - ].join("\n"), - "OpenCode Zen", - ); + if (authChoice === "opencode-zen") { let hasCredential = false; + if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "opencode") { + await setOpencodeZenApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir); + hasCredential = true; + } + + if (!hasCredential) { + await params.prompter.note( + [ + "OpenCode Zen provides access to Claude, GPT, Gemini, and more models.", + "Get your API key at: https://opencode.ai/auth", + "Requires an active OpenCode Zen subscription.", + ].join("\n"), + "OpenCode Zen", + ); + } const envKey = resolveEnvApiKey("opencode"); if (envKey) { const useExisting = await params.prompter.confirm({ diff --git a/src/commands/auth-choice.apply.openai.ts b/src/commands/auth-choice.apply.openai.ts index 4be7762bd..7d96a35a1 100644 --- a/src/commands/auth-choice.apply.openai.ts +++ b/src/commands/auth-choice.apply.openai.ts @@ -20,7 +20,12 @@ import { export async function applyAuthChoiceOpenAI( params: ApplyAuthChoiceParams, ): Promise { - if (params.authChoice === "openai-api-key") { + let authChoice = params.authChoice; + if (authChoice === "apiKey" && params.opts?.tokenProvider === "openai") { + authChoice = "openai-api-key"; + } + + if (authChoice === "openai-api-key") { const envKey = resolveEnvApiKey("openai"); if (envKey) { const useExisting = await params.prompter.confirm({ @@ -43,10 +48,16 @@ export async function applyAuthChoiceOpenAI( } } - const key = await params.prompter.text({ - message: "Enter OpenAI API key", - validate: validateApiKeyInput, - }); + let key: string | undefined; + if (params.opts?.token && params.opts?.tokenProvider === "openai") { + key = params.opts.token; + } else { + key = await params.prompter.text({ + message: "Enter OpenAI API key", + validate: validateApiKeyInput, + }); + } + const trimmed = normalizeApiKeyInput(String(key)); const result = upsertSharedEnvVar({ key: "OPENAI_API_KEY", diff --git a/src/commands/auth-choice.apply.ts b/src/commands/auth-choice.apply.ts index 5ea040d5f..89ff3f380 100644 --- a/src/commands/auth-choice.apply.ts +++ b/src/commands/auth-choice.apply.ts @@ -21,6 +21,10 @@ export type ApplyAuthChoiceParams = { agentDir?: string; setDefaultModel: boolean; agentId?: string; + opts?: { + tokenProvider?: string; + token?: string; + }; }; export type ApplyAuthChoiceResult = { diff --git a/src/wizard/onboarding.ts b/src/wizard/onboarding.ts index 26ce67ca1..5c5590bf2 100644 --- a/src/wizard/onboarding.ts +++ b/src/wizard/onboarding.ts @@ -356,6 +356,10 @@ export async function runOnboardingWizard( prompter, runtime, setDefaultModel: true, + opts: { + tokenProvider: opts.tokenProvider, + token: opts.authChoice === "apiKey" && opts.token ? opts.token : undefined, + }, }); nextConfig = authResult.config;