From eadf6c6750c80878f275c06654c5006879e42673 Mon Sep 17 00:00:00 2001 From: smakosh Date: Mon, 26 Jan 2026 18:26:41 +0100 Subject: [PATCH] feat(providers): add LLM Gateway API key auth option Add support for LLM Gateway (llmgateway.io) as a provider option in onboarding and configuration, following the same pattern as OpenRouter. Changes: - Add "llmgateway-api-key" auth choice to onboarding wizard - Add --llmgateway-api-key CLI option for non-interactive setup - Support LLM_GATEWAY_API_KEY environment variable detection - Set default model to llmgateway/anthropic/claude-sonnet-4 - Add tests for the new auth choice flow Co-Authored-By: Claude Opus 4.5 --- CHANGELOG.md | 1 + src/agents/model-auth.ts | 1 + src/cli/program/register.onboard.ts | 4 +- src/commands/auth-choice-options.ts | 8 ++ .../auth-choice.apply.api-providers.ts | 84 +++++++++++++++++++ .../auth-choice.preferred-provider.ts | 1 + src/commands/auth-choice.test.ts | 73 ++++++++++++++++ src/commands/onboard-auth.config-core.ts | 42 ++++++++++ src/commands/onboard-auth.credentials.ts | 13 +++ src/commands/onboard-auth.test.ts | 42 ++++++++++ src/commands/onboard-auth.ts | 4 + .../local/auth-choice.ts | 21 +++++ src/commands/onboard-types.ts | 2 + src/config/io.ts | 1 + 14 files changed, 296 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c5321870..183b46d52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ Status: beta. - Agents: summarize dropped messages during compaction safeguard pruning. (#2509) Thanks @jogi47. - Memory Search: allow extra paths for memory indexing (ignores symlinks). (#3600) Thanks @kira-ariaki. - Skills: add multi-image input support to Nano Banana Pro skill. (#1958) Thanks @tyler6204. +- CLI/Onboarding: add LLM Gateway API key auth option in configure/onboard. - Agents: honor tools.exec.safeBins in exec allowlist checks. (#2281) - Matrix: switch plugin SDK to @vector-im/matrix-bot-sdk. - Docs: tighten Fly private deployment steps. (#2289) Thanks @dguido. diff --git a/src/agents/model-auth.ts b/src/agents/model-auth.ts index 96e4e4ae6..90a417006 100644 --- a/src/agents/model-auth.ts +++ b/src/agents/model-auth.ts @@ -277,6 +277,7 @@ export function resolveEnvApiKey(provider: string): EnvApiKeyResult | null { cerebras: "CEREBRAS_API_KEY", xai: "XAI_API_KEY", openrouter: "OPENROUTER_API_KEY", + llmgateway: "LLM_GATEWAY_API_KEY", "vercel-ai-gateway": "AI_GATEWAY_API_KEY", moonshot: "MOONSHOT_API_KEY", "kimi-code": "KIMICODE_API_KEY", diff --git a/src/cli/program/register.onboard.ts b/src/cli/program/register.onboard.ts index 8f31635f0..9fbeda572 100644 --- a/src/cli/program/register.onboard.ts +++ b/src/cli/program/register.onboard.ts @@ -52,7 +52,7 @@ export function registerOnboardCommand(program: Command) { .option("--mode ", "Wizard mode: local|remote") .option( "--auth-choice ", - "Auth: setup-token|token|chutes|openai-codex|openai-api-key|openrouter-api-key|ai-gateway-api-key|moonshot-api-key|kimi-code-api-key|synthetic-api-key|venice-api-key|gemini-api-key|zai-api-key|apiKey|minimax-api|minimax-api-lightning|opencode-zen|skip", + "Auth: setup-token|claude-cli|token|chutes|openai-codex|openai-api-key|openrouter-api-key|llmgateway-api-key|ai-gateway-api-key|moonshot-api-key|kimi-code-api-key|synthetic-api-key|venice-api-key|codex-cli|gemini-api-key|zai-api-key|apiKey|minimax-api|minimax-api-lightning|opencode-zen|skip", ) .option( "--token-provider ", @@ -67,6 +67,7 @@ export function registerOnboardCommand(program: Command) { .option("--anthropic-api-key ", "Anthropic API key") .option("--openai-api-key ", "OpenAI API key") .option("--openrouter-api-key ", "OpenRouter API key") + .option("--llmgateway-api-key ", "LLM Gateway API key") .option("--ai-gateway-api-key ", "Vercel AI Gateway API key") .option("--moonshot-api-key ", "Moonshot API key") .option("--kimi-code-api-key ", "Kimi Code API key") @@ -117,6 +118,7 @@ export function registerOnboardCommand(program: Command) { anthropicApiKey: opts.anthropicApiKey as string | undefined, openaiApiKey: opts.openaiApiKey as string | undefined, openrouterApiKey: opts.openrouterApiKey as string | undefined, + llmgatewayApiKey: opts.llmgatewayApiKey as string | undefined, aiGatewayApiKey: opts.aiGatewayApiKey as string | undefined, moonshotApiKey: opts.moonshotApiKey as string | undefined, kimiCodeApiKey: opts.kimiCodeApiKey as string | undefined, diff --git a/src/commands/auth-choice-options.ts b/src/commands/auth-choice-options.ts index 6b49ff17b..f53aa5ba7 100644 --- a/src/commands/auth-choice-options.ts +++ b/src/commands/auth-choice-options.ts @@ -13,6 +13,7 @@ export type AuthChoiceGroupId = | "google" | "copilot" | "openrouter" + | "llmgateway" | "ai-gateway" | "moonshot" | "zai" @@ -89,6 +90,12 @@ const AUTH_CHOICE_GROUP_DEFS: { hint: "API key", choices: ["openrouter-api-key"], }, + { + value: "llmgateway", + label: "LLM Gateway", + hint: "API key", + choices: ["llmgateway-api-key"], + }, { value: "ai-gateway", label: "Vercel AI Gateway", @@ -135,6 +142,7 @@ export function buildAuthChoiceOptions(params: { options.push({ value: "chutes", label: "Chutes (OAuth)" }); options.push({ value: "openai-api-key", label: "OpenAI API key" }); options.push({ value: "openrouter-api-key", label: "OpenRouter API key" }); + options.push({ value: "llmgateway-api-key", label: "LLM Gateway API key" }); options.push({ value: "ai-gateway-api-key", label: "Vercel AI Gateway API key", diff --git a/src/commands/auth-choice.apply.api-providers.ts b/src/commands/auth-choice.apply.api-providers.ts index 8be02008b..707feaf88 100644 --- a/src/commands/auth-choice.apply.api-providers.ts +++ b/src/commands/auth-choice.apply.api-providers.ts @@ -15,6 +15,8 @@ import { applyAuthProfileConfig, applyKimiCodeConfig, applyKimiCodeProviderConfig, + applyLlmgatewayConfig, + applyLlmgatewayProviderConfig, applyMoonshotConfig, applyMoonshotProviderConfig, applyOpencodeZenConfig, @@ -29,6 +31,7 @@ import { applyVercelAiGatewayProviderConfig, applyZaiConfig, KIMI_CODE_MODEL_REF, + LLMGATEWAY_DEFAULT_MODEL_REF, MOONSHOT_DEFAULT_MODEL_REF, OPENROUTER_DEFAULT_MODEL_REF, SYNTHETIC_DEFAULT_MODEL_REF, @@ -36,6 +39,7 @@ import { VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF, setGeminiApiKey, setKimiCodeApiKey, + setLlmgatewayApiKey, setMoonshotApiKey, setOpencodeZenApiKey, setOpenrouterApiKey, @@ -69,6 +73,8 @@ export async function applyAuthChoiceApiProviders( ) { if (params.opts.tokenProvider === "openrouter") { authChoice = "openrouter-api-key"; + } else if (params.opts.tokenProvider === "llmgateway") { + authChoice = "llmgateway-api-key"; } else if (params.opts.tokenProvider === "vercel-ai-gateway") { authChoice = "ai-gateway-api-key"; } else if (params.opts.tokenProvider === "moonshot") { @@ -166,6 +172,84 @@ export async function applyAuthChoiceApiProviders( return { config: nextConfig, agentModelOverride }; } + if (authChoice === "llmgateway-api-key") { + const store = ensureAuthProfileStore(params.agentDir, { + allowKeychainPrompt: false, + }); + const profileOrder = resolveAuthProfileOrder({ + cfg: nextConfig, + store, + provider: "llmgateway", + }); + const existingProfileId = profileOrder.find((profileId) => Boolean(store.profiles[profileId])); + const existingCred = existingProfileId ? store.profiles[existingProfileId] : undefined; + let profileId = "llmgateway:default"; + let mode: "api_key" | "oauth" | "token" = "api_key"; + let hasCredential = false; + + if (existingProfileId && existingCred?.type) { + profileId = existingProfileId; + mode = + existingCred.type === "oauth" + ? "oauth" + : existingCred.type === "token" + ? "token" + : "api_key"; + hasCredential = true; + } + + if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "llmgateway") { + await setLlmgatewayApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir); + hasCredential = true; + } + + if (!hasCredential) { + const envKey = resolveEnvApiKey("llmgateway"); + if (envKey) { + const useExisting = await params.prompter.confirm({ + message: `Use existing LLM_GATEWAY_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`, + initialValue: true, + }); + if (useExisting) { + await setLlmgatewayApiKey(envKey.apiKey, params.agentDir); + hasCredential = true; + } + } + } + + if (!hasCredential) { + const key = await params.prompter.text({ + message: "Enter LLM Gateway API key", + validate: validateApiKeyInput, + }); + await setLlmgatewayApiKey(normalizeApiKeyInput(String(key)), params.agentDir); + hasCredential = true; + } + + if (hasCredential) { + nextConfig = applyAuthProfileConfig(nextConfig, { + profileId, + provider: "llmgateway", + mode, + }); + } + { + const applied = await applyDefaultModelChoice({ + config: nextConfig, + setDefaultModel: params.setDefaultModel, + defaultModel: LLMGATEWAY_DEFAULT_MODEL_REF, + applyDefaultConfig: applyLlmgatewayConfig, + applyProviderConfig: applyLlmgatewayProviderConfig, + noteDefault: LLMGATEWAY_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; diff --git a/src/commands/auth-choice.preferred-provider.ts b/src/commands/auth-choice.preferred-provider.ts index 6fe26b59a..00b329c64 100644 --- a/src/commands/auth-choice.preferred-provider.ts +++ b/src/commands/auth-choice.preferred-provider.ts @@ -11,6 +11,7 @@ const PREFERRED_PROVIDER_BY_AUTH_CHOICE: Partial> = { chutes: "chutes", "openai-api-key": "openai", "openrouter-api-key": "openrouter", + "llmgateway-api-key": "llmgateway", "ai-gateway-api-key": "vercel-ai-gateway", "moonshot-api-key": "moonshot", "kimi-code-api-key": "kimi-code", diff --git a/src/commands/auth-choice.test.ts b/src/commands/auth-choice.test.ts index 981588410..723b40de3 100644 --- a/src/commands/auth-choice.test.ts +++ b/src/commands/auth-choice.test.ts @@ -32,6 +32,7 @@ describe("applyAuthChoice", () => { const previousAgentDir = process.env.CLAWDBOT_AGENT_DIR; const previousPiAgentDir = process.env.PI_CODING_AGENT_DIR; const previousOpenrouterKey = process.env.OPENROUTER_API_KEY; + const previousLlmgatewayKey = process.env.LLM_GATEWAY_API_KEY; const previousAiGatewayKey = process.env.AI_GATEWAY_API_KEY; const previousSshTty = process.env.SSH_TTY; const previousChutesClientId = process.env.CHUTES_CLIENT_ID; @@ -64,6 +65,11 @@ describe("applyAuthChoice", () => { } else { process.env.OPENROUTER_API_KEY = previousOpenrouterKey; } + if (previousLlmgatewayKey === undefined) { + delete process.env.LLM_GATEWAY_API_KEY; + } else { + process.env.LLM_GATEWAY_API_KEY = previousLlmgatewayKey; + } if (previousAiGatewayKey === undefined) { delete process.env.AI_GATEWAY_API_KEY; } else { @@ -342,6 +348,69 @@ describe("applyAuthChoice", () => { delete process.env.OPENROUTER_API_KEY; }); + it("uses existing LLM_GATEWAY_API_KEY when selecting llmgateway-api-key", async () => { + tempStateDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-auth-")); + process.env.CLAWDBOT_STATE_DIR = tempStateDir; + process.env.CLAWDBOT_AGENT_DIR = path.join(tempStateDir, "agent"); + process.env.PI_CODING_AGENT_DIR = process.env.CLAWDBOT_AGENT_DIR; + process.env.LLM_GATEWAY_API_KEY = "sk-llmgateway-test"; + + const text = vi.fn(); + const select: WizardPrompter["select"] = vi.fn( + async (params) => params.options[0]?.value as never, + ); + const multiselect: WizardPrompter["multiselect"] = vi.fn(async () => []); + const confirm = vi.fn(async () => true); + const prompter: WizardPrompter = { + intro: vi.fn(noopAsync), + outro: vi.fn(noopAsync), + note: vi.fn(noopAsync), + select, + multiselect, + text, + confirm, + progress: vi.fn(() => ({ update: noop, stop: noop })), + }; + const runtime: RuntimeEnv = { + log: vi.fn(), + error: vi.fn(), + exit: vi.fn((code: number) => { + throw new Error(`exit:${code}`); + }), + }; + + const result = await applyAuthChoice({ + authChoice: "llmgateway-api-key", + config: {}, + prompter, + runtime, + setDefaultModel: true, + }); + + expect(confirm).toHaveBeenCalledWith( + expect.objectContaining({ + message: expect.stringContaining("LLM_GATEWAY_API_KEY"), + }), + ); + expect(text).not.toHaveBeenCalled(); + expect(result.config.auth?.profiles?.["llmgateway:default"]).toMatchObject({ + provider: "llmgateway", + mode: "api_key", + }); + expect(result.config.agents?.defaults?.model?.primary).toBe( + "llmgateway/anthropic/claude-sonnet-4", + ); + + const authProfilePath = authProfilePathFor(requireAgentDir()); + const raw = await fs.readFile(authProfilePath, "utf8"); + const parsed = JSON.parse(raw) as { + profiles?: Record; + }; + expect(parsed.profiles?.["llmgateway:default"]?.key).toBe("sk-llmgateway-test"); + + delete process.env.LLM_GATEWAY_API_KEY; + }); + it("uses existing AI_GATEWAY_API_KEY when selecting ai-gateway-api-key", async () => { tempStateDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-auth-")); process.env.CLAWDBOT_STATE_DIR = tempStateDir; @@ -597,6 +666,10 @@ describe("resolvePreferredProviderForAuthChoice", () => { expect(resolvePreferredProviderForAuthChoice("qwen-portal")).toBe("qwen-portal"); }); + it("maps llmgateway-api-key to the provider", () => { + expect(resolvePreferredProviderForAuthChoice("llmgateway-api-key")).toBe("llmgateway"); + }); + it("returns undefined for unknown choices", () => { expect(resolvePreferredProviderForAuthChoice("unknown" as AuthChoice)).toBeUndefined(); }); diff --git a/src/commands/onboard-auth.config-core.ts b/src/commands/onboard-auth.config-core.ts index 921ee01d1..6596cdf65 100644 --- a/src/commands/onboard-auth.config-core.ts +++ b/src/commands/onboard-auth.config-core.ts @@ -12,6 +12,7 @@ import { } from "../agents/venice-models.js"; import type { MoltbotConfig } from "../config/config.js"; import { + LLMGATEWAY_DEFAULT_MODEL_REF, OPENROUTER_DEFAULT_MODEL_REF, VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF, ZAI_DEFAULT_MODEL_REF, @@ -137,6 +138,47 @@ export function applyOpenrouterConfig(cfg: MoltbotConfig): MoltbotConfig { }; } +export function applyLlmgatewayProviderConfig(cfg: MoltbotConfig): MoltbotConfig { + const models = { ...cfg.agents?.defaults?.models }; + models[LLMGATEWAY_DEFAULT_MODEL_REF] = { + ...models[LLMGATEWAY_DEFAULT_MODEL_REF], + alias: models[LLMGATEWAY_DEFAULT_MODEL_REF]?.alias ?? "LLM Gateway", + }; + + return { + ...cfg, + agents: { + ...cfg.agents, + defaults: { + ...cfg.agents?.defaults, + models, + }, + }, + }; +} + +export function applyLlmgatewayConfig(cfg: MoltbotConfig): MoltbotConfig { + const next = applyLlmgatewayProviderConfig(cfg); + const existingModel = next.agents?.defaults?.model; + return { + ...next, + agents: { + ...next.agents, + defaults: { + ...next.agents?.defaults, + model: { + ...(existingModel && "fallbacks" in (existingModel as Record) + ? { + fallbacks: (existingModel as { fallbacks?: string[] }).fallbacks, + } + : undefined), + primary: LLMGATEWAY_DEFAULT_MODEL_REF, + }, + }, + }, + }; +} + export function applyMoonshotProviderConfig(cfg: MoltbotConfig): MoltbotConfig { const models = { ...cfg.agents?.defaults?.models }; models[MOONSHOT_DEFAULT_MODEL_REF] = { diff --git a/src/commands/onboard-auth.credentials.ts b/src/commands/onboard-auth.credentials.ts index b2fb58542..59a16a993 100644 --- a/src/commands/onboard-auth.credentials.ts +++ b/src/commands/onboard-auth.credentials.ts @@ -114,6 +114,7 @@ export async function setVeniceApiKey(key: string, agentDir?: string) { export const ZAI_DEFAULT_MODEL_REF = "zai/glm-4.7"; export const OPENROUTER_DEFAULT_MODEL_REF = "openrouter/auto"; +export const LLMGATEWAY_DEFAULT_MODEL_REF = "llmgateway/anthropic/claude-sonnet-4"; export const VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF = "vercel-ai-gateway/anthropic/claude-opus-4.5"; export async function setZaiApiKey(key: string, agentDir?: string) { @@ -141,6 +142,18 @@ export async function setOpenrouterApiKey(key: string, agentDir?: string) { }); } +export async function setLlmgatewayApiKey(key: string, agentDir?: string) { + upsertAuthProfile({ + profileId: "llmgateway:default", + credential: { + type: "api_key", + provider: "llmgateway", + key, + }, + agentDir: resolveAuthAgentDir(agentDir), + }); +} + export async function setVercelAiGatewayApiKey(key: string, agentDir?: string) { upsertAuthProfile({ profileId: "vercel-ai-gateway:default", diff --git a/src/commands/onboard-auth.test.ts b/src/commands/onboard-auth.test.ts index 0a2c67f94..fb98ff4f3 100644 --- a/src/commands/onboard-auth.test.ts +++ b/src/commands/onboard-auth.test.ts @@ -7,6 +7,8 @@ import { afterEach, describe, expect, it } from "vitest"; import { applyAuthProfileConfig, + applyLlmgatewayConfig, + applyLlmgatewayProviderConfig, applyMinimaxApiConfig, applyMinimaxApiProviderConfig, applyOpencodeZenConfig, @@ -15,6 +17,7 @@ import { applyOpenrouterProviderConfig, applySyntheticConfig, applySyntheticProviderConfig, + LLMGATEWAY_DEFAULT_MODEL_REF, OPENROUTER_DEFAULT_MODEL_REF, SYNTHETIC_DEFAULT_MODEL_ID, SYNTHETIC_DEFAULT_MODEL_REF, @@ -420,3 +423,42 @@ describe("applyOpenrouterConfig", () => { expect(cfg.agents?.defaults?.model?.fallbacks).toEqual(["anthropic/claude-opus-4-5"]); }); }); + +describe("applyLlmgatewayProviderConfig", () => { + it("adds allowlist entry for the default model", () => { + const cfg = applyLlmgatewayProviderConfig({}); + const models = cfg.agents?.defaults?.models ?? {}; + expect(Object.keys(models)).toContain(LLMGATEWAY_DEFAULT_MODEL_REF); + }); + + it("preserves existing alias for the default model", () => { + const cfg = applyLlmgatewayProviderConfig({ + agents: { + defaults: { + models: { + [LLMGATEWAY_DEFAULT_MODEL_REF]: { alias: "Gateway" }, + }, + }, + }, + }); + expect(cfg.agents?.defaults?.models?.[LLMGATEWAY_DEFAULT_MODEL_REF]?.alias).toBe("Gateway"); + }); +}); + +describe("applyLlmgatewayConfig", () => { + it("sets correct primary model", () => { + const cfg = applyLlmgatewayConfig({}); + expect(cfg.agents?.defaults?.model?.primary).toBe(LLMGATEWAY_DEFAULT_MODEL_REF); + }); + + it("preserves existing model fallbacks", () => { + const cfg = applyLlmgatewayConfig({ + agents: { + defaults: { + model: { fallbacks: ["anthropic/claude-opus-4-5"] }, + }, + }, + }); + expect(cfg.agents?.defaults?.model?.fallbacks).toEqual(["anthropic/claude-opus-4-5"]); + }); +}); diff --git a/src/commands/onboard-auth.ts b/src/commands/onboard-auth.ts index b122d89cf..00c7489c6 100644 --- a/src/commands/onboard-auth.ts +++ b/src/commands/onboard-auth.ts @@ -7,6 +7,8 @@ export { applyAuthProfileConfig, applyKimiCodeConfig, applyKimiCodeProviderConfig, + applyLlmgatewayConfig, + applyLlmgatewayProviderConfig, applyMoonshotConfig, applyMoonshotProviderConfig, applyOpenrouterConfig, @@ -33,10 +35,12 @@ export { applyOpencodeZenProviderConfig, } from "./onboard-auth.config-opencode.js"; export { + LLMGATEWAY_DEFAULT_MODEL_REF, OPENROUTER_DEFAULT_MODEL_REF, setAnthropicApiKey, setGeminiApiKey, setKimiCodeApiKey, + setLlmgatewayApiKey, setMinimaxApiKey, setMoonshotApiKey, setOpencodeZenApiKey, diff --git a/src/commands/onboard-non-interactive/local/auth-choice.ts b/src/commands/onboard-non-interactive/local/auth-choice.ts index 7d952730c..7fbb450ba 100644 --- a/src/commands/onboard-non-interactive/local/auth-choice.ts +++ b/src/commands/onboard-non-interactive/local/auth-choice.ts @@ -9,6 +9,7 @@ import { applyGoogleGeminiModelDefault } from "../../google-gemini-model-default import { applyAuthProfileConfig, applyKimiCodeConfig, + applyLlmgatewayConfig, applyMinimaxApiConfig, applyMinimaxConfig, applyMoonshotConfig, @@ -21,6 +22,7 @@ import { setAnthropicApiKey, setGeminiApiKey, setKimiCodeApiKey, + setLlmgatewayApiKey, setMinimaxApiKey, setMoonshotApiKey, setOpencodeZenApiKey, @@ -214,6 +216,25 @@ export async function applyNonInteractiveAuthChoice(params: { return applyOpenrouterConfig(nextConfig); } + if (authChoice === "llmgateway-api-key") { + const resolved = await resolveNonInteractiveApiKey({ + provider: "llmgateway", + cfg: baseConfig, + flagValue: opts.llmgatewayApiKey, + flagName: "--llmgateway-api-key", + envVar: "LLM_GATEWAY_API_KEY", + runtime, + }); + if (!resolved) return null; + if (resolved.source !== "profile") await setLlmgatewayApiKey(resolved.key); + nextConfig = applyAuthProfileConfig(nextConfig, { + profileId: "llmgateway:default", + provider: "llmgateway", + mode: "api_key", + }); + return applyLlmgatewayConfig(nextConfig); + } + if (authChoice === "ai-gateway-api-key") { const resolved = await resolveNonInteractiveApiKey({ provider: "vercel-ai-gateway", diff --git a/src/commands/onboard-types.ts b/src/commands/onboard-types.ts index aa1d9afe0..749674115 100644 --- a/src/commands/onboard-types.ts +++ b/src/commands/onboard-types.ts @@ -12,6 +12,7 @@ export type AuthChoice = | "openai-codex" | "openai-api-key" | "openrouter-api-key" + | "llmgateway-api-key" | "ai-gateway-api-key" | "moonshot-api-key" | "kimi-code-api-key" @@ -62,6 +63,7 @@ export type OnboardOptions = { anthropicApiKey?: string; openaiApiKey?: string; openrouterApiKey?: string; + llmgatewayApiKey?: string; aiGatewayApiKey?: string; moonshotApiKey?: string; kimiCodeApiKey?: string; diff --git a/src/config/io.ts b/src/config/io.ts index 50f1edb82..662978e72 100644 --- a/src/config/io.ts +++ b/src/config/io.ts @@ -45,6 +45,7 @@ const SHELL_ENV_EXPECTED_KEYS = [ "GEMINI_API_KEY", "ZAI_API_KEY", "OPENROUTER_API_KEY", + "LLM_GATEWAY_API_KEY", "AI_GATEWAY_API_KEY", "MINIMAX_API_KEY", "SYNTHETIC_API_KEY",