diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index a9b5b49e8..cbe5c10c4 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed `/login moonshot` validating China-platform API keys against the international host instead of honoring `MOONSHOT_BASE_URL` ([#5981](https://github.com/can1357/oh-my-pi/issues/5981)). + ## [17.0.4] - 2026-07-18 ### Fixed diff --git a/packages/ai/src/registry/api-key-login.ts b/packages/ai/src/registry/api-key-login.ts index 94783dbb4..f202eab63 100644 --- a/packages/ai/src/registry/api-key-login.ts +++ b/packages/ai/src/registry/api-key-login.ts @@ -31,7 +31,7 @@ type AnthropicMessagesValidation = { type ModelsEndpointValidation = { kind: "models-endpoint"; provider: string; - modelsUrl: string; + modelsUrl: string | (() => string); headers?: Record | (() => Record | undefined); }; @@ -99,7 +99,10 @@ export function createApiKeyLogin(config: ApiKeyLoginConfig): (options: OAuthCon await validateApiKeyAgainstModelsEndpoint({ provider: config.validation.provider, apiKey: trimmed, - modelsUrl: config.validation.modelsUrl, + modelsUrl: + typeof config.validation.modelsUrl === "function" + ? config.validation.modelsUrl() + : config.validation.modelsUrl, headers: config.validation.headers, signal: options.signal, fetch: options.fetch, diff --git a/packages/ai/src/registry/moonshot.ts b/packages/ai/src/registry/moonshot.ts index 7b38a541c..749ef4047 100644 --- a/packages/ai/src/registry/moonshot.ts +++ b/packages/ai/src/registry/moonshot.ts @@ -1,7 +1,13 @@ +import { $env } from "@oh-my-pi/pi-utils"; import { createApiKeyLogin } from "./api-key-login"; import type { OAuthLoginCallbacks } from "./oauth/types"; import type { ProviderDefinition } from "./types"; +function resolveMoonshotModelsUrl(): string { + const baseUrl = $env.MOONSHOT_BASE_URL?.trim() || "https://api.moonshot.ai/v1"; + return `${baseUrl.replace(/\/+$/, "")}/models`; +} + export const loginMoonshot = createApiKeyLogin({ providerLabel: "Moonshot", authUrl: "https://platform.moonshot.ai/console/api-keys", @@ -11,7 +17,7 @@ export const loginMoonshot = createApiKeyLogin({ validation: { kind: "models-endpoint", provider: "moonshot", - modelsUrl: "https://api.moonshot.ai/v1/models", + modelsUrl: resolveMoonshotModelsUrl, }, }); diff --git a/packages/ai/test/issue-2883-moonshot-base-url.test.ts b/packages/ai/test/issue-2883-moonshot-base-url.test.ts index 66fe160c0..93934111d 100644 --- a/packages/ai/test/issue-2883-moonshot-base-url.test.ts +++ b/packages/ai/test/issue-2883-moonshot-base-url.test.ts @@ -1,5 +1,7 @@ -import { afterEach, describe, expect, test } from "bun:test"; +import { afterEach, describe, expect, test, vi } from "bun:test"; import { resolveOpenAIRequestSetup } from "@oh-my-pi/pi-ai/providers/openai-shared"; +import { loginMoonshot } from "@oh-my-pi/pi-ai/registry/moonshot"; +import type { FetchImpl } from "@oh-my-pi/pi-ai/types"; const ORIGINAL_MOONSHOT_BASE_URL = Bun.env.MOONSHOT_BASE_URL; @@ -42,6 +44,23 @@ describe("Moonshot China base URL override (issue #2883)", () => { expect(setup.baseUrl).toBe("https://api.moonshot.ai/v1"); }); + test("validates login against the configured Moonshot endpoint", async () => { + Bun.env.MOONSHOT_BASE_URL = "https://api.moonshot.cn/v1/"; + const fetchMock: FetchImpl = vi.fn(async (input: string | URL | Request) => { + const url = typeof input === "string" ? input : input.toString(); + expect(url).toBe("https://api.moonshot.cn/v1/models"); + return new Response(JSON.stringify({ object: "list", data: [] }), { status: 200 }); + }); + + const apiKey = await loginMoonshot({ + onPrompt: async () => " sk-china-key ", + fetch: fetchMock, + }); + + expect(apiKey).toBe("sk-china-key"); + expect(fetchMock).toHaveBeenCalledTimes(1); + }); + test("does not redirect other openai-completions providers", () => { Bun.env.MOONSHOT_BASE_URL = "https://api.moonshot.cn/v1"; const setup = resolveOpenAIRequestSetup(