From fddb83c5d0e2441d3acddd11ebc8ee5c86befc29 Mon Sep 17 00:00:00 2001 From: Nand <208036691+Kanaliseren@users.noreply.github.com> Date: Wed, 16 Sep 2026 10:58:18 +0000 Subject: [PATCH 1/2] fix(server): restore Claude subscription usage through proxies --- apps/server/src/usage/cliproxyApi.test.ts | 16 ++++++++++++++++ apps/server/src/usage/cliproxyApi.ts | 7 ++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/apps/server/src/usage/cliproxyApi.test.ts b/apps/server/src/usage/cliproxyApi.test.ts index b0fee3638282..371de9c4e158 100644 --- a/apps/server/src/usage/cliproxyApi.test.ts +++ b/apps/server/src/usage/cliproxyApi.test.ts @@ -177,6 +177,22 @@ describe("CLIProxyAPI built-in management API", () => { }), ); + it.effect("reads Claude usage when the endpoint requires a Claude Code user agent", () => + Effect.gen(function* () { + const test = fixture({ + accounts: [{ ...accounts[0]!, provider: "claude" }], + upstream: (request) => + request.header?.["User-Agent"]?.startsWith("claude-code/") + ? { status: 200, body: { five_hour: { utilization: 10, resets_at: null } } } + : { status: 429, body: { error: { type: "rate_limit_error" } } }, + }); + const api = yield* test.api; + const result = yield* api.readAccounts(config); + expect(result[0]?.usageLimits.unavailable).toBeUndefined(); + expect(result[0]?.usageLimits.windows).toMatchObject([{ id: "five_hour", usedPercent: 10 }]); + }), + ); + it.effect("maps Claude scoped windows without a scheduler plugin", () => Effect.gen(function* () { const test = fixture({ diff --git a/apps/server/src/usage/cliproxyApi.ts b/apps/server/src/usage/cliproxyApi.ts index 6bfd3fe4772e..1afa32381109 100644 --- a/apps/server/src/usage/cliproxyApi.ts +++ b/apps/server/src/usage/cliproxyApi.ts @@ -162,7 +162,12 @@ export const makeCliproxyApi = Effect.gen(function* () { ? { "Chatgpt-Account-Id": account.id_token.chatgpt_account_id } : {}), } - : { Authorization: "Bearer $TOKEN$", "anthropic-beta": "oauth-2025-04-20" }; + : { + Authorization: "Bearer $TOKEN$", + "anthropic-beta": "oauth-2025-04-20", + // The OAuth usage endpoint can return 429 without a Claude Code user agent. + "User-Agent": "claude-code/2.1.273", + }; const raw = yield* management(config, "api-call", { auth_index: account.auth_index, method: data === undefined ? "GET" : "POST", From eee96ed5c92a5d622b376394822dd2073a8b5819 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Sun, 18 Oct 2026 15:51:18 -0700 Subject: [PATCH 2/2] test(server): reuse Claude usage fixture for proxy header --- apps/server/src/usage/cliproxyApi.test.ts | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/apps/server/src/usage/cliproxyApi.test.ts b/apps/server/src/usage/cliproxyApi.test.ts index ae96eb6ab388..5d73d35dc47e 100644 --- a/apps/server/src/usage/cliproxyApi.test.ts +++ b/apps/server/src/usage/cliproxyApi.test.ts @@ -302,28 +302,13 @@ describe("CLIProxyAPI built-in management API", () => { }), ); - it.effect("reads Claude usage when the endpoint requires a Claude Code user agent", () => - Effect.gen(function* () { - const test = fixture({ - accounts: [{ ...accounts[0]!, provider: "claude" }], - upstream: (request) => - request.header?.["User-Agent"]?.startsWith("claude-code/") - ? { status: 200, body: { five_hour: { utilization: 10, resets_at: null } } } - : { status: 429, body: { error: { type: "rate_limit_error" } } }, - }); - const api = yield* test.api; - const result = yield* api.readAccounts(config); - expect(result[0]?.usageLimits.unavailable).toBeUndefined(); - expect(result[0]?.usageLimits.windows).toMatchObject([{ id: "five_hour", usedPercent: 10 }]); - }), - ); - it.effect("maps Claude scoped windows without a scheduler plugin", () => Effect.gen(function* () { const test = fixture({ accounts: [{ ...accounts[0]!, provider: "claude" }], - upstream: () => ({ - status: 200, + // The usage endpoint answers 429 to requests without a Claude Code user agent. + upstream: (request) => ({ + status: request.header?.["User-Agent"]?.startsWith("claude-code/") ? 200 : 429, body: { five_hour: { utilization: 10, resets_at: null }, seven_day: { utilization: 50, resets_at: "2099-01-01T00:00:00Z" },