diff --git a/packages/ai/src/usage/cursor.ts b/packages/ai/src/usage/cursor.ts index b97122172..eb24db28c 100644 --- a/packages/ai/src/usage/cursor.ts +++ b/packages/ai/src/usage/cursor.ts @@ -23,8 +23,10 @@ function parseTimestamp(value: unknown): number | undefined { return Number.isFinite(parsed) ? parsed : undefined; } +const DEFAULT_CURSOR_BASE_URL = "https://api2.cursor.sh"; + function normalizeCursorBaseUrl(baseUrl?: string): string { - if (!baseUrl) return "https://api2.cursor.sh"; + if (!baseUrl) return DEFAULT_CURSOR_BASE_URL; return baseUrl.replace(/\/+$/, ""); } @@ -262,7 +264,7 @@ export const cursorUsageProvider: UsageProvider = { let summaryReportPromise = Promise.resolve(null); let profileEmailPromise = Promise.resolve(undefined); - if (credential.type === "oauth") { + if (credential.type === "oauth" && baseUrl === DEFAULT_CURSOR_BASE_URL) { const userId = extractCursorAccessTokenUserId(token); if (userId) { const sessionHeaders: Record = { diff --git a/packages/ai/test/cursor-usage.test.ts b/packages/ai/test/cursor-usage.test.ts index 93a8b4263..4c8736436 100644 --- a/packages/ai/test/cursor-usage.test.ts +++ b/packages/ai/test/cursor-usage.test.ts @@ -624,6 +624,41 @@ describe("cursor usage provider", () => { expect(report?.metadata).toEqual({ email: "fallback@example.com" }); }); + it("does not send the session cookie outside the default Cursor origin", async () => { + const accessToken = createCursorAccessToken("auth0|user_123"); + const requests: Array<{ url: string; headers: Headers }> = []; + const mockFetch = (async (input: string | URL, init?: RequestInit): Promise => { + requests.push({ + url: typeof input === "string" ? input : input.toString(), + headers: new Headers(init?.headers), + }); + return Response.json({ + "gpt-4": { + numRequests: 10, + maxRequestUsage: 100, + }, + }); + }) as unknown as typeof fetch; + + const report = await cursorUsageProvider.fetchUsage( + { + provider: "cursor", + baseUrl: "https://cursor-proxy.example.com", + credential: { + type: "oauth", + accessToken, + }, + }, + { fetch: mockFetch }, + ); + + expect(requests).toHaveLength(1); + expect(requests[0]?.url).toBe("https://cursor-proxy.example.com/auth/usage"); + expect(requests[0]?.headers.get("Authorization")).toBe(`Bearer ${accessToken}`); + expect(requests[0]?.headers.has("Cookie")).toBe(false); + expect(report?.limits.map(limit => limit.id)).toEqual(["cursor:requests:gpt-4"]); + }); + it("returns null on non-2xx response", async () => { const mockFetch = (async () => new Response("Error", { status: 403 })) as unknown as typeof fetch; const ctx: UsageFetchContext = {