Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/providers/cursor.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion plugins/cursor/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
44 changes: 44 additions & 0 deletions plugins/cursor/plugin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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" }]))
Expand Down
Loading