refactor(onboard): unify auth-choice aliases and provider flags
This commit is contained in:
@@ -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: <provider>:manual)",
|
||||
)
|
||||
.option("--token-expires-in <duration>", "Optional token expiry duration (e.g. 365d, 12h)")
|
||||
.option("--anthropic-api-key <key>", "Anthropic API key")
|
||||
.option("--openai-api-key <key>", "OpenAI API key")
|
||||
.option("--openrouter-api-key <key>", "OpenRouter API key")
|
||||
.option("--ai-gateway-api-key <key>", "Vercel AI Gateway API key")
|
||||
.option("--cloudflare-ai-gateway-account-id <id>", "Cloudflare Account ID")
|
||||
.option("--cloudflare-ai-gateway-gateway-id <id>", "Cloudflare AI Gateway ID")
|
||||
.option("--cloudflare-ai-gateway-api-key <key>", "Cloudflare AI Gateway API key")
|
||||
.option("--moonshot-api-key <key>", "Moonshot API key")
|
||||
.option("--kimi-code-api-key <key>", "Kimi Coding API key")
|
||||
.option("--gemini-api-key <key>", "Gemini API key")
|
||||
.option("--zai-api-key <key>", "Z.AI API key")
|
||||
.option("--xiaomi-api-key <key>", "Xiaomi API key")
|
||||
.option("--minimax-api-key <key>", "MiniMax API key")
|
||||
.option("--synthetic-api-key <key>", "Synthetic API key")
|
||||
.option("--venice-api-key <key>", "Venice API key")
|
||||
.option("--together-api-key <key>", "Together AI API key")
|
||||
.option("--huggingface-api-key <key>", "Hugging Face API key (HF token)")
|
||||
.option("--opencode-zen-api-key <key>", "OpenCode Zen API key")
|
||||
.option("--xai-api-key <key>", "xAI API key")
|
||||
.option("--litellm-api-key <key>", "LiteLLM API key")
|
||||
.option("--qianfan-api-key <key>", "QIANFAN API key")
|
||||
.option("--cloudflare-ai-gateway-gateway-id <id>", "Cloudflare AI Gateway ID");
|
||||
|
||||
for (const providerFlag of ONBOARD_PROVIDER_AUTH_FLAGS) {
|
||||
command.option(providerFlag.cliOption, providerFlag.description);
|
||||
}
|
||||
|
||||
command
|
||||
.option("--custom-base-url <url>", "Custom provider base URL")
|
||||
.option("--custom-api-key <key>", "Custom provider API key (optional)")
|
||||
.option("--custom-model-id <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 <name>", "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,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
28
src/commands/auth-choice-legacy.ts
Normal file
28
src/commands/auth-choice-legacy.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import type { AuthChoice } from "./onboard-types.js";
|
||||
|
||||
export const AUTH_CHOICE_LEGACY_ALIASES_FOR_CLI: ReadonlyArray<AuthChoice> = [
|
||||
"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";
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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<AuthChoiceOption> = [
|
||||
{ value: "custom-api-key", label: "Custom Provider" },
|
||||
];
|
||||
|
||||
const LEGACY_AUTH_CHOICE_ALIASES: ReadonlyArray<AuthChoice> = [
|
||||
"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("|");
|
||||
|
||||
@@ -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<string, { provider?: string; mode?: string }> };
|
||||
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(
|
||||
|
||||
@@ -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<AuthChoiceFlag>;
|
||||
|
||||
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",
|
||||
});
|
||||
|
||||
169
src/commands/onboard-provider-auth-flags.ts
Normal file
169
src/commands/onboard-provider-auth-flags.ts
Normal file
@@ -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} <key>`;
|
||||
description: string;
|
||||
};
|
||||
|
||||
// Shared source for provider API-key flags used by CLI registration + non-interactive inference.
|
||||
export const ONBOARD_PROVIDER_AUTH_FLAGS: ReadonlyArray<OnboardProviderAuthFlag> = [
|
||||
{
|
||||
optionKey: "anthropicApiKey",
|
||||
authChoice: "apiKey",
|
||||
cliFlag: "--anthropic-api-key",
|
||||
cliOption: "--anthropic-api-key <key>",
|
||||
description: "Anthropic API key",
|
||||
},
|
||||
{
|
||||
optionKey: "openaiApiKey",
|
||||
authChoice: "openai-api-key",
|
||||
cliFlag: "--openai-api-key",
|
||||
cliOption: "--openai-api-key <key>",
|
||||
description: "OpenAI API key",
|
||||
},
|
||||
{
|
||||
optionKey: "openrouterApiKey",
|
||||
authChoice: "openrouter-api-key",
|
||||
cliFlag: "--openrouter-api-key",
|
||||
cliOption: "--openrouter-api-key <key>",
|
||||
description: "OpenRouter API key",
|
||||
},
|
||||
{
|
||||
optionKey: "aiGatewayApiKey",
|
||||
authChoice: "ai-gateway-api-key",
|
||||
cliFlag: "--ai-gateway-api-key",
|
||||
cliOption: "--ai-gateway-api-key <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 <key>",
|
||||
description: "Cloudflare AI Gateway API key",
|
||||
},
|
||||
{
|
||||
optionKey: "moonshotApiKey",
|
||||
authChoice: "moonshot-api-key",
|
||||
cliFlag: "--moonshot-api-key",
|
||||
cliOption: "--moonshot-api-key <key>",
|
||||
description: "Moonshot API key",
|
||||
},
|
||||
{
|
||||
optionKey: "kimiCodeApiKey",
|
||||
authChoice: "kimi-code-api-key",
|
||||
cliFlag: "--kimi-code-api-key",
|
||||
cliOption: "--kimi-code-api-key <key>",
|
||||
description: "Kimi Coding API key",
|
||||
},
|
||||
{
|
||||
optionKey: "geminiApiKey",
|
||||
authChoice: "gemini-api-key",
|
||||
cliFlag: "--gemini-api-key",
|
||||
cliOption: "--gemini-api-key <key>",
|
||||
description: "Gemini API key",
|
||||
},
|
||||
{
|
||||
optionKey: "zaiApiKey",
|
||||
authChoice: "zai-api-key",
|
||||
cliFlag: "--zai-api-key",
|
||||
cliOption: "--zai-api-key <key>",
|
||||
description: "Z.AI API key",
|
||||
},
|
||||
{
|
||||
optionKey: "xiaomiApiKey",
|
||||
authChoice: "xiaomi-api-key",
|
||||
cliFlag: "--xiaomi-api-key",
|
||||
cliOption: "--xiaomi-api-key <key>",
|
||||
description: "Xiaomi API key",
|
||||
},
|
||||
{
|
||||
optionKey: "minimaxApiKey",
|
||||
authChoice: "minimax-api",
|
||||
cliFlag: "--minimax-api-key",
|
||||
cliOption: "--minimax-api-key <key>",
|
||||
description: "MiniMax API key",
|
||||
},
|
||||
{
|
||||
optionKey: "syntheticApiKey",
|
||||
authChoice: "synthetic-api-key",
|
||||
cliFlag: "--synthetic-api-key",
|
||||
cliOption: "--synthetic-api-key <key>",
|
||||
description: "Synthetic API key",
|
||||
},
|
||||
{
|
||||
optionKey: "veniceApiKey",
|
||||
authChoice: "venice-api-key",
|
||||
cliFlag: "--venice-api-key",
|
||||
cliOption: "--venice-api-key <key>",
|
||||
description: "Venice API key",
|
||||
},
|
||||
{
|
||||
optionKey: "togetherApiKey",
|
||||
authChoice: "together-api-key",
|
||||
cliFlag: "--together-api-key",
|
||||
cliOption: "--together-api-key <key>",
|
||||
description: "Together AI API key",
|
||||
},
|
||||
{
|
||||
optionKey: "huggingfaceApiKey",
|
||||
authChoice: "huggingface-api-key",
|
||||
cliFlag: "--huggingface-api-key",
|
||||
cliOption: "--huggingface-api-key <key>",
|
||||
description: "Hugging Face API key (HF token)",
|
||||
},
|
||||
{
|
||||
optionKey: "opencodeZenApiKey",
|
||||
authChoice: "opencode-zen",
|
||||
cliFlag: "--opencode-zen-api-key",
|
||||
cliOption: "--opencode-zen-api-key <key>",
|
||||
description: "OpenCode Zen API key",
|
||||
},
|
||||
{
|
||||
optionKey: "xaiApiKey",
|
||||
authChoice: "xai-api-key",
|
||||
cliFlag: "--xai-api-key",
|
||||
cliOption: "--xai-api-key <key>",
|
||||
description: "xAI API key",
|
||||
},
|
||||
{
|
||||
optionKey: "litellmApiKey",
|
||||
authChoice: "litellm-api-key",
|
||||
cliFlag: "--litellm-api-key",
|
||||
cliOption: "--litellm-api-key <key>",
|
||||
description: "LiteLLM API key",
|
||||
},
|
||||
{
|
||||
optionKey: "qianfanApiKey",
|
||||
authChoice: "qianfan-api-key",
|
||||
cliFlag: "--qianfan-api-key",
|
||||
cliOption: "--qianfan-api-key <key>",
|
||||
description: "QIANFAN API key",
|
||||
},
|
||||
];
|
||||
@@ -50,6 +50,7 @@ export type AuthChoice =
|
||||
export type AuthChoiceGroupId =
|
||||
| "openai"
|
||||
| "anthropic"
|
||||
| "chutes"
|
||||
| "vllm"
|
||||
| "google"
|
||||
| "copilot"
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user