diff --git a/src/cli/program/register.onboard.ts b/src/cli/program/register.onboard.ts index 1d34cc99b07..f62d6eb0b97 100644 --- a/src/cli/program/register.onboard.ts +++ b/src/cli/program/register.onboard.ts @@ -8,6 +8,7 @@ import type { TailscaleMode, } from "../../commands/onboard-types.js"; import { formatAuthChoiceChoicesForCli } from "../../commands/auth-choice-options.js"; +import { ONBOARD_PROVIDER_AUTH_FLAGS } from "../../commands/onboard-provider-auth-flags.js"; import { onboardCommand } from "../../commands/onboard.js"; import { defaultRuntime } from "../../runtime.js"; import { formatDocsLink } from "../../terminal/links.js"; @@ -44,7 +45,7 @@ const AUTH_CHOICE_HELP = formatAuthChoiceChoicesForCli({ }); export function registerOnboardCommand(program: Command) { - program + const command = program .command("onboard") .description("Interactive wizard to set up the gateway, workspace, and skills") .addHelpText( @@ -73,27 +74,14 @@ export function registerOnboardCommand(program: Command) { "Auth profile id (non-interactive; default: :manual)", ) .option("--token-expires-in ", "Optional token expiry duration (e.g. 365d, 12h)") - .option("--anthropic-api-key ", "Anthropic API key") - .option("--openai-api-key ", "OpenAI API key") - .option("--openrouter-api-key ", "OpenRouter API key") - .option("--ai-gateway-api-key ", "Vercel AI Gateway API key") .option("--cloudflare-ai-gateway-account-id ", "Cloudflare Account ID") - .option("--cloudflare-ai-gateway-gateway-id ", "Cloudflare AI Gateway ID") - .option("--cloudflare-ai-gateway-api-key ", "Cloudflare AI Gateway API key") - .option("--moonshot-api-key ", "Moonshot API key") - .option("--kimi-code-api-key ", "Kimi Coding API key") - .option("--gemini-api-key ", "Gemini API key") - .option("--zai-api-key ", "Z.AI API key") - .option("--xiaomi-api-key ", "Xiaomi API key") - .option("--minimax-api-key ", "MiniMax API key") - .option("--synthetic-api-key ", "Synthetic API key") - .option("--venice-api-key ", "Venice API key") - .option("--together-api-key ", "Together AI API key") - .option("--huggingface-api-key ", "Hugging Face API key (HF token)") - .option("--opencode-zen-api-key ", "OpenCode Zen API key") - .option("--xai-api-key ", "xAI API key") - .option("--litellm-api-key ", "LiteLLM API key") - .option("--qianfan-api-key ", "QIANFAN API key") + .option("--cloudflare-ai-gateway-gateway-id ", "Cloudflare AI Gateway ID"); + + for (const providerFlag of ONBOARD_PROVIDER_AUTH_FLAGS) { + command.option(providerFlag.cliOption, providerFlag.description); + } + + command .option("--custom-base-url ", "Custom provider base URL") .option("--custom-api-key ", "Custom provider API key (optional)") .option("--custom-model-id ", "Custom provider model ID") @@ -120,76 +108,77 @@ export function registerOnboardCommand(program: Command) { .option("--skip-health", "Skip health check") .option("--skip-ui", "Skip Control UI/TUI prompts") .option("--node-manager ", "Node manager for skills: npm|pnpm|bun") - .option("--json", "Output JSON summary", false) - .action(async (opts, command) => { - await runCommandWithRuntime(defaultRuntime, async () => { - const installDaemon = resolveInstallDaemonFlag(command, { - installDaemon: Boolean(opts.installDaemon), - }); - const gatewayPort = - typeof opts.gatewayPort === "string" ? Number.parseInt(opts.gatewayPort, 10) : undefined; - await onboardCommand( - { - workspace: opts.workspace as string | undefined, - nonInteractive: Boolean(opts.nonInteractive), - acceptRisk: Boolean(opts.acceptRisk), - flow: opts.flow as "quickstart" | "advanced" | "manual" | undefined, - mode: opts.mode as "local" | "remote" | undefined, - authChoice: opts.authChoice as AuthChoice | undefined, - tokenProvider: opts.tokenProvider as string | undefined, - token: opts.token as string | undefined, - tokenProfileId: opts.tokenProfileId as string | undefined, - tokenExpiresIn: opts.tokenExpiresIn as string | undefined, - anthropicApiKey: opts.anthropicApiKey as string | undefined, - openaiApiKey: opts.openaiApiKey as string | undefined, - openrouterApiKey: opts.openrouterApiKey as string | undefined, - aiGatewayApiKey: opts.aiGatewayApiKey as string | undefined, - cloudflareAiGatewayAccountId: opts.cloudflareAiGatewayAccountId as string | undefined, - cloudflareAiGatewayGatewayId: opts.cloudflareAiGatewayGatewayId as string | undefined, - cloudflareAiGatewayApiKey: opts.cloudflareAiGatewayApiKey as string | undefined, - moonshotApiKey: opts.moonshotApiKey as string | undefined, - kimiCodeApiKey: opts.kimiCodeApiKey as string | undefined, - geminiApiKey: opts.geminiApiKey as string | undefined, - zaiApiKey: opts.zaiApiKey as string | undefined, - xiaomiApiKey: opts.xiaomiApiKey as string | undefined, - qianfanApiKey: opts.qianfanApiKey as string | undefined, - minimaxApiKey: opts.minimaxApiKey as string | undefined, - syntheticApiKey: opts.syntheticApiKey as string | undefined, - veniceApiKey: opts.veniceApiKey as string | undefined, - togetherApiKey: opts.togetherApiKey as string | undefined, - huggingfaceApiKey: opts.huggingfaceApiKey as string | undefined, - opencodeZenApiKey: opts.opencodeZenApiKey as string | undefined, - xaiApiKey: opts.xaiApiKey as string | undefined, - litellmApiKey: opts.litellmApiKey as string | undefined, - customBaseUrl: opts.customBaseUrl as string | undefined, - customApiKey: opts.customApiKey as string | undefined, - customModelId: opts.customModelId as string | undefined, - customProviderId: opts.customProviderId as string | undefined, - customCompatibility: opts.customCompatibility as "openai" | "anthropic" | undefined, - gatewayPort: - typeof gatewayPort === "number" && Number.isFinite(gatewayPort) - ? gatewayPort - : undefined, - gatewayBind: opts.gatewayBind as GatewayBind | undefined, - gatewayAuth: opts.gatewayAuth as GatewayAuthChoice | undefined, - gatewayToken: opts.gatewayToken as string | undefined, - gatewayPassword: opts.gatewayPassword as string | undefined, - remoteUrl: opts.remoteUrl as string | undefined, - remoteToken: opts.remoteToken as string | undefined, - tailscale: opts.tailscale as TailscaleMode | undefined, - tailscaleResetOnExit: Boolean(opts.tailscaleResetOnExit), - reset: Boolean(opts.reset), - installDaemon, - daemonRuntime: opts.daemonRuntime as GatewayDaemonRuntime | undefined, - skipChannels: Boolean(opts.skipChannels), - skipSkills: Boolean(opts.skipSkills), - skipHealth: Boolean(opts.skipHealth), - skipUi: Boolean(opts.skipUi), - nodeManager: opts.nodeManager as NodeManagerChoice | undefined, - json: Boolean(opts.json), - }, - defaultRuntime, - ); + .option("--json", "Output JSON summary", false); + + command.action(async (opts, commandRuntime) => { + await runCommandWithRuntime(defaultRuntime, async () => { + const installDaemon = resolveInstallDaemonFlag(commandRuntime, { + installDaemon: Boolean(opts.installDaemon), }); + const gatewayPort = + typeof opts.gatewayPort === "string" ? Number.parseInt(opts.gatewayPort, 10) : undefined; + await onboardCommand( + { + workspace: opts.workspace as string | undefined, + nonInteractive: Boolean(opts.nonInteractive), + acceptRisk: Boolean(opts.acceptRisk), + flow: opts.flow as "quickstart" | "advanced" | "manual" | undefined, + mode: opts.mode as "local" | "remote" | undefined, + authChoice: opts.authChoice as AuthChoice | undefined, + tokenProvider: opts.tokenProvider as string | undefined, + token: opts.token as string | undefined, + tokenProfileId: opts.tokenProfileId as string | undefined, + tokenExpiresIn: opts.tokenExpiresIn as string | undefined, + anthropicApiKey: opts.anthropicApiKey as string | undefined, + openaiApiKey: opts.openaiApiKey as string | undefined, + openrouterApiKey: opts.openrouterApiKey as string | undefined, + aiGatewayApiKey: opts.aiGatewayApiKey as string | undefined, + cloudflareAiGatewayAccountId: opts.cloudflareAiGatewayAccountId as string | undefined, + cloudflareAiGatewayGatewayId: opts.cloudflareAiGatewayGatewayId as string | undefined, + cloudflareAiGatewayApiKey: opts.cloudflareAiGatewayApiKey as string | undefined, + moonshotApiKey: opts.moonshotApiKey as string | undefined, + kimiCodeApiKey: opts.kimiCodeApiKey as string | undefined, + geminiApiKey: opts.geminiApiKey as string | undefined, + zaiApiKey: opts.zaiApiKey as string | undefined, + xiaomiApiKey: opts.xiaomiApiKey as string | undefined, + qianfanApiKey: opts.qianfanApiKey as string | undefined, + minimaxApiKey: opts.minimaxApiKey as string | undefined, + syntheticApiKey: opts.syntheticApiKey as string | undefined, + veniceApiKey: opts.veniceApiKey as string | undefined, + togetherApiKey: opts.togetherApiKey as string | undefined, + huggingfaceApiKey: opts.huggingfaceApiKey as string | undefined, + opencodeZenApiKey: opts.opencodeZenApiKey as string | undefined, + xaiApiKey: opts.xaiApiKey as string | undefined, + litellmApiKey: opts.litellmApiKey as string | undefined, + customBaseUrl: opts.customBaseUrl as string | undefined, + customApiKey: opts.customApiKey as string | undefined, + customModelId: opts.customModelId as string | undefined, + customProviderId: opts.customProviderId as string | undefined, + customCompatibility: opts.customCompatibility as "openai" | "anthropic" | undefined, + gatewayPort: + typeof gatewayPort === "number" && Number.isFinite(gatewayPort) + ? gatewayPort + : undefined, + gatewayBind: opts.gatewayBind as GatewayBind | undefined, + gatewayAuth: opts.gatewayAuth as GatewayAuthChoice | undefined, + gatewayToken: opts.gatewayToken as string | undefined, + gatewayPassword: opts.gatewayPassword as string | undefined, + remoteUrl: opts.remoteUrl as string | undefined, + remoteToken: opts.remoteToken as string | undefined, + tailscale: opts.tailscale as TailscaleMode | undefined, + tailscaleResetOnExit: Boolean(opts.tailscaleResetOnExit), + reset: Boolean(opts.reset), + installDaemon, + daemonRuntime: opts.daemonRuntime as GatewayDaemonRuntime | undefined, + skipChannels: Boolean(opts.skipChannels), + skipSkills: Boolean(opts.skipSkills), + skipHealth: Boolean(opts.skipHealth), + skipUi: Boolean(opts.skipUi), + nodeManager: opts.nodeManager as NodeManagerChoice | undefined, + json: Boolean(opts.json), + }, + defaultRuntime, + ); }); + }); } diff --git a/src/commands/auth-choice-legacy.ts b/src/commands/auth-choice-legacy.ts new file mode 100644 index 00000000000..e93e920503f --- /dev/null +++ b/src/commands/auth-choice-legacy.ts @@ -0,0 +1,28 @@ +import type { AuthChoice } from "./onboard-types.js"; + +export const AUTH_CHOICE_LEGACY_ALIASES_FOR_CLI: ReadonlyArray = [ + "setup-token", + "oauth", + "claude-cli", + "codex-cli", + "minimax-cloud", + "minimax", +]; + +export function normalizeLegacyOnboardAuthChoice( + authChoice: AuthChoice | undefined, +): AuthChoice | undefined { + if (authChoice === "oauth" || authChoice === "claude-cli") { + return "setup-token"; + } + if (authChoice === "codex-cli") { + return "openai-codex"; + } + return authChoice; +} + +export function isDeprecatedAuthChoice( + authChoice: AuthChoice | undefined, +): authChoice is "claude-cli" | "codex-cli" { + return authChoice === "claude-cli" || authChoice === "codex-cli"; +} diff --git a/src/commands/auth-choice-options.e2e.test.ts b/src/commands/auth-choice-options.e2e.test.ts index b54e9edad7a..c87769507c9 100644 --- a/src/commands/auth-choice-options.e2e.test.ts +++ b/src/commands/auth-choice-options.e2e.test.ts @@ -1,6 +1,10 @@ import { describe, expect, it } from "vitest"; import type { AuthProfileStore } from "../agents/auth-profiles.js"; -import { buildAuthChoiceOptions, formatAuthChoiceChoicesForCli } from "./auth-choice-options.js"; +import { + buildAuthChoiceGroups, + buildAuthChoiceOptions, + formatAuthChoiceChoicesForCli, +} from "./auth-choice-options.js"; describe("buildAuthChoiceOptions", () => { it("includes GitHub Copilot", () => { @@ -172,4 +176,16 @@ describe("buildAuthChoiceOptions", () => { expect(cliChoices).toContain("claude-cli"); expect(cliChoices).toContain("codex-cli"); }); + + it("shows Chutes in grouped provider selection", () => { + const store: AuthProfileStore = { version: 1, profiles: {} }; + const { groups } = buildAuthChoiceGroups({ + store, + includeSkip: false, + }); + const chutesGroup = groups.find((group) => group.value === "chutes"); + + expect(chutesGroup).toBeDefined(); + expect(chutesGroup?.options.some((opt) => opt.value === "chutes")).toBe(true); + }); }); diff --git a/src/commands/auth-choice-options.ts b/src/commands/auth-choice-options.ts index 59f4fd334fc..0ad07239eeb 100644 --- a/src/commands/auth-choice-options.ts +++ b/src/commands/auth-choice-options.ts @@ -1,5 +1,6 @@ import type { AuthProfileStore } from "../agents/auth-profiles.js"; import type { AuthChoice, AuthChoiceGroupId } from "./onboard-types.js"; +import { AUTH_CHOICE_LEGACY_ALIASES_FOR_CLI } from "./auth-choice-legacy.js"; export type { AuthChoiceGroupId }; @@ -33,6 +34,12 @@ const AUTH_CHOICE_GROUP_DEFS: { hint: "setup-token + API key", choices: ["token", "apiKey"], }, + { + value: "chutes", + label: "Chutes", + hint: "OAuth", + choices: ["chutes"], + }, { value: "vllm", label: "vLLM", @@ -287,15 +294,6 @@ const BASE_AUTH_CHOICE_OPTIONS: ReadonlyArray = [ { value: "custom-api-key", label: "Custom Provider" }, ]; -const LEGACY_AUTH_CHOICE_ALIASES: ReadonlyArray = [ - "setup-token", - "oauth", - "claude-cli", - "codex-cli", - "minimax-cloud", - "minimax", -]; - export function formatAuthChoiceChoicesForCli(params?: { includeSkip?: boolean; includeLegacyAliases?: boolean; @@ -308,7 +306,7 @@ export function formatAuthChoiceChoicesForCli(params?: { values.push("skip"); } if (includeLegacyAliases) { - values.push(...LEGACY_AUTH_CHOICE_ALIASES); + values.push(...AUTH_CHOICE_LEGACY_ALIASES_FOR_CLI); } return values.join("|"); diff --git a/src/commands/onboard-non-interactive.provider-auth.e2e.test.ts b/src/commands/onboard-non-interactive.provider-auth.e2e.test.ts index c945c0971cf..e4372b7a4c3 100644 --- a/src/commands/onboard-non-interactive.provider-auth.e2e.test.ts +++ b/src/commands/onboard-non-interactive.provider-auth.e2e.test.ts @@ -480,6 +480,36 @@ describe("onboard (non-interactive): provider auth", () => { }); }, 60_000); + it("infers QIANFAN auth choice from --qianfan-api-key and sets default model", async () => { + await withOnboardEnv("openclaw-onboard-qianfan-infer-", async ({ configPath, runtime }) => { + await runNonInteractive( + { + nonInteractive: true, + qianfanApiKey: "qianfan-test-key", + skipHealth: true, + skipChannels: true, + skipSkills: true, + json: true, + }, + runtime, + ); + + const cfg = await readJsonFile<{ + auth?: { profiles?: Record }; + agents?: { defaults?: { model?: { primary?: string } } }; + }>(configPath); + + expect(cfg.auth?.profiles?.["qianfan:default"]?.provider).toBe("qianfan"); + expect(cfg.auth?.profiles?.["qianfan:default"]?.mode).toBe("api_key"); + expect(cfg.agents?.defaults?.model?.primary).toBe("qianfan/deepseek-v3.2"); + await expectApiKeyProfile({ + profileId: "qianfan:default", + provider: "qianfan", + key: "qianfan-test-key", + }); + }); + }, 60_000); + it("configures a custom provider from non-interactive flags", async () => { await withOnboardEnv("openclaw-onboard-custom-provider-", async ({ configPath, runtime }) => { await runNonInteractive( diff --git a/src/commands/onboard-non-interactive/local/auth-choice-inference.ts b/src/commands/onboard-non-interactive/local/auth-choice-inference.ts index 42a321e04ed..bd59df084f2 100644 --- a/src/commands/onboard-non-interactive/local/auth-choice-inference.ts +++ b/src/commands/onboard-non-interactive/local/auth-choice-inference.ts @@ -1,7 +1,8 @@ import type { AuthChoice, OnboardOptions } from "../../onboard-types.js"; +import { ONBOARD_PROVIDER_AUTH_FLAGS } from "../../onboard-provider-auth-flags.js"; type AuthChoiceFlag = { - flag: keyof AuthChoiceFlagOptions; + optionKey: keyof AuthChoiceFlagOptions; authChoice: AuthChoice; label: string; }; @@ -26,36 +27,12 @@ type AuthChoiceFlagOptions = Pick< | "opencodeZenApiKey" | "xaiApiKey" | "litellmApiKey" + | "qianfanApiKey" | "customBaseUrl" | "customModelId" | "customApiKey" >; -const AUTH_CHOICE_FLAG_MAP = [ - { flag: "anthropicApiKey", authChoice: "apiKey", label: "--anthropic-api-key" }, - { flag: "geminiApiKey", authChoice: "gemini-api-key", label: "--gemini-api-key" }, - { flag: "openaiApiKey", authChoice: "openai-api-key", label: "--openai-api-key" }, - { flag: "openrouterApiKey", authChoice: "openrouter-api-key", label: "--openrouter-api-key" }, - { flag: "aiGatewayApiKey", authChoice: "ai-gateway-api-key", label: "--ai-gateway-api-key" }, - { - flag: "cloudflareAiGatewayApiKey", - authChoice: "cloudflare-ai-gateway-api-key", - label: "--cloudflare-ai-gateway-api-key", - }, - { flag: "moonshotApiKey", authChoice: "moonshot-api-key", label: "--moonshot-api-key" }, - { flag: "kimiCodeApiKey", authChoice: "kimi-code-api-key", label: "--kimi-code-api-key" }, - { flag: "syntheticApiKey", authChoice: "synthetic-api-key", label: "--synthetic-api-key" }, - { flag: "veniceApiKey", authChoice: "venice-api-key", label: "--venice-api-key" }, - { flag: "togetherApiKey", authChoice: "together-api-key", label: "--together-api-key" }, - { flag: "zaiApiKey", authChoice: "zai-api-key", label: "--zai-api-key" }, - { flag: "xiaomiApiKey", authChoice: "xiaomi-api-key", label: "--xiaomi-api-key" }, - { flag: "xaiApiKey", authChoice: "xai-api-key", label: "--xai-api-key" }, - { flag: "minimaxApiKey", authChoice: "minimax-api", label: "--minimax-api-key" }, - { flag: "opencodeZenApiKey", authChoice: "opencode-zen", label: "--opencode-zen-api-key" }, - { flag: "huggingfaceApiKey", authChoice: "huggingface-api-key", label: "--huggingface-api-key" }, - { flag: "litellmApiKey", authChoice: "litellm-api-key", label: "--litellm-api-key" }, -] satisfies ReadonlyArray; - export type AuthChoiceInference = { choice?: AuthChoice; matches: AuthChoiceFlag[]; @@ -67,9 +44,13 @@ function hasStringValue(value: unknown): boolean { // Infer auth choice from explicit provider API key flags. export function inferAuthChoiceFromFlags(opts: OnboardOptions): AuthChoiceInference { - const matches: AuthChoiceFlag[] = AUTH_CHOICE_FLAG_MAP.filter(({ flag }) => - hasStringValue(opts[flag]), - ); + const matches: AuthChoiceFlag[] = ONBOARD_PROVIDER_AUTH_FLAGS.filter(({ optionKey }) => + hasStringValue(opts[optionKey]), + ).map((flag) => ({ + optionKey: flag.optionKey, + authChoice: flag.authChoice, + label: flag.cliFlag, + })); if ( hasStringValue(opts.customBaseUrl) || @@ -77,7 +58,7 @@ export function inferAuthChoiceFromFlags(opts: OnboardOptions): AuthChoiceInfere hasStringValue(opts.customApiKey) ) { matches.push({ - flag: "customBaseUrl", + optionKey: "customBaseUrl", authChoice: "custom-api-key", label: "--custom-base-url/--custom-model-id/--custom-api-key", }); diff --git a/src/commands/onboard-provider-auth-flags.ts b/src/commands/onboard-provider-auth-flags.ts new file mode 100644 index 00000000000..2ca75c2aeb7 --- /dev/null +++ b/src/commands/onboard-provider-auth-flags.ts @@ -0,0 +1,169 @@ +import type { AuthChoice, OnboardOptions } from "./onboard-types.js"; + +type OnboardProviderAuthOptionKey = keyof Pick< + OnboardOptions, + | "anthropicApiKey" + | "openaiApiKey" + | "openrouterApiKey" + | "aiGatewayApiKey" + | "cloudflareAiGatewayApiKey" + | "moonshotApiKey" + | "kimiCodeApiKey" + | "geminiApiKey" + | "zaiApiKey" + | "xiaomiApiKey" + | "minimaxApiKey" + | "syntheticApiKey" + | "veniceApiKey" + | "togetherApiKey" + | "huggingfaceApiKey" + | "opencodeZenApiKey" + | "xaiApiKey" + | "litellmApiKey" + | "qianfanApiKey" +>; + +export type OnboardProviderAuthFlag = { + optionKey: OnboardProviderAuthOptionKey; + authChoice: AuthChoice; + cliFlag: `--${string}`; + cliOption: `--${string} `; + description: string; +}; + +// Shared source for provider API-key flags used by CLI registration + non-interactive inference. +export const ONBOARD_PROVIDER_AUTH_FLAGS: ReadonlyArray = [ + { + optionKey: "anthropicApiKey", + authChoice: "apiKey", + cliFlag: "--anthropic-api-key", + cliOption: "--anthropic-api-key ", + description: "Anthropic API key", + }, + { + optionKey: "openaiApiKey", + authChoice: "openai-api-key", + cliFlag: "--openai-api-key", + cliOption: "--openai-api-key ", + description: "OpenAI API key", + }, + { + optionKey: "openrouterApiKey", + authChoice: "openrouter-api-key", + cliFlag: "--openrouter-api-key", + cliOption: "--openrouter-api-key ", + description: "OpenRouter API key", + }, + { + optionKey: "aiGatewayApiKey", + authChoice: "ai-gateway-api-key", + cliFlag: "--ai-gateway-api-key", + cliOption: "--ai-gateway-api-key ", + description: "Vercel AI Gateway API key", + }, + { + optionKey: "cloudflareAiGatewayApiKey", + authChoice: "cloudflare-ai-gateway-api-key", + cliFlag: "--cloudflare-ai-gateway-api-key", + cliOption: "--cloudflare-ai-gateway-api-key ", + description: "Cloudflare AI Gateway API key", + }, + { + optionKey: "moonshotApiKey", + authChoice: "moonshot-api-key", + cliFlag: "--moonshot-api-key", + cliOption: "--moonshot-api-key ", + description: "Moonshot API key", + }, + { + optionKey: "kimiCodeApiKey", + authChoice: "kimi-code-api-key", + cliFlag: "--kimi-code-api-key", + cliOption: "--kimi-code-api-key ", + description: "Kimi Coding API key", + }, + { + optionKey: "geminiApiKey", + authChoice: "gemini-api-key", + cliFlag: "--gemini-api-key", + cliOption: "--gemini-api-key ", + description: "Gemini API key", + }, + { + optionKey: "zaiApiKey", + authChoice: "zai-api-key", + cliFlag: "--zai-api-key", + cliOption: "--zai-api-key ", + description: "Z.AI API key", + }, + { + optionKey: "xiaomiApiKey", + authChoice: "xiaomi-api-key", + cliFlag: "--xiaomi-api-key", + cliOption: "--xiaomi-api-key ", + description: "Xiaomi API key", + }, + { + optionKey: "minimaxApiKey", + authChoice: "minimax-api", + cliFlag: "--minimax-api-key", + cliOption: "--minimax-api-key ", + description: "MiniMax API key", + }, + { + optionKey: "syntheticApiKey", + authChoice: "synthetic-api-key", + cliFlag: "--synthetic-api-key", + cliOption: "--synthetic-api-key ", + description: "Synthetic API key", + }, + { + optionKey: "veniceApiKey", + authChoice: "venice-api-key", + cliFlag: "--venice-api-key", + cliOption: "--venice-api-key ", + description: "Venice API key", + }, + { + optionKey: "togetherApiKey", + authChoice: "together-api-key", + cliFlag: "--together-api-key", + cliOption: "--together-api-key ", + description: "Together AI API key", + }, + { + optionKey: "huggingfaceApiKey", + authChoice: "huggingface-api-key", + cliFlag: "--huggingface-api-key", + cliOption: "--huggingface-api-key ", + description: "Hugging Face API key (HF token)", + }, + { + optionKey: "opencodeZenApiKey", + authChoice: "opencode-zen", + cliFlag: "--opencode-zen-api-key", + cliOption: "--opencode-zen-api-key ", + description: "OpenCode Zen API key", + }, + { + optionKey: "xaiApiKey", + authChoice: "xai-api-key", + cliFlag: "--xai-api-key", + cliOption: "--xai-api-key ", + description: "xAI API key", + }, + { + optionKey: "litellmApiKey", + authChoice: "litellm-api-key", + cliFlag: "--litellm-api-key", + cliOption: "--litellm-api-key ", + description: "LiteLLM API key", + }, + { + optionKey: "qianfanApiKey", + authChoice: "qianfan-api-key", + cliFlag: "--qianfan-api-key", + cliOption: "--qianfan-api-key ", + description: "QIANFAN API key", + }, +]; diff --git a/src/commands/onboard-types.ts b/src/commands/onboard-types.ts index d21a3ff2b82..4aa2e339efa 100644 --- a/src/commands/onboard-types.ts +++ b/src/commands/onboard-types.ts @@ -50,6 +50,7 @@ export type AuthChoice = export type AuthChoiceGroupId = | "openai" | "anthropic" + | "chutes" | "vllm" | "google" | "copilot" diff --git a/src/commands/onboard.ts b/src/commands/onboard.ts index b17d039201d..47653b29675 100644 --- a/src/commands/onboard.ts +++ b/src/commands/onboard.ts @@ -5,33 +5,29 @@ import { readConfigFileSnapshot } from "../config/config.js"; import { assertSupportedRuntime } from "../infra/runtime-guard.js"; import { defaultRuntime } from "../runtime.js"; import { resolveUserPath } from "../utils.js"; +import { isDeprecatedAuthChoice, normalizeLegacyOnboardAuthChoice } from "./auth-choice-legacy.js"; import { DEFAULT_WORKSPACE, handleReset } from "./onboard-helpers.js"; import { runInteractiveOnboarding } from "./onboard-interactive.js"; import { runNonInteractiveOnboarding } from "./onboard-non-interactive.js"; export async function onboardCommand(opts: OnboardOptions, runtime: RuntimeEnv = defaultRuntime) { assertSupportedRuntime(runtime); - const authChoice = opts.authChoice === "oauth" ? ("setup-token" as const) : opts.authChoice; - const normalizedAuthChoice = - authChoice === "claude-cli" - ? ("setup-token" as const) - : authChoice === "codex-cli" - ? ("openai-codex" as const) - : authChoice; - if (opts.nonInteractive && (authChoice === "claude-cli" || authChoice === "codex-cli")) { + const originalAuthChoice = opts.authChoice; + const normalizedAuthChoice = normalizeLegacyOnboardAuthChoice(originalAuthChoice); + if (opts.nonInteractive && isDeprecatedAuthChoice(originalAuthChoice)) { runtime.error( [ - `Auth choice "${authChoice}" is deprecated.`, + `Auth choice "${String(originalAuthChoice)}" is deprecated.`, 'Use "--auth-choice token" (Anthropic setup-token) or "--auth-choice openai-codex".', ].join("\n"), ); runtime.exit(1); return; } - if (authChoice === "claude-cli") { + if (originalAuthChoice === "claude-cli") { runtime.log('Auth choice "claude-cli" is deprecated; using setup-token flow instead.'); } - if (authChoice === "codex-cli") { + if (originalAuthChoice === "codex-cli") { runtime.log('Auth choice "codex-cli" is deprecated; using OpenAI Codex OAuth instead.'); } const flow = opts.flow === "manual" ? ("advanced" as const) : opts.flow;