From 2d75be0ea7af3f995f8e6fa751f9b39e8048f8f6 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Mon, 6 Apr 2026 12:52:24 +0100 Subject: [PATCH] fix(onboard): move provider auth ids out of core types --- src/commands/onboard-core-auth-flags.ts | 2 +- .../local/auth-choice-inference.test.ts | 49 +++++++ .../local/auth-choice-inference.ts | 2 +- src/commands/onboard-types.ts | 129 ++---------------- 4 files changed, 66 insertions(+), 116 deletions(-) create mode 100644 src/commands/onboard-non-interactive/local/auth-choice-inference.test.ts diff --git a/src/commands/onboard-core-auth-flags.ts b/src/commands/onboard-core-auth-flags.ts index 6b1c865de69..140652c8668 100644 --- a/src/commands/onboard-core-auth-flags.ts +++ b/src/commands/onboard-core-auth-flags.ts @@ -1,6 +1,6 @@ import type { AuthChoice, OnboardOptions } from "./onboard-types.js"; -type OnboardCoreAuthOptionKey = keyof Pick; +type OnboardCoreAuthOptionKey = Extract; export type OnboardCoreAuthFlag = { optionKey: OnboardCoreAuthOptionKey; diff --git a/src/commands/onboard-non-interactive/local/auth-choice-inference.test.ts b/src/commands/onboard-non-interactive/local/auth-choice-inference.test.ts new file mode 100644 index 00000000000..79ae35a5341 --- /dev/null +++ b/src/commands/onboard-non-interactive/local/auth-choice-inference.test.ts @@ -0,0 +1,49 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import type { OnboardOptions } from "../../onboard-types.js"; +import { inferAuthChoiceFromFlags } from "./auth-choice-inference.js"; + +const resolveManifestProviderOnboardAuthFlags = vi.hoisted(() => + vi.fn< + () => ReadonlyArray<{ + optionKey: string; + authChoice: string; + cliFlag: string; + }> + >(() => []), +); + +vi.mock("../../../plugins/provider-auth-choices.js", () => ({ + resolveManifestProviderOnboardAuthFlags, +})); + +describe("inferAuthChoiceFromFlags", () => { + beforeEach(() => { + resolveManifestProviderOnboardAuthFlags.mockReset(); + resolveManifestProviderOnboardAuthFlags.mockReturnValue([]); + }); + + it("infers plugin-owned auth choices from manifest option keys", () => { + resolveManifestProviderOnboardAuthFlags.mockReturnValue([ + { + optionKey: "pluginOwnedApiKey", + authChoice: "plugin-api-key", + cliFlag: "--plugin-api-key", + }, + ]); + + const opts: OnboardOptions = { + pluginOwnedApiKey: "sk-plugin-test", + }; + + expect(inferAuthChoiceFromFlags(opts)).toEqual({ + choice: "plugin-api-key", + matches: [ + { + optionKey: "pluginOwnedApiKey", + authChoice: "plugin-api-key", + label: "--plugin-api-key", + }, + ], + }); + }); +}); 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 cb6470666bd..a74b53dc369 100644 --- a/src/commands/onboard-non-interactive/local/auth-choice-inference.ts +++ b/src/commands/onboard-non-interactive/local/auth-choice-inference.ts @@ -41,7 +41,7 @@ export function inferAuthChoiceFromFlags( cliFlag: string; }>; const matches: AuthChoiceFlag[] = flags - .filter(({ optionKey }) => hasStringValue(opts[optionKey as keyof OnboardOptions])) + .filter(({ optionKey }) => hasStringValue(opts[optionKey])) .map((flag) => ({ optionKey: flag.optionKey, authChoice: flag.authChoice as AuthChoice, diff --git a/src/commands/onboard-types.ts b/src/commands/onboard-types.ts index fcf95dd40fc..e8385b96ae7 100644 --- a/src/commands/onboard-types.ts +++ b/src/commands/onboard-types.ts @@ -3,90 +3,12 @@ import type { SecretInputMode } from "../plugins/provider-auth-types.js"; import type { GatewayDaemonRuntime } from "./daemon-runtime.js"; export type OnboardMode = "local" | "remote"; -export type BuiltInAuthChoice = - // Legacy alias for `setup-token` (kept for backwards CLI compatibility). - | "oauth" - | "setup-token" - | "token" - | "chutes" - | "deepseek-api-key" - | "openai-codex" - | "openai-api-key" - | "openrouter-api-key" - | "kilocode-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" - | "together-api-key" - | "huggingface-api-key" - | "apiKey" - | "gemini-api-key" - | "zai-api-key" - | "zai-coding-global" - | "zai-coding-cn" - | "zai-global" - | "zai-cn" - | "xiaomi-api-key" - | "minimax-global-oauth" - | "minimax-global-api" - | "minimax-cn-oauth" - | "minimax-cn-api" - | "opencode-zen" - | "opencode-go" - | "github-copilot" - | "copilot-proxy" - | "xai-api-key" - | "mistral-api-key" - | "volcengine-api-key" - | "byteplus-api-key" - | "qianfan-api-key" - | "qwen-standard-api-key-cn" - | "qwen-standard-api-key" - | "qwen-api-key-cn" - | "qwen-api-key" - | "modelstudio-standard-api-key-cn" - | "modelstudio-standard-api-key" - | "modelstudio-api-key-cn" - | "modelstudio-api-key" - | "custom-api-key" - | "skip"; -export type AuthChoice = BuiltInAuthChoice | (string & {}); - -export type BuiltInAuthChoiceGroupId = - | "openai" - | "anthropic" - | "chutes" - | "deepseek" - | "google" - | "copilot" - | "openrouter" - | "kilocode" - | "litellm" - | "ai-gateway" - | "cloudflare-ai-gateway" - | "moonshot" - | "zai" - | "xiaomi" - | "opencode" - | "minimax" - | "synthetic" - | "venice" - | "mistral" - | "together" - | "huggingface" - | "qianfan" - | "qwen" - | "modelstudio" - | "xai" - | "volcengine" - | "byteplus" - | "custom"; -export type AuthChoiceGroupId = BuiltInAuthChoiceGroupId | (string & {}); +/** + * Auth choices are plugin-owned contract ids plus a few legacy aliases that + * are normalized elsewhere (for example `oauth` -> `setup-token`). + */ +export type AuthChoice = string & {}; +export type AuthChoiceGroupId = string & {}; export type GatewayAuthChoice = "token" | "password"; export type ResetScope = "config" | "config+creds+sessions" | "full"; export type GatewayBind = "loopback" | "lan" | "auto" | "custom" | "tailnet"; @@ -97,7 +19,15 @@ export type ChannelChoice = ChannelId; export type ProviderChoice = ChannelChoice; export type { SecretInputMode } from "../plugins/provider-auth-types.js"; -export type OnboardOptions = { +type OnboardDynamicProviderOptions = { + /** + * Provider-specific non-interactive auth flags are plugin-owned and keyed by + * manifest `providerAuthChoices[].optionKey` values. + */ + [optionKey: string]: unknown; +}; + +export type OnboardOptions = OnboardDynamicProviderOptions & { mode?: OnboardMode; /** "manual" is an alias for "advanced". */ flow?: "quickstart" | "advanced" | "manual"; @@ -118,37 +48,8 @@ export type OnboardOptions = { tokenExpiresIn?: string; /** API key persistence mode for setup flows (default: plaintext). */ secretInputMode?: SecretInputMode; - anthropicApiKey?: string; - deepseekApiKey?: string; - openaiApiKey?: string; - mistralApiKey?: string; - openrouterApiKey?: string; - kilocodeApiKey?: string; - litellmApiKey?: string; - aiGatewayApiKey?: string; cloudflareAiGatewayAccountId?: string; cloudflareAiGatewayGatewayId?: string; - cloudflareAiGatewayApiKey?: string; - moonshotApiKey?: string; - kimiCodeApiKey?: string; - geminiApiKey?: string; - zaiApiKey?: string; - xiaomiApiKey?: string; - minimaxApiKey?: string; - syntheticApiKey?: string; - veniceApiKey?: string; - togetherApiKey?: string; - huggingfaceApiKey?: string; - opencodeZenApiKey?: string; - opencodeGoApiKey?: string; - xaiApiKey?: string; - volcengineApiKey?: string; - byteplusApiKey?: string; - qianfanApiKey?: string; - modelstudioStandardApiKeyCn?: string; - modelstudioStandardApiKey?: string; - modelstudioApiKeyCn?: string; - modelstudioApiKey?: string; customBaseUrl?: string; customApiKey?: string; customModelId?: string;