From 219c4b8f4314445b9ee2db0d556ed031d25a5405 Mon Sep 17 00:00:00 2001 From: rohithgoud30 <34999805+rohithgoud30@users.noreply.github.com> Date: Tue, 2 Jun 2026 22:52:32 -0400 Subject: [PATCH] fix(cursor): handle free account pooled limit --- docs/providers/cursor.md | 2 +- plugins/cursor/plugin.js | 2 +- plugins/cursor/plugin.test.js | 44 +++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/docs/providers/cursor.md b/docs/providers/cursor.md index 43223ead..96b610fc 100644 --- a/docs/providers/cursor.md +++ b/docs/providers/cursor.md @@ -25,7 +25,7 @@ **Enterprise flow** remains request-based via the REST `/api/usage` endpoint -- unchanged. -**Team detection**: an account is treated as "team" when `planName` is `"Team"`, or `spendLimitUsage.limitType` is `"team"`, or `spendLimitUsage.pooledLimit` is present. Team accounts display Total usage in dollars; individual accounts display it as a percentage. +**Team detection**: an account is treated as "team" when `planName` is `"Team"`, or `spendLimitUsage.limitType` is `"team"`, or `spendLimitUsage.pooledLimit` is greater than `0`. Team accounts display Total usage in dollars; individual accounts display it as a percentage. ## Endpoints diff --git a/plugins/cursor/plugin.js b/plugins/cursor/plugin.js index 4229919d..eee9a5d6 100644 --- a/plugins/cursor/plugin.js +++ b/plugins/cursor/plugin.js @@ -595,7 +595,7 @@ const isTeamAccount = ( normalizedPlanName === "team" || (su && su.limitType === "team") || - (su && typeof su.pooledLimit === "number") + (su && typeof su.pooledLimit === "number" && su.pooledLimit > 0) ) if (isTeamAccount) { diff --git a/plugins/cursor/plugin.test.js b/plugins/cursor/plugin.test.js index 88ca3f6b..6805d159 100644 --- a/plugins/cursor/plugin.test.js +++ b/plugins/cursor/plugin.test.js @@ -319,6 +319,50 @@ describe("cursor plugin", () => { expect(totalLine.limit).toBe(100) }) + it("uses percent-only free usage when pooledLimit is zero", async () => { + const ctx = makeCtx() + ctx.host.sqlite.query.mockReturnValue(JSON.stringify([{ value: "token" }])) + ctx.host.http.request.mockImplementation((opts) => { + if (String(opts.url).includes("GetCurrentPeriodUsage")) { + return { + status: 200, + bodyText: JSON.stringify({ + enabled: true, + billingCycleStart: "1772556293029", + billingCycleEnd: "1775234693029", + planUsage: { + autoPercentUsed: 0, + apiPercentUsed: 0, + totalPercentUsed: 0, + }, + spendLimitUsage: { pooledLimit: 0, pooledRemaining: 0 }, + }), + } + } + if (String(opts.url).includes("GetPlanInfo")) { + return { + status: 200, + bodyText: JSON.stringify({ + planInfo: { planName: "Free" }, + }), + } + } + if (String(opts.url).includes("cursor.com/api/usage")) { + throw new Error("unexpected REST usage fallback") + } + return { status: 200, bodyText: "{}" } + }) + + const plugin = await loadPlugin() + const result = plugin.probe(ctx) + expect(result.plan).toBe("Free") + const totalLine = result.lines.find((line) => line.label === "Total usage") + expect(totalLine).toBeTruthy() + expect(totalLine.format).toEqual({ kind: "percent" }) + expect(totalLine.used).toBe(0) + expect(totalLine.limit).toBe(100) + }) + it("renders percent-only usage when plan info is unavailable", async () => { const ctx = makeCtx() ctx.host.sqlite.query.mockReturnValue(JSON.stringify([{ value: "token" }]))