diff --git a/docs/cli/index.md b/docs/cli/index.md index b0b84a1dfa..4fcd4866ba 100644 --- a/docs/cli/index.md +++ b/docs/cli/index.md @@ -303,7 +303,7 @@ Options: - `--non-interactive` - `--mode ` - `--flow ` (manual is an alias for advanced) -- `--auth-choice ` +- `--auth-choice ` - `--token-provider ` (non-interactive; used with `--auth-choice token`) - `--token ` (non-interactive; used with `--auth-choice token`) - `--token-profile-id ` (non-interactive; default: `:manual`) diff --git a/docs/gateway/configuration.md b/docs/gateway/configuration.md index 6baee7e6d0..a7b528b62e 100644 --- a/docs/gateway/configuration.md +++ b/docs/gateway/configuration.md @@ -2552,7 +2552,9 @@ Notes: - Set `MOONSHOT_API_KEY` in the environment or use `openclaw onboard --auth-choice moonshot-api-key`. - Model ref: `moonshot/kimi-k2.5`. -- Use `https://api.moonshot.cn/v1` if you need the China endpoint. +- For the China endpoint, either: + - Run `openclaw onboard --auth-choice moonshot-api-key-cn` (wizard will set `https://api.moonshot.cn/v1`), or + - Manually set `baseUrl: "https://api.moonshot.cn/v1"` in `models.providers.moonshot`. ### Kimi Coding diff --git a/src/cli/program.smoke.test.ts b/src/cli/program.smoke.test.ts index f6b155554f..edb684cfac 100644 --- a/src/cli/program.smoke.test.ts +++ b/src/cli/program.smoke.test.ts @@ -164,6 +164,12 @@ describe("cli program (smoke)", () => { key: "sk-moonshot-test", field: "moonshotApiKey", }, + { + authChoice: "moonshot-api-key-cn", + flag: "--moonshot-api-key", + key: "sk-moonshot-cn-test", + field: "moonshotApiKey", + }, { authChoice: "kimi-code-api-key", flag: "--kimi-code-api-key", diff --git a/src/cli/program/register.onboard.ts b/src/cli/program/register.onboard.ts index 94d46fda37..9c9c1634ce 100644 --- a/src/cli/program/register.onboard.ts +++ b/src/cli/program/register.onboard.ts @@ -58,7 +58,7 @@ export function registerOnboardCommand(program: Command) { .option("--mode ", "Wizard mode: local|remote") .option( "--auth-choice ", - "Auth: setup-token|token|chutes|openai-codex|openai-api-key|openrouter-api-key|ai-gateway-api-key|moonshot-api-key|kimi-code-api-key|synthetic-api-key|venice-api-key|gemini-api-key|zai-api-key|xiaomi-api-key|apiKey|minimax-api|minimax-api-lightning|opencode-zen|skip", + "Auth: setup-token|token|chutes|openai-codex|openai-api-key|openrouter-api-key|ai-gateway-api-key|moonshot-api-key|moonshot-api-key-cn|kimi-code-api-key|synthetic-api-key|venice-api-key|gemini-api-key|zai-api-key|xiaomi-api-key|apiKey|minimax-api|minimax-api-lightning|opencode-zen|skip", ) .option( "--token-provider ", diff --git a/src/commands/auth-choice-options.test.ts b/src/commands/auth-choice-options.test.ts index df00d6afe7..2f1e08faa9 100644 --- a/src/commands/auth-choice-options.test.ts +++ b/src/commands/auth-choice-options.test.ts @@ -61,6 +61,7 @@ describe("buildAuthChoiceOptions", () => { }); expect(options.some((opt) => opt.value === "moonshot-api-key")).toBe(true); + expect(options.some((opt) => opt.value === "moonshot-api-key-cn")).toBe(true); expect(options.some((opt) => opt.value === "kimi-code-api-key")).toBe(true); }); diff --git a/src/commands/auth-choice-options.ts b/src/commands/auth-choice-options.ts index db3c71c5bc..aa78c7fe9b 100644 --- a/src/commands/auth-choice-options.ts +++ b/src/commands/auth-choice-options.ts @@ -56,9 +56,9 @@ const AUTH_CHOICE_GROUP_DEFS: { }, { value: "moonshot", - label: "Moonshot AI", - hint: "Kimi K2 + Kimi Coding", - choices: ["moonshot-api-key", "kimi-code-api-key"], + label: "Moonshot AI (Kimi K2.5)", + hint: "Kimi K2.5 + Kimi Coding", + choices: ["moonshot-api-key", "moonshot-api-key-cn", "kimi-code-api-key"], }, { value: "google", @@ -146,8 +146,15 @@ export function buildAuthChoiceOptions(params: { value: "ai-gateway-api-key", label: "Vercel AI Gateway API key", }); - options.push({ value: "moonshot-api-key", label: "Moonshot AI API key" }); - options.push({ value: "kimi-code-api-key", label: "Kimi Coding API key" }); + options.push({ + value: "moonshot-api-key", + label: "Kimi API key (.ai)", + }); + options.push({ + value: "moonshot-api-key-cn", + label: "Kimi API key (.cn)", + }); + options.push({ value: "kimi-code-api-key", label: "Kimi Code API key (subscription)" }); options.push({ value: "synthetic-api-key", label: "Synthetic API key" }); options.push({ value: "venice-api-key", diff --git a/src/commands/auth-choice.apply.api-providers.ts b/src/commands/auth-choice.apply.api-providers.ts index ef059e3de4..8b8d6b6346 100644 --- a/src/commands/auth-choice.apply.api-providers.ts +++ b/src/commands/auth-choice.apply.api-providers.ts @@ -16,6 +16,7 @@ import { applyKimiCodeConfig, applyKimiCodeProviderConfig, applyMoonshotConfig, + applyMoonshotConfigCn, applyMoonshotProviderConfig, applyOpencodeZenConfig, applyOpencodeZenProviderConfig, @@ -276,6 +277,61 @@ export async function applyAuthChoiceApiProviders( return { config: nextConfig, agentModelOverride }; } + if (authChoice === "moonshot-api-key-cn") { + let hasCredential = false; + + if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "moonshot") { + await setMoonshotApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir); + hasCredential = true; + } + + const envKey = resolveEnvApiKey("moonshot"); + if (envKey) { + const useExisting = await params.prompter.confirm({ + message: `Use existing MOONSHOT_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`, + initialValue: true, + }); + if (useExisting) { + await setMoonshotApiKey(envKey.apiKey, params.agentDir); + hasCredential = true; + } + } + if (!hasCredential) { + const key = await params.prompter.text({ + message: "Enter Moonshot API key (.cn)", + validate: validateApiKeyInput, + }); + await setMoonshotApiKey(normalizeApiKeyInput(String(key)), params.agentDir); + } + nextConfig = applyAuthProfileConfig(nextConfig, { + profileId: "moonshot:default", + provider: "moonshot", + mode: "api_key", + }); + { + const applied = await applyDefaultModelChoice({ + config: nextConfig, + setDefaultModel: params.setDefaultModel, + defaultModel: MOONSHOT_DEFAULT_MODEL_REF, + applyDefaultConfig: applyMoonshotConfigCn, + applyProviderConfig: (cfg) => applyMoonshotProviderConfigCnShim(cfg), + noteAgentModel, + prompter: params.prompter, + }); + nextConfig = applied.config; + agentModelOverride = applied.agentModelOverride ?? agentModelOverride; + } + return { config: nextConfig, agentModelOverride }; + } + + function applyMoonshotProviderConfigCnShim( + cfg: Parameters[0], + ) { + // For now, provider-level CN behavior is fully handled inside applyMoonshotConfigCn. + // Keep a thin shim to satisfy the applyDefaultModelChoice signature. + return applyMoonshotProviderConfig(cfg); + } + if (authChoice === "kimi-code-api-key") { let hasCredential = false; const tokenProvider = params.opts?.tokenProvider?.trim().toLowerCase(); diff --git a/src/commands/auth-choice.preferred-provider.ts b/src/commands/auth-choice.preferred-provider.ts index 861faf5de9..14dcf30b28 100644 --- a/src/commands/auth-choice.preferred-provider.ts +++ b/src/commands/auth-choice.preferred-provider.ts @@ -13,6 +13,7 @@ const PREFERRED_PROVIDER_BY_AUTH_CHOICE: Partial> = { "openrouter-api-key": "openrouter", "ai-gateway-api-key": "vercel-ai-gateway", "moonshot-api-key": "moonshot", + "moonshot-api-key-cn": "moonshot", "kimi-code-api-key": "kimi-coding", "gemini-api-key": "google", "google-antigravity": "google-antigravity", diff --git a/src/commands/onboard-auth.config-core.ts b/src/commands/onboard-auth.config-core.ts index cd74eb590b..30c81ae548 100644 --- a/src/commands/onboard-auth.config-core.ts +++ b/src/commands/onboard-auth.config-core.ts @@ -22,6 +22,7 @@ import { buildMoonshotModelDefinition, KIMI_CODING_MODEL_REF, MOONSHOT_BASE_URL, + MOONSHOT_CN_BASE_URL, MOONSHOT_DEFAULT_MODEL_ID, MOONSHOT_DEFAULT_MODEL_REF, } from "./onboard-auth.models.js"; @@ -137,10 +138,17 @@ export function applyOpenrouterConfig(cfg: OpenClawConfig): OpenClawConfig { } export function applyMoonshotProviderConfig(cfg: OpenClawConfig): OpenClawConfig { + return applyMoonshotProviderConfigWithBaseUrl(cfg, MOONSHOT_BASE_URL); +} + +function applyMoonshotProviderConfigWithBaseUrl( + cfg: OpenClawConfig, + baseUrl: string, +): OpenClawConfig { const models = { ...cfg.agents?.defaults?.models }; models[MOONSHOT_DEFAULT_MODEL_REF] = { ...models[MOONSHOT_DEFAULT_MODEL_REF], - alias: models[MOONSHOT_DEFAULT_MODEL_REF]?.alias ?? "Kimi K2", + alias: models[MOONSHOT_DEFAULT_MODEL_REF]?.alias ?? "Kimi", }; const providers = { ...cfg.models?.providers }; @@ -157,7 +165,7 @@ export function applyMoonshotProviderConfig(cfg: OpenClawConfig): OpenClawConfig const normalizedApiKey = resolvedApiKey?.trim(); providers.moonshot = { ...existingProviderRest, - baseUrl: MOONSHOT_BASE_URL, + baseUrl, api: "openai-completions", ...(normalizedApiKey ? { apiKey: normalizedApiKey } : {}), models: mergedModels.length > 0 ? mergedModels : [defaultModel], @@ -201,6 +209,28 @@ export function applyMoonshotConfig(cfg: OpenClawConfig): OpenClawConfig { }; } +export function applyMoonshotConfigCn(cfg: OpenClawConfig): OpenClawConfig { + const next = applyMoonshotProviderConfigWithBaseUrl(cfg, MOONSHOT_CN_BASE_URL); + 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: MOONSHOT_DEFAULT_MODEL_REF, + }, + }, + }, + }; +} + export function applyKimiCodeProviderConfig(cfg: OpenClawConfig): OpenClawConfig { const models = { ...cfg.agents?.defaults?.models }; models[KIMI_CODING_MODEL_REF] = { diff --git a/src/commands/onboard-auth.models.ts b/src/commands/onboard-auth.models.ts index c77b6bc102..a706c9a036 100644 --- a/src/commands/onboard-auth.models.ts +++ b/src/commands/onboard-auth.models.ts @@ -8,7 +8,8 @@ export const DEFAULT_MINIMAX_CONTEXT_WINDOW = 200000; export const DEFAULT_MINIMAX_MAX_TOKENS = 8192; export const MOONSHOT_BASE_URL = "https://api.moonshot.ai/v1"; -export const MOONSHOT_DEFAULT_MODEL_ID = "kimi-k2-0905-preview"; +export const MOONSHOT_CN_BASE_URL = "https://api.moonshot.cn/v1"; +export const MOONSHOT_DEFAULT_MODEL_ID = "kimi-k2.5"; export const MOONSHOT_DEFAULT_MODEL_REF = `moonshot/${MOONSHOT_DEFAULT_MODEL_ID}`; export const MOONSHOT_DEFAULT_CONTEXT_WINDOW = 256000; export const MOONSHOT_DEFAULT_MAX_TOKENS = 8192; @@ -83,7 +84,7 @@ export function buildMinimaxApiModelDefinition(modelId: string): ModelDefinition export function buildMoonshotModelDefinition(): ModelDefinitionConfig { return { id: MOONSHOT_DEFAULT_MODEL_ID, - name: "Kimi K2 0905 Preview", + name: "Kimi K2.5", reasoning: false, input: ["text"], cost: MOONSHOT_DEFAULT_COST, diff --git a/src/commands/onboard-auth.ts b/src/commands/onboard-auth.ts index af4d22cb1e..8ab8101b12 100644 --- a/src/commands/onboard-auth.ts +++ b/src/commands/onboard-auth.ts @@ -8,6 +8,7 @@ export { applyKimiCodeConfig, applyKimiCodeProviderConfig, applyMoonshotConfig, + applyMoonshotConfigCn, applyMoonshotProviderConfig, applyOpenrouterConfig, applyOpenrouterProviderConfig, @@ -58,6 +59,7 @@ export { buildMinimaxModelDefinition, buildMoonshotModelDefinition, DEFAULT_MINIMAX_BASE_URL, + MOONSHOT_CN_BASE_URL, KIMI_CODING_MODEL_ID, KIMI_CODING_MODEL_REF, MINIMAX_API_BASE_URL, diff --git a/src/commands/onboard-non-interactive/local/auth-choice.ts b/src/commands/onboard-non-interactive/local/auth-choice.ts index 98d11b002c..c38002d651 100644 --- a/src/commands/onboard-non-interactive/local/auth-choice.ts +++ b/src/commands/onboard-non-interactive/local/auth-choice.ts @@ -14,6 +14,7 @@ import { applyMinimaxApiConfig, applyMinimaxConfig, applyMoonshotConfig, + applyMoonshotConfigCn, applyOpencodeZenConfig, applyOpenrouterConfig, applySyntheticConfig, @@ -303,6 +304,29 @@ export async function applyNonInteractiveAuthChoice(params: { return applyMoonshotConfig(nextConfig); } + if (authChoice === "moonshot-api-key-cn") { + const resolved = await resolveNonInteractiveApiKey({ + provider: "moonshot", + cfg: baseConfig, + flagValue: opts.moonshotApiKey, + flagName: "--moonshot-api-key", + envVar: "MOONSHOT_API_KEY", + runtime, + }); + if (!resolved) { + return null; + } + if (resolved.source !== "profile") { + await setMoonshotApiKey(resolved.key); + } + nextConfig = applyAuthProfileConfig(nextConfig, { + profileId: "moonshot:default", + provider: "moonshot", + mode: "api_key", + }); + return applyMoonshotConfigCn(nextConfig); + } + if (authChoice === "kimi-code-api-key") { const resolved = await resolveNonInteractiveApiKey({ provider: "kimi-coding", diff --git a/src/commands/onboard-types.ts b/src/commands/onboard-types.ts index f3d72051d4..2111e4ff17 100644 --- a/src/commands/onboard-types.ts +++ b/src/commands/onboard-types.ts @@ -14,6 +14,7 @@ export type AuthChoice = | "openrouter-api-key" | "ai-gateway-api-key" | "moonshot-api-key" + | "moonshot-api-key-cn" | "kimi-code-api-key" | "synthetic-api-key" | "venice-api-key"