@@ -4,6 +4,12 @@ import path from "node:path";
4
4
import { sort } from "fast-sort" ;
5
5
import { glob } from "tinyglobby" ;
6
6
import * as v from "valibot" ;
7
+ import {
8
+ type ModelPricing ,
9
+ calculateCostFromTokens ,
10
+ fetchModelPricing ,
11
+ getModelPricing ,
12
+ } from "./pricing-fetcher.ts" ;
7
13
8
14
export const getDefaultClaudePath = ( ) => path . join ( homedir ( ) , ".claude" ) ;
9
15
@@ -16,8 +22,9 @@ export const UsageDataSchema = v.object({
16
22
cache_creation_input_tokens : v . optional ( v . number ( ) ) ,
17
23
cache_read_input_tokens : v . optional ( v . number ( ) ) ,
18
24
} ) ,
25
+ model : v . optional ( v . string ( ) ) , // Model is inside message object
19
26
} ) ,
20
- costUSD : v . number ( ) ,
27
+ costUSD : v . optional ( v . number ( ) ) , // Made optional for new schema
21
28
} ) ;
22
29
23
30
export type UsageData = v . InferOutput < typeof UsageDataSchema > ;
@@ -77,6 +84,9 @@ export async function loadUsageData(
77
84
return [ ] ;
78
85
}
79
86
87
+ // Fetch pricing data for cost calculation
88
+ const modelPricing = await fetchModelPricing ( ) ;
89
+
80
90
const dailyMap = new Map < string , DailyUsage > ( ) ;
81
91
82
92
for ( const file of files ) {
@@ -105,13 +115,24 @@ export async function loadUsageData(
105
115
totalCost : 0 ,
106
116
} ;
107
117
108
- existing . inputTokens += data . message . usage . input_tokens || 0 ;
109
- existing . outputTokens += data . message . usage . output_tokens || 0 ;
118
+ existing . inputTokens += data . message . usage . input_tokens ?? 0 ;
119
+ existing . outputTokens += data . message . usage . output_tokens ?? 0 ;
110
120
existing . cacheCreationTokens +=
111
- data . message . usage . cache_creation_input_tokens || 0 ;
121
+ data . message . usage . cache_creation_input_tokens ?? 0 ;
112
122
existing . cacheReadTokens +=
113
- data . message . usage . cache_read_input_tokens || 0 ;
114
- existing . totalCost += data . costUSD || 0 ;
123
+ data . message . usage . cache_read_input_tokens ?? 0 ;
124
+
125
+ // Calculate cost: use costUSD if available, otherwise calculate from tokens
126
+ let cost = 0 ;
127
+ if ( data . costUSD !== undefined ) {
128
+ cost = data . costUSD ;
129
+ } else if ( data . message . model ) {
130
+ const pricing = getModelPricing ( data . message . model , modelPricing ) ;
131
+ if ( pricing ) {
132
+ cost = calculateCostFromTokens ( data . message . usage , pricing ) ;
133
+ }
134
+ }
135
+ existing . totalCost += cost ;
115
136
116
137
dailyMap . set ( date , existing ) ;
117
138
} catch ( e ) {
@@ -150,6 +171,9 @@ export async function loadSessionData(
150
171
return [ ] ;
151
172
}
152
173
174
+ // Fetch pricing data for cost calculation
175
+ const modelPricing = await fetchModelPricing ( ) ;
176
+
153
177
const sessionMap = new Map < string , SessionUsage > ( ) ;
154
178
155
179
for ( const file of files ) {
@@ -190,13 +214,24 @@ export async function loadSessionData(
190
214
lastActivity : "" ,
191
215
} ;
192
216
193
- existing . inputTokens += data . message . usage . input_tokens || 0 ;
194
- existing . outputTokens += data . message . usage . output_tokens || 0 ;
217
+ existing . inputTokens += data . message . usage . input_tokens ?? 0 ;
218
+ existing . outputTokens += data . message . usage . output_tokens ?? 0 ;
195
219
existing . cacheCreationTokens +=
196
- data . message . usage . cache_creation_input_tokens || 0 ;
220
+ data . message . usage . cache_creation_input_tokens ?? 0 ;
197
221
existing . cacheReadTokens +=
198
- data . message . usage . cache_read_input_tokens || 0 ;
199
- existing . totalCost += data . costUSD || 0 ;
222
+ data . message . usage . cache_read_input_tokens ?? 0 ;
223
+
224
+ // Calculate cost: use costUSD if available, otherwise calculate from tokens
225
+ let cost = 0 ;
226
+ if ( data . costUSD !== undefined ) {
227
+ cost = data . costUSD ;
228
+ } else if ( data . message . model ) {
229
+ const pricing = getModelPricing ( data . message . model , modelPricing ) ;
230
+ if ( pricing ) {
231
+ cost = calculateCostFromTokens ( data . message . usage , pricing ) ;
232
+ }
233
+ }
234
+ existing . totalCost += cost ;
200
235
201
236
// Keep track of the latest timestamp
202
237
if ( data . timestamp > lastTimestamp ) {
0 commit comments