refactor(open-sse): D1a/D1b — URL quirks → schema urlSuffix + clean debug logs

buildUrl: switch provider-name → config-driven. ?beta=true thành
urlSuffix per registry (claude/glm/kimi/minimax/minimax-cn/kimi-coding);
gemini path dùng format==="gemini"; accountId substitution giữ generic.
Xóa console.log('[DEBUG]') trong refreshCline.
Golden url-header 142 pass (byte-for-byte URL).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
decolua
2026-06-13 22:00:57 +07:00
co-authored by Cursor
parent 72ce515709
commit ad2a3e5de3
7 changed files with 21 additions and 28 deletions
+8 -21
View File
@@ -55,17 +55,14 @@ export class DefaultExecutor extends BaseExecutor {
const normalized = baseUrl.replace(/\/$/, ""); const normalized = baseUrl.replace(/\/$/, "");
return `${normalized}/messages`; return `${normalized}/messages`;
} }
switch (this.provider) { // gemini-format: build :streamGenerateContent / :generateContent path
case "claude": if (this.config.format === "gemini") {
case "glm":
case "kimi":
case "minimax":
case "minimax-cn":
case "kimi-coding":
return `${this.config.baseUrl}?beta=true`;
case "gemini":
return `${this.config.baseUrl}/${model}:${stream ? "streamGenerateContent?alt=sse" : "generateContent"}`; return `${this.config.baseUrl}/${model}:${stream ? "streamGenerateContent?alt=sse" : "generateContent"}`;
default: { }
// urlSuffix (e.g. ?beta=true) declared per-provider in registry
if (this.config.urlSuffix) {
return `${this.config.baseUrl}${this.config.urlSuffix}`;
}
const url = this.config.baseUrl; const url = this.config.baseUrl;
if (url?.includes("{accountId}")) { if (url?.includes("{accountId}")) {
const accountId = credentials?.providerSpecificData?.accountId; const accountId = credentials?.providerSpecificData?.accountId;
@@ -74,8 +71,6 @@ export class DefaultExecutor extends BaseExecutor {
} }
return url; return url;
} }
}
}
buildHeaders(credentials, stream = true) { buildHeaders(credentials, stream = true) {
const headers = { "Content-Type": "application/json", ...this.config.headers }; const headers = { "Content-Type": "application/json", ...this.config.headers };
@@ -280,24 +275,16 @@ export class DefaultExecutor extends BaseExecutor {
} }
async refreshCline(refreshToken, proxyOptions = null) { async refreshCline(refreshToken, proxyOptions = null) {
console.log('[DEBUG] Refreshing Cline token, refreshToken length:', refreshToken?.length);
const response = await proxyAwareFetch(PROVIDERS.cline.refreshUrl, { const response = await proxyAwareFetch(PROVIDERS.cline.refreshUrl, {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json", "Accept": "application/json" }, headers: { "Content-Type": "application/json", "Accept": "application/json" },
body: JSON.stringify({ refreshToken, grantType: "refresh_token", clientType: "extension" }) body: JSON.stringify({ refreshToken, grantType: "refresh_token", clientType: "extension" })
}, proxyOptions); }, proxyOptions);
console.log('[DEBUG] Cline refresh response status:', response.status); if (!response.ok) return null;
if (!response.ok) {
const errorText = await response.text();
console.log('[DEBUG] Cline refresh error:', errorText);
return null;
}
const payload = await response.json(); const payload = await response.json();
console.log('[DEBUG] Cline refresh payload:', JSON.stringify(payload).substring(0, 200));
const data = payload?.data || payload; const data = payload?.data || payload;
const expiresAtIso = data?.expiresAt; const expiresAtIso = data?.expiresAt;
const expiresIn = expiresAtIso ? Math.max(1, Math.floor((new Date(expiresAtIso).getTime() - Date.now()) / 1000)) : undefined; const expiresIn = expiresAtIso ? Math.max(1, Math.floor((new Date(expiresAtIso).getTime() - Date.now()) / 1000)) : undefined;
console.log('[DEBUG] Cline refresh success, expiresIn:', expiresIn);
return { accessToken: data?.accessToken, refreshToken: data?.refreshToken || refreshToken, expiresIn }; return { accessToken: data?.accessToken, refreshToken: data?.refreshToken || refreshToken, expiresIn };
} }
+1
View File
@@ -6,6 +6,7 @@ export default {
transport: { transport: {
baseUrl: "https://api.anthropic.com/v1/messages", baseUrl: "https://api.anthropic.com/v1/messages",
format: "claude", format: "claude",
urlSuffix: "?beta=true",
headers: { ...CLAUDE_CLI_SPOOF_HEADERS }, headers: { ...CLAUDE_CLI_SPOOF_HEADERS },
clientId: "9d1c250a-e61b-44d9-88ed-5944d1962f5e", clientId: "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
tokenUrl: "https://api.anthropic.com/v1/oauth/token" tokenUrl: "https://api.anthropic.com/v1/oauth/token"
+1
View File
@@ -6,6 +6,7 @@ export default {
transport: { transport: {
baseUrl: "https://api.z.ai/api/anthropic/v1/messages", baseUrl: "https://api.z.ai/api/anthropic/v1/messages",
format: "claude", format: "claude",
urlSuffix: "?beta=true",
headers: { ...CLAUDE_API_HEADERS } headers: { ...CLAUDE_API_HEADERS }
}, },
models: [ models: [
@@ -6,6 +6,7 @@ export default {
transport: { transport: {
baseUrl: KIMI_CODING_BASE_URL, baseUrl: KIMI_CODING_BASE_URL,
format: "claude", format: "claude",
urlSuffix: "?beta=true",
headers: { ...CLAUDE_API_HEADERS }, headers: { ...CLAUDE_API_HEADERS },
clientId: "17e5f671-d194-4dfb-9706-5516cb48c098", clientId: "17e5f671-d194-4dfb-9706-5516cb48c098",
tokenUrl: "https://auth.kimi.com/api/oauth/token", tokenUrl: "https://auth.kimi.com/api/oauth/token",
+1
View File
@@ -6,6 +6,7 @@ export default {
transport: { transport: {
baseUrl: KIMI_CODING_BASE_URL, baseUrl: KIMI_CODING_BASE_URL,
format: "claude", format: "claude",
urlSuffix: "?beta=true",
headers: { ...CLAUDE_API_HEADERS } headers: { ...CLAUDE_API_HEADERS }
}, },
models: [ models: [
@@ -6,6 +6,7 @@ export default {
transport: { transport: {
baseUrl: "https://api.minimaxi.com/anthropic/v1/messages", baseUrl: "https://api.minimaxi.com/anthropic/v1/messages",
format: "claude", format: "claude",
urlSuffix: "?beta=true",
headers: { ...CLAUDE_API_HEADERS } headers: { ...CLAUDE_API_HEADERS }
}, },
models: [ models: [
+1
View File
@@ -6,6 +6,7 @@ export default {
transport: { transport: {
baseUrl: "https://api.minimax.io/anthropic/v1/messages", baseUrl: "https://api.minimax.io/anthropic/v1/messages",
format: "claude", format: "claude",
urlSuffix: "?beta=true",
headers: { ...CLAUDE_API_HEADERS } headers: { ...CLAUDE_API_HEADERS }
}, },
models: [ models: [