fix(kimi-web): migrate to www.kimi.com Connect-RPC API (kimi.moonshot.cn retired) (#5858)

Integrated into release/v3.8.43
This commit is contained in:
janeza2
2026-07-02 08:00:57 +07:00
committed by GitHub
parent 103a0ee2a2
commit b708aa7e75
30 changed files with 1128 additions and 407 deletions

View File

@@ -4836,7 +4836,7 @@
"v0VercelWebLabel": "v0 Vercel Web",
"v0VercelWebDesc": "AI code generation via v0.dev",
"kimiWebLabel": "Kimi Web",
"kimiWebDesc": "Chinese market AI chat via kimi.moonshot.cn",
"kimiWebDesc": "Moonshot AI consumer chat via www.kimi.com (international, Connect-RPC API)",
"doubaoWebLabel": "Doubao Web",
"doubaoWebDesc": "ByteDance AI chat via doubao.com"
},

View File

@@ -4811,7 +4811,7 @@
"v0VercelWebLabel": "__MISSING__:v0 Vercel Web",
"v0VercelWebDesc": "__MISSING__:AI code generation via v0.dev",
"kimiWebLabel": "__MISSING__:Kimi Web",
"kimiWebDesc": "__MISSING__:Chinese market AI chat via kimi.moonshot.cn",
"kimiWebDesc": "__MISSING__:Moonshot AI consumer chat via www.kimi.com (international, Connect-RPC API)",
"doubaoWebLabel": "__MISSING__:Doubao Web",
"doubaoWebDesc": "__MISSING__:ByteDance AI chat via doubao.com"
},

View File

@@ -4811,7 +4811,7 @@
"v0VercelWebLabel": "__MISSING__:v0 Vercel Web",
"v0VercelWebDesc": "__MISSING__:AI code generation via v0.dev",
"kimiWebLabel": "__MISSING__:Kimi Web",
"kimiWebDesc": "__MISSING__:Chinese market AI chat via kimi.moonshot.cn",
"kimiWebDesc": "__MISSING__:Moonshot AI consumer chat via www.kimi.com (international, Connect-RPC API)",
"doubaoWebLabel": "__MISSING__:Doubao Web",
"doubaoWebDesc": "__MISSING__:ByteDance AI chat via doubao.com"
},

View File

@@ -4811,7 +4811,7 @@
"v0VercelWebLabel": "__MISSING__:v0 Vercel Web",
"v0VercelWebDesc": "__MISSING__:AI code generation via v0.dev",
"kimiWebLabel": "__MISSING__:Kimi Web",
"kimiWebDesc": "__MISSING__:Chinese market AI chat via kimi.moonshot.cn",
"kimiWebDesc": "__MISSING__:Moonshot AI consumer chat via www.kimi.com (international, Connect-RPC API)",
"doubaoWebLabel": "__MISSING__:Doubao Web",
"doubaoWebDesc": "__MISSING__:ByteDance AI chat via doubao.com"
},

View File

@@ -4811,7 +4811,7 @@
"v0VercelWebLabel": "__MISSING__:v0 Vercel Web",
"v0VercelWebDesc": "__MISSING__:AI code generation via v0.dev",
"kimiWebLabel": "__MISSING__:Kimi Web",
"kimiWebDesc": "__MISSING__:Chinese market AI chat via kimi.moonshot.cn",
"kimiWebDesc": "__MISSING__:Moonshot AI consumer chat via www.kimi.com (international, Connect-RPC API)",
"doubaoWebLabel": "__MISSING__:Doubao Web",
"doubaoWebDesc": "__MISSING__:ByteDance AI chat via doubao.com"
},

View File

@@ -4811,7 +4811,7 @@
"v0VercelWebLabel": "__MISSING__:v0 Vercel Web",
"v0VercelWebDesc": "__MISSING__:AI code generation via v0.dev",
"kimiWebLabel": "__MISSING__:Kimi Web",
"kimiWebDesc": "__MISSING__:Chinese market AI chat via kimi.moonshot.cn",
"kimiWebDesc": "__MISSING__:Moonshot AI consumer chat via www.kimi.com (international, Connect-RPC API)",
"doubaoWebLabel": "__MISSING__:Doubao Web",
"doubaoWebDesc": "__MISSING__:ByteDance AI chat via doubao.com"
},

View File

@@ -4811,7 +4811,7 @@
"v0VercelWebLabel": "__MISSING__:v0 Vercel Web",
"v0VercelWebDesc": "__MISSING__:AI code generation via v0.dev",
"kimiWebLabel": "__MISSING__:Kimi Web",
"kimiWebDesc": "__MISSING__:Chinese market AI chat via kimi.moonshot.cn",
"kimiWebDesc": "__MISSING__:Moonshot AI consumer chat via www.kimi.com (international, Connect-RPC API)",
"doubaoWebLabel": "__MISSING__:Doubao Web",
"doubaoWebDesc": "__MISSING__:ByteDance AI chat via doubao.com"
},

View File

@@ -4811,7 +4811,7 @@
"v0VercelWebLabel": "__MISSING__:v0 Vercel Web",
"v0VercelWebDesc": "__MISSING__:AI code generation via v0.dev",
"kimiWebLabel": "__MISSING__:Kimi Web",
"kimiWebDesc": "__MISSING__:Chinese market AI chat via kimi.moonshot.cn",
"kimiWebDesc": "__MISSING__:Moonshot AI consumer chat via www.kimi.com (international, Connect-RPC API)",
"doubaoWebLabel": "__MISSING__:Doubao Web",
"doubaoWebDesc": "__MISSING__:ByteDance AI chat via doubao.com"
},

View File

@@ -4811,7 +4811,7 @@
"v0VercelWebLabel": "__MISSING__:v0 Vercel Web",
"v0VercelWebDesc": "__MISSING__:AI code generation via v0.dev",
"kimiWebLabel": "__MISSING__:Kimi Web",
"kimiWebDesc": "__MISSING__:Chinese market AI chat via kimi.moonshot.cn",
"kimiWebDesc": "__MISSING__:Moonshot AI consumer chat via www.kimi.com (international, Connect-RPC API)",
"doubaoWebLabel": "__MISSING__:Doubao Web",
"doubaoWebDesc": "__MISSING__:ByteDance AI chat via doubao.com"
},

View File

@@ -4811,7 +4811,7 @@
"v0VercelWebLabel": "__MISSING__:v0 Vercel Web",
"v0VercelWebDesc": "__MISSING__:AI code generation via v0.dev",
"kimiWebLabel": "__MISSING__:Kimi Web",
"kimiWebDesc": "__MISSING__:Chinese market AI chat via kimi.moonshot.cn",
"kimiWebDesc": "__MISSING__:Moonshot AI consumer chat via www.kimi.com (international, Connect-RPC API)",
"doubaoWebLabel": "__MISSING__:Doubao Web",
"doubaoWebDesc": "__MISSING__:ByteDance AI chat via doubao.com"
},

View File

@@ -4811,7 +4811,7 @@
"v0VercelWebLabel": "__MISSING__:v0 Vercel Web",
"v0VercelWebDesc": "__MISSING__:AI code generation via v0.dev",
"kimiWebLabel": "__MISSING__:Kimi Web",
"kimiWebDesc": "__MISSING__:Chinese market AI chat via kimi.moonshot.cn",
"kimiWebDesc": "__MISSING__:Moonshot AI consumer chat via www.kimi.com (international, Connect-RPC API)",
"doubaoWebLabel": "__MISSING__:Doubao Web",
"doubaoWebDesc": "__MISSING__:ByteDance AI chat via doubao.com"
},

View File

@@ -4811,7 +4811,7 @@
"v0VercelWebLabel": "__MISSING__:v0 Vercel Web",
"v0VercelWebDesc": "__MISSING__:AI code generation via v0.dev",
"kimiWebLabel": "__MISSING__:Kimi Web",
"kimiWebDesc": "__MISSING__:Chinese market AI chat via kimi.moonshot.cn",
"kimiWebDesc": "__MISSING__:Moonshot AI consumer chat via www.kimi.com (international, Connect-RPC API)",
"doubaoWebLabel": "__MISSING__:Doubao Web",
"doubaoWebDesc": "__MISSING__:ByteDance AI chat via doubao.com"
},

View File

@@ -4811,7 +4811,7 @@
"v0VercelWebLabel": "__MISSING__:v0 Vercel Web",
"v0VercelWebDesc": "__MISSING__:AI code generation via v0.dev",
"kimiWebLabel": "__MISSING__:Kimi Web",
"kimiWebDesc": "__MISSING__:Chinese market AI chat via kimi.moonshot.cn",
"kimiWebDesc": "__MISSING__:Moonshot AI consumer chat via www.kimi.com (international, Connect-RPC API)",
"doubaoWebLabel": "__MISSING__:Doubao Web",
"doubaoWebDesc": "__MISSING__:ByteDance AI chat via doubao.com"
},

View File

@@ -4811,7 +4811,7 @@
"v0VercelWebLabel": "v0 Vercel Web",
"v0VercelWebDesc": "Генерация кода ИИ через v0.dev",
"kimiWebLabel": "Kimi Web",
"kimiWebDesc": "Китайский рынок AI чата через kimi.moonshot.cn",
"kimiWebDesc": "Чат Moonshot AI через www.kimi.com (международная версия, Connect-RPC API)",
"doubaoWebLabel": "Doubao Web",
"doubaoWebDesc": "Чат AI ByteDance через doubao.com"
},

View File

@@ -4811,7 +4811,7 @@
"v0VercelWebLabel": "__MISSING__:v0 Vercel Web",
"v0VercelWebDesc": "__MISSING__:AI code generation via v0.dev",
"kimiWebLabel": "__MISSING__:Kimi Web",
"kimiWebDesc": "__MISSING__:Chinese market AI chat via kimi.moonshot.cn",
"kimiWebDesc": "__MISSING__:Moonshot AI consumer chat via www.kimi.com (international, Connect-RPC API)",
"doubaoWebLabel": "__MISSING__:Doubao Web",
"doubaoWebDesc": "__MISSING__:ByteDance AI chat via doubao.com"
},

View File

@@ -4811,7 +4811,7 @@
"v0VercelWebLabel": "__MISSING__:v0 Vercel Web",
"v0VercelWebDesc": "__MISSING__:AI code generation via v0.dev",
"kimiWebLabel": "__MISSING__:Kimi Web",
"kimiWebDesc": "__MISSING__:Chinese market AI chat via kimi.moonshot.cn",
"kimiWebDesc": "__MISSING__:Moonshot AI consumer chat via www.kimi.com (international, Connect-RPC API)",
"doubaoWebLabel": "__MISSING__:Doubao Web",
"doubaoWebDesc": "__MISSING__:ByteDance AI chat via doubao.com"
},

View File

@@ -4667,7 +4667,7 @@
"v0VercelWebLabel": "v0 Vercel Web",
"v0VercelWebDesc": "通过 v0.dev 的 AI 代码生成",
"kimiWebLabel": "Kimi Web",
"kimiWebDesc": "通过 kimi.moonshot.cn 访问中国市场的 AI 聊天",
"kimiWebDesc": "通过 www.kimi.com 访问 Moonshot AI 聊天国际版Connect-RPC API",
"doubaoWebLabel": "豆包网",
"doubaoWebDesc": "字节跳动 AI 聊天通过 doubao.com",
"Account Deactivated": "账户已停用",

View File

@@ -32,6 +32,7 @@ import {
validateChatGptWebProvider,
validatePerplexityWebProvider,
validateBlackboxWebProvider,
validateKimiWebProvider,
} from "./validation/webProvidersA";
import {
validateMuseSparkWebProvider,
@@ -349,6 +350,7 @@ export async function validateProviderApiKey({ provider, apiKey, providerSpecifi
"deepseek-web": validateDeepSeekWebProvider,
"grok-web": validateGrokWebProvider,
"qwen-web": validateQwenWebProvider,
"kimi-web": validateKimiWebProvider,
"chatgpt-web": validateChatGptWebProvider,
"perplexity-web": validatePerplexityWebProvider,
"blackbox-web": validateBlackboxWebProvider,

View File

@@ -8,10 +8,85 @@ import {
buildGrokCookieHeader,
buildQwenCookieHeader,
extractCookieValue,
extractKimiJwt,
extractQwenToken,
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 <JWT>` header is valid.
//
// Auth source: the `kimi-auth` cookie set after login. The user pastes the
// full Cookie header; we extract `kimi-auth` and send it as both the Bearer
// token and a `Cookie: kimi-auth=<jwt>` replay (the latter is what the SPA
// does, though the upstream only consults the Authorization header in
// practice — verified by stripping one of the two at a time).
export async function validateKimiWebProvider({ apiKey }: any) {
const rawCred = String(apiKey ?? "").trim();
if (!rawCred) {
return {
valid: false,
error:
"Missing Kimi session — paste the full Cookie header from www.kimi.com (must contain kimi-auth=<JWT>)",
};
}
const jwt = extractKimiJwt(rawCred);
if (!jwt) {
return {
valid: false,
error:
"Could not find a kimi-auth JWT in the pasted value. Re-login at https://www.kimi.com and copy the full Cookie header.",
};
}
try {
const resp = await fetch("https://www.kimi.com/api/user", {
headers: {
Accept: "application/json, text/plain, */*",
Authorization: `Bearer ${jwt}`,
Cookie: `kimi-auth=${jwt}`,
Origin: "https://www.kimi.com",
Referer: "https://www.kimi.com/",
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36",
},
});
if (resp.status === 401 || resp.status === 403) {
return {
valid: false,
error:
"Kimi session is invalid or expired — re-login at https://www.kimi.com and paste a fresh Cookie header",
};
}
if (!resp.ok) {
return { valid: false, error: `Kimi returned HTTP ${resp.status}` };
}
// Profile response: `{ id, name, email, region, ... }` at the top level.
try {
const data = await resp.json();
if (!data?.id) {
return {
valid: false,
error:
"Kimi session token is invalid or expired — re-login at https://www.kimi.com and paste a fresh Cookie header",
};
}
} catch {
return { valid: false, error: "Kimi returned invalid JSON response" };
}
return { valid: true, error: null };
} catch (error) {
return toValidationErrorResult(error);
}
}
export async function validateDeepSeekWebProvider({ apiKey }: any) {
if (!apiKey) {
return {

View File

@@ -106,6 +106,38 @@ export function extractQwenToken(rawValue: string): string {
return match ? match[1] : "";
}
/**
* Pull the `kimi-auth` JWT out of whatever the user pasted for the
* international Kimi consumer chat (www.kimi.com).
*
* Accepts (all return the same JWT string):
* - bare JWT `eyJhbGci...sig`
* - full Cookie header `_ga=...; kimi-auth=eyJ...; theme=dark`
* - `Cookie:` / `Authorization: Bearer` prefixed forms
* - stray `Bearer eyJ...` without a header label
*
* Returns "" if no JWT can be located.
*/
export function extractKimiJwt(rawValue: string): string {
const trimmed = stripCookieInputPrefix(rawValue);
if (!trimmed) return "";
// Bare JWT — three base64url segments separated by dots.
if (/^eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(trimmed)) {
return trimmed;
}
// Cookie-style pair: pull `kimi-auth=<value>` out of the blob.
const match = trimmed.match(/(?:^|[\s;])kimi-auth=([^;\s]+)/);
if (match) return match[1];
// Last resort: a `Bearer <jwt>` pasted without the header label.
const bearer = trimmed.match(/bearer\s+(eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)/i);
if (bearer) return bearer[1];
return "";
}
export function normalizeSessionCookieHeaders(
rawValues: Array<string | null | undefined>,
defaultCookieName: string

View File

@@ -242,8 +242,8 @@ export const WEB_COOKIE_PROVIDERS = {
icon: "auto_awesome",
color: "#2563EB",
textIcon: "KW",
website: "https://kimi.moonshot.cn",
authHint: "Paste your session cookie from kimi.moonshot.cn (DevTools → Application → Cookies)",
website: "https://www.kimi.com",
authHint: "Paste your Cookie header from www.kimi.com (must contain kimi-auth=...). Find it via DevTools → Network → request → Cookie.",
subscriptionRisk: true,
riskNoticeVariant: "webCookie",
},

View File

@@ -164,10 +164,10 @@ export const WEB_SESSION_CREDENTIAL_REQUIREMENTS = {
},
"kimi-web": {
kind: "cookie",
credentialName: "session",
placeholder: "session=... or full Cookie header from kimi.moonshot.cn",
credentialName: "kimi-auth",
placeholder: "kimi-auth=eyJ... (full Cookie header from www.kimi.com)",
acceptsFullCookieHeader: true,
storageKeys: ["cookie", "session"],
storageKeys: ["cookie", "kimi-auth", "session"],
},
"doubao-web": {
kind: "cookie",