CLI: add OpenRouter auth choice
This commit is contained in:
committed by
Peter Steinberger
parent
cffec07329
commit
b6982236a6
@@ -156,6 +156,29 @@ describe("cli program", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("passes openrouter api key to onboard", async () => {
|
||||
const program = buildProgram();
|
||||
await program.parseAsync(
|
||||
[
|
||||
"onboard",
|
||||
"--non-interactive",
|
||||
"--auth-choice",
|
||||
"openrouter-api-key",
|
||||
"--openrouter-api-key",
|
||||
"sk-openrouter-test",
|
||||
],
|
||||
{ from: "user" },
|
||||
);
|
||||
expect(onboardCommand).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
nonInteractive: true,
|
||||
authChoice: "openrouter-api-key",
|
||||
openrouterApiKey: "sk-openrouter-test",
|
||||
}),
|
||||
runtime,
|
||||
);
|
||||
});
|
||||
|
||||
it("passes zai api key to onboard", async () => {
|
||||
const program = buildProgram();
|
||||
await program.parseAsync(
|
||||
|
||||
@@ -249,7 +249,7 @@ export function buildProgram() {
|
||||
.option("--mode <mode>", "Wizard mode: local|remote")
|
||||
.option(
|
||||
"--auth-choice <choice>",
|
||||
"Auth: setup-token|claude-cli|token|openai-codex|openai-api-key|codex-cli|antigravity|gemini-api-key|zai-api-key|apiKey|minimax-cloud|minimax-api|minimax|opencode-zen|skip",
|
||||
"Auth: setup-token|claude-cli|token|openai-codex|openai-api-key|openrouter-api-key|codex-cli|antigravity|gemini-api-key|zai-api-key|apiKey|minimax-cloud|minimax-api|minimax|opencode-zen|skip",
|
||||
)
|
||||
.option(
|
||||
"--token-provider <id>",
|
||||
@@ -269,6 +269,7 @@ export function buildProgram() {
|
||||
)
|
||||
.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("--gemini-api-key <key>", "Gemini API key")
|
||||
.option("--zai-api-key <key>", "Z.AI API key")
|
||||
.option("--minimax-api-key <key>", "MiniMax API key")
|
||||
@@ -315,6 +316,7 @@ export function buildProgram() {
|
||||
| "token"
|
||||
| "openai-codex"
|
||||
| "openai-api-key"
|
||||
| "openrouter-api-key"
|
||||
| "codex-cli"
|
||||
| "antigravity"
|
||||
| "gemini-api-key"
|
||||
@@ -332,6 +334,7 @@ export function buildProgram() {
|
||||
tokenExpiresIn: opts.tokenExpiresIn as string | undefined,
|
||||
anthropicApiKey: opts.anthropicApiKey as string | undefined,
|
||||
openaiApiKey: opts.openaiApiKey as string | undefined,
|
||||
openrouterApiKey: opts.openrouterApiKey as string | undefined,
|
||||
geminiApiKey: opts.geminiApiKey as string | undefined,
|
||||
zaiApiKey: opts.zaiApiKey as string | undefined,
|
||||
minimaxApiKey: opts.minimaxApiKey as string | undefined,
|
||||
|
||||
@@ -92,6 +92,7 @@ export function buildAuthChoiceOptions(params: {
|
||||
label: "OpenAI Codex (ChatGPT OAuth)",
|
||||
});
|
||||
options.push({ value: "openai-api-key", label: "OpenAI API key" });
|
||||
options.push({ value: "openrouter-api-key", label: "OpenRouter API key" });
|
||||
options.push({
|
||||
value: "antigravity",
|
||||
label: "Google Antigravity (Claude Opus 4.5, Gemini 3, etc.)",
|
||||
|
||||
@@ -42,13 +42,17 @@ import {
|
||||
applyMinimaxHostedConfig,
|
||||
applyMinimaxHostedProviderConfig,
|
||||
applyMinimaxProviderConfig,
|
||||
applyOpenrouterConfig,
|
||||
applyOpenrouterProviderConfig,
|
||||
applyOpencodeZenConfig,
|
||||
applyOpencodeZenProviderConfig,
|
||||
applyZaiConfig,
|
||||
MINIMAX_HOSTED_MODEL_REF,
|
||||
OPENROUTER_DEFAULT_MODEL_REF,
|
||||
setAnthropicApiKey,
|
||||
setGeminiApiKey,
|
||||
setMinimaxApiKey,
|
||||
setOpenrouterApiKey,
|
||||
setOpencodeZenApiKey,
|
||||
setZaiApiKey,
|
||||
writeOAuthCredentials,
|
||||
@@ -366,6 +370,28 @@ export async function applyAuthChoice(params: {
|
||||
`Saved OPENAI_API_KEY to ${result.path} for launchd compatibility.`,
|
||||
"OpenAI API key",
|
||||
);
|
||||
} else if (params.authChoice === "openrouter-api-key") {
|
||||
const key = await params.prompter.text({
|
||||
message: "Enter OpenRouter API key",
|
||||
validate: (value) => (value?.trim() ? undefined : "Required"),
|
||||
});
|
||||
await setOpenrouterApiKey(String(key).trim(), params.agentDir);
|
||||
nextConfig = applyAuthProfileConfig(nextConfig, {
|
||||
profileId: "openrouter:default",
|
||||
provider: "openrouter",
|
||||
mode: "api_key",
|
||||
});
|
||||
if (params.setDefaultModel) {
|
||||
nextConfig = applyOpenrouterConfig(nextConfig);
|
||||
await params.prompter.note(
|
||||
`Default model set to ${OPENROUTER_DEFAULT_MODEL_REF}`,
|
||||
"Model configured",
|
||||
);
|
||||
} else {
|
||||
nextConfig = applyOpenrouterProviderConfig(nextConfig);
|
||||
agentModelOverride = OPENROUTER_DEFAULT_MODEL_REF;
|
||||
await noteAgentModel(OPENROUTER_DEFAULT_MODEL_REF);
|
||||
}
|
||||
} else if (params.authChoice === "openai-codex") {
|
||||
const isRemote = isRemoteEnvironment();
|
||||
await params.prompter.note(
|
||||
@@ -745,6 +771,8 @@ export function resolvePreferredProviderForAuthChoice(
|
||||
return "openai-codex";
|
||||
case "openai-api-key":
|
||||
return "openai";
|
||||
case "openrouter-api-key":
|
||||
return "openrouter";
|
||||
case "gemini-api-key":
|
||||
return "google";
|
||||
case "antigravity":
|
||||
|
||||
@@ -131,6 +131,7 @@ export async function setMinimaxApiKey(key: string, agentDir?: string) {
|
||||
}
|
||||
|
||||
export const ZAI_DEFAULT_MODEL_REF = "zai/glm-4.7";
|
||||
export const OPENROUTER_DEFAULT_MODEL_REF = "openrouter/auto";
|
||||
|
||||
export async function setZaiApiKey(key: string, agentDir?: string) {
|
||||
// Write to the multi-agent path so gateway finds credentials on startup
|
||||
@@ -145,6 +146,18 @@ export async function setZaiApiKey(key: string, agentDir?: string) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function setOpenrouterApiKey(key: string, agentDir?: string) {
|
||||
upsertAuthProfile({
|
||||
profileId: "openrouter:default",
|
||||
credential: {
|
||||
type: "api_key",
|
||||
provider: "openrouter",
|
||||
key,
|
||||
},
|
||||
agentDir: agentDir ?? resolveDefaultAgentDir(),
|
||||
});
|
||||
}
|
||||
|
||||
export function applyZaiConfig(cfg: ClawdbotConfig): ClawdbotConfig {
|
||||
const models = { ...cfg.agents?.defaults?.models };
|
||||
models[ZAI_DEFAULT_MODEL_REF] = {
|
||||
@@ -175,6 +188,50 @@ export function applyZaiConfig(cfg: ClawdbotConfig): ClawdbotConfig {
|
||||
};
|
||||
}
|
||||
|
||||
export function applyOpenrouterProviderConfig(
|
||||
cfg: ClawdbotConfig,
|
||||
): ClawdbotConfig {
|
||||
const models = { ...cfg.agents?.defaults?.models };
|
||||
models[OPENROUTER_DEFAULT_MODEL_REF] = {
|
||||
...models[OPENROUTER_DEFAULT_MODEL_REF],
|
||||
alias: models[OPENROUTER_DEFAULT_MODEL_REF]?.alias ?? "OpenRouter",
|
||||
};
|
||||
|
||||
return {
|
||||
...cfg,
|
||||
agents: {
|
||||
...cfg.agents,
|
||||
defaults: {
|
||||
...cfg.agents?.defaults,
|
||||
models,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function applyOpenrouterConfig(cfg: ClawdbotConfig): ClawdbotConfig {
|
||||
const next = applyOpenrouterProviderConfig(cfg);
|
||||
const existingModel = next.agents?.defaults?.model;
|
||||
return {
|
||||
...next,
|
||||
agents: {
|
||||
...next.agents,
|
||||
defaults: {
|
||||
...next.agents?.defaults,
|
||||
model: {
|
||||
...(existingModel &&
|
||||
"fallbacks" in (existingModel as Record<string, unknown>)
|
||||
? {
|
||||
fallbacks: (existingModel as { fallbacks?: string[] }).fallbacks,
|
||||
}
|
||||
: undefined),
|
||||
primary: OPENROUTER_DEFAULT_MODEL_REF,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function applyAuthProfileConfig(
|
||||
cfg: ClawdbotConfig,
|
||||
params: {
|
||||
|
||||
@@ -35,11 +35,13 @@ import {
|
||||
applyMinimaxApiConfig,
|
||||
applyMinimaxConfig,
|
||||
applyMinimaxHostedConfig,
|
||||
applyOpenrouterConfig,
|
||||
applyOpencodeZenConfig,
|
||||
applyZaiConfig,
|
||||
setAnthropicApiKey,
|
||||
setGeminiApiKey,
|
||||
setMinimaxApiKey,
|
||||
setOpenrouterApiKey,
|
||||
setOpencodeZenApiKey,
|
||||
setZaiApiKey,
|
||||
} from "./onboard-auth.js";
|
||||
@@ -264,6 +266,25 @@ export async function runNonInteractiveOnboarding(
|
||||
});
|
||||
process.env.OPENAI_API_KEY = key;
|
||||
runtime.log(`Saved OPENAI_API_KEY to ${result.path}`);
|
||||
} else if (authChoice === "openrouter-api-key") {
|
||||
const resolved = await resolveNonInteractiveApiKey({
|
||||
provider: "openrouter",
|
||||
cfg: baseConfig,
|
||||
flagValue: opts.openrouterApiKey,
|
||||
flagName: "--openrouter-api-key",
|
||||
envVar: "OPENROUTER_API_KEY",
|
||||
runtime,
|
||||
});
|
||||
if (!resolved) return;
|
||||
if (resolved.source !== "profile") {
|
||||
await setOpenrouterApiKey(resolved.key);
|
||||
}
|
||||
nextConfig = applyAuthProfileConfig(nextConfig, {
|
||||
profileId: "openrouter:default",
|
||||
provider: "openrouter",
|
||||
mode: "api_key",
|
||||
});
|
||||
nextConfig = applyOpenrouterConfig(nextConfig);
|
||||
} else if (authChoice === "minimax-cloud") {
|
||||
const resolved = await resolveNonInteractiveApiKey({
|
||||
provider: "minimax",
|
||||
|
||||
@@ -10,6 +10,7 @@ export type AuthChoice =
|
||||
| "token"
|
||||
| "openai-codex"
|
||||
| "openai-api-key"
|
||||
| "openrouter-api-key"
|
||||
| "codex-cli"
|
||||
| "antigravity"
|
||||
| "apiKey"
|
||||
@@ -43,6 +44,7 @@ export type OnboardOptions = {
|
||||
tokenExpiresIn?: string;
|
||||
anthropicApiKey?: string;
|
||||
openaiApiKey?: string;
|
||||
openrouterApiKey?: string;
|
||||
geminiApiKey?: string;
|
||||
zaiApiKey?: string;
|
||||
minimaxApiKey?: string;
|
||||
|
||||
Reference in New Issue
Block a user