From 302d51fd4008f049a16d5862ba3ed22b9d9ff7a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Jan=C4=8Da=C5=99=C3=ADk?= Date: Thu, 8 Jan 2026 14:44:40 +0100 Subject: [PATCH] feat: add Gemini API key onboarding --- docs/cli/index.md | 3 ++- docs/start/wizard.md | 11 +++++++++++ src/cli/program.ts | 5 ++++- src/commands/auth-choice-options.ts | 1 + src/commands/auth-choice.ts | 12 ++++++++++++ src/commands/configure.ts | 16 ++++++++++++++++ src/commands/onboard-auth.ts | 13 +++++++++++++ src/commands/onboard-non-interactive.ts | 14 ++++++++++++++ src/commands/onboard-types.ts | 2 ++ 9 files changed, 75 insertions(+), 2 deletions(-) diff --git a/docs/cli/index.md b/docs/cli/index.md index 9f163018d..c9d19812b 100644 --- a/docs/cli/index.md +++ b/docs/cli/index.md @@ -165,8 +165,9 @@ Options: - `--workspace ` - `--non-interactive` - `--mode ` -- `--auth-choice ` +- `--auth-choice ` - `--anthropic-api-key ` +- `--gemini-api-key ` - `--gateway-port ` - `--gateway-bind ` - `--gateway-auth ` diff --git a/docs/start/wizard.md b/docs/start/wizard.md index 0e1cb6e90..d399d5b3b 100644 --- a/docs/start/wizard.md +++ b/docs/start/wizard.md @@ -170,6 +170,17 @@ clawdbot onboard --non-interactive \ Add `--json` for a machine‑readable summary. +Gemini example: + +```bash +clawdbot onboard --non-interactive \ + --mode local \ + --auth-choice gemini-api-key \ + --gemini-api-key "$GEMINI_API_KEY" \ + --gateway-port 18789 \ + --gateway-bind loopback +``` + Add agent (non‑interactive) example: ```bash diff --git a/src/cli/program.ts b/src/cli/program.ts index 827e3bc0b..bccfd049e 100644 --- a/src/cli/program.ts +++ b/src/cli/program.ts @@ -232,9 +232,10 @@ export function buildProgram() { .option("--mode ", "Wizard mode: local|remote") .option( "--auth-choice ", - "Auth: oauth|claude-cli|openai-codex|codex-cli|antigravity|apiKey|minimax|skip", + "Auth: oauth|claude-cli|openai-codex|codex-cli|antigravity|gemini-api-key|apiKey|minimax|skip", ) .option("--anthropic-api-key ", "Anthropic API key") + .option("--gemini-api-key ", "Gemini API key") .option("--gateway-port ", "Gateway port") .option("--gateway-bind ", "Gateway bind: loopback|lan|tailnet|auto") .option("--gateway-auth ", "Gateway auth: off|token|password") @@ -263,11 +264,13 @@ export function buildProgram() { | "openai-codex" | "codex-cli" | "antigravity" + | "gemini-api-key" | "apiKey" | "minimax" | "skip" | undefined, anthropicApiKey: opts.anthropicApiKey as string | undefined, + geminiApiKey: opts.geminiApiKey as string | undefined, gatewayPort: typeof opts.gatewayPort === "string" ? Number.parseInt(opts.gatewayPort, 10) diff --git a/src/commands/auth-choice-options.ts b/src/commands/auth-choice-options.ts index 0355eb2c7..6c161ad51 100644 --- a/src/commands/auth-choice-options.ts +++ b/src/commands/auth-choice-options.ts @@ -85,6 +85,7 @@ export function buildAuthChoiceOptions(params: { value: "antigravity", label: "Google Antigravity (Claude Opus 4.5, Gemini 3, etc.)", }); + options.push({ value: "gemini-api-key", label: "Google Gemini API key" }); options.push({ value: "apiKey", label: "Anthropic API key" }); options.push({ value: "minimax", label: "Minimax M2.1 (LM Studio)" }); if (params.includeSkip) { diff --git a/src/commands/auth-choice.ts b/src/commands/auth-choice.ts index 36c4d0fe8..9407be5d9 100644 --- a/src/commands/auth-choice.ts +++ b/src/commands/auth-choice.ts @@ -30,6 +30,7 @@ import { applyMinimaxConfig, applyMinimaxProviderConfig, setAnthropicApiKey, + setGeminiApiKey, writeOAuthCredentials, } from "./onboard-auth.js"; import { openUrl } from "./onboard-helpers.js"; @@ -415,6 +416,17 @@ export async function applyAuthChoice(params: { "OAuth help", ); } + } else if (params.authChoice === "gemini-api-key") { + const key = await params.prompter.text({ + message: "Enter Gemini API key", + validate: (value) => (value?.trim() ? undefined : "Required"), + }); + await setGeminiApiKey(String(key).trim(), params.agentDir); + nextConfig = applyAuthProfileConfig(nextConfig, { + profileId: "google:default", + provider: "google", + mode: "api_key", + }); } else if (params.authChoice === "apiKey") { const key = await params.prompter.text({ message: "Enter Anthropic API key", diff --git a/src/commands/configure.ts b/src/commands/configure.ts index eab3a7934..3c747f316 100644 --- a/src/commands/configure.ts +++ b/src/commands/configure.ts @@ -55,6 +55,7 @@ import { applyAuthProfileConfig, applyMinimaxConfig, setAnthropicApiKey, + setGeminiApiKey, writeOAuthCredentials, } from "./onboard-auth.js"; import { @@ -300,6 +301,7 @@ async function promptAuthConfig( | "openai-codex" | "codex-cli" | "antigravity" + | "gemini-api-key" | "apiKey" | "minimax" | "skip"; @@ -513,6 +515,20 @@ async function promptAuthConfig( runtime.error(String(err)); note("Trouble with OAuth? See https://docs.clawd.bot/start/faq", "OAuth"); } + } else if (authChoice === "gemini-api-key") { + const key = guardCancel( + await text({ + message: "Enter Gemini API key", + validate: (value) => (value?.trim() ? undefined : "Required"), + }), + runtime, + ); + await setGeminiApiKey(String(key).trim()); + next = applyAuthProfileConfig(next, { + profileId: "google:default", + provider: "google", + mode: "api_key", + }); } else if (authChoice === "apiKey") { const key = guardCancel( await text({ diff --git a/src/commands/onboard-auth.ts b/src/commands/onboard-auth.ts index db51f4b84..a4d367d48 100644 --- a/src/commands/onboard-auth.ts +++ b/src/commands/onboard-auth.ts @@ -33,6 +33,19 @@ export async function setAnthropicApiKey(key: string, agentDir?: string) { }); } +export async function setGeminiApiKey(key: string, agentDir?: string) { + // Write to the multi-agent path so gateway finds credentials on startup + upsertAuthProfile({ + profileId: "google:default", + credential: { + type: "api_key", + provider: "google", + key, + }, + agentDir: agentDir ?? resolveDefaultAgentDir(), + }); +} + export function applyAuthProfileConfig( cfg: ClawdbotConfig, params: { diff --git a/src/commands/onboard-non-interactive.ts b/src/commands/onboard-non-interactive.ts index 382317506..8fbc12e72 100644 --- a/src/commands/onboard-non-interactive.ts +++ b/src/commands/onboard-non-interactive.ts @@ -28,6 +28,7 @@ import { applyAuthProfileConfig, applyMinimaxConfig, setAnthropicApiKey, + setGeminiApiKey, } from "./onboard-auth.js"; import { applyWizardMetadata, @@ -119,6 +120,19 @@ export async function runNonInteractiveOnboarding( provider: "anthropic", mode: "api_key", }); + } else if (authChoice === "gemini-api-key") { + const key = opts.geminiApiKey?.trim(); + if (!key) { + runtime.error("Missing --gemini-api-key"); + runtime.exit(1); + return; + } + await setGeminiApiKey(key); + nextConfig = applyAuthProfileConfig(nextConfig, { + profileId: "google:default", + provider: "google", + mode: "api_key", + }); } else if (authChoice === "claude-cli") { const store = ensureAuthProfileStore(undefined, { allowKeychainPrompt: false, diff --git a/src/commands/onboard-types.ts b/src/commands/onboard-types.ts index 09feace3b..6ee3b5fc9 100644 --- a/src/commands/onboard-types.ts +++ b/src/commands/onboard-types.ts @@ -9,6 +9,7 @@ export type AuthChoice = | "codex-cli" | "antigravity" | "apiKey" + | "gemini-api-key" | "minimax" | "skip"; export type GatewayAuthChoice = "off" | "token" | "password"; @@ -24,6 +25,7 @@ export type OnboardOptions = { nonInteractive?: boolean; authChoice?: AuthChoice; anthropicApiKey?: string; + geminiApiKey?: string; gatewayPort?: number; gatewayBind?: GatewayBind; gatewayAuth?: GatewayAuthChoice;