mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-25 16:42:16 +03:00
fix(kimi): point kimi-web to international www.kimi.ai (#11045)
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!
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 <access_token>` 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 <access_token>` 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) {
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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" <Link> above the
|
||||
|
||||
Reference in New Issue
Block a user