diff --git a/src/commands.ts b/src/commands.ts index 47a7cec..d0018a6 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -122,13 +122,19 @@ export async function showDailyReport( const grouped = groupByDay(events); const stats = calculateDailyStats(grouped); + const startStr = startDate.toISOString().split('T')[0]; + const endStr = endDate.toISOString().split('T')[0]; + const periodLabel = options.startDate || options.endDate + ? `${startStr} to ${endStr}` + : `Last ${days} days`; + if (outputJson) { // Output JSON only - no table display - const jsonOutput = statsToJSON('daily', stats, events, { breakdown: options.breakdown, period: `last ${days} days` }); + const jsonOutput = statsToJSON('daily', stats, events, { breakdown: options.breakdown, period: periodLabel }); console.log(jsonOutput); } else { // Display title box - logger.log(createTitleBox(`Cursor Usage Report - Daily (Last ${days} days)`)); + logger.log(createTitleBox(`Cursor Usage Report - Daily (${periodLabel})`)); const tableData = stats.map((day) => { const modelList = Array.from(day.models.entries()) diff --git a/src/event-loader.ts b/src/event-loader.ts index 44032dc..23f2842 100644 --- a/src/event-loader.ts +++ b/src/event-loader.ts @@ -34,7 +34,7 @@ export interface EventsResponse { } /** - * Fetch usage events for a date range + * Fetch usage events for a date range (handles pagination) */ export async function fetchUsageEvents( credentials: CursorCredentials, @@ -42,60 +42,72 @@ export async function fetchUsageEvents( endDate: Date, pageSize: number = 100 ): Promise { + const sessionToken = `${credentials.userId}::${credentials.accessToken}`; + + const headers = { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'User-Agent': USER_AGENT, + Origin: 'https://cursor.com', + }; + + const cookieHeader = `WorkosCursorSessionToken=${encodeURIComponent( + sessionToken + )}`; + + const startTimestamp = startDate.getTime().toString(); + const endTimestamp = endDate.getTime().toString(); + + const allEvents: UsageEvent[] = []; + let page = 1; + try { - const sessionToken = `${credentials.userId}::${credentials.accessToken}`; - - const headers = { - Accept: 'application/json', - 'Content-Type': 'application/json', - 'User-Agent': USER_AGENT, - Origin: 'https://cursor.com', - }; - - const cookieHeader = `WorkosCursorSessionToken=${encodeURIComponent( - sessionToken - )}`; - - // Convert dates to millisecond timestamps - const startTimestamp = startDate.getTime().toString(); - const endTimestamp = endDate.getTime().toString(); - - const body = { - teamId: 0, - startDate: startTimestamp, - endDate: endTimestamp, - page: 1, - pageSize, - }; - - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT); - - const response = await fetch(CURSOR_API_URL_EVENTS, { - method: 'POST', - headers: { - ...headers, - Cookie: cookieHeader, - }, - body: JSON.stringify(body), - signal: controller.signal, - }); + while (true) { + const body = { + teamId: 0, + startDate: startTimestamp, + endDate: endTimestamp, + page, + pageSize, + }; + + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT); + + const response = await fetch(CURSOR_API_URL_EVENTS, { + method: 'POST', + headers: { + ...headers, + Cookie: cookieHeader, + }, + body: JSON.stringify(body), + signal: controller.signal, + }); - clearTimeout(timeoutId); + clearTimeout(timeoutId); - if (!response.ok) { - logger.error( - `Events API request failed with status ${response.status}: ${response.statusText}` - ); - return []; - } + if (!response.ok) { + logger.error( + `Events API request failed with status ${response.status}: ${response.statusText}` + ); + break; + } + + const data = (await response.json()) as any; + const pageEvents = parseEvents(data); + allEvents.push(...pageEvents); - const data = (await response.json()) as any; - return parseEvents(data); + if (pageEvents.length < pageSize) { + break; + } + + page++; + } } catch (error) { logger.error(`Error fetching usage events: ${String(error)}`); - return []; } + + return allEvents; } /**