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:
decolua
2026-07-17 12:09:14 +07:00
parent bc252ea802
commit 68566f53dc
21 changed files with 332 additions and 228 deletions

View File

@@ -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 () => {