llm-usage-metrics 0.10.2 → 0.11.0
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/README.md +4 -2
- package/dist/index.js +987 -240
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -48,12 +48,16 @@ function normalizeSkippedRowReasons(value) {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
// src/domain/normalization.ts
|
|
51
|
+
import { stripVTControlCharacters } from "util";
|
|
51
52
|
var CONTROL_CHARACTER_CLASS = String.raw`[\u0000-\u001F\u007F-\u009F\u061C\u200E\u200F\u2028-\u202E\u2066-\u2069]`;
|
|
52
53
|
var CONTROL_CHARACTERS_PATTERN = new RegExp(CONTROL_CHARACTER_CLASS, "gu");
|
|
53
54
|
var CONTROL_CHARACTER_TEST_PATTERN = new RegExp(CONTROL_CHARACTER_CLASS, "u");
|
|
54
55
|
function stripControlCharacters(value) {
|
|
55
56
|
return value.replace(CONTROL_CHARACTERS_PATTERN, "");
|
|
56
57
|
}
|
|
58
|
+
function toTerminalLine(value) {
|
|
59
|
+
return stripControlCharacters(stripVTControlCharacters(value).replaceAll(/\s+/gu, " ")).trim();
|
|
60
|
+
}
|
|
57
61
|
function hasControlCharacters(value) {
|
|
58
62
|
return CONTROL_CHARACTER_TEST_PATTERN.test(value);
|
|
59
63
|
}
|
|
@@ -764,6 +768,9 @@ CREATE TEMP TABLE IF NOT EXISTS history_departed_files (
|
|
|
764
768
|
file_path TEXT NOT NULL,
|
|
765
769
|
PRIMARY KEY (source, file_path)
|
|
766
770
|
);
|
|
771
|
+
CREATE TEMP TABLE IF NOT EXISTS history_departed_hashes (
|
|
772
|
+
content_hash TEXT PRIMARY KEY
|
|
773
|
+
);
|
|
767
774
|
CREATE TEMP TABLE IF NOT EXISTS history_served_files (
|
|
768
775
|
source TEXT NOT NULL,
|
|
769
776
|
file_path TEXT NOT NULL,
|
|
@@ -773,6 +780,7 @@ CREATE TEMP TABLE IF NOT EXISTS history_served_files (
|
|
|
773
780
|
DELETE FROM history_selected_sources;
|
|
774
781
|
DELETE FROM history_discovered_files;
|
|
775
782
|
DELETE FROM history_departed_files;
|
|
783
|
+
DELETE FROM history_departed_hashes;
|
|
776
784
|
DELETE FROM history_served_files;
|
|
777
785
|
`);
|
|
778
786
|
}
|
|
@@ -849,6 +857,36 @@ function addHashCount(target, hash, count) {
|
|
|
849
857
|
target.set(hash, (target.get(hash) ?? 0) + count);
|
|
850
858
|
}
|
|
851
859
|
function readLiveHashCounts(store) {
|
|
860
|
+
store.database.prepare(
|
|
861
|
+
[
|
|
862
|
+
"INSERT OR IGNORE INTO history_departed_hashes (content_hash)",
|
|
863
|
+
"SELECT events.content_hash",
|
|
864
|
+
"FROM history_departed_files AS departed",
|
|
865
|
+
"CROSS JOIN events",
|
|
866
|
+
"WHERE events.source = departed.source",
|
|
867
|
+
" AND events.file_path = departed.file_path",
|
|
868
|
+
" AND events.content_hash IS NOT NULL"
|
|
869
|
+
].join("\n")
|
|
870
|
+
).run();
|
|
871
|
+
store.database.prepare(
|
|
872
|
+
[
|
|
873
|
+
"INSERT OR IGNORE INTO history_departed_hashes (content_hash)",
|
|
874
|
+
"SELECT events.content_hash",
|
|
875
|
+
"FROM history_discovered_files AS discovered",
|
|
876
|
+
"CROSS JOIN events",
|
|
877
|
+
"WHERE events.source = discovered.source",
|
|
878
|
+
" AND events.file_path = discovered.file_path",
|
|
879
|
+
" AND discovered.counted = 1",
|
|
880
|
+
" AND events.content_hash IS NOT NULL",
|
|
881
|
+
" AND EXISTS (",
|
|
882
|
+
" SELECT 1 FROM history_departed_files AS departed",
|
|
883
|
+
" CROSS JOIN events AS departed_events",
|
|
884
|
+
" WHERE departed_events.source = departed.source",
|
|
885
|
+
" AND departed_events.file_path = departed.file_path",
|
|
886
|
+
" AND departed_events.content_hash IS NULL",
|
|
887
|
+
" )"
|
|
888
|
+
].join("\n")
|
|
889
|
+
).run();
|
|
852
890
|
const rows = store.database.prepare(
|
|
853
891
|
[
|
|
854
892
|
"SELECT content_hash,",
|
|
@@ -857,15 +895,16 @@ function readLiveHashCounts(store) {
|
|
|
857
895
|
"FROM (",
|
|
858
896
|
" SELECT events.content_hash AS content_hash,",
|
|
859
897
|
" selected.repeats_events AS repeats_events, COUNT(*) AS file_count",
|
|
860
|
-
" FROM
|
|
861
|
-
" JOIN history_selected_sources AS selected",
|
|
862
|
-
" ON discovered.source = selected.source",
|
|
898
|
+
" FROM history_departed_hashes AS departed",
|
|
863
899
|
" CROSS JOIN events",
|
|
864
|
-
"
|
|
865
|
-
"
|
|
866
|
-
" AND
|
|
867
|
-
"
|
|
868
|
-
"
|
|
900
|
+
" JOIN history_discovered_files AS discovered",
|
|
901
|
+
" ON discovered.source = events.source",
|
|
902
|
+
" AND discovered.file_path = events.file_path",
|
|
903
|
+
" JOIN history_selected_sources AS selected",
|
|
904
|
+
" ON selected.source = events.source",
|
|
905
|
+
" WHERE events.content_hash = departed.content_hash",
|
|
906
|
+
" AND discovered.counted = 1",
|
|
907
|
+
" GROUP BY events.source, events.file_path, events.content_hash",
|
|
869
908
|
")",
|
|
870
909
|
"GROUP BY content_hash"
|
|
871
910
|
].join("\n")
|
|
@@ -1056,10 +1095,10 @@ function classifyAgainstLiveFiles(store, input) {
|
|
|
1056
1095
|
if (selectedSources.size === 0) {
|
|
1057
1096
|
return { classifiedFiles: [], liveHashCounts: /* @__PURE__ */ new Map() };
|
|
1058
1097
|
}
|
|
1059
|
-
const liveHashCounts = readLiveHashCounts(store);
|
|
1060
|
-
const servedHashCounts = new Map(liveHashCounts);
|
|
1061
1098
|
const departedFiles = readDepartedFiles(store);
|
|
1062
1099
|
const departedHashMultisets = readDepartedHashMultisets(store);
|
|
1100
|
+
const liveHashCounts = readLiveHashCounts(store);
|
|
1101
|
+
const servedHashCounts = new Map(liveHashCounts);
|
|
1063
1102
|
const classifiedFiles = [];
|
|
1064
1103
|
for (const departedFile of departedFiles) {
|
|
1065
1104
|
const fileHashCounts = departedHashMultisets.get(
|
|
@@ -1147,8 +1186,22 @@ CREATE TABLE IF NOT EXISTS events (
|
|
|
1147
1186
|
);
|
|
1148
1187
|
CREATE INDEX IF NOT EXISTS events_file ON events(source, file_path, event_index);
|
|
1149
1188
|
CREATE INDEX IF NOT EXISTS events_content_hash ON events(content_hash);
|
|
1189
|
+
${createOverrideOnlyFilesSql()}
|
|
1150
1190
|
`;
|
|
1151
1191
|
}
|
|
1192
|
+
function createOverrideOnlyFilesSql() {
|
|
1193
|
+
return `
|
|
1194
|
+
CREATE TABLE IF NOT EXISTS override_only_files (
|
|
1195
|
+
source TEXT NOT NULL,
|
|
1196
|
+
file_path TEXT NOT NULL,
|
|
1197
|
+
PRIMARY KEY (source, file_path)
|
|
1198
|
+
);`;
|
|
1199
|
+
}
|
|
1200
|
+
function ensureOverrideOnlyFilesTable(database) {
|
|
1201
|
+
if (!listUserTableNames(database).includes("override_only_files")) {
|
|
1202
|
+
database.exec(createOverrideOnlyFilesSql());
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1152
1205
|
function listUserTableNames(database) {
|
|
1153
1206
|
return database.prepare(
|
|
1154
1207
|
[
|
|
@@ -1267,6 +1320,10 @@ function assertSupportedSchemaVersion(database) {
|
|
|
1267
1320
|
}
|
|
1268
1321
|
}
|
|
1269
1322
|
function initializeSchema(database) {
|
|
1323
|
+
createOrMigrateSchema(database);
|
|
1324
|
+
ensureOverrideOnlyFilesTable(database);
|
|
1325
|
+
}
|
|
1326
|
+
function createOrMigrateSchema(database) {
|
|
1270
1327
|
if (listUserTableNames(database).length === 0) {
|
|
1271
1328
|
const createdSchema = runTransaction(database, () => {
|
|
1272
1329
|
if (listUserTableNames(database).length > 0) {
|
|
@@ -1602,6 +1659,21 @@ function readMetaValues(database) {
|
|
|
1602
1659
|
}
|
|
1603
1660
|
return meta;
|
|
1604
1661
|
}
|
|
1662
|
+
var WIDE_WINDOW_ROW_SHARE = 0.15;
|
|
1663
|
+
function isWideWindow(database, fromTimestamp, toTimestamp) {
|
|
1664
|
+
const rowCount = toNonNegativeInteger(
|
|
1665
|
+
database.prepare("SELECT count(*) AS count FROM events").get()?.count
|
|
1666
|
+
);
|
|
1667
|
+
const threshold = Math.ceil((rowCount ?? 0) * WIDE_WINDOW_ROW_SHARE);
|
|
1668
|
+
const row = database.prepare(
|
|
1669
|
+
[
|
|
1670
|
+
"SELECT count(*) AS count FROM (",
|
|
1671
|
+
" SELECT 1 FROM events WHERE timestamp >= ? AND timestamp < ? LIMIT ?",
|
|
1672
|
+
")"
|
|
1673
|
+
].join("\n")
|
|
1674
|
+
).get(fromTimestamp, toTimestamp, threshold);
|
|
1675
|
+
return (toNonNegativeInteger(row?.count) ?? 0) >= threshold;
|
|
1676
|
+
}
|
|
1605
1677
|
async function readEventStoreEvents(filePath, window = {}, loadSqliteModule = loadEventStoreSqliteModule) {
|
|
1606
1678
|
const sqliteModule = await loadSqliteModule();
|
|
1607
1679
|
if (!isEventStoreSqliteModule(sqliteModule)) {
|
|
@@ -1615,21 +1687,20 @@ async function readEventStoreEvents(filePath, window = {}, loadSqliteModule = lo
|
|
|
1615
1687
|
assertSupportedSchemaVersion(database);
|
|
1616
1688
|
const meta = readMetaValues(database);
|
|
1617
1689
|
const cacheWrite1hColumn = meta.get("schemaVersion") === EVENT_STORE_SCHEMA_VERSION ? "cache_write_1h_tokens" : "0 AS cache_write_1h_tokens";
|
|
1690
|
+
const fromTimestamp = window.fromTimestamp ?? "";
|
|
1691
|
+
const toTimestamp = window.toTimestamp ?? "\uFFFF";
|
|
1618
1692
|
const statement = database.prepare(
|
|
1619
1693
|
[
|
|
1620
1694
|
"SELECT source, session_id, timestamp, model, provider, repo_root,",
|
|
1621
1695
|
" input_tokens, output_tokens, reasoning_tokens, cache_read_tokens,",
|
|
1622
1696
|
` cache_write_tokens, ${cacheWrite1hColumn}, total_tokens, cost_usd, cost_mode`,
|
|
1623
|
-
"FROM events",
|
|
1697
|
+
isWideWindow(database, fromTimestamp, toTimestamp) ? "FROM events INDEXED BY events_file" : "FROM events",
|
|
1624
1698
|
"WHERE timestamp >= ? AND timestamp < ?",
|
|
1625
1699
|
"ORDER BY source, file_path, event_index"
|
|
1626
1700
|
].join("\n")
|
|
1627
1701
|
);
|
|
1628
1702
|
statement.setReturnArrays(true);
|
|
1629
|
-
const rows = statement.all(
|
|
1630
|
-
window.fromTimestamp ?? "",
|
|
1631
|
-
window.toTimestamp ?? "\uFFFF"
|
|
1632
|
-
);
|
|
1703
|
+
const rows = statement.all(fromTimestamp, toTimestamp);
|
|
1633
1704
|
const events = [];
|
|
1634
1705
|
for (const row of rows) {
|
|
1635
1706
|
const event = normalizeStoredEventTuple(row);
|
|
@@ -1776,11 +1847,15 @@ function deleteStoredFiles(store, files) {
|
|
|
1776
1847
|
const deleteFile = store.database.prepare(
|
|
1777
1848
|
"DELETE FROM files WHERE source = ? AND file_path = ?"
|
|
1778
1849
|
);
|
|
1850
|
+
const deleteOverrideOnlyFile = store.database.prepare(
|
|
1851
|
+
"DELETE FROM override_only_files WHERE source = ? AND file_path = ?"
|
|
1852
|
+
);
|
|
1779
1853
|
for (const file of normalizedFiles) {
|
|
1780
1854
|
result.deletedEventCount += countMatchingRows(store, "events", file.source, file.filePath);
|
|
1781
1855
|
result.deletedFileCount += countMatchingRows(store, "files", file.source, file.filePath);
|
|
1782
1856
|
deleteEvents.run(file.source, file.filePath);
|
|
1783
1857
|
deleteFile.run(file.source, file.filePath);
|
|
1858
|
+
deleteOverrideOnlyFile.run(file.source, file.filePath);
|
|
1784
1859
|
}
|
|
1785
1860
|
});
|
|
1786
1861
|
return result;
|
|
@@ -1799,7 +1874,8 @@ function replaceFilesEvents(store, inputs) {
|
|
|
1799
1874
|
skippedRows: toNonNegativeInteger(input.skippedRows) ?? 0,
|
|
1800
1875
|
skippedRowReasons: stringifySkippedRowReasons(input.skippedRowReasons),
|
|
1801
1876
|
ingestedAt: Math.max(0, Math.trunc(input.now)),
|
|
1802
|
-
events: input.events.map((event) => createUsageEvent(event))
|
|
1877
|
+
events: input.events.map((event) => createUsageEvent(event)),
|
|
1878
|
+
overrideOnly: input.overrideOnly === true
|
|
1803
1879
|
}));
|
|
1804
1880
|
const deleteFileEvents = store.statements.deleteFileEvents ??= store.database.prepare(
|
|
1805
1881
|
"DELETE FROM events WHERE source = ? AND file_path = ?"
|
|
@@ -1826,10 +1902,19 @@ function replaceFilesEvents(store, inputs) {
|
|
|
1826
1902
|
" ingested_at = excluded.ingested_at"
|
|
1827
1903
|
].join("\n")
|
|
1828
1904
|
);
|
|
1905
|
+
const markOverrideOnlyFile = store.statements.markOverrideOnlyFile ??= store.database.prepare(
|
|
1906
|
+
[
|
|
1907
|
+
"INSERT OR IGNORE INTO override_only_files (source, file_path)",
|
|
1908
|
+
"SELECT ?, ? WHERE NOT EXISTS (SELECT 1 FROM files WHERE source = ? AND file_path = ?)"
|
|
1909
|
+
].join("\n")
|
|
1910
|
+
);
|
|
1829
1911
|
runTransaction(store.database, () => {
|
|
1830
1912
|
for (const write of writes) {
|
|
1831
1913
|
const { source, filePath } = write;
|
|
1832
1914
|
deleteFileEvents.run(source, filePath);
|
|
1915
|
+
if (write.overrideOnly) {
|
|
1916
|
+
markOverrideOnlyFile.run(source, filePath, source, filePath);
|
|
1917
|
+
}
|
|
1833
1918
|
write.events.forEach((event, eventIndex) => {
|
|
1834
1919
|
insertEvent.run(
|
|
1835
1920
|
source,
|
|
@@ -1863,6 +1948,41 @@ function replaceFilesEvents(store, inputs) {
|
|
|
1863
1948
|
}
|
|
1864
1949
|
});
|
|
1865
1950
|
}
|
|
1951
|
+
function readOverrideOnlyFiles(store) {
|
|
1952
|
+
const rows = store.database.prepare("SELECT source, file_path FROM override_only_files ORDER BY source, file_path").all();
|
|
1953
|
+
const files = [];
|
|
1954
|
+
for (const row of rows) {
|
|
1955
|
+
const source = toText(row.source);
|
|
1956
|
+
const filePath = toText(row.file_path);
|
|
1957
|
+
if (source && filePath) {
|
|
1958
|
+
files.push({ source, filePath });
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
return files;
|
|
1962
|
+
}
|
|
1963
|
+
function claimOverrideOnlyFiles(store, files) {
|
|
1964
|
+
const overrideOnlyKeys = new Set(
|
|
1965
|
+
readOverrideOnlyFiles(store).map((file) => `${file.source}\0${file.filePath}`)
|
|
1966
|
+
);
|
|
1967
|
+
if (overrideOnlyKeys.size === 0) {
|
|
1968
|
+
return;
|
|
1969
|
+
}
|
|
1970
|
+
const claimedFiles = files.map((file) => ({
|
|
1971
|
+
source: normalizeStoreSource(file.source),
|
|
1972
|
+
filePath: normalizeStoreFilePath(file.filePath)
|
|
1973
|
+
})).filter((file) => overrideOnlyKeys.has(`${file.source}\0${file.filePath}`));
|
|
1974
|
+
if (claimedFiles.length === 0) {
|
|
1975
|
+
return;
|
|
1976
|
+
}
|
|
1977
|
+
runTransaction(store.database, () => {
|
|
1978
|
+
const deleteOverrideOnlyFile = store.database.prepare(
|
|
1979
|
+
"DELETE FROM override_only_files WHERE source = ? AND file_path = ?"
|
|
1980
|
+
);
|
|
1981
|
+
for (const file of claimedFiles) {
|
|
1982
|
+
deleteOverrideOnlyFile.run(file.source, file.filePath);
|
|
1983
|
+
}
|
|
1984
|
+
});
|
|
1985
|
+
}
|
|
1866
1986
|
function closeEventStore(store) {
|
|
1867
1987
|
store.database.close();
|
|
1868
1988
|
}
|
|
@@ -3820,7 +3940,7 @@ function exitQuietlyOnBrokenPipe(stream, exit = () => process.exit()) {
|
|
|
3820
3940
|
}
|
|
3821
3941
|
|
|
3822
3942
|
// src/cli/create-cli.ts
|
|
3823
|
-
import { Command as Command6 } from "commander";
|
|
3943
|
+
import { Command as Command6, Help } from "commander";
|
|
3824
3944
|
|
|
3825
3945
|
// src/utils/suggest-closest.ts
|
|
3826
3946
|
function editDistance(left, right4) {
|
|
@@ -3863,7 +3983,7 @@ function suggestClosest(input, candidates) {
|
|
|
3863
3983
|
}
|
|
3864
3984
|
|
|
3865
3985
|
// src/cli/report-definitions/report-definitions.ts
|
|
3866
|
-
import { Command } from "commander";
|
|
3986
|
+
import { Argument, Command, Option as Option2 } from "commander";
|
|
3867
3987
|
|
|
3868
3988
|
// src/utils/time-buckets.ts
|
|
3869
3989
|
var DAY_MS3 = 24 * 60 * 60 * 1e3;
|
|
@@ -3888,7 +4008,7 @@ function getDateTimeFormatter(timezone) {
|
|
|
3888
4008
|
dateTimeFormatterCache.set(timezone, formatter);
|
|
3889
4009
|
return formatter;
|
|
3890
4010
|
}
|
|
3891
|
-
var MAX_MEMOIZED_TIMESTAMPS =
|
|
4011
|
+
var MAX_MEMOIZED_TIMESTAMPS = 1e6;
|
|
3892
4012
|
var localDatePartsByTimestamp = /* @__PURE__ */ new Map();
|
|
3893
4013
|
function extractLocalDateParts(timestampIso, timezone) {
|
|
3894
4014
|
let partsByTimestamp = localDatePartsByTimestamp.get(timezone);
|
|
@@ -3951,7 +4071,8 @@ function coversTimestamp(window, timestampMs) {
|
|
|
3951
4071
|
function createLocalDateWindow(dayIndex, timestampMs, offsetMs, timezone, localDateTime) {
|
|
3952
4072
|
const startMs = dayIndex * DAY_MS3 - offsetMs;
|
|
3953
4073
|
const endMs = startMs + DAY_MS3;
|
|
3954
|
-
const
|
|
4074
|
+
const localDate = toLocalDateParts(localDateTime);
|
|
4075
|
+
const parts = { ...localDate, dateKey: formatLocalDateParts(localDate) };
|
|
3955
4076
|
if (hasStableOffsetWindow(startMs, endMs, offsetMs, timezone)) {
|
|
3956
4077
|
return { startMs, endMs, parts };
|
|
3957
4078
|
}
|
|
@@ -4056,7 +4177,7 @@ function getIsoWeekParts(localDate) {
|
|
|
4056
4177
|
function getPeriodKey(timestampIso, granularity, timezone) {
|
|
4057
4178
|
const localDate = extractLocalDateParts(timestampIso, timezone);
|
|
4058
4179
|
if (granularity === "daily") {
|
|
4059
|
-
return
|
|
4180
|
+
return localDate.dateKey;
|
|
4060
4181
|
}
|
|
4061
4182
|
if (granularity === "monthly") {
|
|
4062
4183
|
return `${localDate.year}-${String(localDate.month).padStart(2, "0")}`;
|
|
@@ -4075,15 +4196,13 @@ function getIsoDayOfWeekFromDateKey(localDateKey) {
|
|
|
4075
4196
|
return toIsoDayOfWeek(createUtcDate(parseLocalDateKey(localDateKey)));
|
|
4076
4197
|
}
|
|
4077
4198
|
function getCurrentLocalDateKey(timezone, now = /* @__PURE__ */ new Date()) {
|
|
4078
|
-
return
|
|
4199
|
+
return extractLocalDateParts(now.toISOString(), timezone).dateKey;
|
|
4079
4200
|
}
|
|
4080
4201
|
function shiftLocalDateKey(localDateKey, days) {
|
|
4081
|
-
return
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
)
|
|
4086
|
-
);
|
|
4202
|
+
return extractLocalDateParts(
|
|
4203
|
+
addDays(createUtcDate(parseLocalDateKey(localDateKey)), days).toISOString(),
|
|
4204
|
+
"UTC"
|
|
4205
|
+
).dateKey;
|
|
4087
4206
|
}
|
|
4088
4207
|
function getLocalDateKeyRange(from, to) {
|
|
4089
4208
|
if (from > to) {
|
|
@@ -5765,7 +5884,10 @@ function comparePendingEvents(left, right4) {
|
|
|
5765
5884
|
var ClaudeSourceAdapter = class {
|
|
5766
5885
|
id = "claude";
|
|
5767
5886
|
parserVersion = 4;
|
|
5768
|
-
capabilities = {
|
|
5887
|
+
capabilities = {
|
|
5888
|
+
eventsPrecedeFileMtime: true,
|
|
5889
|
+
sessionsCopyEvents: true
|
|
5890
|
+
};
|
|
5769
5891
|
rootDirs;
|
|
5770
5892
|
requireDir;
|
|
5771
5893
|
// Sibling forks share a parent; read its message keys once per parent version.
|
|
@@ -9487,7 +9609,7 @@ function createDefaultAdapters(options) {
|
|
|
9487
9609
|
// src/cli/build-usage-data-inputs.ts
|
|
9488
9610
|
function validateDateInput(value, flagName) {
|
|
9489
9611
|
if (!/^\d{4}-\d{2}-\d{2}$/u.test(value)) {
|
|
9490
|
-
throw new Error(`${flagName}
|
|
9612
|
+
throw new Error(`${flagName} "${value}" is not a date; use YYYY-MM-DD`);
|
|
9491
9613
|
}
|
|
9492
9614
|
const parsed = /* @__PURE__ */ new Date(`${value}T00:00:00.000Z`);
|
|
9493
9615
|
if (Number.isNaN(parsed.getTime()) || parsed.toISOString().slice(0, 10) !== value) {
|
|
@@ -9781,6 +9903,101 @@ function formatEnvVarOverrides(overrides) {
|
|
|
9781
9903
|
return lines;
|
|
9782
9904
|
}
|
|
9783
9905
|
|
|
9906
|
+
// src/render/format-model-list.ts
|
|
9907
|
+
function limitNamedModels(models, limit) {
|
|
9908
|
+
if (models.length <= limit) {
|
|
9909
|
+
return [...models];
|
|
9910
|
+
}
|
|
9911
|
+
return [...models.slice(0, limit), `+${models.length - limit} more`];
|
|
9912
|
+
}
|
|
9913
|
+
|
|
9914
|
+
// src/cli/count-copied-session-events-once.ts
|
|
9915
|
+
function getCopyIdentity(event) {
|
|
9916
|
+
return [
|
|
9917
|
+
event.timestamp,
|
|
9918
|
+
event.provider ?? "",
|
|
9919
|
+
event.model ?? "",
|
|
9920
|
+
event.inputTokens,
|
|
9921
|
+
event.outputTokens,
|
|
9922
|
+
event.reasoningTokens,
|
|
9923
|
+
event.cacheReadTokens,
|
|
9924
|
+
event.cacheWriteTokens,
|
|
9925
|
+
event.cacheWrite1hTokens,
|
|
9926
|
+
event.totalTokens
|
|
9927
|
+
].join("\0");
|
|
9928
|
+
}
|
|
9929
|
+
function isCopyOf(left, right4) {
|
|
9930
|
+
return left.totalTokens === right4.totalTokens && left.timestamp === right4.timestamp && left.model === right4.model && left.provider === right4.provider && left.inputTokens === right4.inputTokens && left.outputTokens === right4.outputTokens && left.reasoningTokens === right4.reasoningTokens && left.cacheReadTokens === right4.cacheReadTokens && left.cacheWriteTokens === right4.cacheWriteTokens && left.cacheWrite1hTokens === right4.cacheWrite1hTokens;
|
|
9931
|
+
}
|
|
9932
|
+
function findEventsOtherSessionsHold(events) {
|
|
9933
|
+
const firstByTimestamp = /* @__PURE__ */ new Map();
|
|
9934
|
+
const sameTimestampGroups = /* @__PURE__ */ new Map();
|
|
9935
|
+
for (const event of events) {
|
|
9936
|
+
const first = firstByTimestamp.get(event.timestamp);
|
|
9937
|
+
if (first === void 0) {
|
|
9938
|
+
firstByTimestamp.set(event.timestamp, event);
|
|
9939
|
+
continue;
|
|
9940
|
+
}
|
|
9941
|
+
const group = sameTimestampGroups.get(event.timestamp);
|
|
9942
|
+
if (group) {
|
|
9943
|
+
group.push(event);
|
|
9944
|
+
} else {
|
|
9945
|
+
sameTimestampGroups.set(event.timestamp, [first, event]);
|
|
9946
|
+
}
|
|
9947
|
+
}
|
|
9948
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
9949
|
+
for (const group of sameTimestampGroups.values()) {
|
|
9950
|
+
for (let left = 0; left < group.length; left += 1) {
|
|
9951
|
+
for (let right4 = left + 1; right4 < group.length; right4 += 1) {
|
|
9952
|
+
if (group[left].sessionId !== group[right4].sessionId && isCopyOf(group[left], group[right4])) {
|
|
9953
|
+
candidates.add(group[left]);
|
|
9954
|
+
candidates.add(group[right4]);
|
|
9955
|
+
}
|
|
9956
|
+
}
|
|
9957
|
+
}
|
|
9958
|
+
}
|
|
9959
|
+
return candidates;
|
|
9960
|
+
}
|
|
9961
|
+
function countCopiedSessionEventsOnce(events) {
|
|
9962
|
+
const candidates = findEventsOtherSessionsHold(events);
|
|
9963
|
+
if (candidates.size === 0) {
|
|
9964
|
+
return [...events];
|
|
9965
|
+
}
|
|
9966
|
+
const sessionStarts = /* @__PURE__ */ new Map();
|
|
9967
|
+
const candidatesBySession = /* @__PURE__ */ new Map();
|
|
9968
|
+
for (const event of events) {
|
|
9969
|
+
const start = sessionStarts.get(event.sessionId);
|
|
9970
|
+
if (start === void 0 || compareByCodePoint(event.timestamp, start) < 0) {
|
|
9971
|
+
sessionStarts.set(event.sessionId, event.timestamp);
|
|
9972
|
+
}
|
|
9973
|
+
if (candidates.has(event)) {
|
|
9974
|
+
const sessionCandidates = candidatesBySession.get(event.sessionId) ?? [];
|
|
9975
|
+
sessionCandidates.push(event);
|
|
9976
|
+
candidatesBySession.set(event.sessionId, sessionCandidates);
|
|
9977
|
+
}
|
|
9978
|
+
}
|
|
9979
|
+
const sessionsOldestFirst = [...candidatesBySession.keys()].sort(
|
|
9980
|
+
(left, right4) => compareByCodePoint(sessionStarts.get(left) ?? "", sessionStarts.get(right4) ?? "") || compareByCodePoint(left, right4)
|
|
9981
|
+
);
|
|
9982
|
+
const countedIdentities = /* @__PURE__ */ new Map();
|
|
9983
|
+
const copies = /* @__PURE__ */ new Set();
|
|
9984
|
+
for (const sessionId of sessionsOldestFirst) {
|
|
9985
|
+
const sessionCounts = /* @__PURE__ */ new Map();
|
|
9986
|
+
for (const event of candidatesBySession.get(sessionId) ?? []) {
|
|
9987
|
+
const identity = getCopyIdentity(event);
|
|
9988
|
+
const seen = (sessionCounts.get(identity) ?? 0) + 1;
|
|
9989
|
+
sessionCounts.set(identity, seen);
|
|
9990
|
+
if (seen <= (countedIdentities.get(identity) ?? 0)) {
|
|
9991
|
+
copies.add(event);
|
|
9992
|
+
}
|
|
9993
|
+
}
|
|
9994
|
+
for (const [identity, count] of sessionCounts) {
|
|
9995
|
+
countedIdentities.set(identity, Math.max(count, countedIdentities.get(identity) ?? 0));
|
|
9996
|
+
}
|
|
9997
|
+
}
|
|
9998
|
+
return events.filter((event) => !copies.has(event));
|
|
9999
|
+
}
|
|
10000
|
+
|
|
9784
10001
|
// src/cli/history-live-files.ts
|
|
9785
10002
|
import { stat as stat4 } from "fs/promises";
|
|
9786
10003
|
var MAX_CONCURRENT_STATS = 32;
|
|
@@ -9882,12 +10099,33 @@ function writeParsedFilesToEventStore(context, files) {
|
|
|
9882
10099
|
const now = context.now();
|
|
9883
10100
|
context.replaceFilesEvents(
|
|
9884
10101
|
context.store,
|
|
9885
|
-
files.map((file) => ({
|
|
10102
|
+
files.map((file) => ({
|
|
10103
|
+
...file,
|
|
10104
|
+
now,
|
|
10105
|
+
overrideOnly: context.overrideSourceIds.has(file.source.toLowerCase())
|
|
10106
|
+
}))
|
|
9886
10107
|
);
|
|
9887
10108
|
} catch (error) {
|
|
9888
10109
|
recordEventStoreFailure(context.failureState, error);
|
|
9889
10110
|
}
|
|
9890
10111
|
}
|
|
10112
|
+
function claimFilesInUsualLocations(context, files) {
|
|
10113
|
+
if (context.failureState.disabled) {
|
|
10114
|
+
return void 0;
|
|
10115
|
+
}
|
|
10116
|
+
const usualLocationFiles = files.filter(
|
|
10117
|
+
(file) => !context.overrideSourceIds.has(file.source.toLowerCase())
|
|
10118
|
+
);
|
|
10119
|
+
if (usualLocationFiles.length === 0) {
|
|
10120
|
+
return void 0;
|
|
10121
|
+
}
|
|
10122
|
+
try {
|
|
10123
|
+
context.claimOverrideOnlyFiles(context.store, usualLocationFiles);
|
|
10124
|
+
return void 0;
|
|
10125
|
+
} catch (error) {
|
|
10126
|
+
return `Event store could not record files found in their usual location: ${getErrorReason(error)}`;
|
|
10127
|
+
}
|
|
10128
|
+
}
|
|
9891
10129
|
|
|
9892
10130
|
// src/cli/parse/parse-budget.ts
|
|
9893
10131
|
function createParseBudget(maxParallelFileParsing) {
|
|
@@ -10588,14 +10826,17 @@ async function parseSelectedAdapters(adaptersToParse, maxParallelFileParsing, op
|
|
|
10588
10826
|
getFileEntry: options.eventStoreDeps?.getFileEntry ?? getFileEntry,
|
|
10589
10827
|
readFileEvents: options.eventStoreDeps?.readFileEvents ?? readFileEvents,
|
|
10590
10828
|
replaceFilesEvents: options.eventStoreDeps?.replaceFilesEvents ?? replaceFilesEvents,
|
|
10829
|
+
claimOverrideOnlyFiles: options.eventStoreDeps?.claimOverrideOnlyFiles ?? claimOverrideOnlyFiles,
|
|
10591
10830
|
now: options.now ?? Date.now,
|
|
10592
|
-
failureState: eventStoreFailureState
|
|
10831
|
+
failureState: eventStoreFailureState,
|
|
10832
|
+
overrideSourceIds: options.overrideSourceIds ?? /* @__PURE__ */ new Set()
|
|
10593
10833
|
};
|
|
10594
10834
|
} catch (error) {
|
|
10595
10835
|
recordEventStoreFailure(eventStoreFailureState, error);
|
|
10596
10836
|
}
|
|
10597
10837
|
}
|
|
10598
10838
|
let parseResults;
|
|
10839
|
+
let claimWarning;
|
|
10599
10840
|
try {
|
|
10600
10841
|
parseResults = await Promise.allSettled(
|
|
10601
10842
|
adaptersToParse.map(
|
|
@@ -10621,6 +10862,17 @@ async function parseSelectedAdapters(adaptersToParse, maxParallelFileParsing, op
|
|
|
10621
10862
|
)
|
|
10622
10863
|
)
|
|
10623
10864
|
);
|
|
10865
|
+
if (eventStoreContext) {
|
|
10866
|
+
claimWarning = claimFilesInUsualLocations(
|
|
10867
|
+
eventStoreContext,
|
|
10868
|
+
parseResults.flatMap(
|
|
10869
|
+
(parseResult) => parseResult.status === "fulfilled" ? parseResult.value.filePaths.map((filePath) => ({
|
|
10870
|
+
source: parseResult.value.source,
|
|
10871
|
+
filePath
|
|
10872
|
+
})) : []
|
|
10873
|
+
)
|
|
10874
|
+
);
|
|
10875
|
+
}
|
|
10624
10876
|
} finally {
|
|
10625
10877
|
if (eventStoreContext && ownsEventStore) {
|
|
10626
10878
|
try {
|
|
@@ -10655,7 +10907,9 @@ async function parseSelectedAdapters(adaptersToParse, maxParallelFileParsing, op
|
|
|
10655
10907
|
failedFiles,
|
|
10656
10908
|
eventStoreAvailable: Boolean(options.eventStore?.enabled) && !eventStoreFailureState.disabled,
|
|
10657
10909
|
sourceFailures,
|
|
10658
|
-
warnings:
|
|
10910
|
+
warnings: [eventStoreFailureState.warning, claimWarning].filter(
|
|
10911
|
+
(warning) => warning !== void 0
|
|
10912
|
+
)
|
|
10659
10913
|
};
|
|
10660
10914
|
}
|
|
10661
10915
|
function throwOnExplicitSourceFailures(sourceFailures, explicitSourceIds) {
|
|
@@ -10707,16 +10961,46 @@ function filterByModelRules(events, modelFilter) {
|
|
|
10707
10961
|
const modelFilterRules = resolveModelFilterRules(events, modelFilter);
|
|
10708
10962
|
return events.filter((event) => matchesModel(event.model, modelFilterRules));
|
|
10709
10963
|
}
|
|
10964
|
+
function shiftUtcDate(date, days) {
|
|
10965
|
+
const shifted = /* @__PURE__ */ new Date(`${date}T00:00:00.000Z`);
|
|
10966
|
+
shifted.setUTCDate(shifted.getUTCDate() + days);
|
|
10967
|
+
return shifted.toISOString();
|
|
10968
|
+
}
|
|
10969
|
+
function createDateWindowPrefilter(since, until) {
|
|
10970
|
+
const outsideBefore = since ? shiftUtcDate(since, -1) : void 0;
|
|
10971
|
+
const insideFrom = since ? shiftUtcDate(since, 1) : void 0;
|
|
10972
|
+
const insideBefore = until ? shiftUtcDate(until, 0) : void 0;
|
|
10973
|
+
const outsideFrom = until ? shiftUtcDate(until, 2) : void 0;
|
|
10974
|
+
if ([outsideBefore, insideFrom, insideBefore, outsideFrom].some(
|
|
10975
|
+
(bound) => bound !== void 0 && bound.length !== 24
|
|
10976
|
+
)) {
|
|
10977
|
+
return () => "near-edge";
|
|
10978
|
+
}
|
|
10979
|
+
return (timestamp) => {
|
|
10980
|
+
if (timestamp.length !== 24) {
|
|
10981
|
+
return "near-edge";
|
|
10982
|
+
}
|
|
10983
|
+
if (outsideBefore && timestamp < outsideBefore || outsideFrom && timestamp >= outsideFrom) {
|
|
10984
|
+
return "outside";
|
|
10985
|
+
}
|
|
10986
|
+
return (!insideFrom || timestamp >= insideFrom) && (!insideBefore || timestamp < insideBefore) ? "inside" : "near-edge";
|
|
10987
|
+
};
|
|
10988
|
+
}
|
|
10710
10989
|
function collectProviderAndDateFilteredEvents(eventGroups, options) {
|
|
10711
10990
|
const filteredEvents = [];
|
|
10712
10991
|
const hasDateRange = Boolean(options.since ?? options.until);
|
|
10992
|
+
const prefilterDate = createDateWindowPrefilter(options.since, options.until);
|
|
10713
10993
|
const dateByTimestamp = /* @__PURE__ */ new Map();
|
|
10714
10994
|
for (const events of eventGroups) {
|
|
10715
10995
|
for (const event of events) {
|
|
10716
10996
|
if (!matchesCanonicalProviderFilter(event.provider, options.providerFilter)) {
|
|
10717
10997
|
continue;
|
|
10718
10998
|
}
|
|
10719
|
-
|
|
10999
|
+
const datePosition = hasDateRange ? prefilterDate(event.timestamp) : "inside";
|
|
11000
|
+
if (datePosition === "outside") {
|
|
11001
|
+
continue;
|
|
11002
|
+
}
|
|
11003
|
+
if (datePosition === "near-edge") {
|
|
10720
11004
|
let eventDate = dateByTimestamp.get(event.timestamp);
|
|
10721
11005
|
if (!eventDate) {
|
|
10722
11006
|
eventDate = getPeriodKey(event.timestamp, "daily", options.timezone);
|
|
@@ -11077,6 +11361,9 @@ function applyMachineExport(cache, bundle, now) {
|
|
|
11077
11361
|
function recordMachineSyncFailure(cache, reason, now) {
|
|
11078
11362
|
writeEventStoreMeta(cache, { attemptedAt: String(now), lastError: reason });
|
|
11079
11363
|
}
|
|
11364
|
+
function toStoredError(value) {
|
|
11365
|
+
return value === void 0 ? void 0 : toTerminalLine(value);
|
|
11366
|
+
}
|
|
11080
11367
|
function toEpochMs(value) {
|
|
11081
11368
|
const parsed = value === void 0 ? Number.NaN : Number(value);
|
|
11082
11369
|
return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : void 0;
|
|
@@ -11087,7 +11374,7 @@ function toSyncState(readMeta) {
|
|
|
11087
11374
|
cliVersion: readMeta("cliVersion"),
|
|
11088
11375
|
syncedAt: toEpochMs(readMeta("syncedAt")),
|
|
11089
11376
|
attemptedAt: toEpochMs(readMeta("attemptedAt")),
|
|
11090
|
-
lastError: readMeta("lastError")
|
|
11377
|
+
lastError: toStoredError(readMeta("lastError"))
|
|
11091
11378
|
};
|
|
11092
11379
|
}
|
|
11093
11380
|
function readMachineSyncState(cache) {
|
|
@@ -11268,7 +11555,7 @@ async function loadMachineUsage(params) {
|
|
|
11268
11555
|
// src/machines/machine-ssh.ts
|
|
11269
11556
|
import { spawn } from "child_process";
|
|
11270
11557
|
import { createInterface } from "readline";
|
|
11271
|
-
import { stripVTControlCharacters } from "util";
|
|
11558
|
+
import { stripVTControlCharacters as stripVTControlCharacters2 } from "util";
|
|
11272
11559
|
var DEFAULT_MACHINE_COMMAND = "llm-usage";
|
|
11273
11560
|
var MACHINE_EXPORT_TIMEOUT_MARK = "timed out";
|
|
11274
11561
|
var STDERR_TAIL_BYTES = 4096;
|
|
@@ -11361,7 +11648,7 @@ async function detectRemoteCommand(target, options = {}) {
|
|
|
11361
11648
|
}
|
|
11362
11649
|
function readStderrTail(chunks) {
|
|
11363
11650
|
const text2 = Buffer.concat(chunks).toString("utf8").slice(-STDERR_TAIL_BYTES);
|
|
11364
|
-
return
|
|
11651
|
+
return toTerminalLine(text2);
|
|
11365
11652
|
}
|
|
11366
11653
|
function describeExportFailure(machine, exitCode, stderr) {
|
|
11367
11654
|
const command = machine.command ?? DEFAULT_MACHINE_COMMAND;
|
|
@@ -11455,11 +11742,15 @@ async function fetchMachineExport(machine, knownFiles, options = {}) {
|
|
|
11455
11742
|
cause: readError
|
|
11456
11743
|
});
|
|
11457
11744
|
}
|
|
11458
|
-
const remoteWarnings = Buffer.concat(stderrChunks).toString("utf8").slice(-STDERR_TAIL_BYTES).split("\n").map((line) => stripControlCharacters(
|
|
11745
|
+
const remoteWarnings = Buffer.concat(stderrChunks).toString("utf8").slice(-STDERR_TAIL_BYTES).split("\n").map((line) => stripControlCharacters(stripVTControlCharacters2(line)).trim()).filter((line) => line.length > 0);
|
|
11459
11746
|
return { bundle, remoteWarnings };
|
|
11460
11747
|
}
|
|
11461
11748
|
|
|
11462
11749
|
// src/render/render-machines.ts
|
|
11750
|
+
var MAX_REASON_LENGTH = 300;
|
|
11751
|
+
function formatReason(error) {
|
|
11752
|
+
return error.length > MAX_REASON_LENGTH ? `${error.slice(0, MAX_REASON_LENGTH)}\u2026` : error;
|
|
11753
|
+
}
|
|
11463
11754
|
var integerFormat = new Intl.NumberFormat("en-US");
|
|
11464
11755
|
function formatAge(ageMs) {
|
|
11465
11756
|
const minutes = Math.floor(Math.max(0, ageMs) / 6e4);
|
|
@@ -11494,7 +11785,7 @@ function renderSyncOutcome(outcome, now) {
|
|
|
11494
11785
|
];
|
|
11495
11786
|
}
|
|
11496
11787
|
const cacheNote = outcome.state.syncedAt === void 0 ? "nothing cached yet" : `reports keep its usage from ${formatAge(now - outcome.state.syncedAt)}`;
|
|
11497
|
-
return [`\u2717 ${outcome.name}: ${outcome.error} (${cacheNote})`];
|
|
11788
|
+
return [`\u2717 ${outcome.name}: ${formatReason(outcome.error)} (${cacheNote})`];
|
|
11498
11789
|
}
|
|
11499
11790
|
function describeMachineStatus(entry, now) {
|
|
11500
11791
|
const { state } = entry;
|
|
@@ -11502,11 +11793,11 @@ function describeMachineStatus(entry, now) {
|
|
|
11502
11793
|
return "disabled (cached usage stays in reports)";
|
|
11503
11794
|
}
|
|
11504
11795
|
if (state?.syncedAt === void 0) {
|
|
11505
|
-
return state?.lastError ? `never synced: ${state.lastError}` : "never synced";
|
|
11796
|
+
return state?.lastError ? `never synced: ${formatReason(state.lastError)}` : "never synced";
|
|
11506
11797
|
}
|
|
11507
11798
|
const synced = `synced ${formatAge(now - state.syncedAt)}, ${formatCachedTotals(entry.fileCount, entry.eventCount)}`;
|
|
11508
11799
|
if (state.lastError && (state.attemptedAt ?? 0) >= state.syncedAt) {
|
|
11509
|
-
return `${synced}; last attempt failed ${formatAge(now - (state.attemptedAt ?? 0))}: ${state.lastError}`;
|
|
11800
|
+
return `${synced}; last attempt failed ${formatAge(now - (state.attemptedAt ?? 0))}: ${formatReason(state.lastError)}`;
|
|
11510
11801
|
}
|
|
11511
11802
|
return synced;
|
|
11512
11803
|
}
|
|
@@ -11526,12 +11817,9 @@ function describeIncludedMachine(machine, now, localVersion) {
|
|
|
11526
11817
|
return state?.syncedAt === void 0 ? `${machine.name} (disabled, nothing cached)` : `${machine.name} (disabled, usage from ${formatAge(now - state.syncedAt)})`;
|
|
11527
11818
|
}
|
|
11528
11819
|
if (state?.syncedAt === void 0) {
|
|
11529
|
-
return `${machine.name} (never synced; run llm-usage sync ${machine.name})`;
|
|
11820
|
+
return state?.lastError === void 0 ? `${machine.name} (never synced; run llm-usage sync ${machine.name})` : `${machine.name} (never synced)`;
|
|
11530
11821
|
}
|
|
11531
11822
|
const details = [`synced ${formatAge(now - state.syncedAt)}`];
|
|
11532
|
-
if (state.lastError !== void 0 && (state.attemptedAt ?? 0) >= state.syncedAt) {
|
|
11533
|
-
details.push("last sync failed");
|
|
11534
|
-
}
|
|
11535
11823
|
if (state.cliVersion && state.cliVersion !== localVersion) {
|
|
11536
11824
|
details.push(
|
|
11537
11825
|
`llm-usage-metrics ${state.cliVersion} there; shared sessions may count twice until both run the same version`
|
|
@@ -11552,16 +11840,35 @@ function formatRefreshFailure(outcome, now) {
|
|
|
11552
11840
|
const { syncedAt } = outcome.state;
|
|
11553
11841
|
if (syncedAt === void 0) {
|
|
11554
11842
|
return {
|
|
11555
|
-
text: `Could not sync ${outcome.name} (${outcome.error}); it has no usage cached yet.`,
|
|
11843
|
+
text: `Could not sync ${outcome.name} (${formatReason(outcome.error)}); it has no usage cached yet.`,
|
|
11556
11844
|
stale: true
|
|
11557
11845
|
};
|
|
11558
11846
|
}
|
|
11559
11847
|
const hint = outcome.error.endsWith(`(${MACHINE_EXPORT_TIMEOUT_MARK})`) ? ` Run llm-usage sync ${outcome.name} to wait for it.` : "";
|
|
11560
11848
|
return {
|
|
11561
|
-
text: `Could not sync ${outcome.name} (${outcome.error}); using its usage from ${formatAge(now - syncedAt)}.${hint}`,
|
|
11849
|
+
text: `Could not sync ${outcome.name} (${formatReason(outcome.error)}); using its usage from ${formatAge(now - syncedAt)}.${hint}`,
|
|
11562
11850
|
stale: now - syncedAt >= STALE_CACHE_MS
|
|
11563
11851
|
};
|
|
11564
11852
|
}
|
|
11853
|
+
function formatStoredSyncFailure(machine, now) {
|
|
11854
|
+
const { state } = machine;
|
|
11855
|
+
if (!machine.enabled || state?.lastError === void 0) {
|
|
11856
|
+
return void 0;
|
|
11857
|
+
}
|
|
11858
|
+
if (state.syncedAt === void 0) {
|
|
11859
|
+
return {
|
|
11860
|
+
text: `${machine.name}: never synced (${formatReason(state.lastError)}). Run llm-usage sync ${machine.name} to retry.`,
|
|
11861
|
+
stale: true
|
|
11862
|
+
};
|
|
11863
|
+
}
|
|
11864
|
+
if ((state.attemptedAt ?? 0) < state.syncedAt) {
|
|
11865
|
+
return void 0;
|
|
11866
|
+
}
|
|
11867
|
+
return {
|
|
11868
|
+
text: `${machine.name}: last sync failed (${formatReason(state.lastError)}); using its usage from ${formatAge(now - state.syncedAt)}. Run llm-usage sync ${machine.name} to retry.`,
|
|
11869
|
+
stale: now - state.syncedAt >= STALE_CACHE_MS
|
|
11870
|
+
};
|
|
11871
|
+
}
|
|
11565
11872
|
|
|
11566
11873
|
// src/machines/sync-machine.ts
|
|
11567
11874
|
async function syncMachine(name, machine, options = {}) {
|
|
@@ -11592,7 +11899,7 @@ async function syncMachine(name, machine, options = {}) {
|
|
|
11592
11899
|
const result = applyMachineExport(cache, bundle, now());
|
|
11593
11900
|
return { name, ok: true, result, remoteWarnings, state: readMachineSyncState(cache) };
|
|
11594
11901
|
} catch (error) {
|
|
11595
|
-
const reason = getErrorReason(error);
|
|
11902
|
+
const reason = toTerminalLine(getErrorReason(error));
|
|
11596
11903
|
let state = {};
|
|
11597
11904
|
try {
|
|
11598
11905
|
if (!options.signal?.aborted) {
|
|
@@ -31437,6 +31744,11 @@ async function resolveAndApplyPricingToEvents(events, options, runtimeConfig = g
|
|
|
31437
31744
|
}
|
|
31438
31745
|
|
|
31439
31746
|
// src/sources/source-adapter.ts
|
|
31747
|
+
function getSessionCopyingSourceIds(adapters) {
|
|
31748
|
+
return new Set(
|
|
31749
|
+
adapters.filter((adapter) => adapter.capabilities?.sessionsCopyEvents).map((adapter) => adapter.id)
|
|
31750
|
+
);
|
|
31751
|
+
}
|
|
31440
31752
|
function getRepeatingSourceIds(adapters) {
|
|
31441
31753
|
return adapters.filter((adapter) => adapter.capabilities?.eventsRepeatAcrossFiles).map((adapter) => adapter.id);
|
|
31442
31754
|
}
|
|
@@ -31510,6 +31822,23 @@ function findUnmatchedFilterWarnings(input) {
|
|
|
31510
31822
|
...findUnmatchedModelWarnings(eventsInRange, input.modelFilter, scope)
|
|
31511
31823
|
];
|
|
31512
31824
|
}
|
|
31825
|
+
function hasValue(value) {
|
|
31826
|
+
return Array.isArray(value) ? value.length > 0 : Boolean(value);
|
|
31827
|
+
}
|
|
31828
|
+
function hasCliFilters(options) {
|
|
31829
|
+
return [
|
|
31830
|
+
options.source,
|
|
31831
|
+
options.provider,
|
|
31832
|
+
options.model,
|
|
31833
|
+
options.machine,
|
|
31834
|
+
options.since,
|
|
31835
|
+
options.until,
|
|
31836
|
+
options.last,
|
|
31837
|
+
options.vsSince,
|
|
31838
|
+
options.vsUntil,
|
|
31839
|
+
options.id
|
|
31840
|
+
].some(hasValue);
|
|
31841
|
+
}
|
|
31513
31842
|
|
|
31514
31843
|
// src/cli/runtime-profile.ts
|
|
31515
31844
|
var RUNTIME_PROFILE_ENV_VAR = "LLM_USAGE_PROFILE_RUNTIME";
|
|
@@ -31863,6 +32192,7 @@ async function buildUsageEventDataset(options, deps = {}) {
|
|
|
31863
32192
|
return parseSelectedAdapters(adaptersToParse, parsingRuntimeConfig.maxParallelFileParsing, {
|
|
31864
32193
|
eventStore: parseEventStoreRuntimeConfig,
|
|
31865
32194
|
openedStore: openedEventStore,
|
|
32195
|
+
overrideSourceIds: cliDirectorySourceIds,
|
|
31866
32196
|
since: configuredOptions.since,
|
|
31867
32197
|
parseWorkers: {
|
|
31868
32198
|
workerCount: parsingRuntimeConfig.parseWorkers,
|
|
@@ -31875,7 +32205,12 @@ async function buildUsageEventDataset(options, deps = {}) {
|
|
|
31875
32205
|
const parseWarnings = eventStoreOpenWarning ? [eventStoreOpenWarning, ...warnings] : warnings;
|
|
31876
32206
|
throwOnExplicitSourceFailures(sourceFailures, normalizedInputs.explicitSourceIds);
|
|
31877
32207
|
let parseResultsForFiltering = successfulParseResults;
|
|
31878
|
-
const ledger = eventStoreAvailable ? {
|
|
32208
|
+
const ledger = eventStoreAvailable ? {
|
|
32209
|
+
path: eventStoreRuntimeConfig.path,
|
|
32210
|
+
parsedFiles: [...parsedFiles],
|
|
32211
|
+
historyFiles: [],
|
|
32212
|
+
copiedEventCount: 0
|
|
32213
|
+
} : void 0;
|
|
31879
32214
|
const historyWarnings = [];
|
|
31880
32215
|
const historyNotes = [];
|
|
31881
32216
|
const historyStore = openedEventStore;
|
|
@@ -31900,7 +32235,14 @@ async function buildUsageEventDataset(options, deps = {}) {
|
|
|
31900
32235
|
discoveredFiles: discoveredFiles.filter(
|
|
31901
32236
|
(file) => !failedFileKeys.has(`${file.source}\0${file.filePath}`)
|
|
31902
32237
|
),
|
|
31903
|
-
|
|
32238
|
+
// Files only ever found under a custom directory never departed from
|
|
32239
|
+
// their source's usual location, so a run there leaves them out.
|
|
32240
|
+
presentFiles: [
|
|
32241
|
+
...failedFiles,
|
|
32242
|
+
...readOverrideOnlyFiles(historyStore).filter(
|
|
32243
|
+
(file) => !cliDirectorySourceIds.has(file.source)
|
|
32244
|
+
)
|
|
32245
|
+
],
|
|
31904
32246
|
repeatingSources: getRepeatingSourceIds(adaptersToParse)
|
|
31905
32247
|
},
|
|
31906
32248
|
{ unverifiable: "treat-as-departed" }
|
|
@@ -31961,6 +32303,13 @@ async function buildUsageEventDataset(options, deps = {}) {
|
|
|
31961
32303
|
);
|
|
31962
32304
|
parseResultsForFiltering = appendMachineEvents(parseResultsForFiltering, machineUsage.events);
|
|
31963
32305
|
machineWarnings.push(...machineUsage.warnings);
|
|
32306
|
+
const refreshedNames = new Set(refreshOutcomes.map((outcome) => outcome.name));
|
|
32307
|
+
for (const machine of machineUsage.machines) {
|
|
32308
|
+
const failure = refreshedNames.has(machine.name) ? void 0 : formatStoredSyncFailure(machine, nowMs());
|
|
32309
|
+
if (failure) {
|
|
32310
|
+
(failure.stale ? machineWarnings : machineNotes).push(failure.text);
|
|
32311
|
+
}
|
|
32312
|
+
}
|
|
31964
32313
|
machineNotes.push(
|
|
31965
32314
|
formatMachinesNote(
|
|
31966
32315
|
machineUsage.machines,
|
|
@@ -31969,6 +32318,21 @@ async function buildUsageEventDataset(options, deps = {}) {
|
|
|
31969
32318
|
)
|
|
31970
32319
|
);
|
|
31971
32320
|
}
|
|
32321
|
+
const sessionCopyingSources = getSessionCopyingSourceIds(adaptersToParse);
|
|
32322
|
+
parseResultsForFiltering = measureRuntimeProfileStageSync(
|
|
32323
|
+
runtimeProfile,
|
|
32324
|
+
"usage.dataset.count_copies",
|
|
32325
|
+
() => parseResultsForFiltering.map((result) => {
|
|
32326
|
+
if (!sessionCopyingSources.has(result.source)) {
|
|
32327
|
+
return result;
|
|
32328
|
+
}
|
|
32329
|
+
const events = countCopiedSessionEventsOnce(result.events);
|
|
32330
|
+
if (ledger) {
|
|
32331
|
+
ledger.copiedEventCount += result.events.length - events.length;
|
|
32332
|
+
}
|
|
32333
|
+
return { ...result, events };
|
|
32334
|
+
})
|
|
32335
|
+
);
|
|
31972
32336
|
const filteredEvents = measureRuntimeProfileStageSync(
|
|
31973
32337
|
runtimeProfile,
|
|
31974
32338
|
"usage.dataset.filter_events",
|
|
@@ -32042,6 +32406,22 @@ async function applyPricingToUsageEventDataset(dataset, deps = {}, pricingLoadMo
|
|
|
32042
32406
|
)
|
|
32043
32407
|
);
|
|
32044
32408
|
}
|
|
32409
|
+
var MAX_NAMED_UNPRICED_MODELS = 5;
|
|
32410
|
+
function describeUnpricedEvents(events) {
|
|
32411
|
+
const unpricedEvents = events.filter((event) => event.costUsd === void 0);
|
|
32412
|
+
if (unpricedEvents.length === 0) {
|
|
32413
|
+
return void 0;
|
|
32414
|
+
}
|
|
32415
|
+
const models = [...new Set(unpricedEvents.map((event) => event.model ?? "unknown"))].sort();
|
|
32416
|
+
const eventCount = new Intl.NumberFormat("en-US").format(unpricedEvents.length);
|
|
32417
|
+
const eventLabel = unpricedEvents.length === 1 ? "event" : "events";
|
|
32418
|
+
const modelList = limitNamedModels(models, MAX_NAMED_UNPRICED_MODELS).join(", ");
|
|
32419
|
+
return `~ marks costs that leave out ${eventCount} ${eventLabel} with no known price (models: ${modelList})`;
|
|
32420
|
+
}
|
|
32421
|
+
function withUnpricedEventsNote(notes, events) {
|
|
32422
|
+
const note = describeUnpricedEvents(events);
|
|
32423
|
+
return note ? [...notes, note] : [...notes];
|
|
32424
|
+
}
|
|
32045
32425
|
|
|
32046
32426
|
// src/cli/build-usage-data.ts
|
|
32047
32427
|
var REPORT_PERIOD_NAMES = {
|
|
@@ -32054,14 +32434,14 @@ var DEFAULT_REPORT_WINDOWS = {
|
|
|
32054
32434
|
weekly: { periods: 8, period: "week" }
|
|
32055
32435
|
};
|
|
32056
32436
|
var MAX_LAST_PERIODS = 1e4;
|
|
32057
|
-
function parseLastOption(last) {
|
|
32437
|
+
function parseLastOption(last, flag = "--last") {
|
|
32058
32438
|
if (last === void 0) {
|
|
32059
32439
|
return void 0;
|
|
32060
32440
|
}
|
|
32061
32441
|
const normalized = last.trim();
|
|
32062
32442
|
const periods = Number.parseInt(normalized, 10);
|
|
32063
32443
|
if (!/^[1-9]\d*$/u.test(normalized) || periods > MAX_LAST_PERIODS) {
|
|
32064
|
-
throw new Error(
|
|
32444
|
+
throw new Error(`${flag} must be a whole number from 1 to ${String(MAX_LAST_PERIODS)}`);
|
|
32065
32445
|
}
|
|
32066
32446
|
return periods;
|
|
32067
32447
|
}
|
|
@@ -32148,7 +32528,7 @@ async function buildUsageData(granularity, commandOptions, commandDeps = {}) {
|
|
|
32148
32528
|
pricingOrigin,
|
|
32149
32529
|
pricingWarning,
|
|
32150
32530
|
warnings: dataset.warnings,
|
|
32151
|
-
notes: dataset.notes,
|
|
32531
|
+
notes: options.cost === false ? dataset.notes : withUnpricedEventsNote(dataset.notes, pricedEvents),
|
|
32152
32532
|
activeEnvOverrides: dataset.readEnvVarOverrides(),
|
|
32153
32533
|
activeConfig: dataset.activeConfig,
|
|
32154
32534
|
timezone: dataset.normalizedInputs.timezone,
|
|
@@ -32415,6 +32795,10 @@ function padLine(content, width) {
|
|
|
32415
32795
|
function renderReportHeader(options) {
|
|
32416
32796
|
const { title, useColor = true } = options;
|
|
32417
32797
|
const boxWidth = getBoxWidth(title);
|
|
32798
|
+
const terminalWidth = options.terminalWidth ?? resolveTtyColumns(process.stdout);
|
|
32799
|
+
if (terminalWidth !== void 0 && boxWidth > terminalWidth) {
|
|
32800
|
+
return useColor ? pc2.bold(pc2.white(title)) : title;
|
|
32801
|
+
}
|
|
32418
32802
|
const lines = [];
|
|
32419
32803
|
const topBorder = drawBoxLine(boxWidth, "\u250C", "\u2500", "\u2510");
|
|
32420
32804
|
lines.push(useColor ? pc2.gray(topBorder) : topBorder);
|
|
@@ -32424,6 +32808,9 @@ function renderReportHeader(options) {
|
|
|
32424
32808
|
lines.push(useColor ? pc2.gray(bottomBorder) : bottomBorder);
|
|
32425
32809
|
return lines.join("\n");
|
|
32426
32810
|
}
|
|
32811
|
+
function formatNoUsageMessage(filtered) {
|
|
32812
|
+
return filtered ? "No usage data found for the selected filters." : "No usage data found.";
|
|
32813
|
+
}
|
|
32427
32814
|
|
|
32428
32815
|
// src/render/terminal-table.ts
|
|
32429
32816
|
import pc4 from "picocolors";
|
|
@@ -32901,6 +33288,75 @@ function computeColumnWidths(measureRows, options) {
|
|
|
32901
33288
|
}
|
|
32902
33289
|
return widths;
|
|
32903
33290
|
}
|
|
33291
|
+
function measureBoxedTableWidth(widths) {
|
|
33292
|
+
return widths.reduce((total, width) => total + width + 3, 1);
|
|
33293
|
+
}
|
|
33294
|
+
function measureLinesWidth(cell) {
|
|
33295
|
+
return splitCellLines(cell).reduce((maxWidth, line) => Math.max(maxWidth, visibleWidth(line)), 0);
|
|
33296
|
+
}
|
|
33297
|
+
function joinCellLines(cell) {
|
|
33298
|
+
return splitCellLines(cell).join(" ");
|
|
33299
|
+
}
|
|
33300
|
+
function wrapAtNameBreaks(line, width) {
|
|
33301
|
+
const lines = [];
|
|
33302
|
+
for (const piece of line.split(/(?<=[\s/-])/u)) {
|
|
33303
|
+
const lastLine = lines.at(-1);
|
|
33304
|
+
if (lastLine !== void 0 && visibleWidth((lastLine + piece).trimEnd()) <= width) {
|
|
33305
|
+
lines[lines.length - 1] = lastLine + piece;
|
|
33306
|
+
} else {
|
|
33307
|
+
lines.push(piece);
|
|
33308
|
+
}
|
|
33309
|
+
}
|
|
33310
|
+
return lines.flatMap(
|
|
33311
|
+
(wrappedLine) => splitCellLines(wrapTableColumn([[wrappedLine.trimEnd()]], { columnIndex: 0, width })[0][0])
|
|
33312
|
+
);
|
|
33313
|
+
}
|
|
33314
|
+
function renderStackedRows(options) {
|
|
33315
|
+
const valueColumnIndexes = options.headerCells.map((_, columnIndex) => columnIndex).filter((columnIndex) => columnIndex >= options.titleColumnCount);
|
|
33316
|
+
const labelWidth = valueColumnIndexes.reduce(
|
|
33317
|
+
(maxWidth, columnIndex) => Math.max(maxWidth, visibleWidth(joinCellLines(options.measureHeaderCells[columnIndex]))),
|
|
33318
|
+
0
|
|
33319
|
+
);
|
|
33320
|
+
const continuationIndent = " ".repeat(2 + labelWidth + 2);
|
|
33321
|
+
const availableValueWidth = Math.max(1, options.terminalWidth - continuationIndent.length);
|
|
33322
|
+
const valueWidth = Math.min(
|
|
33323
|
+
availableValueWidth,
|
|
33324
|
+
options.measureBodyRows.reduce(
|
|
33325
|
+
(maxWidth, row) => valueColumnIndexes.reduce(
|
|
33326
|
+
(rowMax, columnIndex) => Math.max(rowMax, measureLinesWidth(row[columnIndex])),
|
|
33327
|
+
maxWidth
|
|
33328
|
+
),
|
|
33329
|
+
0
|
|
33330
|
+
)
|
|
33331
|
+
);
|
|
33332
|
+
const records = options.bodyRows.map((row, rowIndex) => {
|
|
33333
|
+
const title = row.slice(0, options.titleColumnCount).map(joinCellLines).filter((cell) => cell.trim().length > 0).join(" \xB7 ");
|
|
33334
|
+
const valueLines = valueColumnIndexes.flatMap((columnIndex) => {
|
|
33335
|
+
const lines = measureLinesWidth(row[columnIndex]) > availableValueWidth ? splitCellLines(options.measureBodyRows[rowIndex][columnIndex]).flatMap(
|
|
33336
|
+
(line) => wrapAtNameBreaks(line, availableValueWidth)
|
|
33337
|
+
) : splitCellLines(row[columnIndex]);
|
|
33338
|
+
if (lines.every((line) => line.trim().length === 0)) {
|
|
33339
|
+
return [];
|
|
33340
|
+
}
|
|
33341
|
+
const alignment2 = getColumnAlignment(
|
|
33342
|
+
columnIndex,
|
|
33343
|
+
options.multilineColumnIndex,
|
|
33344
|
+
options.leftAlignedColumnIndexes
|
|
33345
|
+
);
|
|
33346
|
+
const label = alignCellLine(
|
|
33347
|
+
joinCellLines(options.headerCells[columnIndex]),
|
|
33348
|
+
labelWidth,
|
|
33349
|
+
"left"
|
|
33350
|
+
);
|
|
33351
|
+
return lines.map((line, lineIndex) => {
|
|
33352
|
+
const value = alignCellLine(line, valueWidth, alignment2).trimEnd();
|
|
33353
|
+
return lineIndex === 0 ? ` ${label} ${value}` : `${continuationIndent}${value}`;
|
|
33354
|
+
});
|
|
33355
|
+
});
|
|
33356
|
+
return [title, ...valueLines].join("\n");
|
|
33357
|
+
});
|
|
33358
|
+
return records.join("\n\n");
|
|
33359
|
+
}
|
|
32904
33360
|
function renderUnicodeTable(options) {
|
|
32905
33361
|
const bodyColumnCount = getMaxRowColumnCount(options.bodyRows, options.headerCells.length);
|
|
32906
33362
|
const measureColumnCount = getMaxRowColumnCount(
|
|
@@ -32932,6 +33388,20 @@ function renderUnicodeTable(options) {
|
|
|
32932
33388
|
multilineColumnIndex: options.multilineColumnIndex,
|
|
32933
33389
|
multilineColumnWidth: options.multilineColumnWidth
|
|
32934
33390
|
});
|
|
33391
|
+
const normalizedBodyRows = renderableRows.map((row) => row.bodyRow);
|
|
33392
|
+
const normalizedRowMetas = renderableRows.map((row) => row.rowMeta);
|
|
33393
|
+
if (options.terminalWidth !== void 0 && measureBoxedTableWidth(widths) > options.terminalWidth) {
|
|
33394
|
+
return renderStackedRows({
|
|
33395
|
+
headerCells: normalizedHeaderRow,
|
|
33396
|
+
measureHeaderCells: normalizedMeasureHeaderRow,
|
|
33397
|
+
bodyRows: normalizedBodyRows,
|
|
33398
|
+
measureBodyRows: renderableRows.map((row) => row.measureBodyRow),
|
|
33399
|
+
titleColumnCount: options.stackedTitleColumnCount ?? options.multilineColumnIndex + 1,
|
|
33400
|
+
terminalWidth: options.terminalWidth,
|
|
33401
|
+
multilineColumnIndex: options.multilineColumnIndex,
|
|
33402
|
+
leftAlignedColumnIndexes: options.leftAlignedColumnIndexes
|
|
33403
|
+
});
|
|
33404
|
+
}
|
|
32935
33405
|
const outputLines = [];
|
|
32936
33406
|
outputLines.push(
|
|
32937
33407
|
buildBorderLine(widths, {
|
|
@@ -32955,8 +33425,6 @@ function renderUnicodeTable(options) {
|
|
|
32955
33425
|
right: "\u2524"
|
|
32956
33426
|
})
|
|
32957
33427
|
);
|
|
32958
|
-
const normalizedBodyRows = renderableRows.map((row) => row.bodyRow);
|
|
32959
|
-
const normalizedRowMetas = renderableRows.map((row) => row.rowMeta);
|
|
32960
33428
|
normalizedBodyRows.forEach((row, rowIndex) => {
|
|
32961
33429
|
outputLines.push(
|
|
32962
33430
|
...toRenderableRowLines(row, {
|
|
@@ -33254,7 +33722,8 @@ function renderPreparedTable(rows, prepared, options) {
|
|
|
33254
33722
|
rowMetas: toRowMetas(rows),
|
|
33255
33723
|
layout: options.tableLayout === "per_model_columns" ? "top_aligned" : "compact",
|
|
33256
33724
|
multilineColumnIndex: prepared.showsModels ? columns.indexOf(modelsColumnIndex2) : columns.indexOf(sourceColumnIndex2),
|
|
33257
|
-
multilineColumnWidth: prepared.showsModels ? options.modelsColumnWidth : void 0
|
|
33725
|
+
multilineColumnWidth: prepared.showsModels ? options.modelsColumnWidth : void 0,
|
|
33726
|
+
terminalWidth: options.stackedWidth
|
|
33258
33727
|
});
|
|
33259
33728
|
return { output, truncatedModelNames };
|
|
33260
33729
|
}
|
|
@@ -33273,12 +33742,20 @@ function renderTerminalTableWithFit(rows, options = {}) {
|
|
|
33273
33742
|
prepared = prepareFitStep(rows, tableLayout, step);
|
|
33274
33743
|
}
|
|
33275
33744
|
}
|
|
33745
|
+
const stacked = tableLayout === "compact" && terminalWidth !== void 0 && !fitsTerminal(prepared, terminalWidth);
|
|
33746
|
+
if (stacked) {
|
|
33747
|
+
const listStep = steps.find((step) => step.hiddenColumns.includes("models"));
|
|
33748
|
+
if (listStep) {
|
|
33749
|
+
prepared = prepareFitStep(rows, tableLayout, listStep);
|
|
33750
|
+
}
|
|
33751
|
+
}
|
|
33276
33752
|
const modelsColumnWidth = resolveModelsColumnWidth(prepared, tableLayout, terminalWidth);
|
|
33277
33753
|
const { output, truncatedModelNames } = renderPreparedTable(rows, prepared, {
|
|
33278
33754
|
tableLayout,
|
|
33279
33755
|
useColor,
|
|
33280
33756
|
modelsColumnWidth,
|
|
33281
|
-
packModels: terminalWidth !== void 0
|
|
33757
|
+
packModels: terminalWidth !== void 0,
|
|
33758
|
+
stackedWidth: stacked ? terminalWidth : void 0
|
|
33282
33759
|
});
|
|
33283
33760
|
if (hasExplicitTerminalWidth && terminalWidth !== void 0) {
|
|
33284
33761
|
const renderedTableWidth = measureTableWidth(output);
|
|
@@ -33293,7 +33770,8 @@ function renderTerminalTableWithFit(rows, options = {}) {
|
|
|
33293
33770
|
fit: {
|
|
33294
33771
|
tokenFormat: prepared.step.tokenFormat,
|
|
33295
33772
|
hiddenColumns: [...prepared.step.hiddenColumns],
|
|
33296
|
-
truncatedModelNames
|
|
33773
|
+
truncatedModelNames,
|
|
33774
|
+
stacked
|
|
33297
33775
|
}
|
|
33298
33776
|
};
|
|
33299
33777
|
}
|
|
@@ -33408,9 +33886,13 @@ function renderTerminalTable(options) {
|
|
|
33408
33886
|
rowMetas: createRowMetas(options.bodyRows.length),
|
|
33409
33887
|
layout: "compact",
|
|
33410
33888
|
multilineColumnIndex: 0,
|
|
33411
|
-
multilineColumnWidth: options.firstColumnWidth
|
|
33889
|
+
multilineColumnWidth: options.firstColumnWidth,
|
|
33890
|
+
terminalWidth: resolveTtyColumns(process.stdout)
|
|
33412
33891
|
});
|
|
33413
33892
|
}
|
|
33893
|
+
function hasNoUsage(compareData) {
|
|
33894
|
+
return compareData.current.totals.events === 0 && compareData.baseline.totals.events === 0;
|
|
33895
|
+
}
|
|
33414
33896
|
function renderTerminalCompareReport(compareData, options) {
|
|
33415
33897
|
const useColor = options.useColor ?? shouldUseColorByDefault();
|
|
33416
33898
|
const lines = [];
|
|
@@ -33421,9 +33903,9 @@ function renderTerminalCompareReport(compareData, options) {
|
|
|
33421
33903
|
})
|
|
33422
33904
|
);
|
|
33423
33905
|
lines.push("");
|
|
33424
|
-
if (compareData
|
|
33425
|
-
lines.push(
|
|
33426
|
-
lines.
|
|
33906
|
+
if (hasNoUsage(compareData)) {
|
|
33907
|
+
lines.push(formatNoUsageMessage(options.filtered ?? false));
|
|
33908
|
+
return lines.join("\n");
|
|
33427
33909
|
}
|
|
33428
33910
|
lines.push(
|
|
33429
33911
|
renderTerminalTable({
|
|
@@ -34224,7 +34706,7 @@ async function buildCompareData(options, deps = {}) {
|
|
|
34224
34706
|
pricingOrigin,
|
|
34225
34707
|
pricingWarning,
|
|
34226
34708
|
warnings: dataset.warnings,
|
|
34227
|
-
notes: dataset.notes,
|
|
34709
|
+
notes: withUnpricedEventsNote(dataset.notes, [...currentEvents, ...baselineEvents]),
|
|
34228
34710
|
activeEnvOverrides: dataset.readEnvVarOverrides(),
|
|
34229
34711
|
activeConfig: dataset.activeConfig,
|
|
34230
34712
|
timezone: dataset.normalizedInputs.timezone,
|
|
@@ -34883,7 +35365,7 @@ async function prepareCompareReport(options, deps = {}) {
|
|
|
34883
35365
|
title: "Compare share card",
|
|
34884
35366
|
render: (theme) => renderCompareShareSvg(compareData, theme)
|
|
34885
35367
|
}) : void 0,
|
|
34886
|
-
render: (compareData, format) => renderCompareReport(compareData, format)
|
|
35368
|
+
render: (compareData, format) => renderCompareReport(compareData, format, { filtered: hasCliFilters(options) })
|
|
34887
35369
|
});
|
|
34888
35370
|
}
|
|
34889
35371
|
async function runCompareReport(options) {
|
|
@@ -35067,19 +35549,21 @@ function formatDecimal(value, options = {}) {
|
|
|
35067
35549
|
const formatted = decimalFormatter.format(value);
|
|
35068
35550
|
return options.approximate ? `~${formatted}` : formatted;
|
|
35069
35551
|
}
|
|
35070
|
-
function toEfficiencyTableCells(rows) {
|
|
35552
|
+
function toEfficiencyTableCells(rows, tokenFormat = "full") {
|
|
35553
|
+
const formatTokens = tokenFormat === "abbreviated" ? formatAbbreviatedTokenCount : formatInteger3;
|
|
35554
|
+
const formatTokenRate = (value) => value !== void 0 && tokenFormat === "abbreviated" ? formatAbbreviatedTokenCount(value) : formatDecimal(value);
|
|
35071
35555
|
return rows.map((row) => [
|
|
35072
35556
|
row.rowType === "period_source" ? ` ${row.source}` : row.periodKey,
|
|
35073
35557
|
formatInteger3(row.rowType === "period_source" ? void 0 : row.commitCount),
|
|
35074
35558
|
formatInteger3(row.rowType === "period_source" ? void 0 : row.linesAdded),
|
|
35075
35559
|
formatInteger3(row.rowType === "period_source" ? void 0 : row.linesDeleted),
|
|
35076
35560
|
formatInteger3(row.rowType === "period_source" ? void 0 : row.linesChanged),
|
|
35077
|
-
|
|
35078
|
-
|
|
35079
|
-
|
|
35080
|
-
|
|
35081
|
-
|
|
35082
|
-
|
|
35561
|
+
formatTokens(row.inputTokens),
|
|
35562
|
+
formatTokens(row.outputTokens),
|
|
35563
|
+
formatTokens(row.reasoningTokens),
|
|
35564
|
+
formatTokens(row.cacheReadTokens),
|
|
35565
|
+
formatTokens(row.cacheWriteTokens),
|
|
35566
|
+
formatTokens(row.totalTokens),
|
|
35083
35567
|
formatUsd4(row.costUsd, { approximate: row.costIncomplete }),
|
|
35084
35568
|
formatUsdRate(row.rowType === "period_source" ? void 0 : row.usdPerCommit, {
|
|
35085
35569
|
approximate: row.costIncomplete
|
|
@@ -35087,7 +35571,7 @@ function toEfficiencyTableCells(rows) {
|
|
|
35087
35571
|
formatUsdRate(row.rowType === "period_source" ? void 0 : row.usdPer1kLinesChanged, {
|
|
35088
35572
|
approximate: row.costIncomplete
|
|
35089
35573
|
}),
|
|
35090
|
-
|
|
35574
|
+
formatTokenRate(row.rowType === "period_source" ? void 0 : row.tokensPerCommit),
|
|
35091
35575
|
formatDecimal(row.rowType === "period_source" ? void 0 : row.commitsPerUsd, {
|
|
35092
35576
|
approximate: row.costIncomplete
|
|
35093
35577
|
})
|
|
@@ -35096,15 +35580,45 @@ function toEfficiencyTableCells(rows) {
|
|
|
35096
35580
|
|
|
35097
35581
|
// src/render/render-efficiency-report.ts
|
|
35098
35582
|
var periodColumnIndex2 = 0;
|
|
35099
|
-
var minimumEfficiencyColumnWidth = 1;
|
|
35100
35583
|
var commitsColumnIndex = 1;
|
|
35101
35584
|
var linesAddedColumnIndex = 2;
|
|
35102
35585
|
var linesDeletedColumnIndex = 3;
|
|
35103
35586
|
var linesChangedColumnIndex = 4;
|
|
35587
|
+
var inputColumnIndex = 5;
|
|
35588
|
+
var outputColumnIndex = 6;
|
|
35589
|
+
var reasoningColumnIndex = 7;
|
|
35590
|
+
var cacheReadColumnIndex = 8;
|
|
35591
|
+
var cacheWriteColumnIndex = 9;
|
|
35104
35592
|
var costColumnIndex = 11;
|
|
35105
35593
|
var usdPerCommitColumnIndex = 12;
|
|
35106
35594
|
var usdPer1kLinesChangedColumnIndex = 13;
|
|
35107
35595
|
var commitsPerUsdColumnIndex = 15;
|
|
35596
|
+
var efficiencyFitSteps = [
|
|
35597
|
+
{ tokenFormat: "full", hiddenColumnIndexes: [] },
|
|
35598
|
+
{ tokenFormat: "abbreviated", hiddenColumnIndexes: [] },
|
|
35599
|
+
{
|
|
35600
|
+
tokenFormat: "abbreviated",
|
|
35601
|
+
hiddenColumnIndexes: [
|
|
35602
|
+
inputColumnIndex,
|
|
35603
|
+
outputColumnIndex,
|
|
35604
|
+
reasoningColumnIndex,
|
|
35605
|
+
cacheWriteColumnIndex
|
|
35606
|
+
]
|
|
35607
|
+
},
|
|
35608
|
+
{
|
|
35609
|
+
tokenFormat: "abbreviated",
|
|
35610
|
+
hiddenColumnIndexes: [
|
|
35611
|
+
linesAddedColumnIndex,
|
|
35612
|
+
linesDeletedColumnIndex,
|
|
35613
|
+
inputColumnIndex,
|
|
35614
|
+
outputColumnIndex,
|
|
35615
|
+
reasoningColumnIndex,
|
|
35616
|
+
cacheReadColumnIndex,
|
|
35617
|
+
cacheWriteColumnIndex,
|
|
35618
|
+
commitsPerUsdColumnIndex
|
|
35619
|
+
]
|
|
35620
|
+
}
|
|
35621
|
+
];
|
|
35108
35622
|
function getReportTitle(granularity) {
|
|
35109
35623
|
switch (granularity) {
|
|
35110
35624
|
case "daily":
|
|
@@ -35129,11 +35643,7 @@ function measureRenderedTableWidth(columnWidths) {
|
|
|
35129
35643
|
return columnWidths.reduce((sum, width) => sum + width, 0) + columnWidths.length * 3 + 1;
|
|
35130
35644
|
}
|
|
35131
35645
|
function computeColumnWidths2(headerCells, bodyRows) {
|
|
35132
|
-
const columnCount = Math.max(
|
|
35133
|
-
headerCells.length,
|
|
35134
|
-
...bodyRows.map((row) => row.length),
|
|
35135
|
-
efficiencyTableHeaders.length
|
|
35136
|
-
);
|
|
35646
|
+
const columnCount = Math.max(headerCells.length, ...bodyRows.map((row) => row.length));
|
|
35137
35647
|
const widths = Array.from({ length: columnCount }, () => 0);
|
|
35138
35648
|
const measureRow = (row) => {
|
|
35139
35649
|
for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {
|
|
@@ -35148,71 +35658,69 @@ function computeColumnWidths2(headerCells, bodyRows) {
|
|
|
35148
35658
|
}
|
|
35149
35659
|
return widths;
|
|
35150
35660
|
}
|
|
35151
|
-
function
|
|
35152
|
-
|
|
35153
|
-
|
|
35154
|
-
|
|
35155
|
-
|
|
35156
|
-
|
|
35157
|
-
|
|
35661
|
+
function splitHeaderPieces(header) {
|
|
35662
|
+
return header.split(" ").flatMap((word) => word.split(/(?<=\/)/u));
|
|
35663
|
+
}
|
|
35664
|
+
function wrapHeader(header, width) {
|
|
35665
|
+
const lines = [];
|
|
35666
|
+
for (const piece of splitHeaderPieces(header)) {
|
|
35667
|
+
const lastLine = lines.at(-1);
|
|
35668
|
+
const separator = lastLine === void 0 || lastLine.endsWith("/") ? "" : " ";
|
|
35669
|
+
if (lastLine !== void 0 && visibleWidth(lastLine + separator + piece) <= width) {
|
|
35670
|
+
lines[lines.length - 1] = lastLine + separator + piece;
|
|
35671
|
+
} else {
|
|
35672
|
+
lines.push(piece);
|
|
35158
35673
|
}
|
|
35159
|
-
wrappedHeaderCells = wrapTableColumn([wrappedHeaderCells], {
|
|
35160
|
-
columnIndex,
|
|
35161
|
-
width: columnWidth
|
|
35162
|
-
})[0] ?? [];
|
|
35163
|
-
wrappedBodyRows = wrapTableColumn(wrappedBodyRows, {
|
|
35164
|
-
columnIndex,
|
|
35165
|
-
width: columnWidth
|
|
35166
|
-
});
|
|
35167
35674
|
}
|
|
35168
|
-
return
|
|
35169
|
-
|
|
35170
|
-
|
|
35171
|
-
|
|
35675
|
+
return lines.join("\n");
|
|
35676
|
+
}
|
|
35677
|
+
function computeMinimumColumnWidths(headerCells, bodyRows) {
|
|
35678
|
+
const headerPieceWidths = headerCells.map(
|
|
35679
|
+
(header) => splitHeaderPieces(header).reduce(
|
|
35680
|
+
(maxWidth, piece) => Math.max(maxWidth, visibleWidth(piece)),
|
|
35681
|
+
0
|
|
35682
|
+
)
|
|
35683
|
+
);
|
|
35684
|
+
return computeColumnWidths2([], bodyRows).map(
|
|
35685
|
+
(bodyWidth, columnIndex) => Math.max(bodyWidth, headerPieceWidths[columnIndex] ?? 0)
|
|
35686
|
+
);
|
|
35172
35687
|
}
|
|
35173
35688
|
function fitTableCellsToTerminal(headerCells, bodyRows) {
|
|
35174
35689
|
const naturalWidths = computeColumnWidths2(headerCells, bodyRows);
|
|
35175
35690
|
const terminalWidth = resolveTtyColumns(process.stdout);
|
|
35691
|
+
const natural = {
|
|
35692
|
+
headerCells: [...headerCells],
|
|
35693
|
+
bodyRows: bodyRows.map((row) => [...row]),
|
|
35694
|
+
widths: naturalWidths
|
|
35695
|
+
};
|
|
35176
35696
|
if (terminalWidth === void 0 || measureRenderedTableWidth(naturalWidths) <= terminalWidth) {
|
|
35177
|
-
return
|
|
35178
|
-
|
|
35179
|
-
|
|
35180
|
-
|
|
35181
|
-
};
|
|
35697
|
+
return natural;
|
|
35698
|
+
}
|
|
35699
|
+
const minimumWidths = computeMinimumColumnWidths(headerCells, bodyRows);
|
|
35700
|
+
if (measureRenderedTableWidth(minimumWidths) > terminalWidth) {
|
|
35701
|
+
return { ...natural, stackedWidth: terminalWidth };
|
|
35182
35702
|
}
|
|
35183
35703
|
const constrainedWidths = [...naturalWidths];
|
|
35184
|
-
let
|
|
35185
|
-
while (
|
|
35186
|
-
let
|
|
35187
|
-
let
|
|
35188
|
-
|
|
35189
|
-
|
|
35190
|
-
if (columnWidth <= minimumEfficiencyColumnWidth || columnWidth <= widestWidth) {
|
|
35191
|
-
continue;
|
|
35704
|
+
let overflowColumns = measureRenderedTableWidth(constrainedWidths) - terminalWidth;
|
|
35705
|
+
while (overflowColumns > 0) {
|
|
35706
|
+
let shrinkIndex = 0;
|
|
35707
|
+
for (let columnIndex = 1; columnIndex < constrainedWidths.length; columnIndex += 1) {
|
|
35708
|
+
if (constrainedWidths[columnIndex] - minimumWidths[columnIndex] > constrainedWidths[shrinkIndex] - minimumWidths[shrinkIndex]) {
|
|
35709
|
+
shrinkIndex = columnIndex;
|
|
35192
35710
|
}
|
|
35193
|
-
widestIndex = columnIndex;
|
|
35194
|
-
widestWidth = columnWidth;
|
|
35195
35711
|
}
|
|
35196
|
-
|
|
35197
|
-
|
|
35198
|
-
|
|
35199
|
-
|
|
35200
|
-
|
|
35201
|
-
|
|
35202
|
-
if (reduction <= 0) {
|
|
35203
|
-
break;
|
|
35204
|
-
}
|
|
35205
|
-
constrainedWidths[widestIndex] -= reduction;
|
|
35206
|
-
renderedTableWidth -= reduction;
|
|
35712
|
+
const reduction = Math.min(
|
|
35713
|
+
overflowColumns,
|
|
35714
|
+
constrainedWidths[shrinkIndex] - minimumWidths[shrinkIndex]
|
|
35715
|
+
);
|
|
35716
|
+
constrainedWidths[shrinkIndex] -= reduction;
|
|
35717
|
+
overflowColumns -= reduction;
|
|
35207
35718
|
}
|
|
35208
|
-
const { wrappedHeaderCells, wrappedBodyRows } = resolveWrappedCells(
|
|
35209
|
-
headerCells,
|
|
35210
|
-
bodyRows,
|
|
35211
|
-
constrainedWidths
|
|
35212
|
-
);
|
|
35213
35719
|
return {
|
|
35214
|
-
headerCells:
|
|
35215
|
-
|
|
35720
|
+
headerCells: headerCells.map(
|
|
35721
|
+
(header, columnIndex) => wrapHeader(header, constrainedWidths[columnIndex])
|
|
35722
|
+
),
|
|
35723
|
+
bodyRows: natural.bodyRows,
|
|
35216
35724
|
widths: constrainedWidths
|
|
35217
35725
|
};
|
|
35218
35726
|
}
|
|
@@ -35282,12 +35790,50 @@ function styleEfficiencyTerminalRows(rows, bodyRows, options) {
|
|
|
35282
35790
|
return styledCells;
|
|
35283
35791
|
});
|
|
35284
35792
|
}
|
|
35793
|
+
function fitEfficiencyStep(rows, step, options) {
|
|
35794
|
+
const visibleColumnIndexes = efficiencyTableHeaders.map((_, columnIndex) => columnIndex).filter((columnIndex) => !step.hiddenColumnIndexes.includes(columnIndex));
|
|
35795
|
+
const styledRows = styleEfficiencyTerminalRows(
|
|
35796
|
+
rows,
|
|
35797
|
+
toEfficiencyTableCells(rows, step.tokenFormat),
|
|
35798
|
+
options
|
|
35799
|
+
);
|
|
35800
|
+
return fitTableCellsToTerminal(
|
|
35801
|
+
selectColumns(Array.from(efficiencyTableHeaders), visibleColumnIndexes),
|
|
35802
|
+
styledRows.map((row) => selectColumns(row, visibleColumnIndexes))
|
|
35803
|
+
);
|
|
35804
|
+
}
|
|
35805
|
+
function describeEfficiencyFit(step, stacked) {
|
|
35806
|
+
if (stacked) {
|
|
35807
|
+
return "Listed rows instead of a table to fit the terminal. Widen the terminal to see the table.";
|
|
35808
|
+
}
|
|
35809
|
+
const changes = [];
|
|
35810
|
+
if (step.tokenFormat === "abbreviated") {
|
|
35811
|
+
changes.push("abbreviated token counts");
|
|
35812
|
+
}
|
|
35813
|
+
const hiddenHeaders = step.hiddenColumnIndexes.map(
|
|
35814
|
+
(columnIndex) => efficiencyTableHeaders[columnIndex]
|
|
35815
|
+
);
|
|
35816
|
+
if (hiddenHeaders.length > 0) {
|
|
35817
|
+
changes.push(`hid ${hiddenHeaders.slice(0, -1).join(", ")} and ${hiddenHeaders.at(-1) ?? ""}`);
|
|
35818
|
+
}
|
|
35819
|
+
return changes.length > 0 ? `Fitted the table to the terminal: ${changes.join(", ")}. Widen the terminal or use --json for full detail.` : void 0;
|
|
35820
|
+
}
|
|
35285
35821
|
function renderTerminalEfficiencyTable(rows, options) {
|
|
35286
|
-
const headerCells = Array.from(efficiencyTableHeaders);
|
|
35287
|
-
const bodyRows = styleEfficiencyTerminalRows(rows, toEfficiencyTableCells(rows), options);
|
|
35288
35822
|
const rowMetas = rows.map((row) => toTableRowMeta(row));
|
|
35289
|
-
|
|
35290
|
-
|
|
35823
|
+
let fittedStep = efficiencyFitSteps[0];
|
|
35824
|
+
let fittedCells = fitEfficiencyStep(rows, fittedStep, options);
|
|
35825
|
+
for (const step of efficiencyFitSteps.slice(1)) {
|
|
35826
|
+
if (fittedCells.stackedWidth === void 0) {
|
|
35827
|
+
break;
|
|
35828
|
+
}
|
|
35829
|
+
const candidate = fitEfficiencyStep(rows, step, options);
|
|
35830
|
+
if (candidate.stackedWidth === void 0) {
|
|
35831
|
+
fittedStep = step;
|
|
35832
|
+
fittedCells = candidate;
|
|
35833
|
+
}
|
|
35834
|
+
}
|
|
35835
|
+
const note = describeEfficiencyFit(fittedStep, fittedCells.stackedWidth !== void 0);
|
|
35836
|
+
const output = renderUnicodeTable({
|
|
35291
35837
|
headerCells: fittedCells.headerCells,
|
|
35292
35838
|
bodyRows: fittedCells.bodyRows,
|
|
35293
35839
|
measureHeaderCells: fittedCells.headerCells,
|
|
@@ -35295,8 +35841,10 @@ function renderTerminalEfficiencyTable(rows, options) {
|
|
|
35295
35841
|
rowMetas,
|
|
35296
35842
|
layout: "compact",
|
|
35297
35843
|
multilineColumnIndex: periodColumnIndex2,
|
|
35298
|
-
multilineColumnWidth: fittedCells.widths[periodColumnIndex2] ?? efficiencyTableHeaders[periodColumnIndex2].length
|
|
35844
|
+
multilineColumnWidth: fittedCells.widths[periodColumnIndex2] ?? efficiencyTableHeaders[periodColumnIndex2].length,
|
|
35845
|
+
terminalWidth: fittedCells.stackedWidth
|
|
35299
35846
|
});
|
|
35847
|
+
return { output, notes: note ? [note] : [] };
|
|
35300
35848
|
}
|
|
35301
35849
|
function renderMarkdownEfficiencyTable(rows) {
|
|
35302
35850
|
const bodyRows = toEfficiencyTableCells(rows).map(
|
|
@@ -35317,19 +35865,22 @@ function renderTerminalEfficiencyReport(efficiencyData, options) {
|
|
|
35317
35865
|
useColor
|
|
35318
35866
|
})
|
|
35319
35867
|
);
|
|
35320
|
-
|
|
35321
|
-
outputLines.push(
|
|
35322
|
-
return outputLines.join("\n");
|
|
35868
|
+
const table = renderTerminalEfficiencyTable(efficiencyData.rows, { useColor });
|
|
35869
|
+
outputLines.push("", table.output);
|
|
35870
|
+
return { output: outputLines.join("\n"), notes: table.notes };
|
|
35323
35871
|
}
|
|
35324
|
-
function
|
|
35872
|
+
function renderEfficiencyReportWithNotes(efficiencyData, format, options) {
|
|
35325
35873
|
switch (format) {
|
|
35326
35874
|
case "json":
|
|
35327
|
-
return
|
|
35328
|
-
|
|
35329
|
-
|
|
35330
|
-
|
|
35875
|
+
return {
|
|
35876
|
+
output: renderReportJson("efficiency", {
|
|
35877
|
+
grouping: efficiencyData.grouping,
|
|
35878
|
+
rows: efficiencyData.rows
|
|
35879
|
+
}),
|
|
35880
|
+
notes: []
|
|
35881
|
+
};
|
|
35331
35882
|
case "markdown":
|
|
35332
|
-
return renderMarkdownEfficiencyTable(efficiencyData.rows);
|
|
35883
|
+
return { output: renderMarkdownEfficiencyTable(efficiencyData.rows), notes: [] };
|
|
35333
35884
|
case "terminal":
|
|
35334
35885
|
return renderTerminalEfficiencyReport(efficiencyData, options);
|
|
35335
35886
|
}
|
|
@@ -35581,12 +36132,22 @@ async function assertRepoDirReadable(repoDir) {
|
|
|
35581
36132
|
throw new Error(`Repository path is not a directory: ${repoDir}`);
|
|
35582
36133
|
}
|
|
35583
36134
|
}
|
|
35584
|
-
async function
|
|
35585
|
-
const gitRepoResult = await runCommand(repoDir, ["rev-parse", "--is-inside-work-tree"])
|
|
35586
|
-
|
|
35587
|
-
|
|
35588
|
-
|
|
35589
|
-
|
|
36135
|
+
async function isGitRepository(repoDir, runCommand) {
|
|
36136
|
+
const gitRepoResult = await runCommand(repoDir, ["rev-parse", "--is-inside-work-tree"]).catch(
|
|
36137
|
+
(error) => {
|
|
36138
|
+
if (getNodeErrorCode2(error) === "ENOENT") {
|
|
36139
|
+
throw new Error("git was not found on PATH; efficiency needs git installed", {
|
|
36140
|
+
cause: error
|
|
36141
|
+
});
|
|
36142
|
+
}
|
|
36143
|
+
throw error;
|
|
36144
|
+
}
|
|
36145
|
+
);
|
|
36146
|
+
return gitRepoResult.exitCode === 0;
|
|
36147
|
+
}
|
|
36148
|
+
function notGitRepositoryMessage(repoDir, repoDirOption) {
|
|
36149
|
+
const hint = repoDirOption === void 0 ? "<path>" : "<path to a git repository>";
|
|
36150
|
+
return `${repoDir} is not a git repository; pass --repo-dir ${hint}`;
|
|
35590
36151
|
}
|
|
35591
36152
|
function isNoCommitHistoryFailure(result) {
|
|
35592
36153
|
if (result.exitCode !== 128) {
|
|
@@ -35859,11 +36420,20 @@ async function collectGitOutcomes(options, deps = {}) {
|
|
|
35859
36420
|
const includeMergeCommits = options.includeMergeCommits ?? false;
|
|
35860
36421
|
const runCommand = deps.runGitCommand ?? runGitCommand;
|
|
35861
36422
|
if (options.activeUsageDays?.size === 0) {
|
|
35862
|
-
|
|
36423
|
+
const emptyCollection = createEmptyOutcomeCollection(repoDir, includeMergeCommits);
|
|
36424
|
+
if (!deps.runGitCommand) {
|
|
36425
|
+
await assertRepoDirReadable(repoDir);
|
|
36426
|
+
if (!await isGitRepository(repoDir, runCommand)) {
|
|
36427
|
+
emptyCollection.warning = notGitRepositoryMessage(repoDir, options.repoDir);
|
|
36428
|
+
}
|
|
36429
|
+
}
|
|
36430
|
+
return emptyCollection;
|
|
35863
36431
|
}
|
|
35864
36432
|
if (!deps.runGitCommand) {
|
|
35865
36433
|
await assertRepoDirReadable(repoDir);
|
|
35866
|
-
await
|
|
36434
|
+
if (!await isGitRepository(repoDir, runCommand)) {
|
|
36435
|
+
throw new Error(notGitRepositoryMessage(repoDir, options.repoDir));
|
|
36436
|
+
}
|
|
35867
36437
|
}
|
|
35868
36438
|
let authorEmail;
|
|
35869
36439
|
try {
|
|
@@ -36174,8 +36744,8 @@ async function buildEfficiencyData(granularity, options, deps = {}) {
|
|
|
36174
36744
|
sourceFailures: dataset.sourceFailures,
|
|
36175
36745
|
pricingOrigin,
|
|
36176
36746
|
pricingWarning,
|
|
36177
|
-
warnings: dataset.warnings,
|
|
36178
|
-
notes: dataset.notes,
|
|
36747
|
+
warnings: gitOutcomes.warning ? [...dataset.warnings, gitOutcomes.warning] : dataset.warnings,
|
|
36748
|
+
notes: withUnpricedEventsNote(dataset.notes, matchedEventsWithSignal),
|
|
36179
36749
|
activeEnvOverrides: dataset.readEnvVarOverrides(),
|
|
36180
36750
|
activeConfig: dataset.activeConfig,
|
|
36181
36751
|
timezone: dataset.normalizedInputs.timezone,
|
|
@@ -36229,9 +36799,12 @@ async function prepareEfficiencyReport(granularity, options, deps = {}) {
|
|
|
36229
36799
|
title: "Efficiency share card",
|
|
36230
36800
|
render: (theme) => renderEfficiencyMonthlyShareSvg(efficiencyData, theme)
|
|
36231
36801
|
}) : void 0,
|
|
36232
|
-
render: (efficiencyData, format) =>
|
|
36233
|
-
|
|
36234
|
-
|
|
36802
|
+
render: (efficiencyData, format) => {
|
|
36803
|
+
const { output, notes } = renderEfficiencyReportWithNotes(efficiencyData, format, {
|
|
36804
|
+
granularity
|
|
36805
|
+
});
|
|
36806
|
+
return { output, hintsAfterOutput: notes };
|
|
36807
|
+
}
|
|
36235
36808
|
});
|
|
36236
36809
|
}
|
|
36237
36810
|
function emitEfficiencyReportDiagnostics(diagnostics) {
|
|
@@ -36465,7 +37038,8 @@ function renderTerminalOptimizeReport(optimizeData, options) {
|
|
|
36465
37038
|
rowMetas: optimizeData.rows.map((row) => toTableRowMeta2(row)),
|
|
36466
37039
|
layout: "compact",
|
|
36467
37040
|
multilineColumnIndex: 1,
|
|
36468
|
-
multilineColumnWidth: candidateColumnWidth
|
|
37041
|
+
multilineColumnWidth: candidateColumnWidth,
|
|
37042
|
+
terminalWidth: resolveTtyColumns(process.stdout)
|
|
36469
37043
|
})
|
|
36470
37044
|
);
|
|
36471
37045
|
return outputLines.join("\n");
|
|
@@ -36643,7 +37217,7 @@ function hasAnyUsageSignal(period) {
|
|
|
36643
37217
|
}
|
|
36644
37218
|
function parseCandidateModelsRaw(candidateModel) {
|
|
36645
37219
|
if (!candidateModel || Array.isArray(candidateModel) && candidateModel.length === 0) {
|
|
36646
|
-
throw new Error("At least one --candidate-model is required");
|
|
37220
|
+
throw new Error("At least one --candidate-model is required, e.g. --candidate-model gpt-5.5");
|
|
36647
37221
|
}
|
|
36648
37222
|
const normalizedCandidates = (Array.isArray(candidateModel) ? candidateModel : [candidateModel]).flatMap((candidate) => candidate.split(",")).map((candidate) => candidate.trim().toLowerCase()).filter((candidate) => candidate.length > 0);
|
|
36649
37223
|
if (normalizedCandidates.length === 0) {
|
|
@@ -36692,9 +37266,7 @@ function createSyntheticEvent(period) {
|
|
|
36692
37266
|
reasoningTokens: period.reasoningTokens,
|
|
36693
37267
|
cacheReadTokens: period.cacheReadTokens,
|
|
36694
37268
|
cacheWriteTokens: period.cacheWriteTokens,
|
|
36695
|
-
|
|
36696
|
-
// standard cache-write rate.
|
|
36697
|
-
cacheWrite1hTokens: 0,
|
|
37269
|
+
cacheWrite1hTokens: period.cacheWrite1hTokens,
|
|
36698
37270
|
totalTokens: period.totalTokens,
|
|
36699
37271
|
costMode: "estimated",
|
|
36700
37272
|
costUsd: void 0
|
|
@@ -36797,7 +37369,7 @@ function compareCandidateRank(left, right4) {
|
|
|
36797
37369
|
}
|
|
36798
37370
|
return compareByCodePoint(left.candidateModel, right4.candidateModel);
|
|
36799
37371
|
}
|
|
36800
|
-
function resolveBaselinePeriods(usageRows) {
|
|
37372
|
+
function resolveBaselinePeriods(usageRows, oneHourCacheWritesByPeriod) {
|
|
36801
37373
|
const periodRows = /* @__PURE__ */ new Map();
|
|
36802
37374
|
let grandTotalRow;
|
|
36803
37375
|
for (const row of usageRows) {
|
|
@@ -36839,6 +37411,7 @@ function resolveBaselinePeriods(usageRows) {
|
|
|
36839
37411
|
reasoningTokens: row.reasoningTokens,
|
|
36840
37412
|
cacheReadTokens: row.cacheReadTokens,
|
|
36841
37413
|
cacheWriteTokens: row.cacheWriteTokens,
|
|
37414
|
+
cacheWrite1hTokens: oneHourCacheWritesByPeriod.get(periodKey) ?? 0,
|
|
36842
37415
|
totalTokens: row.totalTokens,
|
|
36843
37416
|
baselineCostUsd: row.costUsd,
|
|
36844
37417
|
baselineCostIncomplete: row.costIncomplete === true
|
|
@@ -36853,6 +37426,7 @@ function resolveBaselinePeriods(usageRows) {
|
|
|
36853
37426
|
reasoningTokens: allRow.reasoningTokens,
|
|
36854
37427
|
cacheReadTokens: allRow.cacheReadTokens,
|
|
36855
37428
|
cacheWriteTokens: allRow.cacheWriteTokens,
|
|
37429
|
+
cacheWrite1hTokens: oneHourCacheWritesByPeriod.get("ALL") ?? 0,
|
|
36856
37430
|
totalTokens: allRow.totalTokens,
|
|
36857
37431
|
baselineCostUsd: allRow.costUsd,
|
|
36858
37432
|
baselineCostIncomplete: allRow.costIncomplete === true
|
|
@@ -36865,6 +37439,7 @@ function resolveBaselinePeriods(usageRows) {
|
|
|
36865
37439
|
reasoningTokens: 0,
|
|
36866
37440
|
cacheReadTokens: 0,
|
|
36867
37441
|
cacheWriteTokens: 0,
|
|
37442
|
+
cacheWrite1hTokens: 0,
|
|
36868
37443
|
totalTokens: 0,
|
|
36869
37444
|
baselineCostUsd: 0,
|
|
36870
37445
|
baselineCostIncomplete: false
|
|
@@ -36880,7 +37455,7 @@ function buildWarning(periodKeys) {
|
|
|
36880
37455
|
return `Baseline cost exists for zero-token periods (${sortedPeriodKeys.join(", ")}); savings were omitted.`;
|
|
36881
37456
|
}
|
|
36882
37457
|
function buildCounterfactualRows(input) {
|
|
36883
|
-
const baselinePeriods = resolveBaselinePeriods(input.usageRows);
|
|
37458
|
+
const baselinePeriods = resolveBaselinePeriods(input.usageRows, input.oneHourCacheWritesByPeriod);
|
|
36884
37459
|
const allPeriod = baselinePeriods.find((period) => period.periodKey === "ALL");
|
|
36885
37460
|
if (!allPeriod) {
|
|
36886
37461
|
throw new Error("Missing ALL baseline totals");
|
|
@@ -36923,7 +37498,7 @@ function buildCounterfactualRows(input) {
|
|
|
36923
37498
|
}
|
|
36924
37499
|
|
|
36925
37500
|
// src/cli/build-optimize-data.ts
|
|
36926
|
-
function resolveOptimizeProvider(providers, providerFilter) {
|
|
37501
|
+
function resolveOptimizeProvider(providers, providerFilter, candidateModels) {
|
|
36927
37502
|
const distinctProviders = collectCanonicalProviderRoots(providers);
|
|
36928
37503
|
const normalizedProviderFilter = normalizeProviderFilter(providerFilter);
|
|
36929
37504
|
if (distinctProviders.length > 1) {
|
|
@@ -36945,8 +37520,12 @@ function resolveOptimizeProvider(providers, providerFilter) {
|
|
|
36945
37520
|
);
|
|
36946
37521
|
}
|
|
36947
37522
|
}
|
|
37523
|
+
const candidateProviders = inferCanonicalProviderRootsFromModels(candidateModels);
|
|
37524
|
+
const suggestedProvider = candidateProviders?.length === 1 && distinctProviders.includes(candidateProviders[0]) && distinctProviders.filter(
|
|
37525
|
+
(provider) => matchesCanonicalProviderFilter(provider, candidateProviders[0])
|
|
37526
|
+
).length === 1 ? ` ${candidateProviders[0]}` : "";
|
|
36948
37527
|
throw new Error(
|
|
36949
|
-
`Optimize requires a single provider; found providers: ${distinctProviders.join(", ")}. Narrow with --provider.`
|
|
37528
|
+
`Optimize requires a single provider; found providers: ${distinctProviders.join(", ")}. Narrow with --provider${suggestedProvider}.`
|
|
36950
37529
|
);
|
|
36951
37530
|
}
|
|
36952
37531
|
if (distinctProviders.length === 1) {
|
|
@@ -36968,7 +37547,11 @@ async function buildOptimizeData(granularity, options, deps = {}) {
|
|
|
36968
37547
|
const provider = measureRuntimeProfileStageSync(
|
|
36969
37548
|
deps.runtimeProfile,
|
|
36970
37549
|
"optimize.resolve_provider",
|
|
36971
|
-
() => resolveOptimizeProvider(
|
|
37550
|
+
() => resolveOptimizeProvider(
|
|
37551
|
+
detectedProviders,
|
|
37552
|
+
dataset.normalizedInputs.providerFilter,
|
|
37553
|
+
candidateModels
|
|
37554
|
+
)
|
|
36972
37555
|
);
|
|
36973
37556
|
const { pricedEvents, pricingOrigin, pricingWarning, pricingSource } = await applyPricingToUsageEventDataset(dataset, deps, "force");
|
|
36974
37557
|
const usageRows = measureRuntimeProfileStageSync(
|
|
@@ -36981,11 +37564,25 @@ async function buildOptimizeData(granularity, options, deps = {}) {
|
|
|
36981
37564
|
includeModelBreakdown: false
|
|
36982
37565
|
})
|
|
36983
37566
|
);
|
|
37567
|
+
const oneHourCacheWritesByPeriod = /* @__PURE__ */ new Map();
|
|
37568
|
+
for (const event of pricedEvents) {
|
|
37569
|
+
if (event.cacheWrite1hTokens === 0) {
|
|
37570
|
+
continue;
|
|
37571
|
+
}
|
|
37572
|
+
const periodKey = getPeriodKey(event.timestamp, granularity, dataset.normalizedInputs.timezone);
|
|
37573
|
+
for (const key of [periodKey, "ALL"]) {
|
|
37574
|
+
oneHourCacheWritesByPeriod.set(
|
|
37575
|
+
key,
|
|
37576
|
+
(oneHourCacheWritesByPeriod.get(key) ?? 0) + event.cacheWrite1hTokens
|
|
37577
|
+
);
|
|
37578
|
+
}
|
|
37579
|
+
}
|
|
36984
37580
|
const counterfactual = measureRuntimeProfileStageSync(
|
|
36985
37581
|
deps.runtimeProfile,
|
|
36986
37582
|
"optimize.counterfactual",
|
|
36987
37583
|
() => buildCounterfactualRows({
|
|
36988
37584
|
usageRows,
|
|
37585
|
+
oneHourCacheWritesByPeriod,
|
|
36989
37586
|
provider,
|
|
36990
37587
|
candidateModels,
|
|
36991
37588
|
pricingSource,
|
|
@@ -36999,7 +37596,7 @@ async function buildOptimizeData(granularity, options, deps = {}) {
|
|
|
36999
37596
|
pricingOrigin,
|
|
37000
37597
|
pricingWarning,
|
|
37001
37598
|
warnings: dataset.warnings,
|
|
37002
|
-
notes: dataset.notes,
|
|
37599
|
+
notes: withUnpricedEventsNote(dataset.notes, pricedEvents),
|
|
37003
37600
|
activeEnvOverrides: dataset.readEnvVarOverrides(),
|
|
37004
37601
|
activeConfig: dataset.activeConfig,
|
|
37005
37602
|
timezone: dataset.normalizedInputs.timezone,
|
|
@@ -37140,12 +37737,7 @@ function formatModels2(models) {
|
|
|
37140
37737
|
if (models.length === 0) {
|
|
37141
37738
|
return "-";
|
|
37142
37739
|
}
|
|
37143
|
-
|
|
37144
|
-
return models.join("\n");
|
|
37145
|
-
}
|
|
37146
|
-
return [...models.slice(0, MAX_NAMED_MODELS), `+${models.length - MAX_NAMED_MODELS} more`].join(
|
|
37147
|
-
"\n"
|
|
37148
|
-
);
|
|
37740
|
+
return limitNamedModels(models, MAX_NAMED_MODELS).join("\n");
|
|
37149
37741
|
}
|
|
37150
37742
|
function toSessionTableCells(row, options) {
|
|
37151
37743
|
return [
|
|
@@ -37205,7 +37797,7 @@ function renderTerminalSessionReport(sessionData, options) {
|
|
|
37205
37797
|
""
|
|
37206
37798
|
];
|
|
37207
37799
|
if (bodyRows.length === 0) {
|
|
37208
|
-
outputLines.push(
|
|
37800
|
+
outputLines.push(formatNoUsageMessage(options.filtered ?? false));
|
|
37209
37801
|
return outputLines.join("\n");
|
|
37210
37802
|
}
|
|
37211
37803
|
const modelsColumnIndex3 = headers.length - 1;
|
|
@@ -37222,7 +37814,9 @@ function renderTerminalSessionReport(sessionData, options) {
|
|
|
37222
37814
|
rowMetas: wrappedRows.map(() => toRowMeta()),
|
|
37223
37815
|
layout: "top_aligned",
|
|
37224
37816
|
multilineColumnIndex: modelsColumnIndex3,
|
|
37225
|
-
multilineColumnWidth: MODELS_COLUMN_WIDTH
|
|
37817
|
+
multilineColumnWidth: MODELS_COLUMN_WIDTH,
|
|
37818
|
+
terminalWidth: resolveTtyColumns(process.stdout),
|
|
37819
|
+
stackedTitleColumnCount: 1
|
|
37226
37820
|
})
|
|
37227
37821
|
);
|
|
37228
37822
|
return outputLines.join("\n");
|
|
@@ -37504,14 +38098,14 @@ async function buildSessionData(options, deps = {}) {
|
|
|
37504
38098
|
since: options.since,
|
|
37505
38099
|
until: options.until
|
|
37506
38100
|
};
|
|
37507
|
-
const
|
|
38101
|
+
const buildDiagnostics = (shownEvents) => buildUsageDiagnostics({
|
|
37508
38102
|
adaptersToParse: dataset.adaptersToParse,
|
|
37509
38103
|
successfulParseResults: dataset.successfulParseResults,
|
|
37510
38104
|
sourceFailures: dataset.sourceFailures,
|
|
37511
38105
|
pricingOrigin,
|
|
37512
38106
|
pricingWarning,
|
|
37513
38107
|
warnings: dataset.warnings,
|
|
37514
|
-
notes: dataset.notes,
|
|
38108
|
+
notes: withUnpricedEventsNote(dataset.notes, shownEvents),
|
|
37515
38109
|
activeEnvOverrides: dataset.readEnvVarOverrides(),
|
|
37516
38110
|
activeConfig: dataset.activeConfig,
|
|
37517
38111
|
timezone: dataset.normalizedInputs.timezone,
|
|
@@ -37524,11 +38118,14 @@ async function buildSessionData(options, deps = {}) {
|
|
|
37524
38118
|
() => aggregateSessionsByRepo(pricedEvents, aggregateOptions)
|
|
37525
38119
|
);
|
|
37526
38120
|
const limited2 = limitRows(repoRows, limit, "repos");
|
|
38121
|
+
const shownRepos = new Set(limited2.rows.map((row) => row.repoRoot ?? ""));
|
|
37527
38122
|
return {
|
|
37528
38123
|
grouping: "repo",
|
|
37529
38124
|
rows: limited2.rows,
|
|
37530
38125
|
limitNote: limited2.limitNote,
|
|
37531
|
-
diagnostics
|
|
38126
|
+
diagnostics: buildDiagnostics(
|
|
38127
|
+
pricedEvents.filter((event) => shownRepos.has(event.repoRoot ?? ""))
|
|
38128
|
+
)
|
|
37532
38129
|
};
|
|
37533
38130
|
}
|
|
37534
38131
|
const sessionRows = measureRuntimeProfileStageSync(
|
|
@@ -37537,11 +38134,14 @@ async function buildSessionData(options, deps = {}) {
|
|
|
37537
38134
|
() => aggregateSessions(pricedEvents, { ...aggregateOptions, ids: idFilters })
|
|
37538
38135
|
);
|
|
37539
38136
|
const limited = limitRows(sessionRows, limit, "sessions");
|
|
38137
|
+
const shownSessions = new Set(limited.rows.map((row) => getEventSessionKey(row)));
|
|
37540
38138
|
return {
|
|
37541
38139
|
grouping: "session",
|
|
37542
38140
|
rows: limited.rows,
|
|
37543
38141
|
limitNote: limited.limitNote,
|
|
37544
|
-
diagnostics
|
|
38142
|
+
diagnostics: buildDiagnostics(
|
|
38143
|
+
pricedEvents.filter((event) => shownSessions.has(getEventSessionKey(event)))
|
|
38144
|
+
)
|
|
37545
38145
|
};
|
|
37546
38146
|
}
|
|
37547
38147
|
|
|
@@ -37559,7 +38159,8 @@ async function prepareSessionReport(options, deps = {}) {
|
|
|
37559
38159
|
runtimeProfile: deps.runtimeProfile,
|
|
37560
38160
|
render: (sessionData, format) => renderSessionReport(sessionData, format, {
|
|
37561
38161
|
timezone: sessionData.diagnostics.timezone,
|
|
37562
|
-
truncateSessionIds: !options.id?.length
|
|
38162
|
+
truncateSessionIds: !options.id?.length,
|
|
38163
|
+
filtered: hasCliFilters(options)
|
|
37563
38164
|
})
|
|
37564
38165
|
});
|
|
37565
38166
|
}
|
|
@@ -37676,6 +38277,19 @@ function toTableRow(period) {
|
|
|
37676
38277
|
function hasAnyUsage(summaryData) {
|
|
37677
38278
|
return summaryData.periods.some((period) => period.totals.events > 0);
|
|
37678
38279
|
}
|
|
38280
|
+
function toNoRecentUsageMessage(summaryData, filtered) {
|
|
38281
|
+
if (filtered) {
|
|
38282
|
+
return "No usage in the last 7 days or this month for the selected filters.";
|
|
38283
|
+
}
|
|
38284
|
+
const latestDay = summaryData.activity.days.filter((day) => day.totalTokens > 0).at(-1);
|
|
38285
|
+
if (latestDay) {
|
|
38286
|
+
return `No usage in the last 7 days or this month. Latest usage: ${latestDay.date}. Try \`llm-usage monthly --all\`.`;
|
|
38287
|
+
}
|
|
38288
|
+
if (summaryData.diagnostics.sessionStats.some((stat10) => stat10.filesFound > 0)) {
|
|
38289
|
+
return "No usage in the past year. Try `llm-usage monthly --all`.";
|
|
38290
|
+
}
|
|
38291
|
+
return "No usage found in the last 7 days or this month. Run `llm-usage doctor` to see which sources were found.";
|
|
38292
|
+
}
|
|
37679
38293
|
function getTitle(summaryData) {
|
|
37680
38294
|
const today = summaryData.periods[0]?.until ?? "";
|
|
37681
38295
|
return `Usage summary for ${today} (${summaryData.timezone})`;
|
|
@@ -37803,12 +38417,16 @@ function renderTerminalSummaryReport(summaryData, options) {
|
|
|
37803
38417
|
periodKey: "summary",
|
|
37804
38418
|
rowKind: "detail"
|
|
37805
38419
|
}));
|
|
37806
|
-
const lines = [
|
|
38420
|
+
const lines = [
|
|
38421
|
+
renderReportHeader({
|
|
38422
|
+
title: getTitle(summaryData),
|
|
38423
|
+
useColor,
|
|
38424
|
+
terminalWidth: options.terminalWidth
|
|
38425
|
+
}),
|
|
38426
|
+
""
|
|
38427
|
+
];
|
|
37807
38428
|
if (!hasAnyUsage(summaryData)) {
|
|
37808
|
-
lines.push(
|
|
37809
|
-
"No usage found in the last 7 days or this month. Run `llm-usage doctor` to see which sources were found."
|
|
37810
|
-
);
|
|
37811
|
-
lines.push("");
|
|
38429
|
+
lines.push(toNoRecentUsageMessage(summaryData, options.filtered ?? false), "");
|
|
37812
38430
|
}
|
|
37813
38431
|
lines.push(
|
|
37814
38432
|
renderUnicodeTable({
|
|
@@ -37820,7 +38438,8 @@ function renderTerminalSummaryReport(summaryData, options) {
|
|
|
37820
38438
|
layout: "compact",
|
|
37821
38439
|
multilineColumnIndex: 0,
|
|
37822
38440
|
multilineColumnWidth: 13,
|
|
37823
|
-
leftAlignedColumnIndexes: [3]
|
|
38441
|
+
leftAlignedColumnIndexes: [3],
|
|
38442
|
+
terminalWidth: options.terminalWidth ?? resolveTtyColumns(process.stdout)
|
|
37824
38443
|
})
|
|
37825
38444
|
);
|
|
37826
38445
|
const monthNotes = toMonthNotes(summaryData);
|
|
@@ -38291,6 +38910,10 @@ async function buildSummaryData(options, deps = {}) {
|
|
|
38291
38910
|
pricedEvents.filter((event) => isEventWithinWindow(event, monthToDateWindow, timezone)),
|
|
38292
38911
|
pricingSource
|
|
38293
38912
|
) : void 0;
|
|
38913
|
+
const bestDay = activity.bestDay?.costUsd === void 0 ? void 0 : activity.bestDay.date;
|
|
38914
|
+
const shownCostEvents = pricedEvents.filter(
|
|
38915
|
+
(event) => windows.some((window) => isEventWithinWindow(event, window, timezone)) || getPeriodKey(event.timestamp, "daily", timezone) === bestDay
|
|
38916
|
+
);
|
|
38294
38917
|
return {
|
|
38295
38918
|
timezone,
|
|
38296
38919
|
periods,
|
|
@@ -38308,7 +38931,7 @@ async function buildSummaryData(options, deps = {}) {
|
|
|
38308
38931
|
pricingOrigin,
|
|
38309
38932
|
pricingWarning,
|
|
38310
38933
|
warnings: dataset.warnings,
|
|
38311
|
-
notes: dataset.notes,
|
|
38934
|
+
notes: withUnpricedEventsNote(dataset.notes, shownCostEvents),
|
|
38312
38935
|
activeEnvOverrides: dataset.readEnvVarOverrides(),
|
|
38313
38936
|
activeConfig: dataset.activeConfig,
|
|
38314
38937
|
timezone,
|
|
@@ -38333,7 +38956,7 @@ async function prepareSummaryReport(options, deps = {}) {
|
|
|
38333
38956
|
title: "Activity share card",
|
|
38334
38957
|
render: (theme) => renderSummaryShareSvg(summaryData, theme)
|
|
38335
38958
|
}) : void 0,
|
|
38336
|
-
render: (summaryData, format) => renderSummaryReport(summaryData, format),
|
|
38959
|
+
render: (summaryData, format) => renderSummaryReport(summaryData, format, { filtered: hasCliFilters(options) }),
|
|
38337
38960
|
getHintsAfterOutput: (_summaryData, format) => format === "terminal" ? [SUMMARY_NEXT_STEPS_HINT] : []
|
|
38338
38961
|
});
|
|
38339
38962
|
}
|
|
@@ -38428,9 +39051,6 @@ function formatStatusline(summary, session) {
|
|
|
38428
39051
|
return parts.join(SEPARATOR);
|
|
38429
39052
|
}
|
|
38430
39053
|
async function buildStatuslineSummary(options, deps) {
|
|
38431
|
-
if (options.json) {
|
|
38432
|
-
throw new Error("--json is not supported for statusline; use llm-usage summary --json");
|
|
38433
|
-
}
|
|
38434
39054
|
return buildSummaryData({ ...options, pricingOffline: true, sync: false }, deps);
|
|
38435
39055
|
}
|
|
38436
39056
|
async function runStatusline(options, deps = {}) {
|
|
@@ -38577,7 +39197,7 @@ function renderSummaryOnly(trendsData, options) {
|
|
|
38577
39197
|
}
|
|
38578
39198
|
if (trendsData.totalSeries.summary.observedDayCount === 0) {
|
|
38579
39199
|
lines.push("No usage data found for the selected date range.");
|
|
38580
|
-
lines.
|
|
39200
|
+
return lines.join("\n");
|
|
38581
39201
|
}
|
|
38582
39202
|
if (shouldShowNoResolvedCostDataNote(trendsData)) {
|
|
38583
39203
|
lines.push(
|
|
@@ -38736,8 +39356,9 @@ function renderTerminalTrendsReport(trendsData, options) {
|
|
|
38736
39356
|
const terminalWidth = resolveTerminalWidth2(options.terminalWidth);
|
|
38737
39357
|
const accent = trendsData.metric === "cost" ? pc8.green : pc8.cyan;
|
|
38738
39358
|
const minimumChartWidth = 40;
|
|
38739
|
-
|
|
38740
|
-
|
|
39359
|
+
const isEmpty = trendsData.totalSeries.summary.observedDayCount === 0;
|
|
39360
|
+
if (isEmpty || terminalWidth !== void 0 && terminalWidth < minimumChartWidth) {
|
|
39361
|
+
return renderSummaryOnly(trendsData, { useColor, narrowNote: !isEmpty });
|
|
38741
39362
|
}
|
|
38742
39363
|
const lines = [
|
|
38743
39364
|
renderReportHeader({
|
|
@@ -38746,10 +39367,6 @@ function renderTerminalTrendsReport(trendsData, options) {
|
|
|
38746
39367
|
}),
|
|
38747
39368
|
""
|
|
38748
39369
|
];
|
|
38749
|
-
if (trendsData.totalSeries.summary.observedDayCount === 0) {
|
|
38750
|
-
lines.push("No usage data found for the selected date range.");
|
|
38751
|
-
lines.push("");
|
|
38752
|
-
}
|
|
38753
39370
|
if (shouldShowNoResolvedCostDataNote(trendsData)) {
|
|
38754
39371
|
lines.push(
|
|
38755
39372
|
"No resolved cost data for the selected range; use pricing or switch to --metric tokens."
|
|
@@ -39100,16 +39717,6 @@ function aggregateTrends(rows, options) {
|
|
|
39100
39717
|
}
|
|
39101
39718
|
|
|
39102
39719
|
// src/cli/build-trends-data.ts
|
|
39103
|
-
function parseDaysOption(days) {
|
|
39104
|
-
if (days === void 0) {
|
|
39105
|
-
return void 0;
|
|
39106
|
-
}
|
|
39107
|
-
const trimmedDays = days.trim();
|
|
39108
|
-
if (!/^[1-9]\d*$/u.test(trimmedDays)) {
|
|
39109
|
-
throw new Error("--days must be a positive integer");
|
|
39110
|
-
}
|
|
39111
|
-
return Number.parseInt(trimmedDays, 10);
|
|
39112
|
-
}
|
|
39113
39720
|
function resolveMetric(metric) {
|
|
39114
39721
|
if (metric === void 0 || metric.trim() === "") {
|
|
39115
39722
|
return "cost";
|
|
@@ -39180,8 +39787,13 @@ function resolveOutputDateRange(options, today, days, observedDates) {
|
|
|
39180
39787
|
};
|
|
39181
39788
|
}
|
|
39182
39789
|
function resolveTrendsOptions(options, timezone, now) {
|
|
39183
|
-
if (options.
|
|
39184
|
-
throw new Error("--days
|
|
39790
|
+
if (options.last !== void 0 && options.days !== void 0) {
|
|
39791
|
+
throw new Error("--days is an alias of --last; pass only --last");
|
|
39792
|
+
}
|
|
39793
|
+
const daysFlag = options.days === void 0 ? "--last" : "--days";
|
|
39794
|
+
const daysValue = options.last ?? options.days;
|
|
39795
|
+
if (daysValue !== void 0 && (options.since || options.until)) {
|
|
39796
|
+
throw new Error(`${daysFlag} cannot be combined with --since or --until`);
|
|
39185
39797
|
}
|
|
39186
39798
|
if (options.since) {
|
|
39187
39799
|
validateDateInput(options.since, "--since");
|
|
@@ -39193,7 +39805,7 @@ function resolveTrendsOptions(options, timezone, now) {
|
|
|
39193
39805
|
throw new Error("--since must be less than or equal to --until");
|
|
39194
39806
|
}
|
|
39195
39807
|
const metric = resolveMetric(options.metric);
|
|
39196
|
-
const days =
|
|
39808
|
+
const days = parseLastOption(daysValue, daysFlag);
|
|
39197
39809
|
const today = getCurrentLocalDateKey(timezone, now);
|
|
39198
39810
|
return {
|
|
39199
39811
|
days,
|
|
@@ -39300,6 +39912,10 @@ async function buildTrendsData(options, deps = {}) {
|
|
|
39300
39912
|
sourceOrder: dataset.adaptersToParse.map((adapter) => adapter.id)
|
|
39301
39913
|
})
|
|
39302
39914
|
);
|
|
39915
|
+
const chartedEvents = resolved.metric === "cost" ? pricingResult.pricedEvents.filter((event) => {
|
|
39916
|
+
const day = getPeriodKey(event.timestamp, "daily", dataset.normalizedInputs.timezone);
|
|
39917
|
+
return day >= outputDateRange.from && day <= outputDateRange.to;
|
|
39918
|
+
}) : [];
|
|
39303
39919
|
const diagnostics = buildUsageDiagnostics({
|
|
39304
39920
|
adaptersToParse: dataset.adaptersToParse,
|
|
39305
39921
|
successfulParseResults: dataset.successfulParseResults,
|
|
@@ -39307,7 +39923,7 @@ async function buildTrendsData(options, deps = {}) {
|
|
|
39307
39923
|
pricingOrigin: pricingResult.pricingOrigin,
|
|
39308
39924
|
pricingWarning: pricingResult.pricingWarning,
|
|
39309
39925
|
warnings: dataset.warnings,
|
|
39310
|
-
notes: dataset.notes,
|
|
39926
|
+
notes: withUnpricedEventsNote(dataset.notes, chartedEvents),
|
|
39311
39927
|
activeEnvOverrides: dataset.readEnvVarOverrides(),
|
|
39312
39928
|
activeConfig: dataset.activeConfig,
|
|
39313
39929
|
timezone: dataset.normalizedInputs.timezone,
|
|
@@ -39460,7 +40076,8 @@ function describeTableFit(fit, compact, hideCost = false) {
|
|
|
39460
40076
|
if (changes.length === 0) {
|
|
39461
40077
|
return void 0;
|
|
39462
40078
|
}
|
|
39463
|
-
|
|
40079
|
+
const layout = fit.stacked ? "Listed rows instead of a table to fit the terminal" : "Fitted the table to the terminal";
|
|
40080
|
+
return `${layout}: ${changes.join(", ")}. Widen the terminal or use --json for full detail.`;
|
|
39464
40081
|
}
|
|
39465
40082
|
function renderTerminalUsageReport(usageData, options) {
|
|
39466
40083
|
const outputLines = [];
|
|
@@ -39469,12 +40086,13 @@ function renderTerminalUsageReport(usageData, options) {
|
|
|
39469
40086
|
outputLines.push(
|
|
39470
40087
|
renderReportHeader({
|
|
39471
40088
|
title: getReportTitle4(options.granularity),
|
|
39472
|
-
useColor
|
|
40089
|
+
useColor,
|
|
40090
|
+
terminalWidth: options.terminalWidth
|
|
39473
40091
|
})
|
|
39474
40092
|
);
|
|
39475
40093
|
outputLines.push("");
|
|
39476
|
-
if (usageData.rows.
|
|
39477
|
-
outputLines.push(
|
|
40094
|
+
if (usageData.rows.every((row) => row.rowType === "grand_total")) {
|
|
40095
|
+
outputLines.push(formatNoUsageMessage(options.filtered ?? false));
|
|
39478
40096
|
return { output: outputLines.join("\n"), notes: [] };
|
|
39479
40097
|
}
|
|
39480
40098
|
const { output, fit } = renderTerminalTableWithFit(usageData.rows, {
|
|
@@ -39736,7 +40354,8 @@ async function prepareUsageReport(granularity, options, deps = {}) {
|
|
|
39736
40354
|
granularity,
|
|
39737
40355
|
tableLayout,
|
|
39738
40356
|
compact: options.compact,
|
|
39739
|
-
hideCost
|
|
40357
|
+
hideCost,
|
|
40358
|
+
filtered: hasCliFilters(options)
|
|
39740
40359
|
});
|
|
39741
40360
|
return { output, hintsAfterOutput: notes };
|
|
39742
40361
|
},
|
|
@@ -40406,9 +41025,6 @@ async function runPruneReport(options, deps = {}) {
|
|
|
40406
41025
|
var eventsExportFormats = ["jsonl", "csv"];
|
|
40407
41026
|
var CSV_HEADER = "source,sessionId,timestamp,repoRoot,provider,model,inputTokens,outputTokens,reasoningTokens,cacheReadTokens,cacheWriteTokens,totalTokens,costUsd,costMode,machine";
|
|
40408
41027
|
function resolveEventsFormat(options) {
|
|
40409
|
-
if (options.json) {
|
|
40410
|
-
throw new Error("--json is not supported for events; use --format jsonl");
|
|
40411
|
-
}
|
|
40412
41028
|
const format = options.format ?? "jsonl";
|
|
40413
41029
|
const resolved = eventsExportFormats.find((candidate) => candidate === format);
|
|
40414
41030
|
if (!resolved) {
|
|
@@ -40594,17 +41210,31 @@ function renderTopTable(headerCells, bodyRows) {
|
|
|
40594
41210
|
rowMetas,
|
|
40595
41211
|
layout: "compact",
|
|
40596
41212
|
multilineColumnIndex: TOP_ITEM_NAME_COLUMN,
|
|
40597
|
-
multilineColumnWidth: nameColumnWidth
|
|
41213
|
+
multilineColumnWidth: nameColumnWidth,
|
|
41214
|
+
terminalWidth: resolveTtyColumns(process.stdout)
|
|
40598
41215
|
});
|
|
40599
41216
|
}
|
|
41217
|
+
function toEmptyMessage(recap, filtered) {
|
|
41218
|
+
if (recap.eventCount > 0) {
|
|
41219
|
+
return void 0;
|
|
41220
|
+
}
|
|
41221
|
+
return filtered ? formatNoUsageMessage(true) : `No usage in ${recap.year}.`;
|
|
41222
|
+
}
|
|
40600
41223
|
function renderWrappedTerminalReport(recap, options) {
|
|
40601
41224
|
const useColor = options.useColor ?? shouldUseColorByDefault();
|
|
40602
41225
|
const palette = options.palette ?? defaultTerminalStylePalette;
|
|
40603
41226
|
const paint = (styler) => (text2) => useColor ? styler(text2) : text2;
|
|
40604
|
-
|
|
41227
|
+
const heading = [
|
|
40605
41228
|
renderReportHeader({ title: `Wrapped ${recap.year}`, useColor }),
|
|
40606
41229
|
paint(palette.dim)(`${recap.from} to ${recap.to} (${recap.timezone})`),
|
|
40607
|
-
""
|
|
41230
|
+
""
|
|
41231
|
+
];
|
|
41232
|
+
const emptyMessage = toEmptyMessage(recap, options.filtered ?? false);
|
|
41233
|
+
if (emptyMessage) {
|
|
41234
|
+
return [...heading, emptyMessage].join("\n");
|
|
41235
|
+
}
|
|
41236
|
+
return [
|
|
41237
|
+
...heading,
|
|
40608
41238
|
renderStatLines(recap, paint, palette),
|
|
40609
41239
|
"",
|
|
40610
41240
|
"Monthly activity",
|
|
@@ -41022,7 +41652,7 @@ async function buildWrappedData(options, deps = {}) {
|
|
|
41022
41652
|
pricingOrigin,
|
|
41023
41653
|
pricingWarning,
|
|
41024
41654
|
warnings: dataset.warnings,
|
|
41025
|
-
notes: dataset.notes,
|
|
41655
|
+
notes: withUnpricedEventsNote(dataset.notes, pricedEvents),
|
|
41026
41656
|
activeEnvOverrides: dataset.readEnvVarOverrides(),
|
|
41027
41657
|
activeConfig: dataset.activeConfig,
|
|
41028
41658
|
timezone: dataset.normalizedInputs.timezone,
|
|
@@ -41051,7 +41681,7 @@ async function prepareWrappedReport(options, deps = {}) {
|
|
|
41051
41681
|
title: `${wrappedData.recap.year} Wrapped share card`,
|
|
41052
41682
|
render: (theme) => renderWrappedShareSvg(wrappedData.recap, theme)
|
|
41053
41683
|
}) : void 0,
|
|
41054
|
-
render: (wrappedData, format) => renderWrappedReport(wrappedData.recap, format)
|
|
41684
|
+
render: (wrappedData, format) => renderWrappedReport(wrappedData.recap, format, { filtered: hasCliFilters(options) })
|
|
41055
41685
|
});
|
|
41056
41686
|
}
|
|
41057
41687
|
async function runWrappedReport(options) {
|
|
@@ -41064,102 +41694,144 @@ async function runWrappedReport(options) {
|
|
|
41064
41694
|
import { Option } from "commander";
|
|
41065
41695
|
var sharedOptionProfileConfig = {
|
|
41066
41696
|
usage: {
|
|
41697
|
+
includeJson: true,
|
|
41698
|
+
includeQuiet: true,
|
|
41067
41699
|
includeDateFilters: true,
|
|
41068
41700
|
includeMarkdown: true,
|
|
41069
41701
|
includePerModelColumns: true,
|
|
41070
41702
|
includePricing: true,
|
|
41703
|
+
includePricingFetch: true,
|
|
41071
41704
|
includeProviderModelFilters: true,
|
|
41072
41705
|
includeHistory: true,
|
|
41706
|
+
includeSync: true,
|
|
41073
41707
|
includeShare: true,
|
|
41074
41708
|
includeTimezone: true
|
|
41075
41709
|
},
|
|
41076
41710
|
summary: {
|
|
41711
|
+
includeJson: true,
|
|
41712
|
+
includeQuiet: true,
|
|
41077
41713
|
includeDateFilters: false,
|
|
41078
41714
|
includeMarkdown: true,
|
|
41079
41715
|
includePerModelColumns: false,
|
|
41080
41716
|
includePricing: true,
|
|
41717
|
+
includePricingFetch: true,
|
|
41081
41718
|
includeProviderModelFilters: true,
|
|
41082
41719
|
includeHistory: true,
|
|
41720
|
+
includeSync: true,
|
|
41083
41721
|
includeShare: true,
|
|
41084
41722
|
includeTimezone: true
|
|
41085
41723
|
},
|
|
41086
41724
|
specialized: {
|
|
41725
|
+
includeJson: true,
|
|
41726
|
+
includeQuiet: true,
|
|
41087
41727
|
includeDateFilters: true,
|
|
41088
41728
|
includeMarkdown: true,
|
|
41089
41729
|
includePerModelColumns: false,
|
|
41090
41730
|
includePricing: true,
|
|
41731
|
+
includePricingFetch: true,
|
|
41091
41732
|
includeProviderModelFilters: true,
|
|
41092
41733
|
includeHistory: true,
|
|
41734
|
+
includeSync: true,
|
|
41093
41735
|
includeShare: true,
|
|
41094
41736
|
includeTimezone: true
|
|
41095
41737
|
},
|
|
41096
41738
|
compare: {
|
|
41739
|
+
includeJson: true,
|
|
41740
|
+
includeQuiet: true,
|
|
41097
41741
|
includeDateFilters: true,
|
|
41098
41742
|
includeMarkdown: true,
|
|
41099
41743
|
includePerModelColumns: false,
|
|
41100
41744
|
includePricing: true,
|
|
41745
|
+
includePricingFetch: true,
|
|
41101
41746
|
includeProviderModelFilters: true,
|
|
41102
41747
|
includeHistory: true,
|
|
41748
|
+
includeSync: true,
|
|
41103
41749
|
includeShare: true,
|
|
41104
41750
|
includeTimezone: true
|
|
41105
41751
|
},
|
|
41106
41752
|
trends: {
|
|
41753
|
+
includeJson: true,
|
|
41754
|
+
includeQuiet: true,
|
|
41107
41755
|
includeDateFilters: true,
|
|
41108
41756
|
includeMarkdown: true,
|
|
41109
41757
|
includePerModelColumns: false,
|
|
41110
41758
|
includePricing: true,
|
|
41759
|
+
includePricingFetch: true,
|
|
41111
41760
|
includeProviderModelFilters: true,
|
|
41112
41761
|
includeHistory: true,
|
|
41762
|
+
includeSync: true,
|
|
41113
41763
|
includeShare: true,
|
|
41114
41764
|
includeTimezone: true
|
|
41115
41765
|
},
|
|
41116
41766
|
session: {
|
|
41767
|
+
includeJson: true,
|
|
41768
|
+
includeQuiet: true,
|
|
41117
41769
|
includeDateFilters: true,
|
|
41118
41770
|
includeMarkdown: true,
|
|
41119
41771
|
includePerModelColumns: false,
|
|
41120
41772
|
includePricing: true,
|
|
41773
|
+
includePricingFetch: true,
|
|
41121
41774
|
includeProviderModelFilters: true,
|
|
41122
41775
|
includeHistory: true,
|
|
41776
|
+
includeSync: true,
|
|
41123
41777
|
includeShare: false,
|
|
41124
41778
|
includeTimezone: true
|
|
41125
41779
|
},
|
|
41126
41780
|
wrapped: {
|
|
41781
|
+
includeJson: true,
|
|
41782
|
+
includeQuiet: true,
|
|
41127
41783
|
includeDateFilters: false,
|
|
41128
41784
|
includeMarkdown: true,
|
|
41129
41785
|
includePerModelColumns: false,
|
|
41130
41786
|
includePricing: true,
|
|
41787
|
+
includePricingFetch: true,
|
|
41131
41788
|
includeProviderModelFilters: true,
|
|
41132
41789
|
includeHistory: true,
|
|
41790
|
+
includeSync: true,
|
|
41133
41791
|
includeShare: true,
|
|
41134
41792
|
includeTimezone: true
|
|
41135
41793
|
},
|
|
41136
41794
|
events: {
|
|
41795
|
+
includeJson: false,
|
|
41796
|
+
jsonAlternative: "use --format jsonl",
|
|
41797
|
+
includeQuiet: true,
|
|
41137
41798
|
includeDateFilters: true,
|
|
41138
41799
|
includeMarkdown: false,
|
|
41139
41800
|
includePerModelColumns: false,
|
|
41140
41801
|
includePricing: true,
|
|
41802
|
+
includePricingFetch: true,
|
|
41141
41803
|
includeProviderModelFilters: true,
|
|
41142
41804
|
includeHistory: true,
|
|
41805
|
+
includeSync: true,
|
|
41143
41806
|
includeShare: false,
|
|
41144
41807
|
includeTimezone: true
|
|
41145
41808
|
},
|
|
41146
41809
|
statusline: {
|
|
41810
|
+
includeJson: false,
|
|
41811
|
+
jsonAlternative: "use llm-usage summary --json",
|
|
41812
|
+
includeQuiet: false,
|
|
41147
41813
|
includeDateFilters: false,
|
|
41148
41814
|
includeMarkdown: false,
|
|
41149
41815
|
includePerModelColumns: false,
|
|
41150
41816
|
includePricing: true,
|
|
41817
|
+
includePricingFetch: false,
|
|
41151
41818
|
includeProviderModelFilters: true,
|
|
41152
41819
|
includeHistory: true,
|
|
41820
|
+
includeSync: false,
|
|
41153
41821
|
includeShare: false,
|
|
41154
41822
|
includeTimezone: true
|
|
41155
41823
|
},
|
|
41156
41824
|
doctor: {
|
|
41825
|
+
includeJson: true,
|
|
41826
|
+
includeQuiet: true,
|
|
41157
41827
|
includeDateFilters: false,
|
|
41158
41828
|
includeMarkdown: false,
|
|
41159
41829
|
includePerModelColumns: false,
|
|
41160
41830
|
includePricing: false,
|
|
41831
|
+
includePricingFetch: false,
|
|
41161
41832
|
includeProviderModelFilters: false,
|
|
41162
41833
|
includeHistory: false,
|
|
41834
|
+
includeSync: false,
|
|
41163
41835
|
includeShare: false,
|
|
41164
41836
|
includeTimezone: false
|
|
41165
41837
|
}
|
|
@@ -41200,6 +41872,20 @@ function suggestHiddenOptionsOnTypo(command) {
|
|
|
41200
41872
|
}
|
|
41201
41873
|
};
|
|
41202
41874
|
}
|
|
41875
|
+
function redirectUnsupportedJson(command, alternative) {
|
|
41876
|
+
if (!hasUnknownOptionHook(command)) {
|
|
41877
|
+
return;
|
|
41878
|
+
}
|
|
41879
|
+
const reportUnknownOption = command.unknownOption.bind(command);
|
|
41880
|
+
command.unknownOption = (flag) => {
|
|
41881
|
+
if (flag === "--json") {
|
|
41882
|
+
command.error(`error: --json is not supported for ${command.name()}; ${alternative}`, {
|
|
41883
|
+
code: "commander.unknownOption"
|
|
41884
|
+
});
|
|
41885
|
+
}
|
|
41886
|
+
reportUnknownOption(flag);
|
|
41887
|
+
};
|
|
41888
|
+
}
|
|
41203
41889
|
function registerSourcePathOptions(command) {
|
|
41204
41890
|
for (const overrideOption of getSourceOverrideOptions()) {
|
|
41205
41891
|
const option = new Option(
|
|
@@ -41216,7 +41902,7 @@ function registerSourcePathOptions(command) {
|
|
|
41216
41902
|
command.help();
|
|
41217
41903
|
}).addHelpText(
|
|
41218
41904
|
"after",
|
|
41219
|
-
() => hasHiddenOptions(command) ? "\nPer-source path flags (--claude-dir,
|
|
41905
|
+
() => hasHiddenOptions(command) ? "\nPer-source path flags (--claude-dir, ...)\nare listed by --help-all." : ""
|
|
41220
41906
|
);
|
|
41221
41907
|
suggestHiddenOptionsOnTypo(command);
|
|
41222
41908
|
}
|
|
@@ -41241,7 +41927,19 @@ function registerSharedReportOptions(command, profile) {
|
|
|
41241
41927
|
"--source <name>",
|
|
41242
41928
|
`Filter by source id (repeatable or comma-separated, supported sources ${supportedSourcesSummary})`,
|
|
41243
41929
|
collectRepeatedOption
|
|
41244
|
-
)
|
|
41930
|
+
);
|
|
41931
|
+
if (profileConfig.includeJson) {
|
|
41932
|
+
configuredCommand.option("--json", "Render output as JSON");
|
|
41933
|
+
} else {
|
|
41934
|
+
redirectUnsupportedJson(configuredCommand, profileConfig.jsonAlternative);
|
|
41935
|
+
}
|
|
41936
|
+
if (profileConfig.includeQuiet) {
|
|
41937
|
+
configuredCommand.option(
|
|
41938
|
+
"--quiet",
|
|
41939
|
+
"Suppress informational stderr output (warnings still print)"
|
|
41940
|
+
);
|
|
41941
|
+
}
|
|
41942
|
+
configuredCommand.option(
|
|
41245
41943
|
"--verbose",
|
|
41246
41944
|
"Print detailed diagnostics on stderr (per-source counts, routine skips, config, env)"
|
|
41247
41945
|
);
|
|
@@ -41265,10 +41963,20 @@ function registerSharedReportOptions(command, profile) {
|
|
|
41265
41963
|
);
|
|
41266
41964
|
}
|
|
41267
41965
|
if (profileConfig.includePricing) {
|
|
41268
|
-
|
|
41966
|
+
if (profileConfig.includePricingFetch) {
|
|
41967
|
+
configuredCommand.option("--pricing-url <url>", "Override LiteLLM pricing source URL");
|
|
41968
|
+
}
|
|
41969
|
+
configuredCommand.option(
|
|
41269
41970
|
"--pricing-overrides <path>",
|
|
41270
41971
|
"Path to a JSON file of per-model pricing overrides (takes precedence over LiteLLM)"
|
|
41271
|
-
)
|
|
41972
|
+
);
|
|
41973
|
+
if (profileConfig.includePricingFetch) {
|
|
41974
|
+
configuredCommand.option(
|
|
41975
|
+
"--pricing-offline",
|
|
41976
|
+
"Use cached LiteLLM pricing only (no network fetch)"
|
|
41977
|
+
);
|
|
41978
|
+
}
|
|
41979
|
+
configuredCommand.option(
|
|
41272
41980
|
"--ignore-pricing-failures",
|
|
41273
41981
|
"Continue without estimated costs when pricing cannot be loaded"
|
|
41274
41982
|
);
|
|
@@ -41276,12 +41984,18 @@ function registerSharedReportOptions(command, profile) {
|
|
|
41276
41984
|
if (profileConfig.includeHistory) {
|
|
41277
41985
|
configuredCommand.option(
|
|
41278
41986
|
"--history",
|
|
41279
|
-
"
|
|
41280
|
-
).option("--no-history", "
|
|
41987
|
+
"Include usage from files that no longer exist on disk (on by default; when passed, fails if the event store is unavailable)"
|
|
41988
|
+
).option("--no-history", "Leave out usage from files that no longer exist on disk").option(
|
|
41281
41989
|
"--machine <name>",
|
|
41282
41990
|
"Count only these machines (repeatable or comma-separated; local is this one; default: all)",
|
|
41283
41991
|
collectRepeatedOption
|
|
41284
|
-
)
|
|
41992
|
+
);
|
|
41993
|
+
if (profileConfig.includeSync) {
|
|
41994
|
+
configuredCommand.option(
|
|
41995
|
+
"--no-sync",
|
|
41996
|
+
"Use other machines' cached usage without refreshing it over ssh"
|
|
41997
|
+
);
|
|
41998
|
+
}
|
|
41285
41999
|
}
|
|
41286
42000
|
if (profileConfig.includeMarkdown) {
|
|
41287
42001
|
configuredCommand.option("--markdown", "Render output as markdown table");
|
|
@@ -41323,6 +42037,15 @@ function parseGranularityArgument(value) {
|
|
|
41323
42037
|
`Invalid granularity: ${value}. Expected one of: ${GRANULARITY_ARGUMENT_VALUES.join(", ")}`
|
|
41324
42038
|
);
|
|
41325
42039
|
}
|
|
42040
|
+
function addGranularityArgument(command) {
|
|
42041
|
+
const reportMissingGranularity = (name) => command.error(
|
|
42042
|
+
`error: missing required argument '${name}' (choose ${GRANULARITY_ARGUMENT_VALUES.join(", ")})`,
|
|
42043
|
+
{ code: "commander.missingArgument" }
|
|
42044
|
+
);
|
|
42045
|
+
return Object.assign(command, { missingArgument: reportMissingGranularity }).addArgument(
|
|
42046
|
+
new Argument("<granularity>", "Report period").choices(GRANULARITY_ARGUMENT_VALUES).argParser(parseGranularityArgument)
|
|
42047
|
+
);
|
|
42048
|
+
}
|
|
41326
42049
|
var MAX_COMMAND_HELP_EXAMPLES = 4;
|
|
41327
42050
|
function formatHelpExamples(commands) {
|
|
41328
42051
|
return commands.length === 0 ? "" : ["", "Examples:", ...commands.map((command) => ` $ ${command}`)].join("\n");
|
|
@@ -41507,7 +42230,7 @@ var efficiencyReportDefinition = {
|
|
|
41507
42230
|
]
|
|
41508
42231
|
},
|
|
41509
42232
|
register(command) {
|
|
41510
|
-
command
|
|
42233
|
+
addGranularityArgument(command).option("--repo-dir <path>", "Path to repository for Git outcome metrics").option("--include-merge-commits", "Include merge commits in Git outcome metrics").option("--by-source", "Include per-source usage rows under each period").action(
|
|
41511
42234
|
(granularity, options) => runEfficiencyReport(granularity, options)
|
|
41512
42235
|
);
|
|
41513
42236
|
return command;
|
|
@@ -41557,7 +42280,7 @@ var optimizeReportDefinition = {
|
|
|
41557
42280
|
]
|
|
41558
42281
|
},
|
|
41559
42282
|
register(command) {
|
|
41560
|
-
command
|
|
42283
|
+
addGranularityArgument(command).option(
|
|
41561
42284
|
"--candidate-model <name>",
|
|
41562
42285
|
"Candidate model for counterfactual pricing (repeatable or comma-separated)",
|
|
41563
42286
|
collectRepeatedOption
|
|
@@ -41581,7 +42304,7 @@ var trendsReportDefinition = {
|
|
|
41581
42304
|
includeInCliReference: true
|
|
41582
42305
|
},
|
|
41583
42306
|
{
|
|
41584
|
-
command: "llm-usage trends --metric tokens --
|
|
42307
|
+
command: "llm-usage trends --metric tokens --last 7",
|
|
41585
42308
|
includeInCliReference: true
|
|
41586
42309
|
},
|
|
41587
42310
|
{
|
|
@@ -41592,9 +42315,9 @@ var trendsReportDefinition = {
|
|
|
41592
42315
|
},
|
|
41593
42316
|
register(command) {
|
|
41594
42317
|
command.option(
|
|
41595
|
-
"--
|
|
41596
|
-
"
|
|
41597
|
-
).option("--metric <name>", "Trend metric: cost | tokens | active-hours", "cost").option(
|
|
42318
|
+
"--last <n>",
|
|
42319
|
+
"Chart the last N local calendar days, counting today; defaults to 30 when no date flags are provided"
|
|
42320
|
+
).addOption(new Option2("--days <n>", "Alias of --last").hideHelp()).option("--metric <name>", "Trend metric: cost | tokens | active-hours", "cost").option(
|
|
41598
42321
|
"--by-source",
|
|
41599
42322
|
"Render one sparkline row per source instead of a single combined chart"
|
|
41600
42323
|
).action((options) => runTrendsReport(options));
|
|
@@ -42015,7 +42738,7 @@ async function runMachineExport(options, deps = {}) {
|
|
|
42015
42738
|
knownFiles
|
|
42016
42739
|
);
|
|
42017
42740
|
const end = lines.at(-1);
|
|
42018
|
-
const missingEventCount = dataset.filteredEvents.length - (end?.type === "end" ? end.eventCount : 0);
|
|
42741
|
+
const missingEventCount = dataset.filteredEvents.length + dataset.ledger.copiedEventCount - (end?.type === "end" ? end.eventCount : 0);
|
|
42019
42742
|
if (missingEventCount > 0) {
|
|
42020
42743
|
logger.warn(
|
|
42021
42744
|
`machine export left out ${missingEventCount} event(s) that reports count but the event store does not hold`
|
|
@@ -44701,6 +45424,29 @@ function rejectUnknownCommand(program, args) {
|
|
|
44701
45424
|
(Did you mean ${suggestion}?)` : "";
|
|
44702
45425
|
program.error(`error: unknown command '${unknownCommand}'${hint}`);
|
|
44703
45426
|
}
|
|
45427
|
+
var SIDE_BY_SIDE_MIN_DESCRIPTION_WIDTH = 40;
|
|
45428
|
+
var STACKED_DESCRIPTION_INDENT = " ";
|
|
45429
|
+
function formatHelpItem(term, termWidth, description, helper) {
|
|
45430
|
+
const helpWidth = this.helpWidth ?? 80;
|
|
45431
|
+
const sideBySideWidth = helpWidth - termWidth - 4;
|
|
45432
|
+
if (!description || sideBySideWidth >= SIDE_BY_SIDE_MIN_DESCRIPTION_WIDTH) {
|
|
45433
|
+
return Help.prototype.formatItem.call(this, term, termWidth, description, helper);
|
|
45434
|
+
}
|
|
45435
|
+
const wrappedDescription = helper.boxWrap(
|
|
45436
|
+
description,
|
|
45437
|
+
helpWidth - STACKED_DESCRIPTION_INDENT.length
|
|
45438
|
+
);
|
|
45439
|
+
return [
|
|
45440
|
+
` ${term}`,
|
|
45441
|
+
...wrappedDescription.split("\n").map((line) => `${STACKED_DESCRIPTION_INDENT}${line}`)
|
|
45442
|
+
].join("\n");
|
|
45443
|
+
}
|
|
45444
|
+
function wrapHelpOnNarrowTerminals(command) {
|
|
45445
|
+
command.configureHelp({ minWidthToWrap: 20, formatItem: formatHelpItem });
|
|
45446
|
+
for (const subcommand of command.commands) {
|
|
45447
|
+
wrapHelpOnNarrowTerminals(subcommand);
|
|
45448
|
+
}
|
|
45449
|
+
}
|
|
44704
45450
|
function createCli(options = {}) {
|
|
44705
45451
|
const program = new Command6();
|
|
44706
45452
|
program.name("llm-usage").description(createRootDescription()).version(options.version ?? "0.0.0").showHelpAfterError();
|
|
@@ -44736,6 +45482,7 @@ function createCli(options = {}) {
|
|
|
44736
45482
|
setLogLevel("debug");
|
|
44737
45483
|
}
|
|
44738
45484
|
});
|
|
45485
|
+
wrapHelpOnNarrowTerminals(program);
|
|
44739
45486
|
return program;
|
|
44740
45487
|
}
|
|
44741
45488
|
|