From e15af18d1794df023be5959e2361402406fe6c97 Mon Sep 17 00:00:00 2001 From: Reza Rezaei <77894240+MeRezaRezaei@users.noreply.github.com> Date: Sat, 22 Aug 2026 23:55:23 +0200 Subject: [PATCH] fix(kimi): point kimi-web to international www.kimi.ai (#11045) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Validated on the combined board over tip 80d931ae: kimi suites green (executor-kimi-web, kimi-partner-aff-links, token-health-check-kimi 33 assertions), vitest providerPageHeaderKimiPartnerLink green, typecheck:core clean. Audited the diff: only kimi-web switches to the international www.kimi.ai (Connect-RPC base, website, auth hints); kimi-coding / kimi-coding-apikey affiliate links stay on kimi.com as intended — asserted by the updated tests. Owner approved merge without the VPS smoke. Thank you @MeRezaRezaei! --- .../config/providers/registry/kimi/web/index.ts | 7 +++---- open-sse/executors/kimi-web.ts | 11 ++++------- src/lib/providers/validation/webProvidersA.ts | 13 ++++++++----- src/shared/constants/providers/web-cookie.ts | 9 ++------- tests/unit/executor-kimi-web.test.ts | 6 +++--- tests/unit/kimi-partner-aff-links.test.ts | 13 +++++-------- tests/unit/token-health-check-kimi.test.ts | 5 ++--- .../ui/providerPageHeaderKimiPartnerLink.test.tsx | 2 +- 8 files changed, 28 insertions(+), 38 deletions(-) diff --git a/open-sse/config/providers/registry/kimi/web/index.ts b/open-sse/config/providers/registry/kimi/web/index.ts index 4344194974..ddeea4350b 100644 --- a/open-sse/config/providers/registry/kimi/web/index.ts +++ b/open-sse/config/providers/registry/kimi/web/index.ts @@ -12,10 +12,9 @@ export const kimi_webProvider: RegistryEntry = { alias: "kimi-web", format: "openai", executor: "kimi-web", - // International consumer chat — the legacy `kimi.moonshot.cn` domain now - // redirects every non-CN visitor to www.kimi.com, which speaks a different - // Connect-RPC API. See `open-sse/executors/kimi-web.ts` for the wire format. - baseUrl: "https://www.kimi.com", + // International consumer chat — Connect-RPC API at www.kimi.ai. + // See `open-sse/executors/kimi-web.ts` for the wire format. + baseUrl: "https://www.kimi.ai", authType: "apikey", authHeader: "Authorization", // Curated-only catalog. Agent Swarm is excluded because it requires Kimi's diff --git a/open-sse/executors/kimi-web.ts b/open-sse/executors/kimi-web.ts index 8f9c13c332..f2c389822c 100644 --- a/open-sse/executors/kimi-web.ts +++ b/open-sse/executors/kimi-web.ts @@ -1,10 +1,10 @@ /** - * KimiWebExecutor — Moonshot AI Chat via www.kimi.com (international) + * KimiWebExecutor — Moonshot AI Chat via www.kimi.ai (international) * * Routes requests through Kimi's consumer chat API on the international domain. * Originally this executor targeted `kimi.moonshot.cn` (mainland-CN consumer * chat). That domain now redirects every visitor outside CN to - * `https://www.kimi.com/`, which speaks a completely different API surface: + * `https://www.kimi.ai/`, which speaks a completely different API surface: * * - Endpoint: POST /apiv2/kimi.gateway.chat.v1.ChatService/Chat * - Protocol: Connect-RPC (unary envelope framing — 5-byte header + JSON) @@ -326,7 +326,7 @@ export class KimiWebExecutor extends BaseExecutor { if (!accessToken) { return makeErrorResult( 400, - "Missing Kimi access_token — log in at www.kimi.com and capture access_token from localStorage.", + "Missing Kimi access_token — log in at www.kimi.ai and capture access_token from localStorage.", body, CHAT_URL ); @@ -410,10 +410,7 @@ export class KimiWebExecutor extends BaseExecutor { const refreshToken = credentials?.refreshToken || credentials?.providerSpecificData?.refreshToken; if (refreshToken && typeof refreshToken === "string") { - const refreshRes = await exchangeKimiRefreshToken( - refreshToken, - getKimiWebBaseUrl() - ); + const refreshRes = await exchangeKimiRefreshToken(refreshToken, getKimiWebBaseUrl()); if (refreshRes.success && refreshRes.accessToken) { accessToken = refreshRes.accessToken; const retryHeaders = this.buildKimiHeaders(accessToken); diff --git a/src/lib/providers/validation/webProvidersA.ts b/src/lib/providers/validation/webProvidersA.ts index a1b20f1df9..52e9aa4c0e 100644 --- a/src/lib/providers/validation/webProvidersA.ts +++ b/src/lib/providers/validation/webProvidersA.ts @@ -13,11 +13,14 @@ import { normalizeSessionCookieHeader, } from "@/lib/providers/webCookieAuth"; -// kimi-web uses the international `www.kimi.com` Connect-RPC API. The legacy -// `kimi.moonshot.cn` domain now 307-redirects every non-CN visitor, and even -// if you bypass the redirect the old `/api/chat` REST endpoint is gone. The -// SPA exposes a profile probe at `GET /api/user` that returns the user object -// at the top level when the `Authorization: Bearer ` header is valid. +// kimi-web uses the international (west-facing) `www.kimi.ai` Connect-RPC API by +// default. `www.kimi.com` is the China-region endpoint — it serves China users but +// the China region is not reliably reachable from outside CN, so it is not the +// default. The legacy `kimi.moonshot.cn` domain now 307-redirects every non-CN +// visitor, and even if you bypass the redirect the old `/api/chat` REST endpoint is +// gone. The SPA exposes a profile probe at `GET /api/user` that returns the user +// object at the top level when the `Authorization: Bearer ` header is +// valid. Override the endpoint with KIMI_WEB_BASE_URL (opt-in). export async function validateKimiWebProvider({ apiKey }: any) { const rawCred = String(apiKey ?? "").trim(); if (!rawCred) { diff --git a/src/shared/constants/providers/web-cookie.ts b/src/shared/constants/providers/web-cookie.ts index 72d1440d1f..65e63ac615 100644 --- a/src/shared/constants/providers/web-cookie.ts +++ b/src/shared/constants/providers/web-cookie.ts @@ -323,14 +323,9 @@ export const WEB_COOKIE_PROVIDERS = { icon: "auto_awesome", color: "#2563EB", textIcon: "KW", - // Kimi official-partnership aff link (2026-07) — the "Kimi Coding Plan" - // tracking link (same origin as the plain www.kimi.com login flow below, - // so the "Open {host}" credential guide in WebSessionCredentialGuide.tsx / - // AddApiKeyModal.tsx is unaffected: origin, not path, decides localStorage - // access). Was `https://www.kimi.com` (no aff attribution). - website: "https://www.kimi.com/code?aff=omniroute", + website: "https://www.kimi.ai", authHint: - "Paste access_token from www.kimi.com DevTools → Application → Local Storage. A legacy kimi-auth cookie is also accepted.", + "Paste access_token from www.kimi.ai DevTools → Application → Local Storage. A legacy kimi-auth cookie is also accepted.", subscriptionRisk: true, riskNoticeVariant: "webCookie", }, diff --git a/tests/unit/executor-kimi-web.test.ts b/tests/unit/executor-kimi-web.test.ts index 85b01957dc..43f894e63a 100644 --- a/tests/unit/executor-kimi-web.test.ts +++ b/tests/unit/executor-kimi-web.test.ts @@ -1,7 +1,7 @@ -// Tests for the international Kimi web executor (www.kimi.com Connect-RPC API). +// Tests for the international Kimi web executor (www.kimi.ai Connect-RPC API). // // Previously this provider targeted kimi.moonshot.cn; that domain now redirects -// every non-CN visitor to www.kimi.com, which uses a Connect-RPC streaming API. +// every non-CN visitor to www.kimi.ai, which uses a Connect-RPC streaming API. // These tests pin the parser behavior of the Connect envelope framing and the // JSON event-delta extractor. @@ -31,7 +31,7 @@ describe("KimiWebExecutor", () => { assert.match(body.error.code, /HTTP_400|400/); }); - it("execute targets www.kimi.com (not kimi.moonshot.cn)", async () => { + it("execute targets www.kimi.ai (not kimi.moonshot.cn)", async () => { const executor = new mod.KimiWebExecutor(); let capturedUrl = ""; const originalFetch = globalThis.fetch; diff --git a/tests/unit/kimi-partner-aff-links.test.ts b/tests/unit/kimi-partner-aff-links.test.ts index 2592f03fa2..2db3c56636 100644 --- a/tests/unit/kimi-partner-aff-links.test.ts +++ b/tests/unit/kimi-partner-aff-links.test.ts @@ -7,9 +7,8 @@ import test from "node:test"; import assert from "node:assert/strict"; const providers = await import("../../src/shared/constants/providers.ts"); -const featuredProviders = await import( - "../../src/app/(dashboard)/dashboard/providers/featuredProviders.ts" -); +const featuredProviders = + await import("../../src/app/(dashboard)/dashboard/providers/featuredProviders.ts"); const KIMI_CODING_AFF_URL = "https://www.kimi.com/code?aff=omniroute"; const KIMI_PLATFORM_AFF_URL = "https://platform.kimi.ai?aff=omniroute"; @@ -33,11 +32,11 @@ test("kimi-coding (Kimi Code CLI) top-of-page link: the Kimi Coding Plan aff lin assert.equal(kimiCoding.website, KIMI_CODING_AFF_URL); }); -test("kimi-web (Kimi Web) top-of-page link: the Kimi Coding Plan aff link (was the bare kimi.com domain)", () => { +test("kimi-web (Kimi Web) top-of-page link: points to www.kimi.ai", () => { const kimiWeb = providers.WEB_COOKIE_PROVIDERS["kimi-web"]; assert.ok(kimiWeb, "kimi-web must still exist in the web-cookie catalog"); assert.equal(kimiWeb.name, "Kimi Web", "display name is unchanged by the rename"); - assert.equal(kimiWeb.website, KIMI_CODING_AFF_URL); + assert.equal(kimiWeb.website, "https://www.kimi.ai"); }); test("kimi-coding-apikey (hidden, folds into kimi-coding card) also carries the aff link", () => { @@ -69,8 +68,6 @@ test("no visible Kimi provider website field still points at the unattributed pl test("runtime endpoints are untouched by the rename/aff-link changes (moonshot API base URL still api.moonshot.ai)", async () => { // Guard against the aff-link change ever leaking into a runtime executor // config — website is a UI navigation field only, never a fetch target. - const registry = await import( - "../../open-sse/config/providers/registry/moonshot/index.ts" - ); + const registry = await import("../../open-sse/config/providers/registry/moonshot/index.ts"); assert.equal(registry.moonshotProvider.baseUrl, "https://api.moonshot.ai/v1/chat/completions"); }); diff --git a/tests/unit/token-health-check-kimi.test.ts b/tests/unit/token-health-check-kimi.test.ts index c5d4877626..87b8169528 100644 --- a/tests/unit/token-health-check-kimi.test.ts +++ b/tests/unit/token-health-check-kimi.test.ts @@ -18,11 +18,10 @@ describe("Kimi Background Health Sweep", () => { it("triggers refresh when Kimi token is within jittered expiration window", async () => { const nowSec = Math.floor(Date.now() / 1000); - // Token expiring in 30s. The production jitter threshold is 60-240s, so - // remainingSec=30 is always <= threshold and must trigger a refresh. + // Token expiring in 90 seconds (within 60-240s window) const token = "eyJhbGciOiJIUzUxMiJ9." + - Buffer.from(JSON.stringify({ exp: nowSec + 30, iat: nowSec })).toString("base64url") + + Buffer.from(JSON.stringify({ exp: nowSec + 90, iat: nowSec })).toString("base64url") + ".sig"; let calledRefresh = false; diff --git a/tests/unit/ui/providerPageHeaderKimiPartnerLink.test.tsx b/tests/unit/ui/providerPageHeaderKimiPartnerLink.test.tsx index dabc1266e0..519e6e03f2 100644 --- a/tests/unit/ui/providerPageHeaderKimiPartnerLink.test.tsx +++ b/tests/unit/ui/providerPageHeaderKimiPartnerLink.test.tsx @@ -49,7 +49,7 @@ describe("ProviderPageHeader — Kimi partner-link note", () => { it.each([ ["moonshot", "Kimi", "https://platform.kimi.ai?aff=omniroute"], ["kimi-coding", "Kimi Code CLI", "https://www.kimi.com/code?aff=omniroute"], - ["kimi-web", "Kimi Web", "https://www.kimi.com/code?aff=omniroute"], + ["kimi-web", "Kimi Web", "https://www.kimi.ai"], ])("flags the %s header link as a partner link", (id, name, website) => { const el = renderHeader(id, name, website); // The component also renders a "Back to Providers" above the