From 20519d0cfe4f25677d8d6195a577235fa87dc509 Mon Sep 17 00:00:00 2001 From: HashWarlock Date: Sun, 25 Jan 2026 18:55:15 -0600 Subject: [PATCH] feat(commands): add Redpill API key auth handler Co-Authored-By: Claude Sonnet 4.5 --- .../auth-choice.apply.api-providers.ts | 65 +++++++++++++++ src/commands/onboard-auth.config-core.ts | 82 +++++++++++++++++++ src/commands/onboard-auth.credentials.ts | 13 +++ src/commands/onboard-auth.ts | 4 + 4 files changed, 164 insertions(+) diff --git a/src/commands/auth-choice.apply.api-providers.ts b/src/commands/auth-choice.apply.api-providers.ts index 8be02008b..1118e9dca 100644 --- a/src/commands/auth-choice.apply.api-providers.ts +++ b/src/commands/auth-choice.apply.api-providers.ts @@ -21,6 +21,8 @@ import { applyOpencodeZenProviderConfig, applyOpenrouterConfig, applyOpenrouterProviderConfig, + applyRedpillConfig, + applyRedpillProviderConfig, applySyntheticConfig, applySyntheticProviderConfig, applyVeniceConfig, @@ -31,6 +33,7 @@ import { KIMI_CODE_MODEL_REF, MOONSHOT_DEFAULT_MODEL_REF, OPENROUTER_DEFAULT_MODEL_REF, + REDPILL_DEFAULT_MODEL_REF, SYNTHETIC_DEFAULT_MODEL_REF, VENICE_DEFAULT_MODEL_REF, VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF, @@ -39,6 +42,7 @@ import { setMoonshotApiKey, setOpencodeZenApiKey, setOpenrouterApiKey, + setRedpillApiKey, setSyntheticApiKey, setVeniceApiKey, setVercelAiGatewayApiKey, @@ -83,6 +87,8 @@ export async function applyAuthChoiceApiProviders( authChoice = "synthetic-api-key"; } else if (params.opts.tokenProvider === "venice") { authChoice = "venice-api-key"; + } else if (params.opts.tokenProvider === "redpill") { + authChoice = "redpill-api-key"; } else if (params.opts.tokenProvider === "opencode") { authChoice = "opencode-zen"; } @@ -522,6 +528,65 @@ export async function applyAuthChoiceApiProviders( return { config: nextConfig, agentModelOverride }; } + if (authChoice === "redpill-api-key") { + let hasCredential = false; + + if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "redpill") { + await setRedpillApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir); + hasCredential = true; + } + + if (!hasCredential) { + await params.prompter.note( + [ + "Redpill AI provides GPU-TEE protected inference with privacy guarantees.", + "Get your API key at: https://redpill.ai/settings/api", + "All models run in secure enclaves with verifiable privacy.", + ].join("\n"), + "Redpill AI", + ); + } + + const envKey = resolveEnvApiKey("redpill"); + if (envKey) { + const useExisting = await params.prompter.confirm({ + message: `Use existing REDPILL_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`, + initialValue: true, + }); + if (useExisting) { + await setRedpillApiKey(envKey.apiKey, params.agentDir); + hasCredential = true; + } + } + if (!hasCredential) { + const key = await params.prompter.text({ + message: "Enter Redpill AI API key", + validate: validateApiKeyInput, + }); + await setRedpillApiKey(normalizeApiKeyInput(String(key)), params.agentDir); + } + nextConfig = applyAuthProfileConfig(nextConfig, { + profileId: "redpill:default", + provider: "redpill", + mode: "api_key", + }); + { + const applied = await applyDefaultModelChoice({ + config: nextConfig, + setDefaultModel: params.setDefaultModel, + defaultModel: REDPILL_DEFAULT_MODEL_REF, + applyDefaultConfig: applyRedpillConfig, + applyProviderConfig: applyRedpillProviderConfig, + noteDefault: REDPILL_DEFAULT_MODEL_REF, + noteAgentModel, + prompter: params.prompter, + }); + nextConfig = applied.config; + agentModelOverride = applied.agentModelOverride ?? agentModelOverride; + } + return { config: nextConfig, agentModelOverride }; + } + if (authChoice === "opencode-zen") { let hasCredential = false; if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "opencode") { diff --git a/src/commands/onboard-auth.config-core.ts b/src/commands/onboard-auth.config-core.ts index 0d3a8523a..dce0e7690 100644 --- a/src/commands/onboard-auth.config-core.ts +++ b/src/commands/onboard-auth.config-core.ts @@ -10,6 +10,11 @@ import { VENICE_DEFAULT_MODEL_REF, VENICE_MODEL_CATALOG, } from "../agents/venice-models.js"; +import { + discoverRedpillModels, + REDPILL_BASE_URL, + REDPILL_DEFAULT_MODEL_REF, +} from "../agents/redpill-models.js"; import type { ClawdbotConfig } from "../config/config.js"; import { OPENROUTER_DEFAULT_MODEL_REF, @@ -411,6 +416,83 @@ export function applyVeniceConfig(cfg: ClawdbotConfig): ClawdbotConfig { }; } +/** + * Apply Redpill provider configuration without changing the default model. + * Registers Redpill models and sets up the provider, but preserves existing model selection. + */ +export function applyRedpillProviderConfig(cfg: ClawdbotConfig): ClawdbotConfig { + const models = { ...cfg.agents?.defaults?.models }; + models[REDPILL_DEFAULT_MODEL_REF] = { + ...models[REDPILL_DEFAULT_MODEL_REF], + alias: models[REDPILL_DEFAULT_MODEL_REF]?.alias ?? "DeepSeek V3.2", + }; + + const providers = { ...cfg.models?.providers }; + const existingProvider = providers.redpill; + const existingModels = Array.isArray(existingProvider?.models) ? existingProvider.models : []; + const redpillModels = discoverRedpillModels(); + const mergedModels = [ + ...existingModels, + ...redpillModels.filter( + (model) => !existingModels.some((existing) => existing.id === model.id), + ), + ]; + const { apiKey: existingApiKey, ...existingProviderRest } = (existingProvider ?? {}) as Record< + string, + unknown + > as { apiKey?: string }; + const resolvedApiKey = typeof existingApiKey === "string" ? existingApiKey : undefined; + const normalizedApiKey = resolvedApiKey?.trim(); + providers.redpill = { + ...existingProviderRest, + baseUrl: REDPILL_BASE_URL, + api: "openai-completions", + ...(normalizedApiKey ? { apiKey: normalizedApiKey } : {}), + models: mergedModels.length > 0 ? mergedModels : redpillModels, + }; + + return { + ...cfg, + agents: { + ...cfg.agents, + defaults: { + ...cfg.agents?.defaults, + models, + }, + }, + models: { + mode: cfg.models?.mode ?? "merge", + providers, + }, + }; +} + +/** + * Apply Redpill provider configuration AND set Redpill as the default model. + * Use this when Redpill is the primary provider choice during onboarding. + */ +export function applyRedpillConfig(cfg: ClawdbotConfig): ClawdbotConfig { + const next = applyRedpillProviderConfig(cfg); + const existingModel = next.agents?.defaults?.model; + return { + ...next, + agents: { + ...next.agents, + defaults: { + ...next.agents?.defaults, + model: { + ...(existingModel && "fallbacks" in (existingModel as Record) + ? { + fallbacks: (existingModel as { fallbacks?: string[] }).fallbacks, + } + : undefined), + primary: REDPILL_DEFAULT_MODEL_REF, + }, + }, + }, + }; +} + export function applyAuthProfileConfig( cfg: ClawdbotConfig, params: { diff --git a/src/commands/onboard-auth.credentials.ts b/src/commands/onboard-auth.credentials.ts index 0c7dff409..181dbaa54 100644 --- a/src/commands/onboard-auth.credentials.ts +++ b/src/commands/onboard-auth.credentials.ts @@ -112,6 +112,19 @@ export async function setVeniceApiKey(key: string, agentDir?: string) { }); } +export async function setRedpillApiKey(key: string, agentDir?: string) { + // Write to resolved agent dir so gateway finds credentials on startup. + upsertAuthProfile({ + profileId: "redpill:default", + credential: { + type: "api_key", + provider: "redpill", + key, + }, + agentDir: resolveAuthAgentDir(agentDir), + }); +} + export const ZAI_DEFAULT_MODEL_REF = "zai/glm-4.7"; export const OPENROUTER_DEFAULT_MODEL_REF = "openrouter/auto"; export const VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF = "vercel-ai-gateway/anthropic/claude-opus-4.5"; diff --git a/src/commands/onboard-auth.ts b/src/commands/onboard-auth.ts index b122d89cf..688712753 100644 --- a/src/commands/onboard-auth.ts +++ b/src/commands/onboard-auth.ts @@ -3,6 +3,7 @@ export { SYNTHETIC_DEFAULT_MODEL_REF, } from "../agents/synthetic-models.js"; export { VENICE_DEFAULT_MODEL_ID, VENICE_DEFAULT_MODEL_REF } from "../agents/venice-models.js"; +export { REDPILL_DEFAULT_MODEL_REF } from "../agents/redpill-models.js"; export { applyAuthProfileConfig, applyKimiCodeConfig, @@ -11,6 +12,8 @@ export { applyMoonshotProviderConfig, applyOpenrouterConfig, applyOpenrouterProviderConfig, + applyRedpillConfig, + applyRedpillProviderConfig, applySyntheticConfig, applySyntheticProviderConfig, applyVeniceConfig, @@ -41,6 +44,7 @@ export { setMoonshotApiKey, setOpencodeZenApiKey, setOpenrouterApiKey, + setRedpillApiKey, setSyntheticApiKey, setVeniceApiKey, setVercelAiGatewayApiKey,