From eab9dc538a516aa7a7f1ad9325a8fa699e676a13 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 14 Feb 2026 05:51:01 +0100 Subject: [PATCH] refactor(onboard): unify auth-choice catalog for CLI help --- src/cli/program/register.onboard.ts | 12 +- src/commands/auth-choice-options.e2e.test.ts | 30 +- src/commands/auth-choice-options.ts | 293 ++++++++++--------- 3 files changed, 196 insertions(+), 139 deletions(-) diff --git a/src/cli/program/register.onboard.ts b/src/cli/program/register.onboard.ts index 38096aa5c..1d34cc99b 100644 --- a/src/cli/program/register.onboard.ts +++ b/src/cli/program/register.onboard.ts @@ -7,6 +7,7 @@ import type { NodeManagerChoice, TailscaleMode, } from "../../commands/onboard-types.js"; +import { formatAuthChoiceChoicesForCli } from "../../commands/auth-choice-options.js"; import { onboardCommand } from "../../commands/onboard.js"; import { defaultRuntime } from "../../runtime.js"; import { formatDocsLink } from "../../terminal/links.js"; @@ -37,6 +38,11 @@ function resolveInstallDaemonFlag( return undefined; } +const AUTH_CHOICE_HELP = formatAuthChoiceChoicesForCli({ + includeLegacyAliases: true, + includeSkip: true, +}); + export function registerOnboardCommand(program: Command) { program .command("onboard") @@ -56,11 +62,7 @@ export function registerOnboardCommand(program: Command) { ) .option("--flow ", "Wizard flow: quickstart|advanced|manual") .option("--mode ", "Wizard mode: local|remote") - .option( - "--auth-choice ", - "Auth: setup-token|token|chutes|openai-codex|openai-api-key|xai-api-key|qianfan-api-key|openrouter-api-key|litellm-api-key|ai-gateway-api-key|cloudflare-ai-gateway-api-key|moonshot-api-key|moonshot-api-key-cn|kimi-code-api-key|synthetic-api-key|venice-api-key|gemini-api-key|zai-api-key|zai-coding-global|zai-coding-cn|zai-global|zai-cn|xiaomi-api-key|apiKey|minimax-api|minimax-api-lightning|opencode-zen|custom-api-key|skip|together-api-key|huggingface-api-key", - "Auth: setup-token|token|chutes|vllm|openai-codex|openai-api-key|xai-api-key|qianfan-api-key|openrouter-api-key|litellm-api-key|ai-gateway-api-key|cloudflare-ai-gateway-api-key|moonshot-api-key|moonshot-api-key-cn|kimi-code-api-key|synthetic-api-key|venice-api-key|gemini-api-key|zai-api-key|zai-coding-global|zai-coding-cn|zai-global|zai-cn|xiaomi-api-key|apiKey|minimax-api|minimax-api-lightning|opencode-zen|custom-api-key|skip|together-api-key|huggingface-api-key", - ) + .option("--auth-choice ", `Auth: ${AUTH_CHOICE_HELP}`) .option( "--token-provider ", "Token provider id (non-interactive; used with --auth-choice token)", diff --git a/src/commands/auth-choice-options.e2e.test.ts b/src/commands/auth-choice-options.e2e.test.ts index b639853e6..b54e9edad 100644 --- a/src/commands/auth-choice-options.e2e.test.ts +++ b/src/commands/auth-choice-options.e2e.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; import type { AuthProfileStore } from "../agents/auth-profiles.js"; -import { buildAuthChoiceOptions } from "./auth-choice-options.js"; +import { buildAuthChoiceOptions, formatAuthChoiceChoicesForCli } from "./auth-choice-options.js"; describe("buildAuthChoiceOptions", () => { it("includes GitHub Copilot", () => { @@ -144,4 +144,32 @@ describe("buildAuthChoiceOptions", () => { expect(options.some((opt) => opt.value === "vllm")).toBe(true); }); + + it("builds cli help choices from the same catalog", () => { + const store: AuthProfileStore = { version: 1, profiles: {} }; + const options = buildAuthChoiceOptions({ + store, + includeSkip: true, + }); + const cliChoices = formatAuthChoiceChoicesForCli({ + includeLegacyAliases: false, + includeSkip: true, + }).split("|"); + + for (const option of options) { + expect(cliChoices).toContain(option.value); + } + }); + + it("can include legacy aliases in cli help choices", () => { + const cliChoices = formatAuthChoiceChoicesForCli({ + includeLegacyAliases: true, + includeSkip: true, + }).split("|"); + + expect(cliChoices).toContain("setup-token"); + expect(cliChoices).toContain("oauth"); + expect(cliChoices).toContain("claude-cli"); + expect(cliChoices).toContain("codex-cli"); + }); }); diff --git a/src/commands/auth-choice-options.ts b/src/commands/auth-choice-options.ts index 5d9ca2e55..59f4fd334 100644 --- a/src/commands/auth-choice-options.ts +++ b/src/commands/auth-choice-options.ts @@ -155,144 +155,171 @@ const AUTH_CHOICE_GROUP_DEFS: { }, ]; +const BASE_AUTH_CHOICE_OPTIONS: ReadonlyArray = [ + { + value: "token", + label: "Anthropic token (paste setup-token)", + hint: "run `claude setup-token` elsewhere, then paste the token here", + }, + { + value: "openai-codex", + label: "OpenAI Codex (ChatGPT OAuth)", + }, + { value: "chutes", label: "Chutes (OAuth)" }, + { + value: "vllm", + label: "vLLM (custom URL + model)", + hint: "Local/self-hosted OpenAI-compatible server", + }, + { value: "openai-api-key", label: "OpenAI API key" }, + { value: "xai-api-key", label: "xAI (Grok) API key" }, + { + value: "qianfan-api-key", + label: "Qianfan API key", + }, + { value: "openrouter-api-key", label: "OpenRouter API key" }, + { + value: "litellm-api-key", + label: "LiteLLM API key", + hint: "Unified gateway for 100+ LLM providers", + }, + { + value: "ai-gateway-api-key", + label: "Vercel AI Gateway API key", + }, + { + value: "cloudflare-ai-gateway-api-key", + label: "Cloudflare AI Gateway", + hint: "Account ID + Gateway ID + API key", + }, + { + value: "moonshot-api-key", + label: "Kimi API key (.ai)", + }, + { + value: "moonshot-api-key-cn", + label: "Kimi API key (.cn)", + }, + { + value: "kimi-code-api-key", + label: "Kimi Code API key (subscription)", + }, + { value: "synthetic-api-key", label: "Synthetic API key" }, + { + value: "venice-api-key", + label: "Venice AI API key", + hint: "Privacy-focused inference (uncensored models)", + }, + { + value: "together-api-key", + label: "Together AI API key", + hint: "Access to Llama, DeepSeek, Qwen, and more open models", + }, + { + value: "huggingface-api-key", + label: "Hugging Face API key (HF token)", + hint: "Inference Providers — OpenAI-compatible chat", + }, + { + value: "github-copilot", + label: "GitHub Copilot (GitHub device login)", + hint: "Uses GitHub device flow", + }, + { value: "gemini-api-key", label: "Google Gemini API key" }, + { + value: "google-antigravity", + label: "Google Antigravity OAuth", + hint: "Uses the bundled Antigravity auth plugin", + }, + { + value: "google-gemini-cli", + label: "Google Gemini CLI OAuth", + hint: "Uses the bundled Gemini CLI auth plugin", + }, + { value: "zai-api-key", label: "Z.AI API key" }, + { + value: "zai-coding-global", + label: "Coding-Plan-Global", + hint: "GLM Coding Plan Global (api.z.ai)", + }, + { + value: "zai-coding-cn", + label: "Coding-Plan-CN", + hint: "GLM Coding Plan CN (open.bigmodel.cn)", + }, + { + value: "zai-global", + label: "Global", + hint: "Z.AI Global (api.z.ai)", + }, + { + value: "zai-cn", + label: "CN", + hint: "Z.AI CN (open.bigmodel.cn)", + }, + { + value: "xiaomi-api-key", + label: "Xiaomi API key", + }, + { + value: "minimax-portal", + label: "MiniMax OAuth", + hint: "Oauth plugin for MiniMax", + }, + { value: "qwen-portal", label: "Qwen OAuth" }, + { + value: "copilot-proxy", + label: "Copilot Proxy (local)", + hint: "Local proxy for VS Code Copilot models", + }, + { value: "apiKey", label: "Anthropic API key" }, + { + value: "opencode-zen", + label: "OpenCode Zen (multi-model proxy)", + hint: "Claude, GPT, Gemini via opencode.ai/zen", + }, + { value: "minimax-api", label: "MiniMax M2.5" }, + { + value: "minimax-api-lightning", + label: "MiniMax M2.5 Lightning", + hint: "Faster, higher output cost", + }, + { 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; +}): string { + const includeSkip = params?.includeSkip ?? true; + const includeLegacyAliases = params?.includeLegacyAliases ?? false; + const values = BASE_AUTH_CHOICE_OPTIONS.map((opt) => opt.value); + + if (includeSkip) { + values.push("skip"); + } + if (includeLegacyAliases) { + values.push(...LEGACY_AUTH_CHOICE_ALIASES); + } + + return values.join("|"); +} + export function buildAuthChoiceOptions(params: { store: AuthProfileStore; includeSkip: boolean; }): AuthChoiceOption[] { void params.store; - const options: AuthChoiceOption[] = []; - - options.push({ - value: "token", - label: "Anthropic token (paste setup-token)", - hint: "run `claude setup-token` elsewhere, then paste the token here", - }); - - options.push({ - value: "openai-codex", - label: "OpenAI Codex (ChatGPT OAuth)", - }); - options.push({ value: "chutes", label: "Chutes (OAuth)" }); - options.push({ - value: "vllm", - label: "vLLM (custom URL + model)", - hint: "Local/self-hosted OpenAI-compatible server", - }); - options.push({ value: "openai-api-key", label: "OpenAI API key" }); - options.push({ value: "xai-api-key", label: "xAI (Grok) API key" }); - options.push({ - value: "qianfan-api-key", - label: "Qianfan API key", - }); - options.push({ value: "openrouter-api-key", label: "OpenRouter API key" }); - options.push({ - value: "litellm-api-key", - label: "LiteLLM API key", - hint: "Unified gateway for 100+ LLM providers", - }); - options.push({ - value: "ai-gateway-api-key", - label: "Vercel AI Gateway API key", - }); - options.push({ - value: "cloudflare-ai-gateway-api-key", - label: "Cloudflare AI Gateway", - hint: "Account ID + Gateway ID + API key", - }); - options.push({ - value: "moonshot-api-key", - label: "Kimi API key (.ai)", - }); - options.push({ - value: "moonshot-api-key-cn", - label: "Kimi API key (.cn)", - }); - options.push({ - value: "kimi-code-api-key", - label: "Kimi Code API key (subscription)", - }); - options.push({ value: "synthetic-api-key", label: "Synthetic API key" }); - options.push({ - value: "venice-api-key", - label: "Venice AI API key", - hint: "Privacy-focused inference (uncensored models)", - }); - options.push({ - value: "together-api-key", - label: "Together AI API key", - hint: "Access to Llama, DeepSeek, Qwen, and more open models", - }); - options.push({ - value: "huggingface-api-key", - label: "Hugging Face API key (HF token)", - hint: "Inference Providers — OpenAI-compatible chat", - }); - options.push({ - value: "github-copilot", - label: "GitHub Copilot (GitHub device login)", - hint: "Uses GitHub device flow", - }); - options.push({ value: "gemini-api-key", label: "Google Gemini API key" }); - options.push({ - value: "google-antigravity", - label: "Google Antigravity OAuth", - hint: "Uses the bundled Antigravity auth plugin", - }); - options.push({ - value: "google-gemini-cli", - label: "Google Gemini CLI OAuth", - hint: "Uses the bundled Gemini CLI auth plugin", - }); - options.push({ value: "zai-api-key", label: "Z.AI API key" }); - options.push({ - value: "zai-coding-global", - label: "Coding-Plan-Global", - hint: "GLM Coding Plan Global (api.z.ai)", - }); - options.push({ - value: "zai-coding-cn", - label: "Coding-Plan-CN", - hint: "GLM Coding Plan CN (open.bigmodel.cn)", - }); - options.push({ - value: "zai-global", - label: "Global", - hint: "Z.AI Global (api.z.ai)", - }); - options.push({ - value: "zai-cn", - label: "CN", - hint: "Z.AI CN (open.bigmodel.cn)", - }); - options.push({ - value: "xiaomi-api-key", - label: "Xiaomi API key", - }); - options.push({ - value: "minimax-portal", - label: "MiniMax OAuth", - hint: "Oauth plugin for MiniMax", - }); - options.push({ value: "qwen-portal", label: "Qwen OAuth" }); - options.push({ - value: "copilot-proxy", - label: "Copilot Proxy (local)", - hint: "Local proxy for VS Code Copilot models", - }); - options.push({ value: "apiKey", label: "Anthropic API key" }); - // Token flow is currently Anthropic-only; use CLI for advanced providers. - options.push({ - value: "opencode-zen", - label: "OpenCode Zen (multi-model proxy)", - hint: "Claude, GPT, Gemini via opencode.ai/zen", - }); - options.push({ value: "minimax-api", label: "MiniMax M2.5" }); - options.push({ - value: "minimax-api-lightning", - label: "MiniMax M2.5 Lightning", - hint: "Faster, higher output cost", - }); - options.push({ value: "custom-api-key", label: "Custom Provider" }); + const options: AuthChoiceOption[] = [...BASE_AUTH_CHOICE_OPTIONS]; if (params.includeSkip) { options.push({ value: "skip", label: "Skip for now" });