fix(providers): remove Qwen provider support

Qwen OAuth flow (portal.qwen.ai) stopped working reliably; drop the
executor, registry entry, OAuth provider/service, token refresh
profile, usage handler, and related test coverage and baselines.
This commit is contained in:
decolua
2026-08-05 16:17:26 +07:00
parent 6498b3122f
commit dcdd4628b3
27 changed files with 8 additions and 525 deletions

View File

@@ -12,10 +12,6 @@
"token": "https://api.anthropic.com/v1/oauth/token",
"auth": "https://api.anthropic.com/v1/oauth/authorize"
},
"qwen": {
"token": "https://chat.qwen.ai/api/v1/oauth2/token",
"auth": "https://chat.qwen.ai/api/v1/oauth2/device/code"
},
"iflow": {
"token": "https://iflow.cn/oauth/token",
"auth": "https://iflow.cn/oauth"
@@ -29,7 +25,6 @@
"tokenUrls": {
"claude": "https://api.anthropic.com/v1/oauth/token",
"codex": "https://auth.openai.com/oauth/token",
"qwen": "https://chat.qwen.ai/api/v1/oauth2/token",
"iflow": "https://iflow.cn/oauth/token",
"kiro": "https://prod.us-east-1.auth.desktop.kiro.dev/refreshToken",
"xai": "https://auth.x.ai/oauth2/token",
@@ -49,7 +44,6 @@
"clientIds": {
"claude": "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
"codex": "app_EMoamEEZ73f0CkXaXp7hrann",
"qwen": "f0304373b74a44d2b584a3fb70ca9e56",
"iflow": "10009311001",
"kimi": "17e5f671-d194-4dfb-9706-5516cb48c098",
"grok-cli": "b1a00492-073a-47ea-816f-4c329264a828"

View File

@@ -15,7 +15,6 @@ const resolved = {
tokenUrls: {
claude: PROVIDERS.claude?.tokenUrl,
codex: PROVIDERS.codex?.tokenUrl,
qwen: PROVIDERS.qwen?.tokenUrl,
iflow: PROVIDERS.iflow?.tokenUrl,
kiro: PROVIDERS.kiro?.tokenUrl,
xai: PROVIDERS.xai?.tokenUrl,
@@ -25,7 +24,6 @@ const resolved = {
kimi: PROVIDERS.kimi?.tokenUrl,
},
authUrls: {
qwen: PROVIDERS.qwen?.authUrl,
iflow: PROVIDERS.iflow?.authUrl,
kiro: PROVIDERS.kiro?.authUrl,
},
@@ -38,7 +36,6 @@ const resolved = {
clientIds: {
claude: PROVIDERS.claude?.clientId,
codex: PROVIDERS.codex?.clientId,
qwen: PROVIDERS.qwen?.clientId,
iflow: PROVIDERS.iflow?.clientId,
kimi: PROVIDERS.kimi?.clientId,
"grok-cli": PROVIDERS["grok-cli"]?.clientId,

View File

@@ -18,7 +18,7 @@ const SPECIAL_CRED = {
// Chúng được lock riêng ở 11-provider edge tests / unit test chuyên biệt.
const SPECIALIZED = new Set([
"antigravity", "azure", "gemini-cli", "github", "iflow", "qoder", "kiro",
"codex", "cursor", "vertex", "vertex-partner", "qwen", "opencode",
"codex", "cursor", "vertex", "vertex-partner", "opencode",
"opencode-go", "grok-web", "perplexity-web", "ollama-local", "commandcode",
"xiaomi-tokenplan", "mimo-free",
]);

View File

@@ -151,7 +151,6 @@ describe("Codex Refresh Token", () => {
expect(getRefreshLeadMs("codex")).toBe(5 * 24 * 60 * 60 * 1000); // 5 days
expect(getRefreshLeadMs("claude")).toBe(4 * 60 * 60 * 1000); // 4 hours
expect(getRefreshLeadMs("iflow")).toBe(24 * 60 * 60 * 1000); // 24 hours
expect(getRefreshLeadMs("qwen")).toBe(20 * 60 * 1000); // 20 minutes
expect(getRefreshLeadMs("kimi")).toBe(5 * 60 * 1000); // 5 minutes
expect(getRefreshLeadMs("kimi-coding")).toBe(5 * 60 * 1000); // legacy alias
expect(getRefreshLeadMs("antigravity")).toBe(5 * 60 * 1000); // 5 minutes

View File

@@ -1,8 +1,8 @@
/**
* Generic OAuth2 token refresh — config-driven profiles.
*
* Verifies refreshAccessToken() handles the 5 foldable providers
* (qwen, iflow, github, kimi, claude) via a REFRESH_PROFILES table,
* Verifies refreshAccessToken() handles the 4 foldable providers
* (iflow, github, kimi, claude) via a REFRESH_PROFILES table,
* while preserving the legacy generic path for unknown providers.
*/
@@ -25,32 +25,6 @@ describe("refreshAccessToken — config-driven profiles", () => {
beforeEach(() => { vi.clearAllMocks(); vi.resetModules(); global.fetch = originalFetch; });
afterEach(() => { global.fetch = originalFetch; });
it("qwen: form body + clientId, surfaces resource_url as providerSpecificData", async () => {
const fm = mockFetchOnce({
access_token: "qw-acc",
refresh_token: "qw-refresh-rotated",
expires_in: 7200,
resource_url: "https://dashscope.aliyuncs.com",
});
const { refreshAccessToken } = await import("open-sse/services/tokenRefresh/providers.js");
const out = await refreshAccessToken("qwen", "qw-old-refresh", {}, console);
expect(out).toEqual({
accessToken: "qw-acc",
refreshToken: "qw-refresh-rotated",
expiresIn: 7200,
providerSpecificData: { resourceUrl: "https://dashscope.aliyuncs.com" },
});
const [url, init] = fm.mock.calls[0];
expect(init.method).toBe("POST");
expect(init.headers["Content-Type"]).toBe("application/x-www-form-urlencoded");
const body = new URLSearchParams(init.body);
expect(body.get("grant_type")).toBe("refresh_token");
expect(body.get("refresh_token")).toBe("qw-old-refresh");
expect(body.get("client_id")).toBeTruthy();
});
it("iflow: Basic Auth header from clientId:clientSecret, form body keeps client_secret", async () => {
const fm = mockFetchOnce({ access_token: "if-acc", refresh_token: "if-rot", expires_in: 3600 });
const { refreshAccessToken } = await import("open-sse/services/tokenRefresh/providers.js");
@@ -107,13 +81,13 @@ describe("refreshAccessToken — config-driven profiles", () => {
it("returns null on non-ok response", async () => {
mockFetchOnce({ error: "invalid_grant" }, { ok: false, status: 400 });
const { refreshAccessToken } = await import("open-sse/services/tokenRefresh/providers.js");
const out = await refreshAccessToken("qwen", "dead", {}, console);
const out = await refreshAccessToken("iflow", "dead", {}, console);
expect(out).toBeNull();
});
it("returns null when refreshToken missing", async () => {
const { refreshAccessToken } = await import("open-sse/services/tokenRefresh/providers.js");
const out = await refreshAccessToken("qwen", "", {}, console);
const out = await refreshAccessToken("iflow", "", {}, console);
expect(out).toBeNull();
});

View File

@@ -14,7 +14,7 @@ vi.mock("../../open-sse/utils/proxyFetch.js", () => ({
const load = () => import("../../open-sse/services/usage.js");
const SUPPORTED = [
"github", "gemini-cli", "antigravity", "claude", "codex", "kiro",
"qoder", "qwen", "iflow", "ollama", "glm", "glm-cn",
"qoder", "iflow", "ollama", "glm", "glm-cn",
"minimax", "minimax-cn", "vercel-ai-gateway", "grok-cli", "kimi",
"deepseek",
];