11import { loadDailyUsageData } from "ccusage/data-loader" ;
2- import { existsSync , mkdirSync , readdirSync , readFileSync , writeFileSync } from "fs" ;
2+ import {
3+ existsSync ,
4+ mkdirSync ,
5+ readdirSync ,
6+ readFileSync ,
7+ writeFileSync ,
8+ } from "fs" ;
39import os from "os" ;
410import path from "path" ;
511
6- type AgentEntry = { name : string ; found : boolean ; today : number ; month : number } ;
12+ type AgentEntry = {
13+ name : string ;
14+ found : boolean ;
15+ today : number ;
16+ month : number ;
17+ } ;
718type UsagePayload = { agents : AgentEntry [ ] } ;
819
920function formatLocalDay ( date : Date ) : string {
@@ -17,7 +28,10 @@ function formatLocalDay(date: Date): string {
1728
1829// ─── Claude Code ────────────────────────────────────────────────────────────
1930
20- async function loadClaudeData ( since : string , offline : boolean ) : Promise < AgentEntry > {
31+ async function loadClaudeData (
32+ since : string ,
33+ offline : boolean ,
34+ ) : Promise < AgentEntry > {
2135 const dailyData = await loadDailyUsageData ( { since, offline } ) ;
2236 const today = formatLocalDay ( new Date ( ) ) ;
2337
@@ -42,14 +56,24 @@ const CODEX_MODEL_ALIASES: Record<string, string> = {
4256 "gpt-5.3-codex" : "gpt-5.2-codex" ,
4357} ;
4458
45- const CODEX_PROVIDER_PREFIXES = [ "openai/" , "azure/openai/" , "azure/" , "openrouter/openai/" ] ;
59+ const CODEX_PROVIDER_PREFIXES = [
60+ "openai/" ,
61+ "azure/openai/" ,
62+ "azure/" ,
63+ "openrouter/openai/" ,
64+ ] ;
4665
4766// ─── Pricing cache ───────────────────────────────────────────────────────────
4867
4968const LITELLM_URL =
5069 "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json" ;
5170const PRICING_CACHE_TTL_MS = 24 * 60 * 60 * 1000 ;
52- const PRICING_CACHE_PATH = path . join ( os . homedir ( ) , ".cache" , "agenttally" , "codex-pricing.json" ) ;
71+ const PRICING_CACHE_PATH = path . join (
72+ os . homedir ( ) ,
73+ ".cache" ,
74+ "agenttally" ,
75+ "codex-pricing.json" ,
76+ ) ;
5377
5478type PricingCache = {
5579 fetchedAt : number ;
@@ -59,31 +83,45 @@ type PricingCache = {
5983function parseLiteLLMEntry ( data : unknown ) : ModelPricing | null {
6084 if ( typeof data !== "object" || data == null ) return null ;
6185 const d = data as Record < string , unknown > ;
62- const input = typeof d . input_cost_per_token === "number" ? d . input_cost_per_token : null ;
63- const output = typeof d . output_cost_per_token === "number" ? d . output_cost_per_token : null ;
86+ const input =
87+ typeof d . input_cost_per_token === "number" ? d . input_cost_per_token : null ;
88+ const output =
89+ typeof d . output_cost_per_token === "number"
90+ ? d . output_cost_per_token
91+ : null ;
6492 if ( input == null || output == null ) return null ;
6593 return {
6694 input_cost_per_token : input ,
6795 output_cost_per_token : output ,
6896 cache_read_input_token_cost :
69- typeof d . cache_read_input_token_cost === "number" ? d . cache_read_input_token_cost : undefined ,
97+ typeof d . cache_read_input_token_cost === "number"
98+ ? d . cache_read_input_token_cost
99+ : undefined ,
70100 } ;
71101}
72102
73- async function loadCodexPricing ( offline : boolean ) : Promise < Record < string , ModelPricing > > {
103+ async function loadCodexPricing (
104+ offline : boolean ,
105+ ) : Promise < Record < string , ModelPricing > > {
74106 try {
75- const cached = JSON . parse ( readFileSync ( PRICING_CACHE_PATH , "utf8" ) ) as PricingCache ;
107+ const cached = JSON . parse (
108+ readFileSync ( PRICING_CACHE_PATH , "utf8" ) ,
109+ ) as PricingCache ;
76110 if ( Date . now ( ) - cached . fetchedAt < PRICING_CACHE_TTL_MS ) {
77111 return cached . pricing ;
78112 }
79113 } catch {
80114 // cache miss or corrupt
81115 }
82116
83- if ( offline ) throw new Error ( "Codex pricing unavailable: cache stale and in offline mode" ) ;
117+ if ( offline )
118+ throw new Error (
119+ "Codex pricing unavailable: cache stale and in offline mode" ,
120+ ) ;
84121
85122 const response = await fetch ( LITELLM_URL ) ;
86- if ( ! response . ok ) throw new Error ( `Failed to fetch Codex pricing: HTTP ${ response . status } ` ) ;
123+ if ( ! response . ok )
124+ throw new Error ( `Failed to fetch Codex pricing: HTTP ${ response . status } ` ) ;
87125 const raw = ( await response . json ( ) ) as Record < string , unknown > ;
88126
89127 const pricing : Record < string , ModelPricing > = { } ;
@@ -93,18 +131,22 @@ async function loadCodexPricing(offline: boolean): Promise<Record<string, ModelP
93131 }
94132
95133 mkdirSync ( path . dirname ( PRICING_CACHE_PATH ) , { recursive : true } ) ;
96- writeFileSync ( PRICING_CACHE_PATH , JSON . stringify ( { fetchedAt : Date . now ( ) , pricing } ) ) ;
134+ writeFileSync (
135+ PRICING_CACHE_PATH ,
136+ JSON . stringify ( { fetchedAt : Date . now ( ) , pricing } ) ,
137+ ) ;
97138 return pricing ;
98139}
99140
100141function lookupCodexPricing (
101142 modelName : string ,
102- pricing : Record < string , ModelPricing >
143+ pricing : Record < string , ModelPricing > ,
103144) : ModelPricing | null {
104145 // Build candidates: bare name + all provider-prefixed variants
105146 const candidates = [ modelName ] ;
106147 for ( const prefix of CODEX_PROVIDER_PREFIXES ) {
107- if ( modelName . startsWith ( prefix ) ) candidates . push ( modelName . slice ( prefix . length ) ) ;
148+ if ( modelName . startsWith ( prefix ) )
149+ candidates . push ( modelName . slice ( prefix . length ) ) ;
108150 else candidates . push ( `${ prefix } ${ modelName } ` ) ;
109151 }
110152
@@ -118,7 +160,8 @@ function lookupCodexPricing(
118160 // Fuzzy fallback: substring match
119161 const lower = modelName . toLowerCase ( ) ;
120162 for ( const [ key , val ] of Object . entries ( pricing ) ) {
121- if ( key . toLowerCase ( ) . includes ( lower ) || lower . includes ( key . toLowerCase ( ) ) ) return val ;
163+ if ( key . toLowerCase ( ) . includes ( lower ) || lower . includes ( key . toLowerCase ( ) ) )
164+ return val ;
122165 }
123166
124167 return null ;
@@ -133,27 +176,34 @@ type TokenUsage = {
133176function calcCost ( usage : TokenUsage , pricing : ModelPricing ) : number {
134177 const cachedInput = Math . min ( usage . cached_input_tokens , usage . input_tokens ) ;
135178 const nonCachedInput = usage . input_tokens - cachedInput ;
136- const cacheRate = pricing . cache_read_input_token_cost ?? pricing . input_cost_per_token ;
179+ const cacheRate =
180+ pricing . cache_read_input_token_cost ?? pricing . input_cost_per_token ;
137181 return (
138182 nonCachedInput * pricing . input_cost_per_token +
139183 cachedInput * cacheRate +
140184 usage . output_tokens * pricing . output_cost_per_token
141185 ) ;
142186}
143187
144- function subtractTokenUsage ( total : TokenUsage , prev : TokenUsage | null ) : TokenUsage {
188+ function subtractTokenUsage (
189+ total : TokenUsage ,
190+ prev : TokenUsage | null ,
191+ ) : TokenUsage {
145192 if ( ! prev ) return total ;
146193 return {
147194 input_tokens : Math . max ( 0 , total . input_tokens - prev . input_tokens ) ,
148- cached_input_tokens : Math . max ( 0 , total . cached_input_tokens - prev . cached_input_tokens ) ,
195+ cached_input_tokens : Math . max (
196+ 0 ,
197+ total . cached_input_tokens - prev . cached_input_tokens ,
198+ ) ,
149199 output_tokens : Math . max ( 0 , total . output_tokens - prev . output_tokens ) ,
150200 } ;
151201}
152202
153203function parseCodexSession (
154204 filePath : string ,
155205 pricing : Record < string , ModelPricing > ,
156- costsByDate : Map < string , number >
206+ costsByDate : Map < string , number > ,
157207) {
158208 let content : string ;
159209 try {
@@ -184,9 +234,13 @@ function parseCodexSession(
184234
185235 if ( entry . type !== "event_msg" ) continue ;
186236 const payload = entry . payload as Record < string , unknown > | null ;
187- if ( ( payload as Record < string , unknown > | null ) ?. type !== "token_count" ) continue ;
237+ if ( ( payload as Record < string , unknown > | null ) ?. type !== "token_count" )
238+ continue ;
188239
189- const info = ( payload as Record < string , unknown > ) . info as Record < string , unknown > | null ;
240+ const info = ( payload as Record < string , unknown > ) . info as Record <
241+ string ,
242+ unknown
243+ > | null ;
190244 const lastUsage = info ?. last_token_usage as TokenUsage | null ;
191245 const totalUsage = info ?. total_token_usage as TokenUsage | null ;
192246
@@ -199,7 +253,9 @@ function parseCodexSession(
199253 if ( totalUsage ?. input_tokens != null ) prevTotals = totalUsage ;
200254 if ( ! delta ) continue ;
201255
202- const modelPricing = currentModel ? lookupCodexPricing ( currentModel , pricing ) : null ;
256+ const modelPricing = currentModel
257+ ? lookupCodexPricing ( currentModel , pricing )
258+ : null ;
203259 if ( ! modelPricing ) continue ;
204260
205261 const cost = calcCost ( delta , modelPricing ) ;
@@ -210,8 +266,12 @@ function parseCodexSession(
210266 }
211267}
212268
213- async function loadCodexData ( since : string , offline : boolean ) : Promise < AgentEntry > {
214- const codexHome = process . env [ "CODEX_HOME" ] ?? path . join ( os . homedir ( ) , ".codex" ) ;
269+ async function loadCodexData (
270+ since : string ,
271+ offline : boolean ,
272+ ) : Promise < AgentEntry > {
273+ const codexHome =
274+ process . env [ "CODEX_HOME" ] ?? path . join ( os . homedir ( ) , ".codex" ) ;
215275 const sessionsDir = path . join ( codexHome , "sessions" ) ;
216276 if ( ! existsSync ( sessionsDir ) ) {
217277 return { name : "Codex" , found : false , today : 0 , month : 0 } ;
@@ -250,7 +310,10 @@ async function loadCodexData(since: string, offline: boolean): Promise<AgentEntr
250310const AGENT_LOADERS = {
251311 claude : loadClaudeData ,
252312 codex : loadCodexData ,
253- } as const satisfies Record < string , ( since : string , offline : boolean ) => Promise < AgentEntry > > ;
313+ } as const satisfies Record <
314+ string ,
315+ ( since : string , offline : boolean ) => Promise < AgentEntry >
316+ > ;
254317
255318type AgentID = keyof typeof AGENT_LOADERS ;
256319
@@ -287,7 +350,9 @@ function parseRequestedAgents(args: string[]): AgentID[] {
287350 }
288351 }
289352
290- return requested . size > 0 ? ALL_AGENTS . filter ( ( agent ) => requested . has ( agent ) ) : ALL_AGENTS ;
353+ return requested . size > 0
354+ ? ALL_AGENTS . filter ( ( agent ) => requested . has ( agent ) )
355+ : ALL_AGENTS ;
291356}
292357
293358async function main ( ) {
@@ -299,7 +364,7 @@ async function main() {
299364 const requestedAgents = parseRequestedAgents ( process . argv . slice ( 3 ) ) ;
300365
301366 const agents = await Promise . all (
302- requestedAgents . map ( ( agent ) => AGENT_LOADERS [ agent ] ( since , offline ) )
367+ requestedAgents . map ( ( agent ) => AGENT_LOADERS [ agent ] ( since , offline ) ) ,
303368 ) ;
304369
305370 const payload : UsagePayload = {
0 commit comments