From ecd4c9c4f5750d106aa706397a6f46fb89b05dbd Mon Sep 17 00:00:00 2001
From: Tobias Bischoff <>
Date: Thu, 8 Jan 2026 15:10:18 +0100
Subject: [PATCH] Onboarding: add MiniMax hosted API key option
---
docs/cli/index.md | 3 +-
scripts/bench-model.ts | 2 +-
src/agents/minimax.live.test.ts | 2 +-
src/agents/model-auth.ts | 1 +
src/cli/program.ts | 5 +-
src/commands/auth-choice-options.ts | 1 +
src/commands/auth-choice.ts | 22 +++++++
src/commands/configure.ts | 18 ++++++
src/commands/onboard-auth.ts | 85 +++++++++++++++++++++++++
src/commands/onboard-non-interactive.ts | 16 +++++
src/commands/onboard-types.ts | 2 +
11 files changed, 153 insertions(+), 4 deletions(-)
diff --git a/docs/cli/index.md b/docs/cli/index.md
index 0c9c52108..d5b7acd4c 100644
--- a/docs/cli/index.md
+++ b/docs/cli/index.md
@@ -166,8 +166,9 @@ Options:
- `--workspace
`
- `--non-interactive`
- `--mode `
-- `--auth-choice `
+- `--auth-choice `
- `--anthropic-api-key `
+- `--minimax-api-key `
- `--gateway-port `
- `--gateway-bind `
- `--gateway-auth `
diff --git a/scripts/bench-model.ts b/scripts/bench-model.ts
index 32ed20ad0..0b3a60d01 100644
--- a/scripts/bench-model.ts
+++ b/scripts/bench-model.ts
@@ -88,7 +88,7 @@ async function main(): Promise {
const minimaxBaseUrl =
process.env.MINIMAX_BASE_URL?.trim() || "https://api.minimax.io/v1";
const minimaxModelId =
- process.env.MINIMAX_MODEL?.trim() || "minimax-m2.1";
+ process.env.MINIMAX_MODEL?.trim() || "MiniMax-M2.1";
const minimaxModel: Model<"openai-completions"> = {
id: minimaxModelId,
diff --git a/src/agents/minimax.live.test.ts b/src/agents/minimax.live.test.ts
index 666943876..53f033af1 100644
--- a/src/agents/minimax.live.test.ts
+++ b/src/agents/minimax.live.test.ts
@@ -4,7 +4,7 @@ import { describe, expect, it } from "vitest";
const MINIMAX_KEY = process.env.MINIMAX_API_KEY ?? "";
const MINIMAX_BASE_URL =
process.env.MINIMAX_BASE_URL?.trim() || "https://api.minimax.io/v1";
-const MINIMAX_MODEL = process.env.MINIMAX_MODEL?.trim() || "minimax-m2.1";
+const MINIMAX_MODEL = process.env.MINIMAX_MODEL?.trim() || "MiniMax-M2.1";
const LIVE = process.env.MINIMAX_LIVE_TEST === "1" || process.env.LIVE === "1";
const describeLive = LIVE && MINIMAX_KEY ? describe : describe.skip;
diff --git a/src/agents/model-auth.ts b/src/agents/model-auth.ts
index 1716f7800..da6786a3e 100644
--- a/src/agents/model-auth.ts
+++ b/src/agents/model-auth.ts
@@ -135,6 +135,7 @@ export function resolveEnvApiKey(provider: string): EnvApiKeyResult | null {
cerebras: "CEREBRAS_API_KEY",
xai: "XAI_API_KEY",
openrouter: "OPENROUTER_API_KEY",
+ minimax: "MINIMAX_API_KEY",
zai: "ZAI_API_KEY",
mistral: "MISTRAL_API_KEY",
};
diff --git a/src/cli/program.ts b/src/cli/program.ts
index 8c09a5758..4b777094d 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|apiKey|minimax-cloud|minimax|skip",
)
.option("--anthropic-api-key ", "Anthropic API key")
+ .option("--minimax-api-key ", "MiniMax API key")
.option("--gateway-port ", "Gateway port")
.option("--gateway-bind ", "Gateway bind: loopback|lan|tailnet|auto")
.option("--gateway-auth ", "Gateway auth: off|token|password")
@@ -264,10 +265,12 @@ export function buildProgram() {
| "codex-cli"
| "antigravity"
| "apiKey"
+ | "minimax-cloud"
| "minimax"
| "skip"
| undefined,
anthropicApiKey: opts.anthropicApiKey as string | undefined,
+ minimaxApiKey: opts.minimaxApiKey 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 4feacf9f2..50cd7c766 100644
--- a/src/commands/auth-choice-options.ts
+++ b/src/commands/auth-choice-options.ts
@@ -77,6 +77,7 @@ export function buildAuthChoiceOptions(params: {
label: "Google Antigravity (Claude Opus 4.5, Gemini 3, etc.)",
});
options.push({ value: "apiKey", label: "Anthropic API key" });
+ options.push({ value: "minimax-cloud", label: "MiniMax M2.1 (minimax.io)" });
options.push({ value: "minimax", label: "Minimax M2.1 (LM Studio)" });
if (params.includeSkip) {
options.push({ value: "skip", label: "Skip for now" });
diff --git a/src/commands/auth-choice.ts b/src/commands/auth-choice.ts
index 195bcf50b..b7febead2 100644
--- a/src/commands/auth-choice.ts
+++ b/src/commands/auth-choice.ts
@@ -28,8 +28,12 @@ import {
import {
applyAuthProfileConfig,
applyMinimaxConfig,
+ applyMinimaxHostedConfig,
+ applyMinimaxHostedProviderConfig,
applyMinimaxProviderConfig,
+ MINIMAX_HOSTED_MODEL_REF,
setAnthropicApiKey,
+ setMinimaxApiKey,
writeOAuthCredentials,
} from "./onboard-auth.js";
import { openUrl } from "./onboard-helpers.js";
@@ -397,6 +401,24 @@ export async function applyAuthChoice(params: {
provider: "anthropic",
mode: "api_key",
});
+ } else if (params.authChoice === "minimax-cloud") {
+ const key = await params.prompter.text({
+ message: "Enter MiniMax API key",
+ validate: (value) => (value?.trim() ? undefined : "Required"),
+ });
+ await setMinimaxApiKey(String(key).trim(), params.agentDir);
+ nextConfig = applyAuthProfileConfig(nextConfig, {
+ profileId: "minimax:default",
+ provider: "minimax",
+ mode: "api_key",
+ });
+ if (params.setDefaultModel) {
+ nextConfig = applyMinimaxHostedConfig(nextConfig);
+ } else {
+ nextConfig = applyMinimaxHostedProviderConfig(nextConfig);
+ agentModelOverride = MINIMAX_HOSTED_MODEL_REF;
+ await noteAgentModel(MINIMAX_HOSTED_MODEL_REF);
+ }
} else if (params.authChoice === "minimax") {
if (params.setDefaultModel) {
nextConfig = applyMinimaxConfig(nextConfig);
diff --git a/src/commands/configure.ts b/src/commands/configure.ts
index 549e3d95d..ef85bd0ce 100644
--- a/src/commands/configure.ts
+++ b/src/commands/configure.ts
@@ -52,7 +52,9 @@ import { healthCommand } from "./health.js";
import {
applyAuthProfileConfig,
applyMinimaxConfig,
+ applyMinimaxHostedConfig,
setAnthropicApiKey,
+ setMinimaxApiKey,
writeOAuthCredentials,
} from "./onboard-auth.js";
import {
@@ -296,6 +298,7 @@ async function promptAuthConfig(
| "codex-cli"
| "antigravity"
| "apiKey"
+ | "minimax-cloud"
| "minimax"
| "skip";
@@ -522,6 +525,21 @@ async function promptAuthConfig(
provider: "anthropic",
mode: "api_key",
});
+ } else if (authChoice === "minimax-cloud") {
+ const key = guardCancel(
+ await text({
+ message: "Enter MiniMax API key",
+ validate: (value) => (value?.trim() ? undefined : "Required"),
+ }),
+ runtime,
+ );
+ await setMinimaxApiKey(String(key).trim());
+ next = applyAuthProfileConfig(next, {
+ profileId: "minimax:default",
+ provider: "minimax",
+ mode: "api_key",
+ });
+ next = applyMinimaxHostedConfig(next);
} else if (authChoice === "minimax") {
next = applyMinimaxConfig(next);
}
diff --git a/src/commands/onboard-auth.ts b/src/commands/onboard-auth.ts
index db51f4b84..b65c72a88 100644
--- a/src/commands/onboard-auth.ts
+++ b/src/commands/onboard-auth.ts
@@ -3,6 +3,12 @@ import { resolveDefaultAgentDir } from "../agents/agent-scope.js";
import { upsertAuthProfile } from "../agents/auth-profiles.js";
import type { ClawdbotConfig } from "../config/config.js";
+const DEFAULT_MINIMAX_BASE_URL = "https://api.minimax.io/v1";
+export const MINIMAX_HOSTED_MODEL_ID = "MiniMax-M2.1";
+const DEFAULT_MINIMAX_CONTEXT_WINDOW = 200000;
+const DEFAULT_MINIMAX_MAX_TOKENS = 8192;
+export const MINIMAX_HOSTED_MODEL_REF = `minimax/${MINIMAX_HOSTED_MODEL_ID}`;
+
export async function writeOAuthCredentials(
provider: OAuthProvider,
creds: OAuthCredentials,
@@ -33,6 +39,19 @@ export async function setAnthropicApiKey(key: string, agentDir?: string) {
});
}
+export async function setMinimaxApiKey(key: string, agentDir?: string) {
+ // Write to the multi-agent path so gateway finds credentials on startup
+ upsertAuthProfile({
+ profileId: "minimax:default",
+ credential: {
+ type: "api_key",
+ provider: "minimax",
+ key,
+ },
+ agentDir: agentDir ?? resolveDefaultAgentDir(),
+ });
+}
+
export function applyAuthProfileConfig(
cfg: ClawdbotConfig,
params: {
@@ -119,6 +138,49 @@ export function applyMinimaxProviderConfig(
};
}
+export function applyMinimaxHostedProviderConfig(
+ cfg: ClawdbotConfig,
+ params?: { baseUrl?: string },
+): ClawdbotConfig {
+ const models = { ...cfg.agent?.models };
+ models[MINIMAX_HOSTED_MODEL_REF] = {
+ ...models[MINIMAX_HOSTED_MODEL_REF],
+ alias: models[MINIMAX_HOSTED_MODEL_REF]?.alias ?? "Minimax",
+ };
+
+ const providers = { ...cfg.models?.providers };
+ if (!providers.minimax) {
+ providers.minimax = {
+ baseUrl: params?.baseUrl?.trim() || DEFAULT_MINIMAX_BASE_URL,
+ apiKey: "minimax",
+ api: "openai-completions",
+ models: [
+ {
+ id: MINIMAX_HOSTED_MODEL_ID,
+ name: "MiniMax M2.1",
+ reasoning: false,
+ input: ["text"],
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
+ contextWindow: DEFAULT_MINIMAX_CONTEXT_WINDOW,
+ maxTokens: DEFAULT_MINIMAX_MAX_TOKENS,
+ },
+ ],
+ };
+ }
+
+ return {
+ ...cfg,
+ agent: {
+ ...cfg.agent,
+ models,
+ },
+ models: {
+ mode: cfg.models?.mode ?? "merge",
+ providers,
+ },
+ };
+}
+
export function applyMinimaxConfig(cfg: ClawdbotConfig): ClawdbotConfig {
const next = applyMinimaxProviderConfig(cfg);
return {
@@ -138,3 +200,26 @@ export function applyMinimaxConfig(cfg: ClawdbotConfig): ClawdbotConfig {
},
};
}
+
+export function applyMinimaxHostedConfig(
+ cfg: ClawdbotConfig,
+ params?: { baseUrl?: string },
+): ClawdbotConfig {
+ const next = applyMinimaxHostedProviderConfig(cfg, params);
+ return {
+ ...next,
+ agent: {
+ ...next.agent,
+ model: {
+ ...(next.agent?.model &&
+ "fallbacks" in (next.agent.model as Record)
+ ? {
+ fallbacks: (next.agent.model as { fallbacks?: string[] })
+ .fallbacks,
+ }
+ : undefined),
+ primary: MINIMAX_HOSTED_MODEL_REF,
+ },
+ },
+ };
+}
diff --git a/src/commands/onboard-non-interactive.ts b/src/commands/onboard-non-interactive.ts
index 7e3821fa1..8338484c6 100644
--- a/src/commands/onboard-non-interactive.ts
+++ b/src/commands/onboard-non-interactive.ts
@@ -25,7 +25,9 @@ import { healthCommand } from "./health.js";
import {
applyAuthProfileConfig,
applyMinimaxConfig,
+ applyMinimaxHostedConfig,
setAnthropicApiKey,
+ setMinimaxApiKey,
} from "./onboard-auth.js";
import {
applyWizardMetadata,
@@ -117,6 +119,20 @@ export async function runNonInteractiveOnboarding(
provider: "anthropic",
mode: "api_key",
});
+ } else if (authChoice === "minimax-cloud") {
+ const key = opts.minimaxApiKey?.trim();
+ if (!key) {
+ runtime.error("Missing --minimax-api-key");
+ runtime.exit(1);
+ return;
+ }
+ await setMinimaxApiKey(key);
+ nextConfig = applyAuthProfileConfig(nextConfig, {
+ profileId: "minimax:default",
+ provider: "minimax",
+ mode: "api_key",
+ });
+ nextConfig = applyMinimaxHostedConfig(nextConfig);
} else if (authChoice === "claude-cli") {
const store = ensureAuthProfileStore();
if (!store.profiles[CLAUDE_CLI_PROFILE_ID]) {
diff --git a/src/commands/onboard-types.ts b/src/commands/onboard-types.ts
index 09feace3b..53333e5ab 100644
--- a/src/commands/onboard-types.ts
+++ b/src/commands/onboard-types.ts
@@ -9,6 +9,7 @@ export type AuthChoice =
| "codex-cli"
| "antigravity"
| "apiKey"
+ | "minimax-cloud"
| "minimax"
| "skip";
export type GatewayAuthChoice = "off" | "token" | "password";
@@ -24,6 +25,7 @@ export type OnboardOptions = {
nonInteractive?: boolean;
authChoice?: AuthChoice;
anthropicApiKey?: string;
+ minimaxApiKey?: string;
gatewayPort?: number;
gatewayBind?: GatewayBind;
gatewayAuth?: GatewayAuthChoice;