@@ -5,6 +5,7 @@ import type {
55 MemberSpend ,
66 SpendResponse ,
77 GroupsResponse ,
8+ FilteredUsageEvent ,
89 FilteredUsageEventsResponse ,
910 AICodeCommitsResponse ,
1011 AnalyticsDAUResponse ,
@@ -25,6 +26,50 @@ interface CursorClientOptions {
2526 baseUrl ?: string ;
2627}
2728
29+ function num ( v : unknown , fallback = 0 ) : number {
30+ return typeof v === "number" && ! Number . isNaN ( v ) ? v : fallback ;
31+ }
32+
33+ function str ( raw : Record < string , unknown > , ...keys : string [ ] ) : string {
34+ for ( const k of keys ) {
35+ const v = raw [ k ] ;
36+ if ( typeof v === "string" && v !== "" ) return v ;
37+ if ( typeof v === "number" && ! Number . isNaN ( v ) ) return String ( v ) ;
38+ }
39+ return "" ;
40+ }
41+
42+ function bool ( v : unknown ) : boolean {
43+ return v === true || v === 1 || v === "1" ;
44+ }
45+
46+ function normalizeFilteredUsageEvent ( raw : Record < string , unknown > ) : FilteredUsageEvent {
47+ const tuRaw = raw . tokenUsage ?? raw . token_usage ;
48+ let tokenUsage : FilteredUsageEvent [ "tokenUsage" ] ;
49+ if ( tuRaw && typeof tuRaw === "object" ) {
50+ const t = tuRaw as Record < string , unknown > ;
51+ tokenUsage = {
52+ inputTokens : num ( t . inputTokens ?? t . input_tokens ) ,
53+ outputTokens : num ( t . outputTokens ?? t . output_tokens ) ,
54+ cacheWriteTokens : num ( t . cacheWriteTokens ?? t . cache_write_tokens ) ,
55+ cacheReadTokens : num ( t . cacheReadTokens ?? t . cache_read_tokens ) ,
56+ totalCents : num ( t . totalCents ?? t . total_cents ) ,
57+ } ;
58+ }
59+ return {
60+ timestamp : str ( raw , "timestamp" ) ,
61+ model : str ( raw , "model" ) ,
62+ kind : str ( raw , "kind" , "kindLabel" , "kind_label" ) ,
63+ maxMode : bool ( raw . maxMode ?? raw . max_mode ) ,
64+ requestsCosts : num ( raw . requestsCosts ?? raw . requests_costs ) ,
65+ isTokenBasedCall : bool ( raw . isTokenBasedCall ?? raw . is_token_based_call ) ,
66+ tokenUsage,
67+ userEmail : str ( raw , "userEmail" , "user_email" , "email" ) ,
68+ isChargeable : Boolean ( raw . isChargeable ?? raw . is_chargeable ) ,
69+ isHeadless : bool ( raw . isHeadless ?? raw . is_headless ) ,
70+ } ;
71+ }
72+
2873export class CursorClient {
2974 private apiKey : string ;
3075 private baseUrl : string ;
@@ -224,7 +269,7 @@ export class CursorClient {
224269 page ?: number ;
225270 pageSize ?: number ;
226271 } ) : Promise < FilteredUsageEventsResponse > {
227- return this . request < FilteredUsageEventsResponse > ( "/teams/filtered-usage-events" , {
272+ const data = await this . request < FilteredUsageEventsResponse > ( "/teams/filtered-usage-events" , {
228273 method : "POST" ,
229274 body : {
230275 email : options . email ,
@@ -234,6 +279,14 @@ export class CursorClient {
234279 pageSize : options . pageSize ?? 500 ,
235280 } ,
236281 } ) ;
282+ const usageEvents = ( data . usageEvents ?? [ ] ) . map ( ( row ) =>
283+ normalizeFilteredUsageEvent (
284+ row && typeof row === "object" && ! Array . isArray ( row )
285+ ? ( row as unknown as Record < string , unknown > )
286+ : { } ,
287+ ) ,
288+ ) ;
289+ return { ...data , usageEvents } ;
237290 }
238291
239292 async getAICodeCommits (
0 commit comments