@praeviso/code-env-switch 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
1
  import { formatTokenCount } from "../usage";
2
+ import { formatUsdAmount } from "../usage/pricing";
2
3
  import {
3
4
  ICON_CONTEXT,
4
5
  ICON_CWD,
@@ -9,7 +10,6 @@ import {
9
10
  colorize,
10
11
  dim,
11
12
  } from "./style";
12
- import type { StatuslineUsage } from "./types";
13
13
  import * as path from "path";
14
14
 
15
15
  export function getCwdSegment(cwd: string): string | null {
@@ -19,15 +19,14 @@ export function getCwdSegment(cwd: string): string | null {
19
19
  return dim(segment);
20
20
  }
21
21
 
22
- export function formatUsageSegment(usage: StatuslineUsage | null): string | null {
23
- if (!usage) return null;
24
- const today =
25
- usage.todayTokens ??
26
- (usage.inputTokens !== null || usage.outputTokens !== null
27
- ? (usage.inputTokens || 0) + (usage.outputTokens || 0)
28
- : usage.totalTokens);
29
- if (today === null) return null;
30
- const text = `Today ${formatTokenCount(today)}`;
22
+ export function formatUsageSegment(
23
+ todayCost: number | null,
24
+ sessionCost: number | null
25
+ ): string | null {
26
+ if (todayCost === null && sessionCost === null) return null;
27
+ const todayText = `T ${formatUsdAmount(todayCost)}`;
28
+ const sessionText = `S ${formatUsdAmount(sessionCost)}`;
29
+ const text = `${todayText} / ${sessionText}`;
31
30
  return colorize(`${ICON_USAGE} ${text}`, "33");
32
31
  }
33
32
 
@@ -3,7 +3,13 @@
3
3
  */
4
4
  import type { Config, StatuslineArgs } from "../types";
5
5
  import { normalizeType, inferProfileType, getProfileDisplayName } from "../profile/type";
6
- import { syncUsageFromStatuslineInput } from "../usage";
6
+ import {
7
+ readUsageCostIndex,
8
+ readUsageSessionCost,
9
+ resolveUsageCostForProfile,
10
+ syncUsageFromStatuslineInput,
11
+ } from "../usage";
12
+ import { calculateUsageCost, resolvePricingForProfile } from "../usage/pricing";
7
13
  import { appendStatuslineDebug } from "./debug";
8
14
  import {
9
15
  formatContextSegment,
@@ -91,8 +97,17 @@ export function buildStatuslineResult(
91
97
  )!;
92
98
 
93
99
  const sessionId = getSessionId(stdinInput);
94
- const stdinUsageTotals = getUsageTotalsFromInput(stdinInput);
95
100
  const usageType = normalizeType(type || "");
101
+ const stdinUsageTotals = getUsageTotalsFromInput(stdinInput, usageType);
102
+ const model = firstNonEmpty(
103
+ args.model,
104
+ process.env.CODE_ENV_MODEL,
105
+ getModelFromInput(stdinInput)
106
+ );
107
+ const modelProvider = firstNonEmpty(
108
+ process.env.CODE_ENV_MODEL_PROVIDER,
109
+ getModelProviderFromInput(stdinInput)
110
+ );
96
111
  appendStatuslineDebug(configPath, {
97
112
  timestamp: new Date().toISOString(),
98
113
  typeCandidate,
@@ -130,20 +145,11 @@ export function buildStatuslineResult(
130
145
  profileName,
131
146
  sessionId,
132
147
  stdinUsageTotals,
133
- cwd
148
+ cwd,
149
+ model
134
150
  );
135
151
  }
136
152
 
137
- const model = firstNonEmpty(
138
- args.model,
139
- process.env.CODE_ENV_MODEL,
140
- getModelFromInput(stdinInput)
141
- );
142
- const modelProvider = firstNonEmpty(
143
- process.env.CODE_ENV_MODEL_PROVIDER,
144
- getModelProviderFromInput(stdinInput)
145
- );
146
-
147
153
  const usage: StatuslineUsage = {
148
154
  todayTokens: firstNumber(
149
155
  args.usageToday,
@@ -161,6 +167,8 @@ export function buildStatuslineResult(
161
167
  args.usageOutput,
162
168
  process.env.CODE_ENV_USAGE_OUTPUT
163
169
  ),
170
+ cacheReadTokens: null,
171
+ cacheWriteTokens: null,
164
172
  };
165
173
 
166
174
  const hasExplicitUsage =
@@ -169,21 +177,25 @@ export function buildStatuslineResult(
169
177
  usage.inputTokens !== null ||
170
178
  usage.outputTokens !== null;
171
179
 
172
- const stdinUsage = normalizeInputUsage(getInputUsage(stdinInput));
180
+ const stdinUsage = normalizeInputUsage(getInputUsage(stdinInput, usageType));
181
+ const recordsUsage = resolveUsageFromRecords(
182
+ config,
183
+ configPath,
184
+ type,
185
+ profileKey,
186
+ profileName,
187
+ args.syncUsage
188
+ );
173
189
 
174
190
  let finalUsage: StatuslineUsage | null = hasExplicitUsage ? usage : null;
191
+ if (!finalUsage && args.syncUsage && recordsUsage) {
192
+ finalUsage = recordsUsage;
193
+ }
175
194
  if (!finalUsage) {
176
195
  finalUsage = stdinUsage;
177
196
  }
178
- if (!finalUsage) {
179
- finalUsage = resolveUsageFromRecords(
180
- config,
181
- configPath,
182
- type,
183
- profileKey,
184
- profileName,
185
- args.syncUsage
186
- );
197
+ if (!finalUsage && recordsUsage) {
198
+ finalUsage = recordsUsage;
187
199
  }
188
200
 
189
201
  let gitStatus = getGitStatus(cwd);
@@ -198,7 +210,45 @@ export function buildStatuslineResult(
198
210
  const gitSegment = formatGitSegment(gitStatus);
199
211
  const profileSegment = formatProfileSegment(type, profileKey, profileName);
200
212
  const modelSegment = formatModelSegment(model, modelProvider);
201
- const usageSegment = formatUsageSegment(finalUsage);
213
+ let profile = profileKey && config.profiles ? config.profiles[profileKey] : null;
214
+ if (!profile && profileName && config.profiles) {
215
+ const matches = Object.entries(config.profiles).find(([key, entry]) => {
216
+ const displayName = getProfileDisplayName(key, entry);
217
+ return (
218
+ displayName === profileName ||
219
+ entry.name === profileName ||
220
+ key === profileName
221
+ );
222
+ });
223
+ if (matches) profile = matches[1];
224
+ }
225
+ const sessionUsage = hasExplicitUsage ? usage : stdinUsage;
226
+ const pricing = resolvePricingForProfile(config, profile || null, model);
227
+ let sessionCost: number | null = null;
228
+ if (hasExplicitUsage) {
229
+ sessionCost = sessionUsage
230
+ ? calculateUsageCost(sessionUsage, pricing)
231
+ : null;
232
+ } else {
233
+ const sessionCostFromRecords = sessionId
234
+ ? readUsageSessionCost(
235
+ config,
236
+ configPath,
237
+ type,
238
+ sessionId,
239
+ args.syncUsage
240
+ )
241
+ : null;
242
+ sessionCost =
243
+ sessionCostFromRecords ??
244
+ (sessionUsage ? calculateUsageCost(sessionUsage, pricing) : null);
245
+ }
246
+ const costIndex = readUsageCostIndex(config, configPath, args.syncUsage);
247
+ const costTotals = costIndex
248
+ ? resolveUsageCostForProfile(costIndex, type, profileKey, profileName)
249
+ : null;
250
+ const todayCost = costTotals ? costTotals.today : null;
251
+ const usageSegment = formatUsageSegment(todayCost, sessionCost);
202
252
  const contextLeft = getContextLeftPercent(stdinInput, type);
203
253
  const contextSegment = formatContextSegment(contextLeft);
204
254
  const contextUsedTokens = getContextUsedTokens(stdinInput);
@@ -7,7 +7,9 @@ import type {
7
7
  StatuslineInputProfile,
8
8
  StatuslineInputUsage,
9
9
  } from "./types";
10
- import { coerceNumber, firstNonEmpty, firstNumber, isRecord } from "./utils";
10
+ import { coerceNumber, firstNonEmpty, isRecord } from "./utils";
11
+ import { getClaudeInputUsage } from "./usage/claude";
12
+ import { getCodexInputUsage } from "./usage/codex";
11
13
 
12
14
  export function readStdinJson(): StatuslineInput | null {
13
15
  if (process.stdin.isTTY) return null;
@@ -83,162 +85,19 @@ export function getInputProfile(
83
85
  return input.profile as StatuslineInputProfile;
84
86
  }
85
87
 
86
- export function getInputUsage(input: StatuslineInput | null): StatuslineInputUsage | null {
88
+ export function getInputUsage(
89
+ input: StatuslineInput | null,
90
+ type: string | null
91
+ ): StatuslineInputUsage | null {
87
92
  if (!input) return null;
88
- if (isRecord(input.usage)) {
89
- return input.usage as StatuslineInputUsage;
93
+ const normalized = normalizeTypeValue(type);
94
+ if (normalized === "codex") {
95
+ return getCodexInputUsage(input);
90
96
  }
91
- const tokenUsage = input.token_usage;
92
- if (tokenUsage !== null && tokenUsage !== undefined) {
93
- if (typeof tokenUsage === "number") {
94
- return {
95
- todayTokens: null,
96
- totalTokens: coerceNumber(tokenUsage),
97
- inputTokens: null,
98
- outputTokens: null,
99
- };
100
- }
101
- if (isRecord(tokenUsage)) {
102
- const record = tokenUsage as Record<string, unknown>;
103
- const todayTokens =
104
- firstNumber(
105
- record.todayTokens,
106
- record.today,
107
- record.today_tokens,
108
- record.daily,
109
- record.daily_tokens
110
- ) ?? null;
111
- const totalTokens =
112
- firstNumber(
113
- record.totalTokens,
114
- record.total,
115
- record.total_tokens
116
- ) ?? null;
117
- const inputTokens =
118
- firstNumber(
119
- record.inputTokens,
120
- record.input,
121
- record.input_tokens
122
- ) ?? null;
123
- const outputTokens =
124
- firstNumber(
125
- record.outputTokens,
126
- record.output,
127
- record.output_tokens
128
- ) ?? null;
129
- const cacheRead =
130
- firstNumber(
131
- record.cache_read_input_tokens,
132
- record.cacheReadInputTokens,
133
- record.cache_read,
134
- record.cacheRead
135
- ) ?? null;
136
- const cacheWrite =
137
- firstNumber(
138
- record.cache_creation_input_tokens,
139
- record.cacheCreationInputTokens,
140
- record.cache_write_input_tokens,
141
- record.cacheWriteInputTokens,
142
- record.cache_write,
143
- record.cacheWrite
144
- ) ?? null;
145
- if (
146
- todayTokens === null &&
147
- totalTokens === null &&
148
- inputTokens === null &&
149
- outputTokens === null &&
150
- cacheRead === null &&
151
- cacheWrite === null
152
- ) {
153
- return null;
154
- }
155
- const hasCacheTokens = cacheRead !== null || cacheWrite !== null;
156
- const computedTotal = hasCacheTokens
157
- ? (inputTokens || 0) +
158
- (outputTokens || 0) +
159
- (cacheRead || 0) +
160
- (cacheWrite || 0)
161
- : null;
162
- const resolvedTodayTokens = hasCacheTokens
163
- ? todayTokens ?? totalTokens ?? computedTotal
164
- : todayTokens;
165
- return {
166
- todayTokens: resolvedTodayTokens,
167
- totalTokens: totalTokens ?? null,
168
- inputTokens,
169
- outputTokens,
170
- };
171
- }
97
+ if (normalized === "claude") {
98
+ return getClaudeInputUsage(input);
172
99
  }
173
- const contextWindow = isRecord(input.context_window)
174
- ? (input.context_window as Record<string, unknown>)
175
- : isRecord(input.contextWindow)
176
- ? (input.contextWindow as Record<string, unknown>)
177
- : null;
178
- if (!contextWindow) return null;
179
- const totalInputTokens =
180
- firstNumber(
181
- contextWindow.total_input_tokens,
182
- contextWindow.totalInputTokens
183
- ) ?? null;
184
- const totalOutputTokens =
185
- firstNumber(
186
- contextWindow.total_output_tokens,
187
- contextWindow.totalOutputTokens
188
- ) ?? null;
189
- if (totalInputTokens !== null || totalOutputTokens !== null) {
190
- return {
191
- todayTokens: null,
192
- totalTokens: null,
193
- inputTokens: totalInputTokens,
194
- outputTokens: totalOutputTokens,
195
- };
196
- }
197
- const currentUsage = isRecord(contextWindow.current_usage)
198
- ? (contextWindow.current_usage as Record<string, unknown>)
199
- : isRecord(contextWindow.currentUsage)
200
- ? (contextWindow.currentUsage as Record<string, unknown>)
201
- : null;
202
- if (!currentUsage) return null;
203
- const inputTokens =
204
- firstNumber(
205
- currentUsage.input_tokens,
206
- currentUsage.inputTokens
207
- ) ?? null;
208
- const outputTokens =
209
- firstNumber(
210
- currentUsage.output_tokens,
211
- currentUsage.outputTokens
212
- ) ?? null;
213
- const cacheRead =
214
- firstNumber(
215
- currentUsage.cache_read_input_tokens,
216
- currentUsage.cacheReadInputTokens
217
- ) ?? null;
218
- const cacheWrite =
219
- firstNumber(
220
- currentUsage.cache_creation_input_tokens,
221
- currentUsage.cacheCreationInputTokens
222
- ) ?? null;
223
- if (
224
- inputTokens === null &&
225
- outputTokens === null &&
226
- cacheRead === null &&
227
- cacheWrite === null
228
- ) {
229
- return null;
230
- }
231
- const totalTokens =
232
- (inputTokens || 0) +
233
- (outputTokens || 0) +
234
- (cacheRead || 0) +
235
- (cacheWrite || 0);
236
- return {
237
- todayTokens: totalTokens,
238
- totalTokens: null,
239
- inputTokens,
240
- outputTokens,
241
- };
100
+ return getCodexInputUsage(input) || getClaudeInputUsage(input);
242
101
  }
243
102
 
244
103
  export function getSessionId(input: StatuslineInput | null): string | null {
@@ -9,6 +9,8 @@ export interface StatuslineInputUsage {
9
9
  totalTokens?: number;
10
10
  inputTokens?: number;
11
11
  outputTokens?: number;
12
+ cacheReadTokens?: number;
13
+ cacheWriteTokens?: number;
12
14
  }
13
15
 
14
16
  export interface StatuslineInputContextWindowUsage {
@@ -72,11 +74,15 @@ export interface StatuslineUsage {
72
74
  totalTokens: number | null;
73
75
  inputTokens: number | null;
74
76
  outputTokens: number | null;
77
+ cacheReadTokens: number | null;
78
+ cacheWriteTokens: number | null;
75
79
  }
76
80
 
77
81
  export interface StatuslineUsageTotals {
78
82
  inputTokens: number | null;
79
83
  outputTokens: number | null;
84
+ cacheReadTokens: number | null;
85
+ cacheWriteTokens: number | null;
80
86
  totalTokens: number | null;
81
87
  }
82
88
 
@@ -0,0 +1,299 @@
1
+ import type {
2
+ StatuslineInput,
3
+ StatuslineInputUsage,
4
+ StatuslineUsageTotals,
5
+ } from "../types";
6
+ import { firstNumber, isRecord } from "../utils";
7
+
8
+ function resolveContextWindow(
9
+ input: StatuslineInput
10
+ ): Record<string, unknown> | null {
11
+ if (isRecord(input.context_window)) {
12
+ return input.context_window as Record<string, unknown>;
13
+ }
14
+ if (isRecord(input.contextWindow)) {
15
+ return input.contextWindow as Record<string, unknown>;
16
+ }
17
+ return null;
18
+ }
19
+
20
+ function resolveCurrentUsage(
21
+ contextWindow: Record<string, unknown>
22
+ ): Record<string, unknown> | null {
23
+ if (isRecord(contextWindow.current_usage)) {
24
+ return contextWindow.current_usage as Record<string, unknown>;
25
+ }
26
+ if (isRecord(contextWindow.currentUsage)) {
27
+ return contextWindow.currentUsage as Record<string, unknown>;
28
+ }
29
+ return null;
30
+ }
31
+
32
+ function parseClaudeUsageTotalsRecord(
33
+ record: Record<string, unknown>
34
+ ): StatuslineUsageTotals | null {
35
+ const inputTokens =
36
+ firstNumber(
37
+ record.inputTokens,
38
+ record.input,
39
+ record.input_tokens
40
+ ) ?? null;
41
+ const outputTokens =
42
+ firstNumber(
43
+ record.outputTokens,
44
+ record.output,
45
+ record.output_tokens
46
+ ) ?? null;
47
+ const cacheRead =
48
+ firstNumber(
49
+ record.cache_read_input_tokens,
50
+ record.cacheReadInputTokens,
51
+ record.cache_read,
52
+ record.cacheRead
53
+ ) ?? null;
54
+ const cacheWrite =
55
+ firstNumber(
56
+ record.cache_creation_input_tokens,
57
+ record.cacheCreationInputTokens,
58
+ record.cache_write_input_tokens,
59
+ record.cacheWriteInputTokens,
60
+ record.cache_write,
61
+ record.cacheWrite
62
+ ) ?? null;
63
+ const totalTokens =
64
+ firstNumber(
65
+ record.totalTokens,
66
+ record.total,
67
+ record.total_tokens
68
+ ) ?? null;
69
+ let computedTotal: number | null = null;
70
+ if (
71
+ inputTokens !== null ||
72
+ outputTokens !== null ||
73
+ cacheRead !== null ||
74
+ cacheWrite !== null
75
+ ) {
76
+ computedTotal =
77
+ (inputTokens || 0) +
78
+ (outputTokens || 0) +
79
+ (cacheRead || 0) +
80
+ (cacheWrite || 0);
81
+ }
82
+ const resolvedTotal = totalTokens ?? computedTotal;
83
+ if (
84
+ inputTokens === null &&
85
+ outputTokens === null &&
86
+ cacheRead === null &&
87
+ cacheWrite === null &&
88
+ resolvedTotal === null
89
+ ) {
90
+ return null;
91
+ }
92
+ return {
93
+ inputTokens,
94
+ outputTokens,
95
+ cacheReadTokens: cacheRead,
96
+ cacheWriteTokens: cacheWrite,
97
+ totalTokens: resolvedTotal,
98
+ };
99
+ }
100
+
101
+ function parseClaudeInputUsageRecord(
102
+ record: Record<string, unknown>
103
+ ): StatuslineInputUsage | null {
104
+ const todayTokens =
105
+ firstNumber(
106
+ record.todayTokens,
107
+ record.today,
108
+ record.today_tokens,
109
+ record.daily,
110
+ record.daily_tokens
111
+ ) ?? null;
112
+ const totalTokens =
113
+ firstNumber(
114
+ record.totalTokens,
115
+ record.total,
116
+ record.total_tokens
117
+ ) ?? null;
118
+ const inputTokens =
119
+ firstNumber(
120
+ record.inputTokens,
121
+ record.input,
122
+ record.input_tokens
123
+ ) ?? null;
124
+ const outputTokens =
125
+ firstNumber(
126
+ record.outputTokens,
127
+ record.output,
128
+ record.output_tokens
129
+ ) ?? null;
130
+ const cacheRead =
131
+ firstNumber(
132
+ record.cache_read_input_tokens,
133
+ record.cacheReadInputTokens,
134
+ record.cache_read,
135
+ record.cacheRead
136
+ ) ?? null;
137
+ const cacheWrite =
138
+ firstNumber(
139
+ record.cache_creation_input_tokens,
140
+ record.cacheCreationInputTokens,
141
+ record.cache_write_input_tokens,
142
+ record.cacheWriteInputTokens,
143
+ record.cache_write,
144
+ record.cacheWrite
145
+ ) ?? null;
146
+ if (
147
+ todayTokens === null &&
148
+ totalTokens === null &&
149
+ inputTokens === null &&
150
+ outputTokens === null &&
151
+ cacheRead === null &&
152
+ cacheWrite === null
153
+ ) {
154
+ return null;
155
+ }
156
+ const hasCacheTokens = cacheRead !== null || cacheWrite !== null;
157
+ const computedTotal = hasCacheTokens
158
+ ? (inputTokens || 0) +
159
+ (outputTokens || 0) +
160
+ (cacheRead || 0) +
161
+ (cacheWrite || 0)
162
+ : null;
163
+ const resolvedTodayTokens = hasCacheTokens
164
+ ? todayTokens ?? totalTokens ?? computedTotal
165
+ : todayTokens;
166
+ return {
167
+ todayTokens: resolvedTodayTokens,
168
+ totalTokens: totalTokens ?? null,
169
+ inputTokens,
170
+ outputTokens,
171
+ cacheReadTokens: cacheRead,
172
+ cacheWriteTokens: cacheWrite,
173
+ };
174
+ }
175
+
176
+ function parseTotalsFromContextWindow(
177
+ contextWindow: Record<string, unknown>
178
+ ): StatuslineUsageTotals | null {
179
+ const inputTokens =
180
+ firstNumber(
181
+ contextWindow.total_input_tokens,
182
+ contextWindow.totalInputTokens
183
+ ) ?? null;
184
+ const outputTokens =
185
+ firstNumber(
186
+ contextWindow.total_output_tokens,
187
+ contextWindow.totalOutputTokens
188
+ ) ?? null;
189
+ if (inputTokens === null && outputTokens === null) return null;
190
+ const currentUsage = resolveCurrentUsage(contextWindow);
191
+ const cacheRead =
192
+ currentUsage
193
+ ? firstNumber(
194
+ currentUsage.cache_read_input_tokens,
195
+ currentUsage.cacheReadInputTokens
196
+ ) ?? null
197
+ : null;
198
+ const cacheWrite =
199
+ currentUsage
200
+ ? firstNumber(
201
+ currentUsage.cache_creation_input_tokens,
202
+ currentUsage.cacheCreationInputTokens
203
+ ) ?? null
204
+ : null;
205
+ const totalTokens =
206
+ (inputTokens || 0) +
207
+ (outputTokens || 0) +
208
+ (cacheRead || 0) +
209
+ (cacheWrite || 0);
210
+ return {
211
+ inputTokens,
212
+ outputTokens,
213
+ cacheReadTokens: cacheRead,
214
+ cacheWriteTokens: cacheWrite,
215
+ totalTokens,
216
+ };
217
+ }
218
+
219
+ export function getClaudeUsageTotalsFromInput(
220
+ input: StatuslineInput | null
221
+ ): StatuslineUsageTotals | null {
222
+ if (!input) return null;
223
+ const contextWindow = resolveContextWindow(input);
224
+ if (contextWindow) {
225
+ const totals = parseTotalsFromContextWindow(contextWindow);
226
+ if (totals) return totals;
227
+ }
228
+ if (isRecord(input.usage)) {
229
+ return parseClaudeUsageTotalsRecord(input.usage as Record<string, unknown>);
230
+ }
231
+ return null;
232
+ }
233
+
234
+ export function getClaudeInputUsage(
235
+ input: StatuslineInput | null
236
+ ): StatuslineInputUsage | null {
237
+ if (!input) return null;
238
+ if (isRecord(input.usage)) {
239
+ const parsed = parseClaudeInputUsageRecord(input.usage as Record<string, unknown>);
240
+ if (parsed) return parsed;
241
+ return input.usage as StatuslineInputUsage;
242
+ }
243
+ const contextWindow = resolveContextWindow(input);
244
+ if (!contextWindow) return null;
245
+ const totals = parseTotalsFromContextWindow(contextWindow);
246
+ if (totals) {
247
+ return {
248
+ todayTokens: null,
249
+ totalTokens: null,
250
+ inputTokens: totals.inputTokens,
251
+ outputTokens: totals.outputTokens,
252
+ cacheReadTokens: totals.cacheReadTokens,
253
+ cacheWriteTokens: totals.cacheWriteTokens,
254
+ };
255
+ }
256
+ const currentUsage = resolveCurrentUsage(contextWindow);
257
+ if (!currentUsage) return null;
258
+ const inputTokens =
259
+ firstNumber(
260
+ currentUsage.input_tokens,
261
+ currentUsage.inputTokens
262
+ ) ?? null;
263
+ const outputTokens =
264
+ firstNumber(
265
+ currentUsage.output_tokens,
266
+ currentUsage.outputTokens
267
+ ) ?? null;
268
+ const cacheRead =
269
+ firstNumber(
270
+ currentUsage.cache_read_input_tokens,
271
+ currentUsage.cacheReadInputTokens
272
+ ) ?? null;
273
+ const cacheWrite =
274
+ firstNumber(
275
+ currentUsage.cache_creation_input_tokens,
276
+ currentUsage.cacheCreationInputTokens
277
+ ) ?? null;
278
+ if (
279
+ inputTokens === null &&
280
+ outputTokens === null &&
281
+ cacheRead === null &&
282
+ cacheWrite === null
283
+ ) {
284
+ return null;
285
+ }
286
+ const totalTokens =
287
+ (inputTokens || 0) +
288
+ (outputTokens || 0) +
289
+ (cacheRead || 0) +
290
+ (cacheWrite || 0);
291
+ return {
292
+ todayTokens: totalTokens,
293
+ totalTokens: null,
294
+ inputTokens,
295
+ outputTokens,
296
+ cacheReadTokens: cacheRead,
297
+ cacheWriteTokens: cacheWrite,
298
+ };
299
+ }