From 335637de1eafb6aa4507b9eacb9d3de7fac4449d Mon Sep 17 00:00:00 2001 From: roboomp Date: Fri, 14 Aug 2026 04:50:46 +0000 Subject: [PATCH] fix(ai): restored Beijing Token Plan quota reporting - Routed Beijing credentials through the Bailian console session and usage endpoints. - Added region-aware Cookie capture guidance and regression coverage. Fixes #8509 --- packages/ai/CHANGELOG.md | 4 + .../ai/src/registry/alibaba-token-plan.ts | 8 +- packages/ai/src/usage/alibaba-token-plan.ts | 123 +++++++++++++----- .../ai/test/alibaba-token-plan-usage.test.ts | 83 +++++++++++- packages/ai/test/alibaba-token-plan.test.ts | 10 +- 5 files changed, 190 insertions(+), 38 deletions(-) diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index 4420c3286..986271e23 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed quota reporting and Cookie capture guidance for China (Beijing) Alibaba Token Plan credentials ([#8509](https://github.com/can1357/oh-my-pi/issues/8509)). + ## [17.3.3] - 2026-08-14 ### Fixed diff --git a/packages/ai/src/registry/alibaba-token-plan.ts b/packages/ai/src/registry/alibaba-token-plan.ts index 3aec3d451..2802fcac3 100644 --- a/packages/ai/src/registry/alibaba-token-plan.ts +++ b/packages/ai/src/registry/alibaba-token-plan.ts @@ -86,9 +86,13 @@ export async function loginAlibabaTokenPlan(options: OAuthController): Promise = { Accept: "application/json, text/plain, */*", "Content-Type": "application/x-www-form-urlencoded", Cookie: cookie, - Origin: CONSOLE_ORIGIN, - Referer: DASHBOARD_URL, + Origin: consoleConfig.origin, + Referer: consoleConfig.dashboardUrl, "User-Agent": BROWSER_USER_AGENT, "X-Requested-With": "XMLHttpRequest", }; @@ -140,16 +188,21 @@ async function fetchAlibabaTokenPlanUsage( } const body = new URLSearchParams({ product: "sfm_bailian", - action: GATEWAY_ACTION, - region: "ap-southeast-1", + action: consoleConfig.gatewayAction, + region: consoleConfig.region, sec_token: secToken, params: JSON.stringify({ Api: USAGE_API, - Data: { cornerstoneParam: CONSOLE_CORNERSTONE_PARAM }, + Data: { + cornerstoneParam: { + ...(isChina ? { feTraceId: crypto.randomUUID() } : {}), + ...consoleConfig.cornerstoneParam, + }, + }, V: "1.0", }), }); - const usageResponse = await ctx.fetch(USAGE_URL, { + const usageResponse = await ctx.fetch(consoleConfig.usageUrl, { method: "POST", headers, body, @@ -157,16 +210,18 @@ async function fetchAlibabaTokenPlanUsage( signal: params.signal, }); if (!usageResponse.ok) { - ctx.logger?.warn("QwenCloud usage fetch failed", { provider: PROVIDER, status: usageResponse.status }); + ctx.logger?.warn("Alibaba Token Plan usage fetch failed", { + provider: PROVIDER, + status: usageResponse.status, + }); return null; } const payload: unknown = await usageResponse.json(); if (!isRecord(payload) || payload.successResponse === false || !isRecord(payload.data)) { - ctx.logger?.warn("QwenCloud usage response invalid", { provider: PROVIDER }); + ctx.logger?.warn("Alibaba Token Plan usage response invalid", { provider: PROVIDER }); return null; } const responseData = unwrapGatewayData(payload.data); - const accountId = accountIdFromUserData(userPayload.data); const limits = [ buildLimit( "5h", @@ -190,10 +245,10 @@ async function fetchAlibabaTokenPlanUsage( provider: PROVIDER, fetchedAt: Date.now(), limits, - metadata: { source: "qwencloud-console", ...(accountId ? { accountId } : {}) }, + metadata: { source: isChina ? "bailian-console" : "qwencloud-console", ...(accountId ? { accountId } : {}) }, }; } catch (error) { - ctx.logger?.warn("QwenCloud usage request failed", { + ctx.logger?.warn("Alibaba Token Plan usage request failed", { provider: PROVIDER, error: error instanceof Error ? error.name : "unknown", }); diff --git a/packages/ai/test/alibaba-token-plan-usage.test.ts b/packages/ai/test/alibaba-token-plan-usage.test.ts index 46e6ac59c..e0f0fb0d9 100644 --- a/packages/ai/test/alibaba-token-plan-usage.test.ts +++ b/packages/ai/test/alibaba-token-plan-usage.test.ts @@ -5,7 +5,10 @@ import { alibabaTokenPlanRankingStrategy, alibabaTokenPlanUsageProvider, } from "@oh-my-pi/pi-ai/usage/alibaba-token-plan"; -import { serializeAlibabaTokenPlanCredential } from "@oh-my-pi/pi-catalog/wire/alibaba-token-plan"; +import { + ALIBABA_TOKEN_PLAN_CN_BASE_URL, + serializeAlibabaTokenPlanCredential, +} from "@oh-my-pi/pi-catalog/wire/alibaba-token-plan"; function params(apiKey: string): UsageFetchParams { return { @@ -102,6 +105,84 @@ describe("QwenCloud Token Plan opt-in usage", () => { expect(windows.secondary?.id).toBe("credits:7d"); }); + test("fetches China quota through the Beijing console gateway", async () => { + const requests: { url: string; init?: RequestInit }[] = []; + const fetchMock: FetchImpl = (input, init) => { + requests.push({ url: String(input), init }); + if (requests.length === 1) { + return Promise.resolve( + new Response(''), + ); + } + return Promise.resolve( + Response.json({ + code: "200", + data: { + DataV2: { + data: { + data: { + per1WeekPercentage: 0.7913113, + per1WeekResetTime: 1_786_716_480_000, + }, + }, + }, + }, + successResponse: true, + }), + ); + }; + const cookie = "login_aliyunid_csrf=cn-csrf; aliyun_lang=zh"; + const credential = serializeAlibabaTokenPlanCredential("sk-sp-beijing", cookie, ALIBABA_TOKEN_PLAN_CN_BASE_URL); + + const report = await alibabaTokenPlanUsageProvider.fetchUsage(params(credential), { fetch: fetchMock }); + + expect(requests).toHaveLength(2); + expect(requests[0]?.url).toBe("https://bailian.console.aliyun.com/cn-beijing?tab=plan"); + expect(new Headers(requests[0]?.init?.headers).get("Cookie")).toBe(cookie); + expect(requests[1]?.url).toBe( + "https://bailian-cs.console.aliyun.com/data/api.json?action=BroadScopeAspnGateway&product=sfm_bailian&api=zeldaHttp.apikeyMgr.%2Ftokenplan%2Fpersonal%2Fapi%2Fv2%2Fusage", + ); + const usageHeaders = new Headers(requests[1]?.init?.headers); + expect(usageHeaders.get("Origin")).toBe("https://bailian.console.aliyun.com"); + expect(usageHeaders.get("Referer")).toBe("https://bailian.console.aliyun.com/cn-beijing?tab=plan"); + const body = new URLSearchParams(String(requests[1]?.init?.body)); + expect(body.get("action")).toBe("BroadScopeAspnGateway"); + expect(body.get("region")).toBe("cn-beijing"); + expect(body.get("sec_token")).toBe("cn-sec-token"); + const gatewayParams: unknown = JSON.parse(body.get("params") ?? "null"); + expect(gatewayParams).toMatchObject({ + Api: "zeldaHttp.apikeyMgr./tokenplan/personal/api/v2/usage", + Data: { + cornerstoneParam: { + feTraceId: expect.any(String), + feURL: "https://bailian.console.aliyun.com/cn-beijing?tab=plan#/efm/subscription/token-plan/personal", + protocol: "V2", + console: "ONE_CONSOLE", + productCode: "p_efm", + switchAgent: 12608464, + switchUserType: 3, + domain: "bailian.console.aliyun.com", + consoleSite: "BAILIAN_ALIYUN", + userNickName: "", + userPrincipalName: "", + xsp_lang: "zh-CN", + }, + }, + V: "1.0", + }); + expect(report).toMatchObject({ + provider: "alibaba-token-plan", + limits: [ + { + id: "credits:7d", + window: { id: "7d", durationMs: 604_800_000, resetsAt: 1_786_716_480_000 }, + amount: { usedFraction: 0.7913113, unit: "percent" }, + }, + ], + }); + expect(report?.limits).toHaveLength(1); + }); + test("does not claim quota support for API-key-only credentials", async () => { let fetched = false; const fetchMock: FetchImpl = () => { diff --git a/packages/ai/test/alibaba-token-plan.test.ts b/packages/ai/test/alibaba-token-plan.test.ts index fb9f71375..c5d14a2b3 100644 --- a/packages/ai/test/alibaba-token-plan.test.ts +++ b/packages/ai/test/alibaba-token-plan.test.ts @@ -36,10 +36,17 @@ describe("QwenCloud Token Plan login", () => { test("China (Beijing) region validates against and routes inference to cn-beijing", async () => { const authRequests: { url: string; instructions?: string }[] = []; let requestedUrl = ""; + let cookiePrompt = ""; const prompts = ["2", "sk-sp-beijing"]; const credential = await loginAlibabaTokenPlan({ onAuth: request => authRequests.push(request), - onPrompt: async prompt => (prompt.allowEmpty ? "" : (prompts.shift() ?? "")), + onPrompt: async prompt => { + if (prompt.allowEmpty) { + cookiePrompt = prompt.message; + return ""; + } + return prompts.shift() ?? ""; + }, fetch: input => { requestedUrl = String(input); return Promise.resolve(Response.json({ data: [{ id: "qwen3.7-plus" }] })); @@ -48,6 +55,7 @@ describe("QwenCloud Token Plan login", () => { expect(requestedUrl).toBe("https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1/models"); expect(authRequests[0]?.url).toBe("https://www.aliyun.com/benefit/scene/tokenplan"); + expect(cookiePrompt).toContain("bailian-cs.console.aliyun.com/data/api.json"); expect(JSON.parse(credential)).toEqual({ token: "sk-sp-beijing", baseUrl: "https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1",