letmecode 0.1.3 → 0.1.4
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.
package/ink-app/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import React, { useEffect, useState } from "react";
|
|
3
|
-
import { Box, Text, useApp, useInput, render } from "ink";
|
|
2
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { Box, Text, measureElement, useApp, useInput, useStdout, render } from "ink";
|
|
4
4
|
import { configureCopilotVsCodeLogging, createProviders } from "./providers/index.js";
|
|
5
5
|
const VERTICAL_TABS = [
|
|
6
6
|
{ id: "limit-windows", label: "Limits" },
|
|
@@ -55,8 +55,14 @@ const ANTHROPIC_DAY_USAGE_COLUMNS = {
|
|
|
55
55
|
const COPILOT_ACTIONS = [
|
|
56
56
|
{ id: "vscode", label: "Start logging VS Code", enabled: true }
|
|
57
57
|
];
|
|
58
|
+
const ENTER_FULLSCREEN_MODE = "\u001B[?1049h\u001B[2J\u001B[H";
|
|
59
|
+
const EXIT_FULLSCREEN_MODE = "\u001B[?1049l";
|
|
60
|
+
const SCROLLBAR_TRACK_GLYPH = "│";
|
|
61
|
+
const SCROLLBAR_THUMB_GLYPH = "█";
|
|
58
62
|
function App(props) {
|
|
59
63
|
const { exit } = useApp();
|
|
64
|
+
const viewportHeight = useViewportHeight();
|
|
65
|
+
const { ref: contentPanelRef, height: contentPanelHeight } = useMeasuredElementSize();
|
|
60
66
|
const providers = React.useState(() => createProviders())[0];
|
|
61
67
|
const [providerStates, setProviderStates] = useState(providers.map((provider) => ({ provider, status: "loading" })));
|
|
62
68
|
const [selectedProviderId, setSelectedProviderId] = useState(providers[0]?.id ?? "");
|
|
@@ -185,7 +191,7 @@ function App(props) {
|
|
|
185
191
|
setSelectedVerticalTabIndex((current) => (current - 1 + VERTICAL_TABS.length) % VERTICAL_TABS.length);
|
|
186
192
|
}
|
|
187
193
|
});
|
|
188
|
-
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "letmecode usage dashboard" }), _jsx(Text, { color: "gray", children: "[/]/tab to switch providers, j/k or up/down for details, left/right to select a row, enter for actions, q to quit" }), _jsx(Box, { marginTop: 1, children: sortedProviderStates.map((state) => (_jsx(ProviderTab, { label: state.provider.label, active: state.provider.id === selectedProvider.provider.id, status: state.status }, state.provider.id))) }), _jsxs(Box, { marginTop: 1, children: [_jsx(Box, { flexDirection: "column", width: VERTICAL_TAB_WIDTH, marginRight: 2, children: VERTICAL_TABS.map((tab, index) => (_jsx(VerticalTab, { label: tab.label, active: index === selectedVerticalTabIndex }, tab.id))) }), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: _jsx(ContentPanel, { providerState: selectedProvider, tabId: selectedVerticalTab.id, selectedLimitRowKey: selectedLimitRow ? getLimitRowKey(selectedLimitRow) : undefined, selectedDayKey: selectedDayRow?.dayKey, selectedModelId: selectedModelRow?.modelId }) })] }), _jsx(SelectionDetailsPanel, { providerState: selectedProvider, tabId: selectedVerticalTab.id, selectedLimitRow: selectedLimitRow, selectedDayRow: selectedDayRow, selectedModelRow: selectedModelRow }), _jsx(CopilotActionsPanel, { providerState: selectedProvider, actionMessage: copilotActionMessage, selectedActionIndex: selectedCopilotActionIndex }), selectedProvider.status === "ready" && selectedProvider.stats.warnings.length > 0 ? (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: "yellow", paddingX: 1, flexDirection: "column", children: [_jsx(Text, { color: "yellow", children: "Warnings" }), selectedProvider.stats.warnings.map((warning) => (_jsx(Text, { children: warning }, warning)))] })) : null] }));
|
|
194
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, height: viewportHeight, overflow: "hidden", children: [_jsx(Text, { bold: true, color: "cyan", children: "letmecode usage dashboard" }), _jsx(Text, { color: "gray", children: "[/]/tab to switch providers, j/k or up/down for details, left/right to select a row, enter for actions, q to quit" }), _jsx(Box, { marginTop: 1, children: sortedProviderStates.map((state) => (_jsx(ProviderTab, { label: state.provider.label, active: state.provider.id === selectedProvider.provider.id, status: state.status }, state.provider.id))) }), _jsxs(Box, { marginTop: 1, flexDirection: "column", flexGrow: 1, overflow: "hidden", children: [_jsxs(Box, { flexGrow: 1, overflow: "hidden", children: [_jsx(Box, { flexDirection: "column", width: VERTICAL_TAB_WIDTH, marginRight: 2, overflow: "hidden", children: VERTICAL_TABS.map((tab, index) => (_jsx(VerticalTab, { label: tab.label, active: index === selectedVerticalTabIndex }, tab.id))) }), _jsx(Box, { ref: contentPanelRef, flexDirection: "column", flexGrow: 1, overflow: "hidden", children: _jsx(ContentPanel, { providerState: selectedProvider, tabId: selectedVerticalTab.id, selectedLimitRowKey: selectedLimitRow ? getLimitRowKey(selectedLimitRow) : undefined, selectedDayKey: selectedDayRow?.dayKey, selectedModelId: selectedModelRow?.modelId, availableHeight: contentPanelHeight }) })] }), _jsx(SelectionDetailsPanel, { providerState: selectedProvider, tabId: selectedVerticalTab.id, selectedLimitRow: selectedLimitRow, selectedDayRow: selectedDayRow, selectedModelRow: selectedModelRow }), _jsx(CopilotActionsPanel, { providerState: selectedProvider, actionMessage: copilotActionMessage, selectedActionIndex: selectedCopilotActionIndex }), selectedProvider.status === "ready" && selectedProvider.stats.warnings.length > 0 ? (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: "yellow", paddingX: 1, flexDirection: "column", overflow: "hidden", children: [_jsx(Text, { color: "yellow", children: "Warnings" }), selectedProvider.stats.warnings.map((warning) => (_jsx(Text, { children: warning }, warning)))] })) : null] })] }));
|
|
189
195
|
}
|
|
190
196
|
function CopilotActionsPanel(props) {
|
|
191
197
|
if (props.providerState.provider.id !== "copilot") {
|
|
@@ -239,29 +245,25 @@ function ContentPanel(props) {
|
|
|
239
245
|
return _jsxs(Text, { color: "red", children: ["Provider error: ", props.providerState.errorMessage] });
|
|
240
246
|
}
|
|
241
247
|
if (props.tabId === "limit-windows") {
|
|
242
|
-
return _jsx(LimitWindowsPanel, { stats: props.providerState.stats, selectedRowKey: props.selectedLimitRowKey });
|
|
248
|
+
return (_jsx(LimitWindowsPanel, { stats: props.providerState.stats, selectedRowKey: props.selectedLimitRowKey, availableHeight: props.availableHeight }));
|
|
243
249
|
}
|
|
244
250
|
if (props.tabId === "summary") {
|
|
245
251
|
return _jsx(SummaryPanel, { stats: props.providerState.stats });
|
|
246
252
|
}
|
|
247
253
|
if (props.tabId === "day-to-day-analyses") {
|
|
248
|
-
return _jsx(DayToDayPanel, { stats: props.providerState.stats, selectedDayKey: props.selectedDayKey });
|
|
254
|
+
return (_jsx(DayToDayPanel, { stats: props.providerState.stats, selectedDayKey: props.selectedDayKey, availableHeight: props.availableHeight }));
|
|
249
255
|
}
|
|
250
|
-
return _jsx(UsageByModelPanel, { stats: props.providerState.stats, selectedModelId: props.selectedModelId });
|
|
256
|
+
return (_jsx(UsageByModelPanel, { stats: props.providerState.stats, selectedModelId: props.selectedModelId, availableHeight: props.availableHeight }));
|
|
251
257
|
}
|
|
252
258
|
function LimitWindowsPanel(props) {
|
|
253
|
-
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
const usedLabel = `${window.minUsedPercent}%->${window.maxUsedPercent}%`;
|
|
262
|
-
const isSelected = props.selectedRowKey === getLimitRowKey(window);
|
|
263
|
-
return (_jsxs(Text, { inverse: isSelected, color: isSelected ? "cyan" : undefined, children: [pad(window.planType, LIMIT_WINDOW_COLUMNS.plan), " ", pad(windowLabel, LIMIT_WINDOW_COLUMNS.window), " ", pad(usedLabel, LIMIT_WINDOW_COLUMNS.used), " ", pad(formatLocalDateTime(window.startTimeUtcIso), LIMIT_WINDOW_COLUMNS.date), " ", pad(formatLocalDateTime(window.endTimeUtcIso), LIMIT_WINDOW_COLUMNS.date), " ", pad(formatUsd(window.totals.estimatedCredits * CODEX_CREDIT_COST_USD), LIMIT_WINDOW_COLUMNS.value)] }, getLimitRowKey(window)));
|
|
264
|
-
})] }));
|
|
259
|
+
const bodyLines = [
|
|
260
|
+
{ key: "primary-title", text: "Primary Limit Windows", bold: true },
|
|
261
|
+
...buildLimitWindowSectionLines("primary", props.stats.primaryLimitWindows, props.selectedRowKey),
|
|
262
|
+
{ key: "section-gap", text: "" },
|
|
263
|
+
{ key: "secondary-title", text: "Secondary Limit Windows", bold: true },
|
|
264
|
+
...buildLimitWindowSectionLines("secondary", props.stats.secondaryLimitWindows, props.selectedRowKey)
|
|
265
|
+
];
|
|
266
|
+
return (_jsx(ScrollableLineViewport, { bodyLines: bodyLines, selectedBodyLineKey: props.selectedRowKey ? `limit-row:${props.selectedRowKey}` : undefined, availableHeight: props.availableHeight }));
|
|
265
267
|
}
|
|
266
268
|
function UsageByModelPanel(props) {
|
|
267
269
|
if (props.stats.modelUsage.length === 0) {
|
|
@@ -269,21 +271,57 @@ function UsageByModelPanel(props) {
|
|
|
269
271
|
}
|
|
270
272
|
const totals = props.stats.summary.totals;
|
|
271
273
|
if (totals.tokenBreakdown.schema === "anthropic") {
|
|
272
|
-
return (
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
274
|
+
return (_jsx(ScrollableLineViewport, { headerLines: [
|
|
275
|
+
{
|
|
276
|
+
key: "anthropic-model-header",
|
|
277
|
+
text: `${pad("model", ANTHROPIC_MODEL_USAGE_COLUMNS.model)} ${pad("input", ANTHROPIC_MODEL_USAGE_COLUMNS.input)} ${pad("cacheW5m", ANTHROPIC_MODEL_USAGE_COLUMNS.cacheWrite5m)} ${pad("cacheW1h", ANTHROPIC_MODEL_USAGE_COLUMNS.cacheWrite1h)} ${pad("cacheRead", ANTHROPIC_MODEL_USAGE_COLUMNS.cacheRead)} ${pad("output", ANTHROPIC_MODEL_USAGE_COLUMNS.output)} ${pad("credits", ANTHROPIC_MODEL_USAGE_COLUMNS.credits)} value`,
|
|
278
|
+
color: "gray"
|
|
279
|
+
}
|
|
280
|
+
], bodyLines: props.stats.modelUsage.flatMap((row) => {
|
|
281
|
+
if (row.totals.tokenBreakdown.schema !== "anthropic") {
|
|
282
|
+
return [];
|
|
283
|
+
}
|
|
284
|
+
return [
|
|
285
|
+
{
|
|
286
|
+
key: `model-row:${row.modelId}`,
|
|
287
|
+
text: `${pad(row.modelId, ANTHROPIC_MODEL_USAGE_COLUMNS.model)} ${pad(formatInteger(row.totals.tokenBreakdown.inputTokens), ANTHROPIC_MODEL_USAGE_COLUMNS.input)} ${pad(formatInteger(row.totals.tokenBreakdown.cacheWrite5mInputTokens), ANTHROPIC_MODEL_USAGE_COLUMNS.cacheWrite5m)} ${pad(formatInteger(row.totals.tokenBreakdown.cacheWrite1hInputTokens), ANTHROPIC_MODEL_USAGE_COLUMNS.cacheWrite1h)} ${pad(formatInteger(row.totals.tokenBreakdown.cacheReadInputTokens), ANTHROPIC_MODEL_USAGE_COLUMNS.cacheRead)} ${pad(formatInteger(row.totals.outputTokens), ANTHROPIC_MODEL_USAGE_COLUMNS.output)} ${pad(formatUsageCredits(row.totals, row.modelId), ANTHROPIC_MODEL_USAGE_COLUMNS.credits)} ${pad(formatUsageUsd(row.totals, row.modelId), ANTHROPIC_MODEL_USAGE_COLUMNS.value)}`,
|
|
288
|
+
inverse: props.selectedModelId === row.modelId,
|
|
289
|
+
color: props.selectedModelId === row.modelId ? "cyan" : undefined
|
|
276
290
|
}
|
|
277
|
-
|
|
278
|
-
|
|
291
|
+
];
|
|
292
|
+
}), footerLines: [
|
|
293
|
+
{
|
|
294
|
+
key: "anthropic-model-total",
|
|
295
|
+
text: `${pad("TOTAL", ANTHROPIC_MODEL_USAGE_COLUMNS.model)} ${pad(formatInteger(totals.tokenBreakdown.inputTokens), ANTHROPIC_MODEL_USAGE_COLUMNS.input)} ${pad(formatInteger(totals.tokenBreakdown.cacheWrite5mInputTokens), ANTHROPIC_MODEL_USAGE_COLUMNS.cacheWrite5m)} ${pad(formatInteger(totals.tokenBreakdown.cacheWrite1hInputTokens), ANTHROPIC_MODEL_USAGE_COLUMNS.cacheWrite1h)} ${pad(formatInteger(totals.tokenBreakdown.cacheReadInputTokens), ANTHROPIC_MODEL_USAGE_COLUMNS.cacheRead)} ${pad(formatInteger(totals.outputTokens), ANTHROPIC_MODEL_USAGE_COLUMNS.output)} ${pad(formatUsageCredits(totals), ANTHROPIC_MODEL_USAGE_COLUMNS.credits)} ${pad(formatUsageUsd(totals), ANTHROPIC_MODEL_USAGE_COLUMNS.value)}`,
|
|
296
|
+
color: "cyan"
|
|
297
|
+
}
|
|
298
|
+
], selectedBodyLineKey: props.selectedModelId ? `model-row:${props.selectedModelId}` : undefined, availableHeight: props.availableHeight }));
|
|
279
299
|
}
|
|
280
|
-
return (
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
300
|
+
return (_jsx(ScrollableLineViewport, { headerLines: [
|
|
301
|
+
{
|
|
302
|
+
key: "openai-model-header",
|
|
303
|
+
text: `${pad("model", OPENAI_MODEL_USAGE_COLUMNS.model)} ${pad("uncached", OPENAI_MODEL_USAGE_COLUMNS.input)} ${pad("cached", OPENAI_MODEL_USAGE_COLUMNS.cached)} ${pad("output", OPENAI_MODEL_USAGE_COLUMNS.output)} ${pad("credits", OPENAI_MODEL_USAGE_COLUMNS.credits)} value`,
|
|
304
|
+
color: "gray"
|
|
305
|
+
}
|
|
306
|
+
], bodyLines: props.stats.modelUsage.flatMap((row) => {
|
|
307
|
+
if (row.totals.tokenBreakdown.schema !== "openai") {
|
|
308
|
+
return [];
|
|
309
|
+
}
|
|
310
|
+
return [
|
|
311
|
+
{
|
|
312
|
+
key: `model-row:${row.modelId}`,
|
|
313
|
+
text: `${pad(row.modelId, OPENAI_MODEL_USAGE_COLUMNS.model)} ${pad(formatOpenAiTokens(row.totals, "non-cached"), OPENAI_MODEL_USAGE_COLUMNS.input)} ${pad(formatOpenAiTokens(row.totals, "cached"), OPENAI_MODEL_USAGE_COLUMNS.cached)} ${pad(formatInteger(row.totals.outputTokens), OPENAI_MODEL_USAGE_COLUMNS.output)} ${pad(formatUsageCredits(row.totals, row.modelId), OPENAI_MODEL_USAGE_COLUMNS.credits)} ${pad(formatUsageUsd(row.totals, row.modelId), OPENAI_MODEL_USAGE_COLUMNS.value)}`,
|
|
314
|
+
inverse: props.selectedModelId === row.modelId,
|
|
315
|
+
color: props.selectedModelId === row.modelId ? "cyan" : undefined
|
|
284
316
|
}
|
|
285
|
-
|
|
286
|
-
|
|
317
|
+
];
|
|
318
|
+
}), footerLines: [
|
|
319
|
+
{
|
|
320
|
+
key: "openai-model-total",
|
|
321
|
+
text: `${pad("TOTAL", OPENAI_MODEL_USAGE_COLUMNS.model)} ${pad(formatOpenAiTokens(totals, "non-cached"), OPENAI_MODEL_USAGE_COLUMNS.input)} ${pad(formatOpenAiTokens(totals, "cached"), OPENAI_MODEL_USAGE_COLUMNS.cached)} ${pad(formatInteger(totals.outputTokens), OPENAI_MODEL_USAGE_COLUMNS.output)} ${pad(formatUsageCredits(totals), OPENAI_MODEL_USAGE_COLUMNS.credits)} ${pad(formatUsageUsd(totals), OPENAI_MODEL_USAGE_COLUMNS.value)}`,
|
|
322
|
+
color: "cyan"
|
|
323
|
+
}
|
|
324
|
+
], selectedBodyLineKey: props.selectedModelId ? `model-row:${props.selectedModelId}` : undefined, availableHeight: props.availableHeight }));
|
|
287
325
|
}
|
|
288
326
|
function DayToDayPanel(props) {
|
|
289
327
|
if (props.stats.dayUsage.length === 0) {
|
|
@@ -291,21 +329,87 @@ function DayToDayPanel(props) {
|
|
|
291
329
|
}
|
|
292
330
|
const totals = props.stats.summary.totals;
|
|
293
331
|
if (totals.tokenBreakdown.schema === "anthropic") {
|
|
294
|
-
return (
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
332
|
+
return (_jsx(ScrollableLineViewport, { headerLines: [
|
|
333
|
+
{
|
|
334
|
+
key: "anthropic-day-header",
|
|
335
|
+
text: `${pad("day", ANTHROPIC_DAY_USAGE_COLUMNS.day)} ${pad("events", ANTHROPIC_DAY_USAGE_COLUMNS.events)} ${pad("input", ANTHROPIC_DAY_USAGE_COLUMNS.input)} ${pad("cacheW5m", ANTHROPIC_DAY_USAGE_COLUMNS.cacheWrite5m)} ${pad("cacheW1h", ANTHROPIC_DAY_USAGE_COLUMNS.cacheWrite1h)} ${pad("cacheRead", ANTHROPIC_DAY_USAGE_COLUMNS.cacheRead)} ${pad("output", ANTHROPIC_DAY_USAGE_COLUMNS.output)} value`,
|
|
336
|
+
color: "gray"
|
|
337
|
+
}
|
|
338
|
+
], bodyLines: props.stats.dayUsage.flatMap((row) => {
|
|
339
|
+
if (row.totals.tokenBreakdown.schema !== "anthropic") {
|
|
340
|
+
return [];
|
|
341
|
+
}
|
|
342
|
+
return [
|
|
343
|
+
{
|
|
344
|
+
key: `day-row:${row.dayKey}`,
|
|
345
|
+
text: `${pad(formatUtcDay(row.dayKey), ANTHROPIC_DAY_USAGE_COLUMNS.day)} ${pad(formatInteger(row.totals.eventCount), ANTHROPIC_DAY_USAGE_COLUMNS.events)} ${pad(formatInteger(row.totals.tokenBreakdown.inputTokens), ANTHROPIC_DAY_USAGE_COLUMNS.input)} ${pad(formatInteger(row.totals.tokenBreakdown.cacheWrite5mInputTokens), ANTHROPIC_DAY_USAGE_COLUMNS.cacheWrite5m)} ${pad(formatInteger(row.totals.tokenBreakdown.cacheWrite1hInputTokens), ANTHROPIC_DAY_USAGE_COLUMNS.cacheWrite1h)} ${pad(formatInteger(row.totals.tokenBreakdown.cacheReadInputTokens), ANTHROPIC_DAY_USAGE_COLUMNS.cacheRead)} ${pad(formatInteger(row.totals.outputTokens), ANTHROPIC_DAY_USAGE_COLUMNS.output)} ${pad(formatUsageUsd(row.totals), ANTHROPIC_DAY_USAGE_COLUMNS.value)}`,
|
|
346
|
+
inverse: props.selectedDayKey === row.dayKey,
|
|
347
|
+
color: props.selectedDayKey === row.dayKey ? "cyan" : undefined
|
|
298
348
|
}
|
|
299
|
-
|
|
300
|
-
|
|
349
|
+
];
|
|
350
|
+
}), selectedBodyLineKey: props.selectedDayKey ? `day-row:${props.selectedDayKey}` : undefined, availableHeight: props.availableHeight }));
|
|
301
351
|
}
|
|
302
|
-
return (
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
352
|
+
return (_jsx(ScrollableLineViewport, { headerLines: [
|
|
353
|
+
{
|
|
354
|
+
key: "openai-day-header",
|
|
355
|
+
text: `${pad("day", OPENAI_DAY_USAGE_COLUMNS.day)} ${pad("events", OPENAI_DAY_USAGE_COLUMNS.events)} ${pad("input", OPENAI_DAY_USAGE_COLUMNS.input)} ${pad("output", OPENAI_DAY_USAGE_COLUMNS.output)} value`,
|
|
356
|
+
color: "gray"
|
|
357
|
+
}
|
|
358
|
+
], bodyLines: props.stats.dayUsage.flatMap((row) => {
|
|
359
|
+
if (row.totals.tokenBreakdown.schema !== "openai") {
|
|
360
|
+
return [];
|
|
361
|
+
}
|
|
362
|
+
return [
|
|
363
|
+
{
|
|
364
|
+
key: `day-row:${row.dayKey}`,
|
|
365
|
+
text: `${pad(formatUtcDay(row.dayKey), OPENAI_DAY_USAGE_COLUMNS.day)} ${pad(formatInteger(row.totals.eventCount), OPENAI_DAY_USAGE_COLUMNS.events)} ${pad(formatInteger(row.totals.inputTotalTokens), OPENAI_DAY_USAGE_COLUMNS.input)} ${pad(formatInteger(row.totals.outputTokens), OPENAI_DAY_USAGE_COLUMNS.output)} ${pad(formatUsageUsd(row.totals), OPENAI_DAY_USAGE_COLUMNS.value)}`,
|
|
366
|
+
inverse: props.selectedDayKey === row.dayKey,
|
|
367
|
+
color: props.selectedDayKey === row.dayKey ? "cyan" : undefined
|
|
306
368
|
}
|
|
307
|
-
|
|
308
|
-
|
|
369
|
+
];
|
|
370
|
+
}), selectedBodyLineKey: props.selectedDayKey ? `day-row:${props.selectedDayKey}` : undefined, availableHeight: props.availableHeight }));
|
|
371
|
+
}
|
|
372
|
+
function ScrollableLineViewport(props) {
|
|
373
|
+
const headerLines = props.headerLines ?? [];
|
|
374
|
+
const footerLines = props.footerLines ?? [];
|
|
375
|
+
const layout = resolveScrollableViewportLayout(props.availableHeight, headerLines.length, props.bodyLines.length, footerLines.length);
|
|
376
|
+
const selectedBodyLineIndex = props.selectedBodyLineKey
|
|
377
|
+
? props.bodyLines.findIndex((line) => line.key === props.selectedBodyLineKey)
|
|
378
|
+
: -1;
|
|
379
|
+
const scrollOffset = useAutoScrollOffset(selectedBodyLineIndex, props.bodyLines.length, layout.bodyVisibleCount);
|
|
380
|
+
const visibleHeaderLines = headerLines.slice(0, layout.headerVisibleCount);
|
|
381
|
+
const visibleBodyLines = props.bodyLines.slice(scrollOffset, scrollOffset + layout.bodyVisibleCount);
|
|
382
|
+
const visibleFooterLines = footerLines.slice(Math.max(0, footerLines.length - layout.footerVisibleCount));
|
|
383
|
+
const bodyScrollbarLines = buildScrollbarLines(layout.bodyVisibleCount, props.bodyLines.length, scrollOffset);
|
|
384
|
+
const showScrollbar = bodyScrollbarLines.length > 0;
|
|
385
|
+
return (_jsxs(Box, { flexDirection: "row", overflow: "hidden", children: [_jsxs(Box, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: [visibleHeaderLines.map((line) => (_jsx(ScrollableViewportLine, { line: line }, line.key))), visibleBodyLines.map((line) => (_jsx(ScrollableViewportLine, { line: line }, line.key))), visibleFooterLines.map((line) => (_jsx(ScrollableViewportLine, { line: line }, line.key)))] }), showScrollbar ? (_jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [visibleHeaderLines.map((line) => (_jsx(Text, { color: "gray", children: " " }, `${line.key}-scrollbar`))), bodyScrollbarLines.map((line, index) => (_jsx(Text, { color: line === "#" ? "cyan" : "gray", children: line }, `scrollbar-${index}`))), visibleFooterLines.map((line) => (_jsx(Text, { color: "gray", children: " " }, `${line.key}-scrollbar`)))] })) : null] }));
|
|
386
|
+
}
|
|
387
|
+
function ScrollableViewportLine(props) {
|
|
388
|
+
return (_jsx(Text, { bold: props.line.bold, color: props.line.color, inverse: props.line.inverse, wrap: "truncate-end", children: props.line.text }));
|
|
389
|
+
}
|
|
390
|
+
function buildLimitWindowSectionLines(scope, windows, selectedRowKey) {
|
|
391
|
+
if (windows.length === 0) {
|
|
392
|
+
return [{ key: `${scope}-empty`, text: "No windows found.", color: "gray" }];
|
|
393
|
+
}
|
|
394
|
+
return [
|
|
395
|
+
{
|
|
396
|
+
key: `${scope}-header`,
|
|
397
|
+
text: `${pad("plan", LIMIT_WINDOW_COLUMNS.plan)} ${pad("window", LIMIT_WINDOW_COLUMNS.window)} ${pad("used", LIMIT_WINDOW_COLUMNS.used)} ${pad("start", LIMIT_WINDOW_COLUMNS.date)} ${pad("end", LIMIT_WINDOW_COLUMNS.date)} value`,
|
|
398
|
+
color: "gray"
|
|
399
|
+
},
|
|
400
|
+
...windows.map((window) => {
|
|
401
|
+
const lineKey = getLimitRowKey(window);
|
|
402
|
+
const windowLabel = formatWindowMinutes(window.windowMinutes);
|
|
403
|
+
const usedLabel = `${window.minUsedPercent}%->${window.maxUsedPercent}%`;
|
|
404
|
+
const isSelected = selectedRowKey === lineKey;
|
|
405
|
+
return {
|
|
406
|
+
key: `limit-row:${lineKey}`,
|
|
407
|
+
text: `${pad(window.planType, LIMIT_WINDOW_COLUMNS.plan)} ${pad(windowLabel, LIMIT_WINDOW_COLUMNS.window)} ${pad(usedLabel, LIMIT_WINDOW_COLUMNS.used)} ${pad(formatLocalDateTime(window.startTimeUtcIso), LIMIT_WINDOW_COLUMNS.date)} ${pad(formatLocalDateTime(window.endTimeUtcIso), LIMIT_WINDOW_COLUMNS.date)} ${pad(formatUsd(window.totals.estimatedCredits * CODEX_CREDIT_COST_USD), LIMIT_WINDOW_COLUMNS.value)}`,
|
|
408
|
+
inverse: isSelected,
|
|
409
|
+
color: isSelected ? "cyan" : undefined
|
|
410
|
+
};
|
|
411
|
+
})
|
|
412
|
+
];
|
|
309
413
|
}
|
|
310
414
|
function SelectionDetailsPanel(props) {
|
|
311
415
|
if (props.providerState.status !== "ready") {
|
|
@@ -320,13 +424,13 @@ function SelectionDetailsPanel(props) {
|
|
|
320
424
|
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", paddingX: 1, flexDirection: "column", children: [_jsx(Text, { color: "cyan", children: "Day details" }), _jsxs(Text, { children: ["day: ", formatUtcDay(row.dayKey), " events: ", formatInteger(row.totals.eventCount), " models: ", formatInteger(row.distinctModels.length), " plans: ", formatInteger(row.distinctPlanTypes.length)] }), _jsxs(Text, { children: ["range: ", formatEventRange(row.firstEventUtcIso, row.lastEventUtcIso)] }), _jsxs(Text, { children: ["models: ", row.distinctModels.join(", ") || "none"] }), _jsxs(Text, { children: ["plans: ", row.distinctPlanTypes.join(", ") || "none"] }), _jsx(UsageTotalsDetails, { totals: row.totals })] }));
|
|
321
425
|
}
|
|
322
426
|
if (props.tabId === "usage-by-model" && props.selectedModelRow) {
|
|
323
|
-
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", paddingX: 1, flexDirection: "column", children: [_jsx(Text, { color: "cyan", children: "Model details" }), _jsxs(Text, { children: ["model: ", props.selectedModelRow.modelId, " events: ", formatInteger(props.selectedModelRow.totals.eventCount)] }), _jsx(UsageTotalsDetails, { totals: props.selectedModelRow.totals })] }));
|
|
427
|
+
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", paddingX: 1, flexDirection: "column", children: [_jsx(Text, { color: "cyan", children: "Model details" }), _jsxs(Text, { children: ["model: ", props.selectedModelRow.modelId, " events: ", formatInteger(props.selectedModelRow.totals.eventCount)] }), _jsx(UsageTotalsDetails, { totals: props.selectedModelRow.totals, modelId: props.selectedModelRow.modelId })] }));
|
|
324
428
|
}
|
|
325
429
|
return null;
|
|
326
430
|
}
|
|
327
431
|
function UsageTotalsDetails(props) {
|
|
328
432
|
const { totals } = props;
|
|
329
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(UsageBreakdownLines, { totals: totals }), _jsxs(Text, { children: ["
|
|
433
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(UsageBreakdownLines, { totals: totals }), _jsxs(Text, { children: ["Burned tokens for: ", formatUsageUsd(totals, props.modelId)] }), _jsxs(Text, { children: ["IpO: ", formatInputPerOutput(totals)] })] }));
|
|
330
434
|
}
|
|
331
435
|
function formatInteger(value) {
|
|
332
436
|
return Math.round(value).toLocaleString("en-US");
|
|
@@ -340,14 +444,23 @@ function formatCredits(value) {
|
|
|
340
444
|
maximumFractionDigits: 2
|
|
341
445
|
});
|
|
342
446
|
}
|
|
343
|
-
function formatUsageCredits(totals) {
|
|
447
|
+
function formatUsageCredits(totals, modelId) {
|
|
448
|
+
if (isInternalUsageModel(modelId)) {
|
|
449
|
+
return "N/A";
|
|
450
|
+
}
|
|
344
451
|
return totals.estimatedCreditsStatus === "unavailable" ? "unknown" : formatCredits(totals.estimatedCredits);
|
|
345
452
|
}
|
|
346
|
-
function formatUsageUsd(totals) {
|
|
453
|
+
function formatUsageUsd(totals, modelId) {
|
|
454
|
+
if (isInternalUsageModel(modelId)) {
|
|
455
|
+
return "N/A";
|
|
456
|
+
}
|
|
347
457
|
return totals.estimatedCreditsStatus === "unavailable"
|
|
348
458
|
? "unknown"
|
|
349
459
|
: formatUsd(totals.estimatedCredits * CODEX_CREDIT_COST_USD);
|
|
350
460
|
}
|
|
461
|
+
function isInternalUsageModel(modelId) {
|
|
462
|
+
return modelId === "codex-auto-review" || modelId === "<synthetic>";
|
|
463
|
+
}
|
|
351
464
|
function formatUsd(value) {
|
|
352
465
|
if (value > 0 && value < 0.0001) {
|
|
353
466
|
return "<$0.0001";
|
|
@@ -426,6 +539,82 @@ function formatInputPerOutput(totals) {
|
|
|
426
539
|
}
|
|
427
540
|
return `uncached:cached:output = ${formatInteger(Math.round(totals.tokenBreakdown.nonCachedInputTokens / totals.outputTokens))}:${formatInteger(Math.round(totals.tokenBreakdown.cachedInputTokens / totals.outputTokens))}:1`;
|
|
428
541
|
}
|
|
542
|
+
function useMeasuredElementSize() {
|
|
543
|
+
const ref = useRef(null);
|
|
544
|
+
const [size, setSize] = useState({ width: 0, height: 0 });
|
|
545
|
+
useEffect(() => {
|
|
546
|
+
if (!ref.current) {
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
const nextSize = measureElement(ref.current);
|
|
550
|
+
setSize((current) => current.width === nextSize.width && current.height === nextSize.height ? current : nextSize);
|
|
551
|
+
});
|
|
552
|
+
return {
|
|
553
|
+
ref,
|
|
554
|
+
width: size.width,
|
|
555
|
+
height: size.height
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
function useAutoScrollOffset(selectedIndex, rowCount, viewportSize) {
|
|
559
|
+
const [scrollOffset, setScrollOffset] = useState(0);
|
|
560
|
+
useEffect(() => {
|
|
561
|
+
const maxOffset = Math.max(0, rowCount - Math.max(0, viewportSize));
|
|
562
|
+
setScrollOffset((current) => {
|
|
563
|
+
let next = Math.max(0, Math.min(current, maxOffset));
|
|
564
|
+
if (selectedIndex < 0 || viewportSize <= 0) {
|
|
565
|
+
return next;
|
|
566
|
+
}
|
|
567
|
+
if (selectedIndex < next) {
|
|
568
|
+
next = selectedIndex;
|
|
569
|
+
}
|
|
570
|
+
else if (selectedIndex >= next + viewportSize) {
|
|
571
|
+
next = selectedIndex - viewportSize + 1;
|
|
572
|
+
}
|
|
573
|
+
return Math.max(0, Math.min(next, maxOffset));
|
|
574
|
+
});
|
|
575
|
+
}, [rowCount, selectedIndex, viewportSize]);
|
|
576
|
+
return scrollOffset;
|
|
577
|
+
}
|
|
578
|
+
function resolveScrollableViewportLayout(availableHeight, headerCount, bodyCount, footerCount) {
|
|
579
|
+
const totalHeight = Math.max(1, availableHeight || 1);
|
|
580
|
+
if (bodyCount === 0) {
|
|
581
|
+
const headerVisibleCount = Math.min(headerCount, totalHeight);
|
|
582
|
+
return {
|
|
583
|
+
headerVisibleCount,
|
|
584
|
+
bodyVisibleCount: 0,
|
|
585
|
+
footerVisibleCount: Math.min(footerCount, Math.max(0, totalHeight - headerVisibleCount))
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
let headerVisibleCount = Math.min(headerCount, totalHeight);
|
|
589
|
+
let footerVisibleCount = Math.min(footerCount, Math.max(0, totalHeight - headerVisibleCount - 1));
|
|
590
|
+
let bodyVisibleCount = Math.min(bodyCount, Math.max(0, totalHeight - headerVisibleCount - footerVisibleCount));
|
|
591
|
+
if (bodyVisibleCount === 0 && footerVisibleCount > 0) {
|
|
592
|
+
footerVisibleCount -= 1;
|
|
593
|
+
bodyVisibleCount = Math.min(bodyCount, Math.max(0, totalHeight - headerVisibleCount - footerVisibleCount));
|
|
594
|
+
}
|
|
595
|
+
if (bodyVisibleCount === 0 && headerVisibleCount > 0) {
|
|
596
|
+
headerVisibleCount -= 1;
|
|
597
|
+
bodyVisibleCount = Math.min(bodyCount, Math.max(0, totalHeight - headerVisibleCount - footerVisibleCount));
|
|
598
|
+
}
|
|
599
|
+
if (bodyVisibleCount === 0) {
|
|
600
|
+
return {
|
|
601
|
+
headerVisibleCount: 0,
|
|
602
|
+
bodyVisibleCount: Math.min(bodyCount, totalHeight),
|
|
603
|
+
footerVisibleCount: 0
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
return { headerVisibleCount, bodyVisibleCount, footerVisibleCount };
|
|
607
|
+
}
|
|
608
|
+
function buildScrollbarLines(viewportSize, rowCount, scrollOffset) {
|
|
609
|
+
if (viewportSize <= 0 || rowCount <= viewportSize) {
|
|
610
|
+
return [];
|
|
611
|
+
}
|
|
612
|
+
const thumbSize = Math.max(1, Math.round((viewportSize * viewportSize) / rowCount));
|
|
613
|
+
const maxThumbOffset = Math.max(0, viewportSize - thumbSize);
|
|
614
|
+
const maxScrollOffset = Math.max(1, rowCount - viewportSize);
|
|
615
|
+
const thumbOffset = Math.round((Math.max(0, Math.min(scrollOffset, maxScrollOffset)) / maxScrollOffset) * maxThumbOffset);
|
|
616
|
+
return Array.from({ length: viewportSize }, (_, index) => index >= thumbOffset && index < thumbOffset + thumbSize ? SCROLLBAR_THUMB_GLYPH : SCROLLBAR_TRACK_GLYPH);
|
|
617
|
+
}
|
|
429
618
|
function clampSelectionIndex(value, rowCount) {
|
|
430
619
|
if (rowCount === 0) {
|
|
431
620
|
return -1;
|
|
@@ -472,7 +661,59 @@ function parseStatsOptions(argv) {
|
|
|
472
661
|
verbose: argv.includes("-v") || argv.includes("--verbose")
|
|
473
662
|
};
|
|
474
663
|
}
|
|
664
|
+
function useViewportHeight() {
|
|
665
|
+
const { stdout } = useStdout();
|
|
666
|
+
const [viewportHeight, setViewportHeight] = useState(() => resolveViewportHeight(stdout.rows));
|
|
667
|
+
useEffect(() => {
|
|
668
|
+
const updateViewportHeight = () => {
|
|
669
|
+
setViewportHeight(resolveViewportHeight(stdout.rows));
|
|
670
|
+
};
|
|
671
|
+
updateViewportHeight();
|
|
672
|
+
if (!stdout.isTTY) {
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
stdout.on("resize", updateViewportHeight);
|
|
676
|
+
return () => {
|
|
677
|
+
stdout.off("resize", updateViewportHeight);
|
|
678
|
+
};
|
|
679
|
+
}, [stdout]);
|
|
680
|
+
return viewportHeight;
|
|
681
|
+
}
|
|
682
|
+
function resolveViewportHeight(rows) {
|
|
683
|
+
const terminalRows = typeof rows === "number" && rows > 0 ? rows : 24;
|
|
684
|
+
// Keep Ink below the terminal height so it stays on its incremental redraw path
|
|
685
|
+
// instead of the full-screen print path that can leave the viewport scrolled.
|
|
686
|
+
return Math.max(1, terminalRows - 1);
|
|
687
|
+
}
|
|
475
688
|
export function main(argv = process.argv.slice(2)) {
|
|
476
|
-
|
|
689
|
+
const restoreFullscreen = enterFullscreenMode(process.stdout);
|
|
690
|
+
const exitHandler = () => {
|
|
691
|
+
restoreFullscreen();
|
|
692
|
+
};
|
|
693
|
+
process.once("exit", exitHandler);
|
|
694
|
+
const instance = render(_jsx(App, { statsOptions: parseStatsOptions(argv) }), {
|
|
695
|
+
stdout: process.stdout,
|
|
696
|
+
stdin: process.stdin,
|
|
697
|
+
stderr: process.stderr
|
|
698
|
+
});
|
|
699
|
+
void instance.waitUntilExit().finally(() => {
|
|
700
|
+
process.off("exit", exitHandler);
|
|
701
|
+
instance.cleanup();
|
|
702
|
+
restoreFullscreen();
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
function enterFullscreenMode(stdout) {
|
|
706
|
+
if (!stdout.isTTY) {
|
|
707
|
+
return () => { };
|
|
708
|
+
}
|
|
709
|
+
let restored = false;
|
|
710
|
+
stdout.write(ENTER_FULLSCREEN_MODE);
|
|
711
|
+
return () => {
|
|
712
|
+
if (restored) {
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
restored = true;
|
|
716
|
+
stdout.write(EXIT_FULLSCREEN_MODE);
|
|
717
|
+
};
|
|
477
718
|
}
|
|
478
719
|
main();
|
|
@@ -73,7 +73,7 @@ export class ClaudeUsageProvider extends UsageProviderBase {
|
|
|
73
73
|
.sort((left, right) => right.totals.estimatedCredits - left.totals.estimatedCredits);
|
|
74
74
|
const unknownPricedModels = modelUsage
|
|
75
75
|
.map((row) => row.modelId)
|
|
76
|
-
.filter((modelId) => !resolveRate(modelId));
|
|
76
|
+
.filter((modelId) => !resolveRate(modelId) && !isInternalClaudeModel(modelId));
|
|
77
77
|
if (unknownPricedModels.length > 0) {
|
|
78
78
|
warnings.push(`No credit rate configured for: ${unknownPricedModels.join(", ")}.`);
|
|
79
79
|
}
|
|
@@ -135,6 +135,9 @@ function resolveRate(modelId) {
|
|
|
135
135
|
}
|
|
136
136
|
return undefined;
|
|
137
137
|
}
|
|
138
|
+
function isInternalClaudeModel(modelId) {
|
|
139
|
+
return modelId === "<synthetic>";
|
|
140
|
+
}
|
|
138
141
|
function creditsFor(modelId, usage) {
|
|
139
142
|
const rate = resolveRate(modelId);
|
|
140
143
|
if (!rate) {
|
|
@@ -17,6 +17,7 @@ export class CodexUsageProvider extends UsageProviderBase {
|
|
|
17
17
|
}
|
|
18
18
|
async getStats(_options = {}) {
|
|
19
19
|
const sessionsRoot = path.join(this.root, ".codex", "sessions");
|
|
20
|
+
const knownModels = await readCodexModelMetadata(this.root);
|
|
20
21
|
const byModel = new Map();
|
|
21
22
|
const byDay = createDailyUsageAggregates();
|
|
22
23
|
const windows = createLimitWindowAggregates();
|
|
@@ -30,7 +31,7 @@ export class CodexUsageProvider extends UsageProviderBase {
|
|
|
30
31
|
};
|
|
31
32
|
for await (const file of walkSessionFiles(sessionsRoot)) {
|
|
32
33
|
parseTotals.filesScanned += 1;
|
|
33
|
-
const fileStats = await parseSessionFile(file, byModel, byDay, windows, planTypes);
|
|
34
|
+
const fileStats = await parseSessionFile(file, byModel, byDay, windows, planTypes, knownModels);
|
|
34
35
|
parseTotals.linesRead += fileStats.linesRead;
|
|
35
36
|
parseTotals.tokenEvents += fileStats.tokenEvents;
|
|
36
37
|
parseTotals.malformedLines += fileStats.malformedLines;
|
|
@@ -43,7 +44,7 @@ export class CodexUsageProvider extends UsageProviderBase {
|
|
|
43
44
|
.sort((left, right) => right.totals.estimatedCredits - left.totals.estimatedCredits);
|
|
44
45
|
const unknownPricedModels = modelUsage
|
|
45
46
|
.map((row) => row.modelId)
|
|
46
|
-
.filter((modelId) => !RATE_CARD[modelId]);
|
|
47
|
+
.filter((modelId) => !RATE_CARD[modelId] && !isAssumedZeroRatedCodexModel(modelId, knownModels));
|
|
47
48
|
if (unknownPricedModels.length > 0) {
|
|
48
49
|
warnings.push(`No credit rate configured for: ${unknownPricedModels.join(", ")}.`);
|
|
49
50
|
}
|
|
@@ -74,6 +75,39 @@ export class CodexUsageProvider extends UsageProviderBase {
|
|
|
74
75
|
};
|
|
75
76
|
}
|
|
76
77
|
}
|
|
78
|
+
async function readCodexModelMetadata(root) {
|
|
79
|
+
const modelsCachePath = path.join(root, ".codex", "models_cache.json");
|
|
80
|
+
let fileText;
|
|
81
|
+
try {
|
|
82
|
+
fileText = await fs.promises.readFile(modelsCachePath, "utf8");
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return new Map();
|
|
86
|
+
}
|
|
87
|
+
let payload;
|
|
88
|
+
try {
|
|
89
|
+
payload = JSON.parse(fileText);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return new Map();
|
|
93
|
+
}
|
|
94
|
+
const models = asRecord(payload)?.models;
|
|
95
|
+
if (!Array.isArray(models)) {
|
|
96
|
+
return new Map();
|
|
97
|
+
}
|
|
98
|
+
const metadata = new Map();
|
|
99
|
+
for (const model of models) {
|
|
100
|
+
const record = asRecord(model);
|
|
101
|
+
const slug = typeof record?.slug === "string" ? record.slug : "";
|
|
102
|
+
if (!slug) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
metadata.set(slug, {
|
|
106
|
+
visibility: typeof record?.visibility === "string" ? record.visibility : ""
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return metadata;
|
|
110
|
+
}
|
|
77
111
|
function createEmptyRawUsage() {
|
|
78
112
|
return {
|
|
79
113
|
inputTokens: 0,
|
|
@@ -128,11 +162,14 @@ function rawUsageToTotals(usage) {
|
|
|
128
162
|
}
|
|
129
163
|
};
|
|
130
164
|
}
|
|
131
|
-
function createUsageTotalsForModel(modelId, usage) {
|
|
165
|
+
function createUsageTotalsForModel(modelId, usage, knownModels) {
|
|
132
166
|
const resolvedModelId = modelId || "unknown";
|
|
133
167
|
const deltaTotals = rawUsageToTotals(usage);
|
|
134
168
|
deltaTotals.estimatedCredits = creditsFor(resolvedModelId, usage);
|
|
135
169
|
deltaTotals.eventCount = 1;
|
|
170
|
+
if (!RATE_CARD[resolvedModelId] && !isAssumedZeroRatedCodexModel(resolvedModelId, knownModels)) {
|
|
171
|
+
deltaTotals.estimatedCreditsStatus = "unavailable";
|
|
172
|
+
}
|
|
136
173
|
return deltaTotals;
|
|
137
174
|
}
|
|
138
175
|
function addModelUsage(byModel, modelId, deltaTotals) {
|
|
@@ -141,6 +178,14 @@ function addModelUsage(byModel, modelId, deltaTotals) {
|
|
|
141
178
|
addUsageTotals(totals, deltaTotals);
|
|
142
179
|
byModel.set(resolvedModelId, totals);
|
|
143
180
|
}
|
|
181
|
+
function isHiddenCodexModel(modelId, knownModels) {
|
|
182
|
+
return knownModels.get(modelId)?.visibility === "hide";
|
|
183
|
+
}
|
|
184
|
+
function isAssumedZeroRatedCodexModel(modelId, knownModels) {
|
|
185
|
+
// Hidden internal Codex models do not have a public rate card entry. For dashboard
|
|
186
|
+
// rollups we treat them as zero-rated so they do not turn aggregate totals unknown.
|
|
187
|
+
return isHiddenCodexModel(modelId, knownModels);
|
|
188
|
+
}
|
|
144
189
|
function isSessionFile(filePath) {
|
|
145
190
|
return filePath.endsWith(".jsonl") && filePath.includes(`${path.sep}.codex${path.sep}sessions${path.sep}`);
|
|
146
191
|
}
|
|
@@ -162,7 +207,7 @@ async function* walkSessionFiles(directory) {
|
|
|
162
207
|
}
|
|
163
208
|
}
|
|
164
209
|
}
|
|
165
|
-
async function parseSessionFile(filePath, byModel, byDay, windows, planTypes) {
|
|
210
|
+
async function parseSessionFile(filePath, byModel, byDay, windows, planTypes, knownModels) {
|
|
166
211
|
const stream = fs.createReadStream(filePath, { encoding: "utf8" });
|
|
167
212
|
const lineReader = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
168
213
|
let currentModel = "unknown";
|
|
@@ -203,7 +248,7 @@ async function parseSessionFile(filePath, byModel, byDay, windows, planTypes) {
|
|
|
203
248
|
const usage = lastUsage ? normalizeRawUsage(lastUsage) : previousTotal ? subtractRawUsage(totalUsage, previousTotal) : totalUsage;
|
|
204
249
|
previousTotal = totalUsage;
|
|
205
250
|
const resolvedModelId = currentModel || "unknown";
|
|
206
|
-
const deltaTotals = createUsageTotalsForModel(resolvedModelId, usage);
|
|
251
|
+
const deltaTotals = createUsageTotalsForModel(resolvedModelId, usage, knownModels);
|
|
207
252
|
tokenEvents += 1;
|
|
208
253
|
addModelUsage(byModel, resolvedModelId, deltaTotals);
|
|
209
254
|
const eventTimeMs = Date.parse(String(payloadObject.timestamp ?? ""));
|