Spaces:
Paused
Paused
| import type { ApplyAuthChoiceParams, ApplyAuthChoiceResult } from "./auth-choice.apply.js"; | |
| import { ensureAuthProfileStore, resolveAuthProfileOrder } from "../agents/auth-profiles.js"; | |
| import { resolveEnvApiKey } from "../agents/model-auth.js"; | |
| import { | |
| formatApiKeyPreview, | |
| normalizeApiKeyInput, | |
| validateApiKeyInput, | |
| } from "./auth-choice.api-key.js"; | |
| import { applyAuthChoiceHuggingface } from "./auth-choice.apply.huggingface.js"; | |
| import { applyAuthChoiceOpenRouter } from "./auth-choice.apply.openrouter.js"; | |
| import { applyDefaultModelChoice } from "./auth-choice.default-model.js"; | |
| import { | |
| applyGoogleGeminiModelDefault, | |
| GOOGLE_GEMINI_DEFAULT_MODEL, | |
| } from "./google-gemini-model-default.js"; | |
| import { | |
| applyAuthProfileConfig, | |
| applyCloudflareAiGatewayConfig, | |
| applyCloudflareAiGatewayProviderConfig, | |
| applyQianfanConfig, | |
| applyQianfanProviderConfig, | |
| applyKimiCodeConfig, | |
| applyKimiCodeProviderConfig, | |
| applyLitellmConfig, | |
| applyLitellmProviderConfig, | |
| applyMoonshotConfig, | |
| applyMoonshotConfigCn, | |
| applyMoonshotProviderConfig, | |
| applyMoonshotProviderConfigCn, | |
| applyOpencodeZenConfig, | |
| applyOpencodeZenProviderConfig, | |
| applySyntheticConfig, | |
| applySyntheticProviderConfig, | |
| applyTogetherConfig, | |
| applyTogetherProviderConfig, | |
| applyVeniceConfig, | |
| applyVeniceProviderConfig, | |
| applyVercelAiGatewayConfig, | |
| applyVercelAiGatewayProviderConfig, | |
| applyXiaomiConfig, | |
| applyXiaomiProviderConfig, | |
| applyZaiConfig, | |
| applyZaiProviderConfig, | |
| CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_REF, | |
| LITELLM_DEFAULT_MODEL_REF, | |
| QIANFAN_DEFAULT_MODEL_REF, | |
| KIMI_CODING_MODEL_REF, | |
| MOONSHOT_DEFAULT_MODEL_REF, | |
| SYNTHETIC_DEFAULT_MODEL_REF, | |
| TOGETHER_DEFAULT_MODEL_REF, | |
| VENICE_DEFAULT_MODEL_REF, | |
| VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF, | |
| XIAOMI_DEFAULT_MODEL_REF, | |
| setCloudflareAiGatewayConfig, | |
| setQianfanApiKey, | |
| setGeminiApiKey, | |
| setLitellmApiKey, | |
| setKimiCodingApiKey, | |
| setMoonshotApiKey, | |
| setOpencodeZenApiKey, | |
| setSyntheticApiKey, | |
| setTogetherApiKey, | |
| setVeniceApiKey, | |
| setVercelAiGatewayApiKey, | |
| setXiaomiApiKey, | |
| setZaiApiKey, | |
| ZAI_DEFAULT_MODEL_REF, | |
| } from "./onboard-auth.js"; | |
| import { OPENCODE_ZEN_DEFAULT_MODEL } from "./opencode-zen-model-default.js"; | |
| import { detectZaiEndpoint } from "./zai-endpoint-detect.js"; | |
| export async function applyAuthChoiceApiProviders( | |
| params: ApplyAuthChoiceParams, | |
| ): Promise<ApplyAuthChoiceResult | null> { | |
| let nextConfig = params.config; | |
| let agentModelOverride: string | undefined; | |
| const noteAgentModel = async (model: string) => { | |
| if (!params.agentId) { | |
| return; | |
| } | |
| await params.prompter.note( | |
| `Default model set to ${model} for agent "${params.agentId}".`, | |
| "Model configured", | |
| ); | |
| }; | |
| 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 === "litellm") { | |
| authChoice = "litellm-api-key"; | |
| } else if (params.opts.tokenProvider === "vercel-ai-gateway") { | |
| authChoice = "ai-gateway-api-key"; | |
| } else if (params.opts.tokenProvider === "cloudflare-ai-gateway") { | |
| authChoice = "cloudflare-ai-gateway-api-key"; | |
| } else if (params.opts.tokenProvider === "moonshot") { | |
| authChoice = "moonshot-api-key"; | |
| } else if ( | |
| params.opts.tokenProvider === "kimi-code" || | |
| params.opts.tokenProvider === "kimi-coding" | |
| ) { | |
| 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 === "xiaomi") { | |
| authChoice = "xiaomi-api-key"; | |
| } else if (params.opts.tokenProvider === "synthetic") { | |
| authChoice = "synthetic-api-key"; | |
| } else if (params.opts.tokenProvider === "venice") { | |
| authChoice = "venice-api-key"; | |
| } else if (params.opts.tokenProvider === "together") { | |
| authChoice = "together-api-key"; | |
| } else if (params.opts.tokenProvider === "huggingface") { | |
| authChoice = "huggingface-api-key"; | |
| } else if (params.opts.tokenProvider === "opencode") { | |
| authChoice = "opencode-zen"; | |
| } else if (params.opts.tokenProvider === "qianfan") { | |
| authChoice = "qianfan-api-key"; | |
| } | |
| } | |
| if (authChoice === "openrouter-api-key") { | |
| return applyAuthChoiceOpenRouter(params); | |
| } | |
| if (authChoice === "litellm-api-key") { | |
| const store = ensureAuthProfileStore(params.agentDir, { allowKeychainPrompt: false }); | |
| const profileOrder = resolveAuthProfileOrder({ cfg: nextConfig, store, provider: "litellm" }); | |
| const existingProfileId = profileOrder.find((profileId) => Boolean(store.profiles[profileId])); | |
| const existingCred = existingProfileId ? store.profiles[existingProfileId] : undefined; | |
| let profileId = "litellm:default"; | |
| let hasCredential = false; | |
| if (existingProfileId && existingCred?.type === "api_key") { | |
| profileId = existingProfileId; | |
| hasCredential = true; | |
| } | |
| if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "litellm") { | |
| await setLitellmApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir); | |
| hasCredential = true; | |
| } | |
| if (!hasCredential) { | |
| await params.prompter.note( | |
| "LiteLLM provides a unified API to 100+ LLM providers.\nGet your API key from your LiteLLM proxy or https://litellm.ai\nDefault proxy runs on http://localhost:4000", | |
| "LiteLLM", | |
| ); | |
| const envKey = resolveEnvApiKey("litellm"); | |
| if (envKey) { | |
| const useExisting = await params.prompter.confirm({ | |
| message: `Use existing LITELLM_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`, | |
| initialValue: true, | |
| }); | |
| if (useExisting) { | |
| await setLitellmApiKey(envKey.apiKey, params.agentDir); | |
| hasCredential = true; | |
| } | |
| } | |
| if (!hasCredential) { | |
| const key = await params.prompter.text({ | |
| message: "Enter LiteLLM API key", | |
| validate: validateApiKeyInput, | |
| }); | |
| await setLitellmApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir); | |
| hasCredential = true; | |
| } | |
| } | |
| if (hasCredential) { | |
| nextConfig = applyAuthProfileConfig(nextConfig, { | |
| profileId, | |
| provider: "litellm", | |
| mode: "api_key", | |
| }); | |
| } | |
| const applied = await applyDefaultModelChoice({ | |
| config: nextConfig, | |
| setDefaultModel: params.setDefaultModel, | |
| defaultModel: LITELLM_DEFAULT_MODEL_REF, | |
| applyDefaultConfig: applyLitellmConfig, | |
| applyProviderConfig: applyLitellmProviderConfig, | |
| noteDefault: LITELLM_DEFAULT_MODEL_REF, | |
| noteAgentModel, | |
| prompter: params.prompter, | |
| }); | |
| nextConfig = applied.config; | |
| agentModelOverride = applied.agentModelOverride ?? agentModelOverride; | |
| return { config: nextConfig, agentModelOverride }; | |
| } | |
| 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({ | |
| message: `Use existing AI_GATEWAY_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`, | |
| initialValue: true, | |
| }); | |
| if (useExisting) { | |
| await setVercelAiGatewayApiKey(envKey.apiKey, params.agentDir); | |
| hasCredential = true; | |
| } | |
| } | |
| if (!hasCredential) { | |
| const key = await params.prompter.text({ | |
| message: "Enter Vercel AI Gateway API key", | |
| validate: validateApiKeyInput, | |
| }); | |
| await setVercelAiGatewayApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir); | |
| } | |
| nextConfig = applyAuthProfileConfig(nextConfig, { | |
| profileId: "vercel-ai-gateway:default", | |
| provider: "vercel-ai-gateway", | |
| mode: "api_key", | |
| }); | |
| { | |
| const applied = await applyDefaultModelChoice({ | |
| config: nextConfig, | |
| setDefaultModel: params.setDefaultModel, | |
| defaultModel: VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF, | |
| applyDefaultConfig: applyVercelAiGatewayConfig, | |
| applyProviderConfig: applyVercelAiGatewayProviderConfig, | |
| noteDefault: VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF, | |
| noteAgentModel, | |
| prompter: params.prompter, | |
| }); | |
| nextConfig = applied.config; | |
| agentModelOverride = applied.agentModelOverride ?? agentModelOverride; | |
| } | |
| return { config: nextConfig, agentModelOverride }; | |
| } | |
| if (authChoice === "cloudflare-ai-gateway-api-key") { | |
| let hasCredential = false; | |
| let accountId = params.opts?.cloudflareAiGatewayAccountId?.trim() ?? ""; | |
| let gatewayId = params.opts?.cloudflareAiGatewayGatewayId?.trim() ?? ""; | |
| const ensureAccountGateway = async () => { | |
| if (!accountId) { | |
| const value = await params.prompter.text({ | |
| message: "Enter Cloudflare Account ID", | |
| validate: (val) => (String(val ?? "").trim() ? undefined : "Account ID is required"), | |
| }); | |
| accountId = String(value ?? "").trim(); | |
| } | |
| if (!gatewayId) { | |
| const value = await params.prompter.text({ | |
| message: "Enter Cloudflare AI Gateway ID", | |
| validate: (val) => (String(val ?? "").trim() ? undefined : "Gateway ID is required"), | |
| }); | |
| gatewayId = String(value ?? "").trim(); | |
| } | |
| }; | |
| const optsApiKey = normalizeApiKeyInput(params.opts?.cloudflareAiGatewayApiKey ?? ""); | |
| if (!hasCredential && accountId && gatewayId && optsApiKey) { | |
| await setCloudflareAiGatewayConfig(accountId, gatewayId, optsApiKey, params.agentDir); | |
| hasCredential = true; | |
| } | |
| const envKey = resolveEnvApiKey("cloudflare-ai-gateway"); | |
| if (!hasCredential && envKey) { | |
| const useExisting = await params.prompter.confirm({ | |
| message: `Use existing CLOUDFLARE_AI_GATEWAY_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`, | |
| initialValue: true, | |
| }); | |
| if (useExisting) { | |
| await ensureAccountGateway(); | |
| await setCloudflareAiGatewayConfig( | |
| accountId, | |
| gatewayId, | |
| normalizeApiKeyInput(envKey.apiKey), | |
| params.agentDir, | |
| ); | |
| hasCredential = true; | |
| } | |
| } | |
| if (!hasCredential && optsApiKey) { | |
| await ensureAccountGateway(); | |
| await setCloudflareAiGatewayConfig(accountId, gatewayId, optsApiKey, params.agentDir); | |
| hasCredential = true; | |
| } | |
| if (!hasCredential) { | |
| await ensureAccountGateway(); | |
| const key = await params.prompter.text({ | |
| message: "Enter Cloudflare AI Gateway API key", | |
| validate: validateApiKeyInput, | |
| }); | |
| await setCloudflareAiGatewayConfig( | |
| accountId, | |
| gatewayId, | |
| normalizeApiKeyInput(String(key ?? "")), | |
| params.agentDir, | |
| ); | |
| hasCredential = true; | |
| } | |
| if (hasCredential) { | |
| nextConfig = applyAuthProfileConfig(nextConfig, { | |
| profileId: "cloudflare-ai-gateway:default", | |
| provider: "cloudflare-ai-gateway", | |
| mode: "api_key", | |
| }); | |
| } | |
| { | |
| const applied = await applyDefaultModelChoice({ | |
| config: nextConfig, | |
| setDefaultModel: params.setDefaultModel, | |
| defaultModel: CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_REF, | |
| applyDefaultConfig: (cfg) => | |
| applyCloudflareAiGatewayConfig(cfg, { | |
| accountId: accountId || params.opts?.cloudflareAiGatewayAccountId, | |
| gatewayId: gatewayId || params.opts?.cloudflareAiGatewayGatewayId, | |
| }), | |
| applyProviderConfig: (cfg) => | |
| applyCloudflareAiGatewayProviderConfig(cfg, { | |
| accountId: accountId || params.opts?.cloudflareAiGatewayAccountId, | |
| gatewayId: gatewayId || params.opts?.cloudflareAiGatewayGatewayId, | |
| }), | |
| noteDefault: CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_REF, | |
| noteAgentModel, | |
| prompter: params.prompter, | |
| }); | |
| nextConfig = applied.config; | |
| agentModelOverride = applied.agentModelOverride ?? agentModelOverride; | |
| } | |
| return { config: nextConfig, agentModelOverride }; | |
| } | |
| 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({ | |
| message: `Use existing MOONSHOT_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`, | |
| initialValue: true, | |
| }); | |
| if (useExisting) { | |
| await setMoonshotApiKey(envKey.apiKey, params.agentDir); | |
| hasCredential = true; | |
| } | |
| } | |
| if (!hasCredential) { | |
| const key = await params.prompter.text({ | |
| message: "Enter Moonshot API key", | |
| validate: validateApiKeyInput, | |
| }); | |
| await setMoonshotApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir); | |
| } | |
| nextConfig = applyAuthProfileConfig(nextConfig, { | |
| profileId: "moonshot:default", | |
| provider: "moonshot", | |
| mode: "api_key", | |
| }); | |
| { | |
| const applied = await applyDefaultModelChoice({ | |
| config: nextConfig, | |
| setDefaultModel: params.setDefaultModel, | |
| defaultModel: MOONSHOT_DEFAULT_MODEL_REF, | |
| applyDefaultConfig: applyMoonshotConfig, | |
| applyProviderConfig: applyMoonshotProviderConfig, | |
| noteAgentModel, | |
| prompter: params.prompter, | |
| }); | |
| nextConfig = applied.config; | |
| agentModelOverride = applied.agentModelOverride ?? agentModelOverride; | |
| } | |
| return { config: nextConfig, agentModelOverride }; | |
| } | |
| if (authChoice === "moonshot-api-key-cn") { | |
| 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({ | |
| message: `Use existing MOONSHOT_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`, | |
| initialValue: true, | |
| }); | |
| if (useExisting) { | |
| await setMoonshotApiKey(envKey.apiKey, params.agentDir); | |
| hasCredential = true; | |
| } | |
| } | |
| if (!hasCredential) { | |
| const key = await params.prompter.text({ | |
| message: "Enter Moonshot API key (.cn)", | |
| validate: validateApiKeyInput, | |
| }); | |
| await setMoonshotApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir); | |
| } | |
| nextConfig = applyAuthProfileConfig(nextConfig, { | |
| profileId: "moonshot:default", | |
| provider: "moonshot", | |
| mode: "api_key", | |
| }); | |
| { | |
| const applied = await applyDefaultModelChoice({ | |
| config: nextConfig, | |
| setDefaultModel: params.setDefaultModel, | |
| defaultModel: MOONSHOT_DEFAULT_MODEL_REF, | |
| applyDefaultConfig: applyMoonshotConfigCn, | |
| applyProviderConfig: applyMoonshotProviderConfigCn, | |
| noteAgentModel, | |
| prompter: params.prompter, | |
| }); | |
| nextConfig = applied.config; | |
| agentModelOverride = applied.agentModelOverride ?? agentModelOverride; | |
| } | |
| return { config: nextConfig, agentModelOverride }; | |
| } | |
| if (authChoice === "kimi-code-api-key") { | |
| let hasCredential = false; | |
| const tokenProvider = params.opts?.tokenProvider?.trim().toLowerCase(); | |
| if ( | |
| !hasCredential && | |
| params.opts?.token && | |
| (tokenProvider === "kimi-code" || tokenProvider === "kimi-coding") | |
| ) { | |
| await setKimiCodingApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir); | |
| hasCredential = true; | |
| } | |
| if (!hasCredential) { | |
| await params.prompter.note( | |
| [ | |
| "Kimi Coding uses a dedicated endpoint and API key.", | |
| "Get your API key at: https://www.kimi.com/code/en", | |
| ].join("\n"), | |
| "Kimi Coding", | |
| ); | |
| } | |
| const envKey = resolveEnvApiKey("kimi-coding"); | |
| if (envKey) { | |
| const useExisting = await params.prompter.confirm({ | |
| message: `Use existing KIMI_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`, | |
| initialValue: true, | |
| }); | |
| if (useExisting) { | |
| await setKimiCodingApiKey(envKey.apiKey, params.agentDir); | |
| hasCredential = true; | |
| } | |
| } | |
| if (!hasCredential) { | |
| const key = await params.prompter.text({ | |
| message: "Enter Kimi Coding API key", | |
| validate: validateApiKeyInput, | |
| }); | |
| await setKimiCodingApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir); | |
| } | |
| nextConfig = applyAuthProfileConfig(nextConfig, { | |
| profileId: "kimi-coding:default", | |
| provider: "kimi-coding", | |
| mode: "api_key", | |
| }); | |
| { | |
| const applied = await applyDefaultModelChoice({ | |
| config: nextConfig, | |
| setDefaultModel: params.setDefaultModel, | |
| defaultModel: KIMI_CODING_MODEL_REF, | |
| applyDefaultConfig: applyKimiCodeConfig, | |
| applyProviderConfig: applyKimiCodeProviderConfig, | |
| noteDefault: KIMI_CODING_MODEL_REF, | |
| noteAgentModel, | |
| prompter: params.prompter, | |
| }); | |
| nextConfig = applied.config; | |
| agentModelOverride = applied.agentModelOverride ?? agentModelOverride; | |
| } | |
| return { config: nextConfig, agentModelOverride }; | |
| } | |
| 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({ | |
| message: `Use existing GEMINI_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`, | |
| initialValue: true, | |
| }); | |
| if (useExisting) { | |
| await setGeminiApiKey(envKey.apiKey, params.agentDir); | |
| hasCredential = true; | |
| } | |
| } | |
| if (!hasCredential) { | |
| const key = await params.prompter.text({ | |
| message: "Enter Gemini API key", | |
| validate: validateApiKeyInput, | |
| }); | |
| await setGeminiApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir); | |
| } | |
| nextConfig = applyAuthProfileConfig(nextConfig, { | |
| profileId: "google:default", | |
| provider: "google", | |
| mode: "api_key", | |
| }); | |
| if (params.setDefaultModel) { | |
| const applied = applyGoogleGeminiModelDefault(nextConfig); | |
| nextConfig = applied.next; | |
| if (applied.changed) { | |
| await params.prompter.note( | |
| `Default model set to ${GOOGLE_GEMINI_DEFAULT_MODEL}`, | |
| "Model configured", | |
| ); | |
| } | |
| } else { | |
| agentModelOverride = GOOGLE_GEMINI_DEFAULT_MODEL; | |
| await noteAgentModel(GOOGLE_GEMINI_DEFAULT_MODEL); | |
| } | |
| return { config: nextConfig, agentModelOverride }; | |
| } | |
| if ( | |
| authChoice === "zai-api-key" || | |
| authChoice === "zai-coding-global" || | |
| authChoice === "zai-coding-cn" || | |
| authChoice === "zai-global" || | |
| authChoice === "zai-cn" | |
| ) { | |
| let endpoint: "global" | "cn" | "coding-global" | "coding-cn" | undefined; | |
| if (authChoice === "zai-coding-global") { | |
| endpoint = "coding-global"; | |
| } else if (authChoice === "zai-coding-cn") { | |
| endpoint = "coding-cn"; | |
| } else if (authChoice === "zai-global") { | |
| endpoint = "global"; | |
| } else if (authChoice === "zai-cn") { | |
| endpoint = "cn"; | |
| } | |
| // Input API key | |
| let hasCredential = false; | |
| let apiKey = ""; | |
| if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "zai") { | |
| apiKey = normalizeApiKeyInput(params.opts.token); | |
| await setZaiApiKey(apiKey, params.agentDir); | |
| hasCredential = true; | |
| } | |
| const envKey = resolveEnvApiKey("zai"); | |
| if (envKey) { | |
| const useExisting = await params.prompter.confirm({ | |
| message: `Use existing ZAI_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`, | |
| initialValue: true, | |
| }); | |
| if (useExisting) { | |
| apiKey = envKey.apiKey; | |
| await setZaiApiKey(apiKey, params.agentDir); | |
| hasCredential = true; | |
| } | |
| } | |
| if (!hasCredential) { | |
| const key = await params.prompter.text({ | |
| message: "Enter Z.AI API key", | |
| validate: validateApiKeyInput, | |
| }); | |
| apiKey = normalizeApiKeyInput(String(key ?? "")); | |
| await setZaiApiKey(apiKey, params.agentDir); | |
| } | |
| // zai-api-key: auto-detect endpoint + choose a working default model. | |
| let modelIdOverride: string | undefined; | |
| if (!endpoint) { | |
| const detected = await detectZaiEndpoint({ apiKey }); | |
| if (detected) { | |
| endpoint = detected.endpoint; | |
| modelIdOverride = detected.modelId; | |
| await params.prompter.note(detected.note, "Z.AI endpoint"); | |
| } else { | |
| endpoint = await params.prompter.select({ | |
| message: "Select Z.AI endpoint", | |
| options: [ | |
| { | |
| value: "coding-global", | |
| label: "Coding-Plan-Global", | |
| hint: "GLM Coding Plan Global (api.z.ai)", | |
| }, | |
| { | |
| value: "coding-cn", | |
| label: "Coding-Plan-CN", | |
| hint: "GLM Coding Plan CN (open.bigmodel.cn)", | |
| }, | |
| { | |
| value: "global", | |
| label: "Global", | |
| hint: "Z.AI Global (api.z.ai)", | |
| }, | |
| { | |
| value: "cn", | |
| label: "CN", | |
| hint: "Z.AI CN (open.bigmodel.cn)", | |
| }, | |
| ], | |
| initialValue: "global", | |
| }); | |
| } | |
| } | |
| nextConfig = applyAuthProfileConfig(nextConfig, { | |
| profileId: "zai:default", | |
| provider: "zai", | |
| mode: "api_key", | |
| }); | |
| const defaultModel = modelIdOverride ? `zai/${modelIdOverride}` : ZAI_DEFAULT_MODEL_REF; | |
| const applied = await applyDefaultModelChoice({ | |
| config: nextConfig, | |
| setDefaultModel: params.setDefaultModel, | |
| defaultModel, | |
| applyDefaultConfig: (config) => | |
| applyZaiConfig(config, { | |
| endpoint, | |
| ...(modelIdOverride ? { modelId: modelIdOverride } : {}), | |
| }), | |
| applyProviderConfig: (config) => | |
| applyZaiProviderConfig(config, { | |
| endpoint, | |
| ...(modelIdOverride ? { modelId: modelIdOverride } : {}), | |
| }), | |
| noteDefault: defaultModel, | |
| noteAgentModel, | |
| prompter: params.prompter, | |
| }); | |
| nextConfig = applied.config; | |
| agentModelOverride = applied.agentModelOverride ?? agentModelOverride; | |
| return { config: nextConfig, agentModelOverride }; | |
| } | |
| if (authChoice === "xiaomi-api-key") { | |
| let hasCredential = false; | |
| if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "xiaomi") { | |
| await setXiaomiApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir); | |
| hasCredential = true; | |
| } | |
| const envKey = resolveEnvApiKey("xiaomi"); | |
| if (envKey) { | |
| const useExisting = await params.prompter.confirm({ | |
| message: `Use existing XIAOMI_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`, | |
| initialValue: true, | |
| }); | |
| if (useExisting) { | |
| await setXiaomiApiKey(envKey.apiKey, params.agentDir); | |
| hasCredential = true; | |
| } | |
| } | |
| if (!hasCredential) { | |
| const key = await params.prompter.text({ | |
| message: "Enter Xiaomi API key", | |
| validate: validateApiKeyInput, | |
| }); | |
| await setXiaomiApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir); | |
| } | |
| nextConfig = applyAuthProfileConfig(nextConfig, { | |
| profileId: "xiaomi:default", | |
| provider: "xiaomi", | |
| mode: "api_key", | |
| }); | |
| { | |
| const applied = await applyDefaultModelChoice({ | |
| config: nextConfig, | |
| setDefaultModel: params.setDefaultModel, | |
| defaultModel: XIAOMI_DEFAULT_MODEL_REF, | |
| applyDefaultConfig: applyXiaomiConfig, | |
| applyProviderConfig: applyXiaomiProviderConfig, | |
| noteDefault: XIAOMI_DEFAULT_MODEL_REF, | |
| noteAgentModel, | |
| prompter: params.prompter, | |
| }); | |
| nextConfig = applied.config; | |
| agentModelOverride = applied.agentModelOverride ?? agentModelOverride; | |
| } | |
| return { config: nextConfig, agentModelOverride }; | |
| } | |
| 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", | |
| mode: "api_key", | |
| }); | |
| { | |
| const applied = await applyDefaultModelChoice({ | |
| config: nextConfig, | |
| setDefaultModel: params.setDefaultModel, | |
| defaultModel: SYNTHETIC_DEFAULT_MODEL_REF, | |
| applyDefaultConfig: applySyntheticConfig, | |
| applyProviderConfig: applySyntheticProviderConfig, | |
| noteDefault: SYNTHETIC_DEFAULT_MODEL_REF, | |
| noteAgentModel, | |
| prompter: params.prompter, | |
| }); | |
| nextConfig = applied.config; | |
| agentModelOverride = applied.agentModelOverride ?? agentModelOverride; | |
| } | |
| return { config: nextConfig, agentModelOverride }; | |
| } | |
| if (authChoice === "venice-api-key") { | |
| let hasCredential = false; | |
| if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "venice") { | |
| await setVeniceApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir); | |
| hasCredential = true; | |
| } | |
| if (!hasCredential) { | |
| await params.prompter.note( | |
| [ | |
| "Venice AI provides privacy-focused inference with uncensored models.", | |
| "Get your API key at: https://venice.ai/settings/api", | |
| "Supports 'private' (fully private) and 'anonymized' (proxy) modes.", | |
| ].join("\n"), | |
| "Venice AI", | |
| ); | |
| } | |
| const envKey = resolveEnvApiKey("venice"); | |
| if (envKey) { | |
| const useExisting = await params.prompter.confirm({ | |
| message: `Use existing VENICE_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`, | |
| initialValue: true, | |
| }); | |
| if (useExisting) { | |
| await setVeniceApiKey(envKey.apiKey, params.agentDir); | |
| hasCredential = true; | |
| } | |
| } | |
| if (!hasCredential) { | |
| const key = await params.prompter.text({ | |
| message: "Enter Venice AI API key", | |
| validate: validateApiKeyInput, | |
| }); | |
| await setVeniceApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir); | |
| } | |
| nextConfig = applyAuthProfileConfig(nextConfig, { | |
| profileId: "venice:default", | |
| provider: "venice", | |
| mode: "api_key", | |
| }); | |
| { | |
| const applied = await applyDefaultModelChoice({ | |
| config: nextConfig, | |
| setDefaultModel: params.setDefaultModel, | |
| defaultModel: VENICE_DEFAULT_MODEL_REF, | |
| applyDefaultConfig: applyVeniceConfig, | |
| applyProviderConfig: applyVeniceProviderConfig, | |
| noteDefault: VENICE_DEFAULT_MODEL_REF, | |
| noteAgentModel, | |
| prompter: params.prompter, | |
| }); | |
| nextConfig = applied.config; | |
| agentModelOverride = applied.agentModelOverride ?? agentModelOverride; | |
| } | |
| return { config: nextConfig, agentModelOverride }; | |
| } | |
| 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", | |
| "OpenCode Zen bills per request. Check your OpenCode dashboard for details.", | |
| ].join("\n"), | |
| "OpenCode Zen", | |
| ); | |
| } | |
| const envKey = resolveEnvApiKey("opencode"); | |
| if (envKey) { | |
| const useExisting = await params.prompter.confirm({ | |
| message: `Use existing OPENCODE_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`, | |
| initialValue: true, | |
| }); | |
| if (useExisting) { | |
| await setOpencodeZenApiKey(envKey.apiKey, params.agentDir); | |
| hasCredential = true; | |
| } | |
| } | |
| if (!hasCredential) { | |
| const key = await params.prompter.text({ | |
| message: "Enter OpenCode Zen API key", | |
| validate: validateApiKeyInput, | |
| }); | |
| await setOpencodeZenApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir); | |
| } | |
| nextConfig = applyAuthProfileConfig(nextConfig, { | |
| profileId: "opencode:default", | |
| provider: "opencode", | |
| mode: "api_key", | |
| }); | |
| { | |
| const applied = await applyDefaultModelChoice({ | |
| config: nextConfig, | |
| setDefaultModel: params.setDefaultModel, | |
| defaultModel: OPENCODE_ZEN_DEFAULT_MODEL, | |
| applyDefaultConfig: applyOpencodeZenConfig, | |
| applyProviderConfig: applyOpencodeZenProviderConfig, | |
| noteDefault: OPENCODE_ZEN_DEFAULT_MODEL, | |
| noteAgentModel, | |
| prompter: params.prompter, | |
| }); | |
| nextConfig = applied.config; | |
| agentModelOverride = applied.agentModelOverride ?? agentModelOverride; | |
| } | |
| return { config: nextConfig, agentModelOverride }; | |
| } | |
| if (authChoice === "together-api-key") { | |
| let hasCredential = false; | |
| if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "together") { | |
| await setTogetherApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir); | |
| hasCredential = true; | |
| } | |
| if (!hasCredential) { | |
| await params.prompter.note( | |
| [ | |
| "Together AI provides access to leading open-source models including Llama, DeepSeek, Qwen, and more.", | |
| "Get your API key at: https://api.together.xyz/settings/api-keys", | |
| ].join("\n"), | |
| "Together AI", | |
| ); | |
| } | |
| const envKey = resolveEnvApiKey("together"); | |
| if (envKey) { | |
| const useExisting = await params.prompter.confirm({ | |
| message: `Use existing TOGETHER_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`, | |
| initialValue: true, | |
| }); | |
| if (useExisting) { | |
| await setTogetherApiKey(envKey.apiKey, params.agentDir); | |
| hasCredential = true; | |
| } | |
| } | |
| if (!hasCredential) { | |
| const key = await params.prompter.text({ | |
| message: "Enter Together AI API key", | |
| validate: validateApiKeyInput, | |
| }); | |
| await setTogetherApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir); | |
| } | |
| nextConfig = applyAuthProfileConfig(nextConfig, { | |
| profileId: "together:default", | |
| provider: "together", | |
| mode: "api_key", | |
| }); | |
| { | |
| const applied = await applyDefaultModelChoice({ | |
| config: nextConfig, | |
| setDefaultModel: params.setDefaultModel, | |
| defaultModel: TOGETHER_DEFAULT_MODEL_REF, | |
| applyDefaultConfig: applyTogetherConfig, | |
| applyProviderConfig: applyTogetherProviderConfig, | |
| noteDefault: TOGETHER_DEFAULT_MODEL_REF, | |
| noteAgentModel, | |
| prompter: params.prompter, | |
| }); | |
| nextConfig = applied.config; | |
| agentModelOverride = applied.agentModelOverride ?? agentModelOverride; | |
| } | |
| return { config: nextConfig, agentModelOverride }; | |
| } | |
| if (authChoice === "huggingface-api-key") { | |
| return applyAuthChoiceHuggingface({ ...params, authChoice }); | |
| } | |
| if (authChoice === "qianfan-api-key") { | |
| let hasCredential = false; | |
| if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "qianfan") { | |
| setQianfanApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir); | |
| hasCredential = true; | |
| } | |
| if (!hasCredential) { | |
| await params.prompter.note( | |
| [ | |
| "Get your API key at: https://console.bce.baidu.com/qianfan/ais/console/apiKey", | |
| "API key format: bce-v3/ALTAK-...", | |
| ].join("\n"), | |
| "QIANFAN", | |
| ); | |
| } | |
| const envKey = resolveEnvApiKey("qianfan"); | |
| if (envKey) { | |
| const useExisting = await params.prompter.confirm({ | |
| message: `Use existing QIANFAN_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`, | |
| initialValue: true, | |
| }); | |
| if (useExisting) { | |
| setQianfanApiKey(envKey.apiKey, params.agentDir); | |
| hasCredential = true; | |
| } | |
| } | |
| if (!hasCredential) { | |
| const key = await params.prompter.text({ | |
| message: "Enter QIANFAN API key", | |
| validate: validateApiKeyInput, | |
| }); | |
| setQianfanApiKey(normalizeApiKeyInput(String(key ?? "")), params.agentDir); | |
| } | |
| nextConfig = applyAuthProfileConfig(nextConfig, { | |
| profileId: "qianfan:default", | |
| provider: "qianfan", | |
| mode: "api_key", | |
| }); | |
| { | |
| const applied = await applyDefaultModelChoice({ | |
| config: nextConfig, | |
| setDefaultModel: params.setDefaultModel, | |
| defaultModel: QIANFAN_DEFAULT_MODEL_REF, | |
| applyDefaultConfig: applyQianfanConfig, | |
| applyProviderConfig: applyQianfanProviderConfig, | |
| noteDefault: QIANFAN_DEFAULT_MODEL_REF, | |
| noteAgentModel, | |
| prompter: params.prompter, | |
| }); | |
| nextConfig = applied.config; | |
| agentModelOverride = applied.agentModelOverride ?? agentModelOverride; | |
| } | |
| return { config: nextConfig, agentModelOverride }; | |
| } | |
| return null; | |
| } | |