diff --git a/CHANGELOG.md b/CHANGELOG.md index 723771cf6..128ac9798 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Discord: fix forum thread starters and cache channel lookups for thread context. (#585) — thanks @thewilloftheshadow - Commands: accept /models as an alias for /model. - Commands: add `/usage` as an alias for `/status`. (#492) — thanks @lc0rp +- Models/Auth: add MiniMax Anthropic-compatible API onboarding (minimax-api). (#590) — thanks @mneves75 - Commands: harden slash command registry and list text-only commands in `/commands`. - Models/Auth: show per-agent auth candidates in `/model status`, and add `clawdbot models auth order {get,set,clear}` (per-agent auth rotation overrides). — thanks @steipete - Debugging: add raw model stream logging flags and document gateway watch mode. diff --git a/src/commands/onboard-auth.test.ts b/src/commands/onboard-auth.test.ts index 6ef8a6980..5ae385e9a 100644 --- a/src/commands/onboard-auth.test.ts +++ b/src/commands/onboard-auth.test.ts @@ -7,8 +7,8 @@ import { afterEach, describe, expect, it } from "vitest"; import { applyAuthProfileConfig, - applyMinimaxApiProviderConfig, applyMinimaxApiConfig, + applyMinimaxApiProviderConfig, writeOAuthCredentials, } from "./onboard-auth.js"; @@ -119,7 +119,9 @@ describe("applyMinimaxApiConfig", () => { it("sets correct primary model", () => { const cfg = applyMinimaxApiConfig({}, "MiniMax-M2.1-lightning"); - expect(cfg.agents?.defaults?.model?.primary).toBe("minimax/MiniMax-M2.1-lightning"); + expect(cfg.agents?.defaults?.model?.primary).toBe( + "minimax/MiniMax-M2.1-lightning", + ); }); it("sets reasoning flag for MiniMax-M2 model", () => { @@ -140,28 +142,37 @@ describe("applyMinimaxApiConfig", () => { }, }, }); - expect(cfg.agents?.defaults?.model?.fallbacks).toEqual(["anthropic/claude-opus-4-5"]); + expect(cfg.agents?.defaults?.model?.fallbacks).toEqual([ + "anthropic/claude-opus-4-5", + ]); }); it("adds model alias", () => { const cfg = applyMinimaxApiConfig({}, "MiniMax-M2.1"); - expect(cfg.agents?.defaults?.models?.["minimax/MiniMax-M2.1"]?.alias).toBe("Minimax"); + expect(cfg.agents?.defaults?.models?.["minimax/MiniMax-M2.1"]?.alias).toBe( + "Minimax", + ); }); it("preserves existing model params when adding alias", () => { - const cfg = applyMinimaxApiConfig({ - agents: { - defaults: { - models: { - "minimax/MiniMax-M2.1": { alias: "MiniMax", params: { custom: "value" } }, + const cfg = applyMinimaxApiConfig( + { + agents: { + defaults: { + models: { + "minimax/MiniMax-M2.1": { + alias: "MiniMax", + params: { custom: "value" }, + }, + }, }, }, }, - }, "MiniMax-M2.1"); - expect(cfg.agents?.defaults?.models?.["minimax/MiniMax-M2.1"]).toMatchObject({ - alias: "Minimax", - params: { custom: "value" }, - }); + "MiniMax-M2.1", + ); + expect( + cfg.agents?.defaults?.models?.["minimax/MiniMax-M2.1"], + ).toMatchObject({ alias: "Minimax", params: { custom: "value" } }); }); it("replaces existing minimax provider entirely", () => { @@ -172,12 +183,24 @@ describe("applyMinimaxApiConfig", () => { baseUrl: "https://old.example.com", apiKey: "old-key", api: "openai-completions", - models: [{ id: "old-model", name: "Old", reasoning: false, input: ["text"], cost: { input: 1, output: 2, cacheRead: 0, cacheWrite: 0 }, contextWindow: 1000, maxTokens: 100 }], + models: [ + { + id: "old-model", + name: "Old", + reasoning: false, + input: ["text"], + cost: { input: 1, output: 2, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 1000, + maxTokens: 100, + }, + ], }, }, }, }); - expect(cfg.models?.providers?.minimax?.baseUrl).toBe("https://api.minimax.io/anthropic"); + expect(cfg.models?.providers?.minimax?.baseUrl).toBe( + "https://api.minimax.io/anthropic", + ); expect(cfg.models?.providers?.minimax?.api).toBe("anthropic-messages"); expect(cfg.models?.providers?.minimax?.models[0]?.id).toBe("MiniMax-M2.1"); }); @@ -190,7 +213,17 @@ describe("applyMinimaxApiConfig", () => { baseUrl: "https://api.anthropic.com", apiKey: "anthropic-key", api: "anthropic-messages", - models: [{ id: "claude-opus-4-5", name: "Claude Opus 4.5", reasoning: false, input: ["text"], cost: { input: 15, output: 75, cacheRead: 0, cacheWrite: 0 }, contextWindow: 200000, maxTokens: 8192 }], + models: [ + { + id: "claude-opus-4-5", + name: "Claude Opus 4.5", + reasoning: false, + input: ["text"], + cost: { input: 15, output: 75, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 200000, + maxTokens: 8192, + }, + ], }, }, }, diff --git a/src/commands/onboard-non-interactive.ts b/src/commands/onboard-non-interactive.ts index a3477ffcb..99ab69646 100644 --- a/src/commands/onboard-non-interactive.ts +++ b/src/commands/onboard-non-interactive.ts @@ -175,7 +175,8 @@ export async function runNonInteractiveOnboarding( }); nextConfig = applyMinimaxHostedConfig(nextConfig); } else if (authChoice === "minimax-api") { - const key = opts.minimaxApiKey?.trim() || resolveEnvApiKey("minimax")?.apiKey; + const key = + opts.minimaxApiKey?.trim() || resolveEnvApiKey("minimax")?.apiKey; if (!key) { runtime.error("Missing --minimax-api-key (or MINIMAX_API_KEY in env)."); runtime.exit(1); @@ -221,8 +222,6 @@ export async function runNonInteractiveOnboarding( nextConfig = applyOpenAICodexModelDefault(nextConfig).next; } else if (authChoice === "minimax") { nextConfig = applyMinimaxConfig(nextConfig); - } else if (authChoice === "minimax-api") { - nextConfig = applyMinimaxApiConfig(nextConfig); } else if ( authChoice === "token" || authChoice === "oauth" ||