feat(kimi): merge OAuth into dual-auth provider, add K3/K2.7 models
Gộp kimi-coding vào kimi (oauth+apikey), parity CLIProxyAPI device flow/headers/refresh. Thêm K3 + K2.7 Code (+ Kimi Code ids), pricing/caps vision, cập nhật baseline. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { platform, arch } from "os";
|
||||
import { platform, arch, hostname } from "os";
|
||||
import { PROVIDERS, PROVIDER_OAUTH } from "./providers.js";
|
||||
import { ANTIGRAVITY_IDE_USER_AGENT } from "../providers/shared.js";
|
||||
import { createRequire } from "module";
|
||||
|
||||
// === Gemini CLI === derive từ registry gemini-cli.transport
|
||||
export const GEMINI_CLI_VERSION = PROVIDERS["gemini-cli"]?.cliVersion;
|
||||
@@ -171,12 +172,44 @@ export const OAUTH_ENDPOINTS = {
|
||||
github: { token: PROVIDER_OAUTH["github"]?.tokenUrl, auth: PROVIDER_OAUTH["github"]?.authorizeUrl, deviceCode: PROVIDER_OAUTH["github"]?.deviceCodeUrl },
|
||||
};
|
||||
|
||||
// Generate Kimi OAuth custom headers
|
||||
export function buildKimiHeaders() {
|
||||
let _appVersion;
|
||||
function getAppPackageVersion() {
|
||||
if (_appVersion) return _appVersion;
|
||||
try {
|
||||
const require = createRequire(import.meta.url);
|
||||
_appVersion = require("../../package.json").version || "0.0.0";
|
||||
} catch {
|
||||
_appVersion = process.env.npm_package_version || "0.0.0";
|
||||
}
|
||||
return _appVersion;
|
||||
}
|
||||
|
||||
// Kimi Code OAuth / API headers (CLIProxyAPI internal/auth/kimi commonHeaders parity).
|
||||
// deviceId must stay stable per connection for the whole OAuth session.
|
||||
export function buildKimiHeaders(deviceId) {
|
||||
const osName = platform();
|
||||
const architecture = arch();
|
||||
let deviceModel = `${osName} ${architecture}`;
|
||||
if (osName === "darwin") deviceModel = `macOS ${architecture}`;
|
||||
else if (osName === "win32") deviceModel = `Windows ${architecture}`;
|
||||
else if (osName === "linux") deviceModel = `Linux ${architecture}`;
|
||||
|
||||
let deviceName = "unknown";
|
||||
try {
|
||||
deviceName = hostname() || "unknown";
|
||||
} catch {
|
||||
deviceName = "unknown";
|
||||
}
|
||||
|
||||
const resolvedId = (typeof deviceId === "string" && deviceId.trim())
|
||||
? deviceId.trim()
|
||||
: `kimi-${Date.now()}`;
|
||||
|
||||
return {
|
||||
"X-Msh-Platform": "9router",
|
||||
"X-Msh-Version": "2.1.2",
|
||||
"X-Msh-Device-Model": typeof process !== "undefined" ? `${process.platform} ${process.arch}` : "unknown",
|
||||
"X-Msh-Device-Id": `kimi-${Date.now()}`
|
||||
"X-Msh-Version": getAppPackageVersion(),
|
||||
"X-Msh-Device-Name": deviceName,
|
||||
"X-Msh-Device-Model": deviceModel,
|
||||
"X-Msh-Device-Id": resolvedId,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,7 +38,8 @@ function applyAuth(headers, desc, credentials) {
|
||||
|
||||
// Provider-specific header quirks kept as small hooks (not pure auth).
|
||||
const HEADER_HOOKS = {
|
||||
kimiHeaders: (h) => Object.assign(h, buildKimiHeaders()),
|
||||
// Stable device_id from OAuth connection (CLIProxyAPI KimiTokenStorage.DeviceID)
|
||||
kimiHeaders: (h, c) => Object.assign(h, buildKimiHeaders(c?.providerSpecificData?.deviceId)),
|
||||
clineHeaders: (h, c) => Object.assign(h, buildClineHeaders(c.apiKey || c.accessToken)),
|
||||
kilocodeOrg: (h, c) => { if (c.providerSpecificData?.orgId) h["X-Kilocode-OrganizationID"] = c.providerSpecificData.orgId; },
|
||||
claudeOverlay: (h) => {
|
||||
@@ -227,7 +228,8 @@ export class DefaultExecutor extends BaseExecutor {
|
||||
kiro: () => this.refreshKiro(credentials.refreshToken, proxyOptions),
|
||||
cline: () => this.refreshCline(credentials.refreshToken, proxyOptions),
|
||||
clinepass: () => this.refreshCline(credentials.refreshToken, proxyOptions),
|
||||
"kimi-coding": () => this.refreshKimiCoding(credentials.refreshToken, proxyOptions),
|
||||
kimi: () => this.refreshKimi(credentials, proxyOptions),
|
||||
"kimi-coding": () => this.refreshKimi(credentials, proxyOptions),
|
||||
kilocode: () => this.refreshKilocode(credentials.refreshToken, proxyOptions)
|
||||
};
|
||||
|
||||
@@ -307,16 +309,20 @@ export class DefaultExecutor extends BaseExecutor {
|
||||
return { accessToken, refreshToken: data?.refreshToken || refreshToken, expiresIn };
|
||||
}
|
||||
|
||||
async refreshKimiCoding(refreshToken, proxyOptions = null) {
|
||||
const kimiHeaders = buildKimiHeaders();
|
||||
const response = await proxyAwareFetch(PROVIDERS["kimi-coding"].refreshUrl, {
|
||||
// CLIProxyAPI DeviceFlowClient.RefreshToken — form body + X-Msh-* headers + stable device_id
|
||||
async refreshKimi(credentials, proxyOptions = null) {
|
||||
const refreshToken = credentials.refreshToken;
|
||||
const cfg = PROVIDERS.kimi || PROVIDERS["kimi-coding"];
|
||||
if (!cfg?.refreshUrl || !cfg?.clientId) return null;
|
||||
const kimiHeaders = buildKimiHeaders(credentials?.providerSpecificData?.deviceId);
|
||||
const response = await proxyAwareFetch(cfg.refreshUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
"Accept": "application/json",
|
||||
...kimiHeaders
|
||||
},
|
||||
body: new URLSearchParams({ grant_type: "refresh_token", refresh_token: refreshToken, client_id: PROVIDERS["kimi-coding"].clientId })
|
||||
body: new URLSearchParams({ grant_type: "refresh_token", refresh_token: refreshToken, client_id: cfg.clientId })
|
||||
}, proxyOptions);
|
||||
if (!response.ok) return null;
|
||||
const tokens = await response.json();
|
||||
|
||||
@@ -96,6 +96,14 @@ export const MODEL_CAPABILITIES = {
|
||||
// Qwen plain coder/text (no vision) — registry "vision-model" / "coder-model" aliases
|
||||
"vision-model": { vision: true, reasoning: true, thinkingFormat: "qwen", contextWindow: 1000000 },
|
||||
"coder-model": { reasoning: true, thinkingFormat: "qwen", contextWindow: 1000000 },
|
||||
|
||||
// Kimi flagship + coding (platform + Kimi Code ids) — vision/video native
|
||||
"kimi-k3": { vision: true, videoInput: true, reasoning: true, thinkingFormat: "kimi", thinkingCanDisable: false, contextWindow: 1048576, maxOutput: 131072 },
|
||||
"k3": { vision: true, videoInput: true, reasoning: true, thinkingFormat: "kimi", thinkingCanDisable: false, contextWindow: 1048576, maxOutput: 131072 },
|
||||
"kimi-for-coding": { vision: true, videoInput: true, reasoning: true, thinkingFormat: "kimi", thinkingCanDisable: false, contextWindow: 262144, maxOutput: 65536 },
|
||||
"kimi-for-coding-highspeed": { vision: true, videoInput: true, reasoning: true, thinkingFormat: "kimi", thinkingCanDisable: false, contextWindow: 262144, maxOutput: 65536 },
|
||||
"kimi-k2.7-code": { vision: true, videoInput: true, reasoning: true, thinkingFormat: "kimi", thinkingCanDisable: false, contextWindow: 262144, maxOutput: 65536 },
|
||||
"kimi-k2.7-code-highspeed": { vision: true, videoInput: true, reasoning: true, thinkingFormat: "kimi", thinkingCanDisable: false, contextWindow: 262144, maxOutput: 65536 },
|
||||
};
|
||||
|
||||
const KIRO_GPT_5_6_CAPABILITIES = { vision: true, reasoning: true, search: true, thinkingFormat: "openai", contextWindow: 272000, maxOutput: 128000 };
|
||||
@@ -222,7 +230,9 @@ export const PATTERN_CAPABILITIES = [
|
||||
{ pattern: "*qwen*", caps: { reasoning: true, thinkingFormat: "qwen", contextWindow: 262144 } },
|
||||
|
||||
// ── Kimi (enabled→reasoning_effort; K2.7-code cannot disable) ─────
|
||||
{ pattern: "*kimi*k2.7*code*", caps: { vision: true, reasoning: true, thinkingFormat: "kimi", thinkingCanDisable: false, contextWindow: 262144, maxOutput: 262144 } },
|
||||
{ pattern: "*kimi*k3*", caps: { vision: true, videoInput: true, reasoning: true, thinkingFormat: "kimi", thinkingCanDisable: false, contextWindow: 1048576, maxOutput: 131072 } },
|
||||
{ pattern: "*kimi*for-coding*", caps: { vision: true, videoInput: true, reasoning: true, thinkingFormat: "kimi", thinkingCanDisable: false, contextWindow: 262144, maxOutput: 65536 } },
|
||||
{ pattern: "*kimi*k2.7*code*", caps: { vision: true, videoInput: true, reasoning: true, thinkingFormat: "kimi", thinkingCanDisable: false, contextWindow: 262144, maxOutput: 65536 } },
|
||||
{ pattern: "*kimi*k2*", caps: { vision: true, reasoning: true, thinkingFormat: "kimi", contextWindow: 262144, maxOutput: 262144 } },
|
||||
{ pattern: "*kimi*", caps: { reasoning: true, thinkingFormat: "kimi", contextWindow: 262144 } },
|
||||
|
||||
|
||||
@@ -75,10 +75,18 @@ export const MODEL_PRICING = {
|
||||
"qwen3-coder-flash": { input: 0.50, output: 2.00, cached: 0.25, reasoning: 3.00, cache_creation: 0.50 },
|
||||
|
||||
// === Kimi ===
|
||||
// Official platform.kimi.ai: cache-hit / cache-miss / output per 1M tokens
|
||||
"kimi-k3": { input: 3.00, output: 15.00, cached: 0.30, reasoning: 15.00, cache_creation: 3.00 },
|
||||
"k3": { input: 3.00, output: 15.00, cached: 0.30, reasoning: 15.00, cache_creation: 3.00 },
|
||||
"kimi-k2.7-code": { input: 0.95, output: 4.00, cached: 0.19, reasoning: 4.00, cache_creation: 0.95 },
|
||||
"kimi-k2.7-code-highspeed": { input: 1.90, output: 8.00, cached: 0.38, reasoning: 8.00, cache_creation: 1.90 },
|
||||
"kimi-for-coding": { input: 0.95, output: 4.00, cached: 0.19, reasoning: 4.00, cache_creation: 0.95 },
|
||||
"kimi-for-coding-highspeed": { input: 1.90, output: 8.00, cached: 0.38, reasoning: 8.00, cache_creation: 1.90 },
|
||||
"kimi-k2": { input: 1.00, output: 4.00, cached: 0.50, reasoning: 6.00, cache_creation: 1.00 },
|
||||
"kimi-k2-thinking": { input: 1.50, output: 6.00, cached: 0.75, reasoning: 9.00, cache_creation: 1.50 },
|
||||
"kimi-k2.5": { input: 1.20, output: 4.80, cached: 0.60, reasoning: 7.20, cache_creation: 1.20 },
|
||||
"kimi-k2.5-thinking": { input: 1.80, output: 7.20, cached: 0.90, reasoning: 10.80, cache_creation: 1.80 },
|
||||
"kimi-k2.6": { input: 1.00, output: 4.00, cached: 0.50, reasoning: 6.00, cache_creation: 1.00 },
|
||||
"kimi-latest": { input: 1.00, output: 4.00, cached: 0.50, reasoning: 6.00, cache_creation: 1.00 },
|
||||
|
||||
// === DeepSeek ===
|
||||
@@ -185,6 +193,7 @@ export const PATTERN_PRICING = [
|
||||
|
||||
// --- Kimi ---
|
||||
{ pattern: "kimi-*-thinking", pricing: { input: 1.80, output: 7.20, cached: 0.90, reasoning: 10.80, cache_creation: 1.80 } },
|
||||
{ pattern: "kimi-k3*", pricing: { input: 3.00, output: 15.00, cached: 0.30, reasoning: 15.00, cache_creation: 3.00 } },
|
||||
{ pattern: "kimi-k2*", pricing: { input: 1.20, output: 4.80, cached: 0.60, reasoning: 7.20, cache_creation: 1.20 } },
|
||||
{ pattern: "kimi-*", pricing: { input: 1.00, output: 4.00, cached: 0.50, reasoning: 6.00, cache_creation: 1.00 } },
|
||||
|
||||
|
||||
@@ -52,53 +52,52 @@ import p49 from "./jina-ai.js";
|
||||
import p50 from "./jina-reader.js";
|
||||
import p51 from "./kilocode.js";
|
||||
import p52 from "./kimchi.js";
|
||||
import p53 from "./kimi-coding.js";
|
||||
import p54 from "./kimi.js";
|
||||
import p55 from "./kiro.js";
|
||||
import p56 from "./linkup.js";
|
||||
import p57 from "./local-device.js";
|
||||
import p58 from "./mimo-free.js";
|
||||
import p59 from "./minimax-cn.js";
|
||||
import p60 from "./minimax.js";
|
||||
import p61 from "./mistral.js";
|
||||
import p62 from "./mmf.js";
|
||||
import p63 from "./nanobanana.js";
|
||||
import p64 from "./nebius.js";
|
||||
import p65 from "./nvidia.js";
|
||||
import p66 from "./ollama-local.js";
|
||||
import p67 from "./ollama.js";
|
||||
import p68 from "./openai.js";
|
||||
import p69 from "./opencode-go.js";
|
||||
import p70 from "./opencode.js";
|
||||
import p71 from "./openrouter.js";
|
||||
import p72 from "./perplexity-web.js";
|
||||
import p73 from "./perplexity.js";
|
||||
import p74 from "./perplexity-agent.js";
|
||||
import p75 from "./playht.js";
|
||||
import p76 from "./qoder.js";
|
||||
import p77 from "./qwen.js";
|
||||
import p78 from "./recraft.js";
|
||||
import p79 from "./runwayml.js";
|
||||
import p80 from "./sdwebui.js";
|
||||
import p81 from "./searchapi.js";
|
||||
import p82 from "./searxng.js";
|
||||
import p83 from "./serper.js";
|
||||
import p84 from "./siliconflow.js";
|
||||
import p85 from "./stability-ai.js";
|
||||
import p86 from "./tavily.js";
|
||||
import p87 from "./together.js";
|
||||
import p88 from "./topaz.js";
|
||||
import p89 from "./tortoise.js";
|
||||
import p90 from "./venice.js";
|
||||
import p91 from "./vercel-ai-gateway.js";
|
||||
import p92 from "./vertex-partner.js";
|
||||
import p93 from "./vertex.js";
|
||||
import p94 from "./volcengine-ark.js";
|
||||
import p95 from "./voyage-ai.js";
|
||||
import p96 from "./xai.js";
|
||||
import p97 from "./xiaomi-mimo.js";
|
||||
import p98 from "./xiaomi-tokenplan.js";
|
||||
import p99 from "./youcom.js";
|
||||
import p53 from "./kimi.js";
|
||||
import p54 from "./kiro.js";
|
||||
import p55 from "./linkup.js";
|
||||
import p56 from "./local-device.js";
|
||||
import p57 from "./mimo-free.js";
|
||||
import p58 from "./minimax-cn.js";
|
||||
import p59 from "./minimax.js";
|
||||
import p60 from "./mistral.js";
|
||||
import p61 from "./mmf.js";
|
||||
import p62 from "./nanobanana.js";
|
||||
import p63 from "./nebius.js";
|
||||
import p64 from "./nvidia.js";
|
||||
import p65 from "./ollama-local.js";
|
||||
import p66 from "./ollama.js";
|
||||
import p67 from "./openai.js";
|
||||
import p68 from "./opencode-go.js";
|
||||
import p69 from "./opencode.js";
|
||||
import p70 from "./openrouter.js";
|
||||
import p71 from "./perplexity-web.js";
|
||||
import p72 from "./perplexity.js";
|
||||
import p73 from "./perplexity-agent.js";
|
||||
import p74 from "./playht.js";
|
||||
import p75 from "./qoder.js";
|
||||
import p76 from "./qwen.js";
|
||||
import p77 from "./recraft.js";
|
||||
import p78 from "./runwayml.js";
|
||||
import p79 from "./sdwebui.js";
|
||||
import p80 from "./searchapi.js";
|
||||
import p81 from "./searxng.js";
|
||||
import p82 from "./serper.js";
|
||||
import p83 from "./siliconflow.js";
|
||||
import p84 from "./stability-ai.js";
|
||||
import p85 from "./tavily.js";
|
||||
import p86 from "./together.js";
|
||||
import p87 from "./topaz.js";
|
||||
import p88 from "./tortoise.js";
|
||||
import p89 from "./venice.js";
|
||||
import p90 from "./vercel-ai-gateway.js";
|
||||
import p91 from "./vertex-partner.js";
|
||||
import p92 from "./vertex.js";
|
||||
import p93 from "./volcengine-ark.js";
|
||||
import p94 from "./voyage-ai.js";
|
||||
import p95 from "./xai.js";
|
||||
import p96 from "./xiaomi-mimo.js";
|
||||
import p97 from "./xiaomi-tokenplan.js";
|
||||
import p98 from "./youcom.js";
|
||||
|
||||
export default [
|
||||
p0,
|
||||
@@ -200,5 +199,4 @@ export default [
|
||||
p96,
|
||||
p97,
|
||||
p98,
|
||||
p99
|
||||
];
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import { CLAUDE_API_HEADERS, KIMI_CODING_BASE_URL } from "../shared.js";
|
||||
|
||||
export default {
|
||||
id: "kimi-coding",
|
||||
hidden: true,
|
||||
priority: 120,
|
||||
alias: "kmc",
|
||||
display: {
|
||||
name: "Kimi Coding",
|
||||
icon: "psychology",
|
||||
color: "#1E40AF",
|
||||
textIcon: "KC",
|
||||
website: "https://kimi.moonshot.cn",
|
||||
notice: {
|
||||
signupUrl: "https://kimi.moonshot.cn",
|
||||
},
|
||||
},
|
||||
category: "oauth",
|
||||
transport: {
|
||||
baseUrl: "https://api.kimi.com/coding/v1/messages",
|
||||
format: "claude",
|
||||
urlSuffix: "?beta=true",
|
||||
headers: { ...CLAUDE_API_HEADERS },
|
||||
clientId: "17e5f671-d194-4dfb-9706-5516cb48c098",
|
||||
tokenUrl: "https://auth.kimi.com/api/oauth/token",
|
||||
refreshUrl: "https://auth.kimi.com/api/oauth/token",
|
||||
auth: {
|
||||
combined: true,
|
||||
header: "x-api-key",
|
||||
scheme: "raw",
|
||||
hooks: [
|
||||
"kimiHeaders",
|
||||
],
|
||||
},
|
||||
},
|
||||
// Multi-endpoint: pick the transport matching client sourceFormat to skip translation.
|
||||
transports: [
|
||||
{
|
||||
format: "openai",
|
||||
baseUrl: "https://api.kimi.com/coding/v1/chat/completions",
|
||||
auth: { combined: true, header: "Authorization", scheme: "bearer", hooks: ["kimiHeaders"] },
|
||||
},
|
||||
{
|
||||
format: "claude",
|
||||
baseUrl: "https://api.kimi.com/coding/v1/messages",
|
||||
urlSuffix: "?beta=true",
|
||||
headers: { ...CLAUDE_API_HEADERS },
|
||||
auth: { combined: true, header: "x-api-key", scheme: "raw", hooks: ["kimiHeaders"] },
|
||||
},
|
||||
],
|
||||
models: [
|
||||
{ id: "kimi-k2.6", name: "Kimi K2.6" },
|
||||
{ id: "kimi-k2.5", name: "Kimi K2.5" },
|
||||
{ id: "kimi-k2.5-thinking", name: "Kimi K2.5 Thinking" },
|
||||
{ id: "kimi-latest", name: "Kimi Latest" },
|
||||
],
|
||||
oauth: {
|
||||
deviceCodeUrl: "https://auth.kimi.com/api/oauth/device_authorization",
|
||||
tokenUrl: "https://auth.kimi.com/api/oauth/token",
|
||||
refreshLeadMs: 300000,
|
||||
},
|
||||
features: {
|
||||
usage: true,
|
||||
},
|
||||
};
|
||||
@@ -1,9 +1,14 @@
|
||||
import { CLAUDE_API_HEADERS, KIMI_CODING_BASE_URL } from "../shared.js";
|
||||
import { CLAUDE_API_HEADERS } from "../shared.js";
|
||||
|
||||
// Dual auth (same pattern as xai): OAuth = Kimi Code subscription (device code),
|
||||
// API key = platform.moonshot / api.kimi.com. Transport is shared.
|
||||
// CLIProxyAPI parity: client_id, auth.kimi.com device+token, X-Msh-* headers, device_id.
|
||||
export default {
|
||||
id: "kimi",
|
||||
priority: 170,
|
||||
alias: "kimi",
|
||||
// Legacy id + short alias from former kimi-coding registry entry
|
||||
aliases: ["kimi-coding", "kmc"],
|
||||
display: {
|
||||
name: "Kimi",
|
||||
icon: "psychology",
|
||||
@@ -12,18 +17,25 @@ export default {
|
||||
website: "https://kimi.moonshot.cn",
|
||||
notice: {
|
||||
apiKeyUrl: "https://platform.moonshot.ai/console/api-keys",
|
||||
signupUrl: "https://www.kimi.com/code",
|
||||
},
|
||||
},
|
||||
category: "apikey",
|
||||
category: "oauth",
|
||||
authModes: ["oauth", "apikey"],
|
||||
hasOAuth: true,
|
||||
transport: {
|
||||
baseUrl: "https://api.kimi.com/coding/v1/messages",
|
||||
format: "claude",
|
||||
urlSuffix: "?beta=true",
|
||||
headers: { ...CLAUDE_API_HEADERS },
|
||||
clientId: "17e5f671-d194-4dfb-9706-5516cb48c098",
|
||||
tokenUrl: "https://auth.kimi.com/api/oauth/token",
|
||||
refreshUrl: "https://auth.kimi.com/api/oauth/token",
|
||||
auth: {
|
||||
combined: true,
|
||||
header: "x-api-key",
|
||||
scheme: "raw",
|
||||
hooks: ["kimiHeaders"],
|
||||
},
|
||||
},
|
||||
// Multi-endpoint: pick the transport matching client sourceFormat to skip translation.
|
||||
@@ -31,26 +43,47 @@ export default {
|
||||
{
|
||||
format: "openai",
|
||||
baseUrl: "https://api.kimi.com/coding/v1/chat/completions",
|
||||
auth: { combined: true, header: "Authorization", scheme: "bearer" },
|
||||
auth: { combined: true, header: "Authorization", scheme: "bearer", hooks: ["kimiHeaders"] },
|
||||
},
|
||||
{
|
||||
format: "claude",
|
||||
baseUrl: "https://api.kimi.com/coding/v1/messages",
|
||||
urlSuffix: "?beta=true",
|
||||
headers: { ...CLAUDE_API_HEADERS },
|
||||
auth: { combined: true, header: "x-api-key", scheme: "raw" },
|
||||
auth: { combined: true, header: "x-api-key", scheme: "raw", hooks: ["kimiHeaders"] },
|
||||
},
|
||||
],
|
||||
models: [
|
||||
// Flagship K3 — platform.kimi.ai id `kimi-k3`, Kimi Code OAuth id `k3` (up to 1M)
|
||||
{ id: "kimi-k3", name: "Kimi K3" },
|
||||
{ id: "k3", name: "Kimi K3 (Code)" },
|
||||
// Kimi Code subscription stable ids (map to K2.7 Code backend)
|
||||
{ id: "kimi-for-coding", name: "Kimi for Coding" },
|
||||
{ id: "kimi-for-coding-highspeed", name: "Kimi for Coding Highspeed" },
|
||||
// Pay-as-you-go platform ids
|
||||
{ id: "kimi-k2.7-code", name: "Kimi K2.7 Code" },
|
||||
{ id: "kimi-k2.7-code-highspeed", name: "Kimi K2.7 Code Highspeed" },
|
||||
{ id: "kimi-k2.6", name: "Kimi K2.6" },
|
||||
{ id: "kimi-k2.5", name: "Kimi K2.5" },
|
||||
{ id: "kimi-k2.5-thinking", name: "Kimi K2.5 Thinking" },
|
||||
{ id: "kimi-latest", name: "Kimi Latest" },
|
||||
],
|
||||
serviceKinds: ["llm","webSearch"],
|
||||
serviceKinds: ["llm", "webSearch"],
|
||||
searchViaChat: {
|
||||
defaultModel: "kimi-k2.5",
|
||||
defaultModel: "kimi-k3",
|
||||
endpoint: "https://api.moonshot.cn/v1/chat/completions",
|
||||
pricingUrl: "https://platform.moonshot.ai/docs/pricing/chat",
|
||||
pricingUrl: "https://platform.kimi.ai/docs/pricing/chat",
|
||||
},
|
||||
oauth: {
|
||||
clientId: "17e5f671-d194-4dfb-9706-5516cb48c098",
|
||||
deviceCodeUrl: "https://auth.kimi.com/api/oauth/device_authorization",
|
||||
tokenUrl: "https://auth.kimi.com/api/oauth/token",
|
||||
refreshUrl: "https://auth.kimi.com/api/oauth/token",
|
||||
// CLIProxyAPI refreshThresholdSeconds = 300
|
||||
refreshLeadMs: 300000,
|
||||
authorizeDeviceUrl: "https://www.kimi.com/code/authorize_device",
|
||||
},
|
||||
features: {
|
||||
usage: true,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@ import { OAUTH_ENDPOINTS, REFRESH_LEAD_MS } from "../config/appConstants.js";
|
||||
import {
|
||||
refreshXaiToken,
|
||||
refreshAccessToken,
|
||||
refreshKimiToken,
|
||||
refreshClaudeOAuthToken,
|
||||
refreshGoogleToken,
|
||||
refreshQwenToken,
|
||||
@@ -18,6 +19,7 @@ import {
|
||||
// Re-export all provider refresh functions (preserves public API for all consumers)
|
||||
export {
|
||||
refreshAccessToken,
|
||||
refreshKimiToken,
|
||||
refreshClaudeOAuthToken,
|
||||
refreshGoogleToken,
|
||||
refreshQwenToken,
|
||||
@@ -44,7 +46,10 @@ export function isUnrecoverableRefreshError(result) {
|
||||
}
|
||||
|
||||
export function getRefreshLeadMs(provider) {
|
||||
return REFRESH_LEAD_MS[provider] || TOKEN_EXPIRY_BUFFER_MS;
|
||||
if (REFRESH_LEAD_MS[provider]) return REFRESH_LEAD_MS[provider];
|
||||
// Legacy id after kimi-coding → kimi merge
|
||||
if (provider === "kimi-coding" && REFRESH_LEAD_MS.kimi) return REFRESH_LEAD_MS.kimi;
|
||||
return TOKEN_EXPIRY_BUFFER_MS;
|
||||
}
|
||||
|
||||
export function parseVertexSaJson(apiKey) {
|
||||
@@ -133,6 +138,9 @@ const REFRESH_HANDLERS = {
|
||||
"grok-cli": (c, log) => refreshXaiToken(c.refreshToken, log),
|
||||
gcli: (c, log) => refreshXaiToken(c.refreshToken, log),
|
||||
"codebuddy-cn": (c, log) => refreshCodebuddyToken(c.refreshToken, log),
|
||||
// Kimi Code OAuth (merged into id `kimi`); legacy id still routes here
|
||||
kimi: (c, log) => refreshKimiToken(c.refreshToken, c, log),
|
||||
"kimi-coding": (c, log) => refreshKimiToken(c.refreshToken, c, log),
|
||||
vertex: vertexRefreshHandler,
|
||||
"vertex-partner": vertexRefreshHandler
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PROVIDERS, PROVIDER_OAUTH } from "../../config/providers.js";
|
||||
import { OAUTH_ENDPOINTS, GITHUB_COPILOT } from "../../config/appConstants.js";
|
||||
import { OAUTH_ENDPOINTS, GITHUB_COPILOT, buildKimiHeaders } from "../../config/appConstants.js";
|
||||
import { proxyAwareFetch } from "../../utils/proxyFetch.js";
|
||||
import { dedupRefresh } from "./dedup.js";
|
||||
import { buildExternalIdpRefreshParams } from "../../../src/lib/oauth/kiroExternalIdp.js";
|
||||
@@ -91,6 +91,52 @@ export async function refreshAccessToken(provider, refreshToken, credentials, lo
|
||||
}, log);
|
||||
}
|
||||
|
||||
// CLIProxyAPI DeviceFlowClient.RefreshToken: form body (no client_secret) + X-Msh-* headers
|
||||
export async function refreshKimiToken(refreshToken, credentials, log) {
|
||||
const config = PROVIDERS.kimi;
|
||||
if (!config?.refreshUrl || !config?.clientId) {
|
||||
log?.warn?.("TOKEN_REFRESH", "No Kimi refresh URL/clientId configured");
|
||||
return null;
|
||||
}
|
||||
if (!refreshToken) return null;
|
||||
|
||||
return dedupRefresh("kimi", refreshToken, async () => {
|
||||
try {
|
||||
const headers = {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
Accept: "application/json",
|
||||
...buildKimiHeaders(credentials?.providerSpecificData?.deviceId),
|
||||
};
|
||||
const response = await fetch(config.refreshUrl, {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: new URLSearchParams({
|
||||
grant_type: "refresh_token",
|
||||
refresh_token: refreshToken,
|
||||
client_id: config.clientId,
|
||||
}),
|
||||
});
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
log?.error?.("TOKEN_REFRESH", `Failed to refresh token for kimi`, {
|
||||
status: response.status,
|
||||
error: errorText,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
const tokens = await response.json();
|
||||
return {
|
||||
accessToken: tokens.access_token,
|
||||
refreshToken: tokens.refresh_token || refreshToken,
|
||||
expiresIn: tokens.expires_in,
|
||||
};
|
||||
} catch (error) {
|
||||
log?.error?.("TOKEN_REFRESH", `Error refreshing token for kimi`, { error: error.message });
|
||||
return null;
|
||||
}
|
||||
}, log);
|
||||
}
|
||||
|
||||
export async function refreshClaudeOAuthToken(refreshToken, log) {
|
||||
if (!refreshToken) return null;
|
||||
return dedupRefresh("claude", refreshToken, async () => {
|
||||
|
||||
Reference in New Issue
Block a user