llm-usage-metrics 0.5.2 → 0.6.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/dist/index.js CHANGED
@@ -889,9 +889,6 @@ ${noData}
889
889
  import { markdownTable } from "markdown-table";
890
890
  import pc4 from "picocolors";
891
891
 
892
- // src/render/report-header.ts
893
- import pc from "picocolors";
894
-
895
892
  // src/render/table-text-layout.ts
896
893
  var ansiEscapePattern = new RegExp(String.raw`\u001B\[[0-9;]*m`, "gu");
897
894
  var combiningMarkPattern = /\p{Mark}/u;
@@ -1065,7 +1062,30 @@ function wrapTableColumn(rows, options) {
1065
1062
  });
1066
1063
  }
1067
1064
 
1065
+ // src/render/markdown-safe-cell.ts
1066
+ var markdownSpecialCharacterPattern = /[\\`*_~[\]()!|]/gu;
1067
+ var bareUrlPattern = /\bhttps?:\/\/[^\s<]+|\bwww\.[^\s<]+/giu;
1068
+ var bareEmailPattern = /(^|[^\w.+-])([\w.+-]+@[\w.-]+\.[a-z]{2,})(?=$|[^\w.-])/giu;
1069
+ function escapeBareAutolinks(value) {
1070
+ const withoutBareUrls = value.replace(
1071
+ bareUrlPattern,
1072
+ (match) => match.startsWith("www.") ? match.replace("www.", "www\\.") : match.replace("://", "\\://")
1073
+ );
1074
+ return withoutBareUrls.replace(
1075
+ bareEmailPattern,
1076
+ (_, prefix, email) => `${prefix}${email.replace("@", "\\@")}`
1077
+ );
1078
+ }
1079
+ function escapeMarkdownText(value) {
1080
+ const escapedMarkdownText = value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replace(markdownSpecialCharacterPattern, "\\$&");
1081
+ return escapeBareAutolinks(escapedMarkdownText);
1082
+ }
1083
+ function toMarkdownSafeCell(value) {
1084
+ return splitCellLines(value).map((line) => escapeMarkdownText(line)).join("<br>");
1085
+ }
1086
+
1068
1087
  // src/render/report-header.ts
1088
+ import pc from "picocolors";
1069
1089
  function getBoxWidth(content) {
1070
1090
  return visibleWidth(content) + 4;
1071
1091
  }
@@ -1188,12 +1208,68 @@ var defaultTerminalStylePalette = {
1188
1208
  dim: pc2.dim
1189
1209
  };
1190
1210
  var passthroughStyler = (text) => text;
1211
+ var periodColumnIndex = 0;
1212
+ var sourceColumnIndex = 1;
1213
+ var modelsColumnIndex = 2;
1214
+ var totalColumnIndex = 8;
1215
+ var packedModelStartPattern = /(^| {2,})(?=• )/gu;
1191
1216
  function styleCellLines(cell, styler) {
1192
- return cell.split("\n").map((line) => line.length === 0 ? "" : styler(line)).join("\n");
1217
+ return splitCellLines(cell).map((line) => line.length === 0 ? "" : styler(line)).join("\n");
1218
+ }
1219
+ function styleModelsCell(cell, palette, options = {}) {
1220
+ const emphasizePrimaryWhenSingleLine = options.emphasizePrimaryWhenSingleLine ?? false;
1221
+ const primaryStyler = options.primaryStyler ?? palette.bold;
1222
+ const secondaryStyler = options.secondaryStyler ?? passthroughStyler;
1223
+ const totalStyler = options.totalStyler ?? ((text) => palette.bold(palette.green(text)));
1224
+ const lines = splitCellLines(cell);
1225
+ const modelEntryCount = lines.reduce((count, line) => {
1226
+ if (line === "\u03A3 TOTAL") {
1227
+ return count;
1228
+ }
1229
+ const segmentStarts = [...line.matchAll(packedModelStartPattern)];
1230
+ return count + segmentStarts.length;
1231
+ }, 0);
1232
+ const shouldEmphasizePrimary = emphasizePrimaryWhenSingleLine || modelEntryCount > 1;
1233
+ let currentStyler = shouldEmphasizePrimary ? primaryStyler : passthroughStyler;
1234
+ let modelLineCount = 0;
1235
+ return lines.map((line) => {
1236
+ if (line.length === 0) {
1237
+ return "";
1238
+ }
1239
+ if (line === "\u03A3 TOTAL") {
1240
+ currentStyler = totalStyler;
1241
+ return totalStyler(line);
1242
+ }
1243
+ const segmentStarts = [...line.matchAll(packedModelStartPattern)].map((match) => match.index);
1244
+ if (segmentStarts.length === 0) {
1245
+ return currentStyler(line);
1246
+ }
1247
+ const prefix = segmentStarts[0] > 0 ? currentStyler(line.slice(0, segmentStarts[0])) : "";
1248
+ let nextContinuationStyler = currentStyler;
1249
+ const renderedSegments = segmentStarts.map((segmentStart, segmentIndex) => {
1250
+ const segmentStyler = modelLineCount === 0 && shouldEmphasizePrimary ? primaryStyler : secondaryStyler;
1251
+ modelLineCount += 1;
1252
+ nextContinuationStyler = segmentStyler;
1253
+ const segmentEnd = segmentStarts[segmentIndex + 1] ?? line.length;
1254
+ return segmentStyler(line.slice(segmentStart, segmentEnd));
1255
+ }).join("");
1256
+ currentStyler = nextContinuationStyler;
1257
+ return prefix + renderedSegments;
1258
+ }).join("\n");
1259
+ }
1260
+ function styleCellAtIndex(cells, index, styler) {
1261
+ if (index < 0 || index >= cells.length) {
1262
+ return cells;
1263
+ }
1264
+ const styledCells = [...cells];
1265
+ styledCells[index] = styleCellLines(styledCells[index], styler);
1266
+ return styledCells;
1193
1267
  }
1194
1268
  var sourceStylePolicies = /* @__PURE__ */ new Map([
1195
1269
  ["pi", (palette) => palette.cyan],
1196
1270
  ["codex", (palette) => palette.magenta],
1271
+ ["gemini", (palette) => palette.yellow],
1272
+ ["droid", (palette) => palette.green],
1197
1273
  ["opencode", (palette) => palette.blue]
1198
1274
  ]);
1199
1275
  function resolveSourceStyler(source, palette = defaultTerminalStylePalette) {
@@ -1203,28 +1279,98 @@ function resolveSourceStyler(source, palette = defaultTerminalStylePalette) {
1203
1279
  }
1204
1280
  return stylePolicy(palette);
1205
1281
  }
1282
+ function getTrailingColumnIndex(cells) {
1283
+ return cells.length === 0 ? void 0 : cells.length - 1;
1284
+ }
1206
1285
  var rowTypeStylePolicies = {
1207
1286
  period_source: (cells, palette) => {
1208
- const styledCells = [...cells];
1209
- const costColumnIndex2 = styledCells.length - 1;
1210
- styledCells[costColumnIndex2] = styleCellLines(styledCells[costColumnIndex2], palette.yellow);
1287
+ let styledCells = [...cells];
1288
+ const trailingColumnIndex = getTrailingColumnIndex(styledCells);
1289
+ if (modelsColumnIndex < styledCells.length) {
1290
+ styledCells[modelsColumnIndex] = styleModelsCell(styledCells[modelsColumnIndex], palette, {
1291
+ secondaryStyler: palette.dim
1292
+ });
1293
+ }
1294
+ styledCells = styleCellAtIndex(styledCells, totalColumnIndex, palette.green);
1295
+ if (trailingColumnIndex !== void 0) {
1296
+ styledCells = styleCellAtIndex(
1297
+ styledCells,
1298
+ trailingColumnIndex,
1299
+ (line) => palette.bold(palette.yellow(line))
1300
+ );
1301
+ }
1302
+ return styledCells;
1303
+ },
1304
+ period_combined: (cells, palette) => {
1305
+ let styledCells = [...cells];
1306
+ const trailingColumnIndex = getTrailingColumnIndex(styledCells);
1307
+ styledCells = styleCellAtIndex(styledCells, periodColumnIndex, palette.white);
1308
+ styledCells = styleCellAtIndex(
1309
+ styledCells,
1310
+ sourceColumnIndex,
1311
+ (line) => palette.bold(palette.yellow(line))
1312
+ );
1313
+ if (modelsColumnIndex < styledCells.length) {
1314
+ styledCells[modelsColumnIndex] = styleModelsCell(styledCells[modelsColumnIndex], palette, {
1315
+ secondaryStyler: palette.dim
1316
+ });
1317
+ }
1318
+ styledCells = styleCellAtIndex(
1319
+ styledCells,
1320
+ totalColumnIndex,
1321
+ (line) => palette.bold(palette.green(line))
1322
+ );
1323
+ if (trailingColumnIndex !== void 0) {
1324
+ styledCells = styleCellAtIndex(
1325
+ styledCells,
1326
+ trailingColumnIndex,
1327
+ (line) => palette.bold(palette.yellow(line))
1328
+ );
1329
+ }
1211
1330
  return styledCells;
1212
1331
  },
1213
- period_combined: (cells, palette) => cells.map((cell, cellIndex) => {
1214
- if (cellIndex === 1) {
1215
- return styleCellLines(cell, (line) => palette.bold(palette.yellow(line)));
1332
+ grand_total: (cells, palette) => {
1333
+ const styledCells = cells.map((cell, cellIndex) => {
1334
+ if (cellIndex === modelsColumnIndex) {
1335
+ return cell;
1336
+ }
1337
+ return styleCellLines(cell, palette.bold);
1338
+ });
1339
+ if (periodColumnIndex < styledCells.length) {
1340
+ styledCells[periodColumnIndex] = styleCellLines(
1341
+ cells[periodColumnIndex],
1342
+ (line) => palette.bold(palette.white(line))
1343
+ );
1216
1344
  }
1217
- return styleCellLines(cell, palette.dim);
1218
- }),
1219
- grand_total: (cells, palette) => cells.map((cell, cellIndex) => {
1220
- if (cellIndex === 0) {
1221
- return styleCellLines(cell, (line) => palette.bold(palette.white(line)));
1345
+ if (sourceColumnIndex < styledCells.length) {
1346
+ styledCells[sourceColumnIndex] = styleCellLines(
1347
+ cells[sourceColumnIndex],
1348
+ (line) => palette.bold(palette.green(line))
1349
+ );
1350
+ }
1351
+ if (modelsColumnIndex < styledCells.length) {
1352
+ styledCells[modelsColumnIndex] = styleModelsCell(styledCells[modelsColumnIndex], palette, {
1353
+ emphasizePrimaryWhenSingleLine: true,
1354
+ primaryStyler: (line) => palette.bold(palette.white(line)),
1355
+ secondaryStyler: palette.dim,
1356
+ totalStyler: (line) => palette.bold(palette.green(line))
1357
+ });
1222
1358
  }
1223
- if (cellIndex === 1) {
1224
- return styleCellLines(cell, (line) => palette.bold(palette.green(line)));
1359
+ if (totalColumnIndex < styledCells.length) {
1360
+ styledCells[totalColumnIndex] = styleCellLines(
1361
+ cells[totalColumnIndex],
1362
+ (line) => palette.bold(palette.green(line))
1363
+ );
1364
+ }
1365
+ const trailingColumnIndex = getTrailingColumnIndex(styledCells);
1366
+ if (trailingColumnIndex !== void 0) {
1367
+ styledCells[trailingColumnIndex] = styleCellLines(
1368
+ cells[trailingColumnIndex],
1369
+ (line) => palette.bold(palette.yellow(line))
1370
+ );
1225
1371
  }
1226
- return styleCellLines(cell, palette.bold);
1227
- })
1372
+ return styledCells;
1373
+ }
1228
1374
  };
1229
1375
  function applyRowTypeStyle(rowType, cells, palette = defaultTerminalStylePalette) {
1230
1376
  return rowTypeStylePolicies[rowType](cells, palette);
@@ -1244,8 +1390,9 @@ function colorizeUsageBodyRows(bodyRows, rows, options) {
1244
1390
  }
1245
1391
  const palette = options.palette ?? defaultTerminalStylePalette;
1246
1392
  return rows.map((row, index) => {
1393
+ const bodyRow = bodyRows[index] ?? [];
1247
1394
  const sourceStyler = row.rowType === "period_source" ? resolveSourceStyler(String(row.source), palette) : passthroughStyler;
1248
- const baseStyledCells = applyBaseCellStyle(bodyRows[index], palette, sourceStyler);
1395
+ const baseStyledCells = row.rowType === "period_source" ? applyBaseCellStyle(bodyRow, palette, sourceStyler) : [...bodyRow];
1249
1396
  return applyRowTypeStyle(row.rowType, baseStyledCells, palette);
1250
1397
  });
1251
1398
  }
@@ -1348,7 +1495,7 @@ function getColumnAlignment(columnIndex, multilineColumnIndex) {
1348
1495
  return "right";
1349
1496
  }
1350
1497
  function getVerticalAlignment(columnIndex, layout, multilineColumnIndex) {
1351
- if (columnIndex === multilineColumnIndex) {
1498
+ if (columnIndex <= multilineColumnIndex) {
1352
1499
  return "top";
1353
1500
  }
1354
1501
  return layout === "top_aligned" ? "top" : "middle";
@@ -1406,7 +1553,7 @@ function shouldDrawBodySeparator(index, rowMetas) {
1406
1553
  }
1407
1554
  const previousRow = rowMetas[index];
1408
1555
  const nextRow = rowMetas[index + 1];
1409
- return previousRow.rowKind === "combined" || nextRow.rowKind === "total" || previousRow.periodKey !== nextRow.periodKey;
1556
+ return previousRow.rowKind === "detail" && (nextRow.rowKind === "detail" || nextRow.rowKind === "combined") && previousRow.periodKey === nextRow.periodKey || previousRow.rowKind === "combined" || nextRow.rowKind === "total" || previousRow.periodKey !== nextRow.periodKey;
1410
1557
  }
1411
1558
  function getRowKindWeight(rowKind) {
1412
1559
  switch (rowKind) {
@@ -1566,9 +1713,10 @@ function renderUnicodeTable(options) {
1566
1713
  }
1567
1714
 
1568
1715
  // src/render/terminal-table.ts
1569
- var modelsColumnIndex = 2;
1716
+ var modelsColumnIndex2 = 2;
1570
1717
  var defaultModelsColumnWidth = 32;
1571
1718
  var minimumModelsColumnWidth = 12;
1719
+ var compactModelsColumnGap = 2;
1572
1720
  function shouldUseColorByDefault() {
1573
1721
  if (process.env.NO_COLOR !== void 0) {
1574
1722
  return false;
@@ -1584,7 +1732,28 @@ function colorizeHeader(useColor) {
1584
1732
  if (!useColor) {
1585
1733
  return headerCells;
1586
1734
  }
1587
- return headerCells.map((header) => pc3.bold(pc3.white(header)));
1735
+ return headerCells.map((header, index) => {
1736
+ switch (index) {
1737
+ case 1:
1738
+ return pc3.bold(pc3.cyan(header));
1739
+ case 2:
1740
+ return pc3.bold(pc3.magenta(header));
1741
+ case 3:
1742
+ case 6:
1743
+ return pc3.bold(pc3.blue(header));
1744
+ case 4:
1745
+ case 7:
1746
+ return pc3.bold(pc3.cyan(header));
1747
+ case 5:
1748
+ return pc3.bold(pc3.magenta(header));
1749
+ case 8:
1750
+ return pc3.bold(pc3.green(header));
1751
+ case 9:
1752
+ return pc3.bold(pc3.yellow(header));
1753
+ default:
1754
+ return pc3.bold(pc3.white(header));
1755
+ }
1756
+ });
1588
1757
  }
1589
1758
  function isValidTerminalWidth(width) {
1590
1759
  return typeof width === "number" && Number.isFinite(width) && width > 0;
@@ -1598,12 +1767,119 @@ function resolveTerminalWidth(override) {
1598
1767
  function measureTableWidth(tableOutput) {
1599
1768
  return tableOutput.trimEnd().split("\n").reduce((maxWidth, line) => Math.max(maxWidth, visibleWidth(line)), 0);
1600
1769
  }
1601
- function renderTableWithModelsWidth(rows, tableLayout, useColor, modelsColumnWidth) {
1602
- const uncoloredBodyRows = toUsageTableCells(rows, { layout: tableLayout });
1603
- const wrappedBodyRows = wrapTableColumn(uncoloredBodyRows, {
1604
- columnIndex: modelsColumnIndex,
1770
+ function padVisibleEnd(value, width) {
1771
+ return `${value}${" ".repeat(Math.max(0, width - visibleWidth(value)))}`;
1772
+ }
1773
+ function formatCompactModelsCell(value, width) {
1774
+ const modelLines = splitCellLines(value);
1775
+ if (modelLines.length < 2) {
1776
+ return value;
1777
+ }
1778
+ const longestLineWidth = modelLines.reduce(
1779
+ (maxWidth, line) => Math.max(maxWidth, visibleWidth(line)),
1780
+ 0
1781
+ );
1782
+ const maxColumnCount = Math.floor(
1783
+ (width + compactModelsColumnGap) / (longestLineWidth + compactModelsColumnGap)
1784
+ );
1785
+ if (maxColumnCount <= 1) {
1786
+ return value;
1787
+ }
1788
+ const columnCount = Math.min(modelLines.length, maxColumnCount);
1789
+ const rowCount = Math.ceil(modelLines.length / columnCount);
1790
+ const compactLines = [];
1791
+ for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {
1792
+ const rowStart = rowIndex * columnCount;
1793
+ const cells = modelLines.slice(rowStart, rowStart + columnCount);
1794
+ compactLines.push(
1795
+ cells.map(
1796
+ (cell, columnIndex) => columnIndex === cells.length - 1 ? cell : padVisibleEnd(cell, longestLineWidth)
1797
+ ).join(" ".repeat(compactModelsColumnGap))
1798
+ );
1799
+ }
1800
+ return compactLines.join("\n");
1801
+ }
1802
+ function layoutModelsColumn(bodyRows, tableLayout, modelsColumnWidth) {
1803
+ if (tableLayout !== "compact" || modelsColumnWidth <= defaultModelsColumnWidth) {
1804
+ return bodyRows.map((row) => [...row]);
1805
+ }
1806
+ return bodyRows.map((row) => {
1807
+ const nextRow = [...row];
1808
+ nextRow[modelsColumnIndex2] = formatCompactModelsCell(
1809
+ nextRow[modelsColumnIndex2],
1810
+ modelsColumnWidth
1811
+ );
1812
+ return nextRow;
1813
+ });
1814
+ }
1815
+ function prepareWrappedBodyRows(bodyRows, tableLayout, modelsColumnWidth) {
1816
+ const laidOutRows = layoutModelsColumn(bodyRows, tableLayout, modelsColumnWidth);
1817
+ return wrapTableColumn(laidOutRows, {
1818
+ columnIndex: modelsColumnIndex2,
1605
1819
  width: modelsColumnWidth
1606
1820
  });
1821
+ }
1822
+ function measureCompactBodyHeight(bodyRows, modelsColumnWidth) {
1823
+ return prepareWrappedBodyRows(bodyRows, "compact", modelsColumnWidth).reduce(
1824
+ (totalHeight, row) => totalHeight + splitCellLines(row[modelsColumnIndex2] ?? "").length,
1825
+ 0
1826
+ );
1827
+ }
1828
+ function resolveExpandedModelsColumnWidth(bodyRows, tableLayout, currentWidth, maximumWidth) {
1829
+ if (maximumWidth <= currentWidth) {
1830
+ return currentWidth;
1831
+ }
1832
+ if (tableLayout === "per_model_columns") {
1833
+ const longestModelLineWidth = bodyRows.reduce((maxWidth, row) => {
1834
+ const cellMaxWidth = splitCellLines(row[modelsColumnIndex2] ?? "").reduce(
1835
+ (lineMaxWidth, line) => Math.max(lineMaxWidth, visibleWidth(line)),
1836
+ 0
1837
+ );
1838
+ return Math.max(maxWidth, cellMaxWidth);
1839
+ }, currentWidth);
1840
+ const preferredWidth = Math.max(
1841
+ currentWidth,
1842
+ defaultModelsColumnWidth + 16,
1843
+ longestModelLineWidth
1844
+ );
1845
+ return Math.min(maximumWidth, preferredWidth);
1846
+ }
1847
+ const candidateWidths = /* @__PURE__ */ new Set([currentWidth]);
1848
+ for (const row of bodyRows) {
1849
+ const modelLines = splitCellLines(row[modelsColumnIndex2] ?? "");
1850
+ const longestLineWidth = modelLines.reduce(
1851
+ (maxLineWidth, line) => Math.max(maxLineWidth, visibleWidth(line)),
1852
+ 0
1853
+ );
1854
+ if (longestLineWidth > currentWidth && longestLineWidth <= maximumWidth) {
1855
+ candidateWidths.add(longestLineWidth);
1856
+ }
1857
+ if (modelLines.length < 2) {
1858
+ continue;
1859
+ }
1860
+ for (let columnCount = 2; columnCount <= modelLines.length; columnCount += 1) {
1861
+ const candidateWidth = columnCount * longestLineWidth + (columnCount - 1) * compactModelsColumnGap;
1862
+ if (candidateWidth > maximumWidth) {
1863
+ break;
1864
+ }
1865
+ if (candidateWidth > currentWidth) {
1866
+ candidateWidths.add(candidateWidth);
1867
+ }
1868
+ }
1869
+ }
1870
+ let bestWidth = currentWidth;
1871
+ let bestHeight = measureCompactBodyHeight(bodyRows, currentWidth);
1872
+ for (const candidateWidth of Array.from(candidateWidths).sort((left, right) => left - right)) {
1873
+ const candidateHeight = measureCompactBodyHeight(bodyRows, candidateWidth);
1874
+ if (candidateHeight < bestHeight) {
1875
+ bestHeight = candidateHeight;
1876
+ bestWidth = candidateWidth;
1877
+ }
1878
+ }
1879
+ return bestWidth;
1880
+ }
1881
+ function renderTableWithModelsWidth(rows, uncoloredBodyRows, tableLayout, useColor, modelsColumnWidth) {
1882
+ const wrappedBodyRows = prepareWrappedBodyRows(uncoloredBodyRows, tableLayout, modelsColumnWidth);
1607
1883
  const bodyRows = colorizeUsageBodyRows(wrappedBodyRows, rows, { useColor });
1608
1884
  const rowMetas = rows.map((row) => ({
1609
1885
  periodKey: row.periodKey,
@@ -1617,7 +1893,7 @@ function renderTableWithModelsWidth(rows, tableLayout, useColor, modelsColumnWid
1617
1893
  measureBodyRows: wrappedBodyRows,
1618
1894
  rowMetas,
1619
1895
  layout: tableLayout === "per_model_columns" ? "top_aligned" : "compact",
1620
- multilineColumnIndex: modelsColumnIndex,
1896
+ multilineColumnIndex: modelsColumnIndex2,
1621
1897
  multilineColumnWidth: modelsColumnWidth
1622
1898
  });
1623
1899
  }
@@ -1626,8 +1902,15 @@ function renderTerminalTable(rows, options = {}) {
1626
1902
  const tableLayout = options.tableLayout ?? "compact";
1627
1903
  const hasExplicitTerminalWidth = isValidTerminalWidth(options.terminalWidth);
1628
1904
  const terminalWidth = resolveTerminalWidth(options.terminalWidth);
1905
+ const uncoloredBodyRows = toUsageTableCells(rows, { layout: tableLayout });
1629
1906
  let modelsColumnWidth = defaultModelsColumnWidth;
1630
- let renderedTable = renderTableWithModelsWidth(rows, tableLayout, useColor, modelsColumnWidth);
1907
+ let renderedTable = renderTableWithModelsWidth(
1908
+ rows,
1909
+ uncoloredBodyRows,
1910
+ tableLayout,
1911
+ useColor,
1912
+ modelsColumnWidth
1913
+ );
1631
1914
  if (terminalWidth !== void 0) {
1632
1915
  let renderedTableWidth = measureTableWidth(renderedTable);
1633
1916
  while (renderedTableWidth > terminalWidth && modelsColumnWidth > minimumModelsColumnWidth) {
@@ -1640,9 +1923,35 @@ function renderTerminalTable(rows, options = {}) {
1640
1923
  break;
1641
1924
  }
1642
1925
  modelsColumnWidth = nextModelsColumnWidth;
1643
- renderedTable = renderTableWithModelsWidth(rows, tableLayout, useColor, modelsColumnWidth);
1926
+ renderedTable = renderTableWithModelsWidth(
1927
+ rows,
1928
+ uncoloredBodyRows,
1929
+ tableLayout,
1930
+ useColor,
1931
+ modelsColumnWidth
1932
+ );
1644
1933
  renderedTableWidth = measureTableWidth(renderedTable);
1645
1934
  }
1935
+ if (renderedTableWidth < terminalWidth) {
1936
+ const maximumWidth = modelsColumnWidth + (terminalWidth - renderedTableWidth);
1937
+ const expandedWidth = resolveExpandedModelsColumnWidth(
1938
+ uncoloredBodyRows,
1939
+ tableLayout,
1940
+ modelsColumnWidth,
1941
+ maximumWidth
1942
+ );
1943
+ if (expandedWidth > modelsColumnWidth) {
1944
+ modelsColumnWidth = expandedWidth;
1945
+ renderedTable = renderTableWithModelsWidth(
1946
+ rows,
1947
+ uncoloredBodyRows,
1948
+ tableLayout,
1949
+ useColor,
1950
+ modelsColumnWidth
1951
+ );
1952
+ renderedTableWidth = measureTableWidth(renderedTable);
1953
+ }
1954
+ }
1646
1955
  if (hasExplicitTerminalWidth && renderedTableWidth > terminalWidth) {
1647
1956
  throw new Error(
1648
1957
  `Configured terminal width (${terminalWidth}) is too narrow for table rendering (minimum ${renderedTableWidth}).`
@@ -1653,7 +1962,7 @@ function renderTerminalTable(rows, options = {}) {
1653
1962
  }
1654
1963
 
1655
1964
  // src/render/render-efficiency-report.ts
1656
- var periodColumnIndex = 0;
1965
+ var periodColumnIndex2 = 0;
1657
1966
  var minimumEfficiencyColumnWidth = 1;
1658
1967
  var commitsColumnIndex = 1;
1659
1968
  var linesAddedColumnIndex = 2;
@@ -1793,8 +2102,8 @@ function styleEfficiencyTerminalRows(rows, bodyRows, options) {
1793
2102
  }
1794
2103
  const row = rows[rowIndex];
1795
2104
  const styledCells = [...cells];
1796
- const periodCell = styledCells[periodColumnIndex];
1797
- styledCells[periodColumnIndex] = row.rowType === "grand_total" ? pc4.bold(pc4.cyan(periodCell)) : pc4.bold(periodCell);
2105
+ const periodCell = styledCells[periodColumnIndex2];
2106
+ styledCells[periodColumnIndex2] = row.rowType === "grand_total" ? pc4.bold(pc4.cyan(periodCell)) : pc4.bold(periodCell);
1798
2107
  styledCells[commitsColumnIndex] = pc4.bold(styledCells[commitsColumnIndex]);
1799
2108
  styledCells[linesAddedColumnIndex] = styleDeltaCell(
1800
2109
  row.linesAdded,
@@ -1844,13 +2153,10 @@ function renderTerminalEfficiencyTable(rows, options) {
1844
2153
  measureBodyRows: fittedCells.bodyRows,
1845
2154
  rowMetas,
1846
2155
  layout: "compact",
1847
- multilineColumnIndex: periodColumnIndex,
1848
- multilineColumnWidth: fittedCells.widths[periodColumnIndex] ?? efficiencyTableHeaders[periodColumnIndex].length
2156
+ multilineColumnIndex: periodColumnIndex2,
2157
+ multilineColumnWidth: fittedCells.widths[periodColumnIndex2] ?? efficiencyTableHeaders[periodColumnIndex2].length
1849
2158
  });
1850
2159
  }
1851
- function toMarkdownSafeCell(value) {
1852
- return value.replace(/\r?\n/gu, "<br>");
1853
- }
1854
2160
  function renderMarkdownEfficiencyTable(rows) {
1855
2161
  const bodyRows = toEfficiencyTableCells(rows).map(
1856
2162
  (row) => row.map((cell) => toMarkdownSafeCell(cell))
@@ -1908,52 +2214,6 @@ var logger = {
1908
2214
  }
1909
2215
  };
1910
2216
 
1911
- // src/domain/normalization.ts
1912
- function normalizeNonNegativeInteger(value) {
1913
- if (value === null || value === void 0) {
1914
- return 0;
1915
- }
1916
- const parsed = typeof value === "number" ? value : Number(value);
1917
- if (!Number.isFinite(parsed)) {
1918
- return 0;
1919
- }
1920
- return Math.max(0, Math.trunc(parsed));
1921
- }
1922
- function normalizeUsdCost(value) {
1923
- if (value === null || value === void 0) {
1924
- return void 0;
1925
- }
1926
- if (typeof value === "string" && value.trim() === "") {
1927
- return void 0;
1928
- }
1929
- const parsed = typeof value === "number" ? value : Number(value);
1930
- if (!Number.isFinite(parsed)) {
1931
- return void 0;
1932
- }
1933
- return Math.max(0, parsed);
1934
- }
1935
- function normalizeTimestamp(value) {
1936
- const date = value instanceof Date ? value : new Date(value);
1937
- if (Number.isNaN(date.getTime())) {
1938
- throw new Error(`Invalid timestamp: ${String(value)}`);
1939
- }
1940
- return date.toISOString();
1941
- }
1942
- function normalizeModelList(models) {
1943
- const deduplicated = /* @__PURE__ */ new Set();
1944
- for (const model of models) {
1945
- if (!model) {
1946
- continue;
1947
- }
1948
- const normalized = model.trim();
1949
- if (!normalized) {
1950
- continue;
1951
- }
1952
- deduplicated.add(normalized);
1953
- }
1954
- return [...deduplicated].sort(compareByCodePoint);
1955
- }
1956
-
1957
2217
  // src/utils/time-buckets.ts
1958
2218
  var formatterCache = /* @__PURE__ */ new Map();
1959
2219
  function getDateFormatter(timezone) {
@@ -2138,15 +2398,29 @@ function mergeModelTotals(targetModelTotals, sourceModelTotals) {
2138
2398
  targetModelTotals.set(model, targetTotals);
2139
2399
  }
2140
2400
  }
2141
- function toModelUsageBreakdown(modelTotals) {
2142
- const sortedModels = normalizeModelList(modelTotals.keys());
2143
- return sortedModels.map((model) => {
2144
- const totals = modelTotals.get(model) ?? createEmptyTotals();
2145
- return {
2146
- model,
2147
- ...totals
2148
- };
2149
- });
2401
+ function compareModelUsageBreakdown(left, right) {
2402
+ if (left.totalTokens !== right.totalTokens) {
2403
+ return right.totalTokens - left.totalTokens;
2404
+ }
2405
+ const leftCost = left.costUsd ?? Number.NEGATIVE_INFINITY;
2406
+ const rightCost = right.costUsd ?? Number.NEGATIVE_INFINITY;
2407
+ if (leftCost !== rightCost) {
2408
+ return rightCost - leftCost;
2409
+ }
2410
+ if (left.inputTokens !== right.inputTokens) {
2411
+ return right.inputTokens - left.inputTokens;
2412
+ }
2413
+ return compareByCodePoint(left.model, right.model);
2414
+ }
2415
+ function toRankedModelUsage(modelTotals) {
2416
+ const modelBreakdown = [...modelTotals.entries()].map(([model, totals]) => ({
2417
+ model,
2418
+ ...totals
2419
+ })).sort(compareModelUsageBreakdown);
2420
+ return {
2421
+ models: modelBreakdown.map((modelUsage) => modelUsage.model),
2422
+ modelBreakdown
2423
+ };
2150
2424
  }
2151
2425
  function sourceSortComparator(left, right, sourceWeightMap) {
2152
2426
  const leftWeight = sourceWeightMap.get(left) ?? Number.MAX_SAFE_INTEGER;
@@ -2190,12 +2464,13 @@ function aggregateUsage(events, options) {
2190
2464
  if (!accumulator) {
2191
2465
  continue;
2192
2466
  }
2467
+ const rankedModelUsage = includeModelBreakdown && accumulator.modelTotals ? toRankedModelUsage(accumulator.modelTotals) : { models: [], modelBreakdown: [] };
2193
2468
  const sourceRow = {
2194
2469
  rowType: "period_source",
2195
2470
  periodKey,
2196
2471
  source,
2197
- models: includeModelBreakdown && accumulator.modelTotals ? normalizeModelList(accumulator.modelTotals.keys()) : [],
2198
- modelBreakdown: includeModelBreakdown && accumulator.modelTotals ? toModelUsageBreakdown(accumulator.modelTotals) : [],
2472
+ models: rankedModelUsage.models,
2473
+ modelBreakdown: rankedModelUsage.modelBreakdown,
2199
2474
  ...accumulator.totals
2200
2475
  };
2201
2476
  rows.push(sourceRow);
@@ -2207,24 +2482,26 @@ function aggregateUsage(events, options) {
2207
2482
  }
2208
2483
  }
2209
2484
  if (sortedSources.length > 1) {
2485
+ const rankedCombinedModels = includeModelBreakdown ? toRankedModelUsage(periodCombinedModelTotals) : { models: [], modelBreakdown: [] };
2210
2486
  const combinedRow = {
2211
2487
  rowType: "period_combined",
2212
2488
  periodKey,
2213
2489
  source: "combined",
2214
- models: includeModelBreakdown ? normalizeModelList(periodCombinedModelTotals.keys()) : [],
2215
- modelBreakdown: includeModelBreakdown ? toModelUsageBreakdown(periodCombinedModelTotals) : [],
2490
+ models: rankedCombinedModels.models,
2491
+ modelBreakdown: rankedCombinedModels.modelBreakdown,
2216
2492
  ...periodCombinedTotals
2217
2493
  };
2218
2494
  rows.push(combinedRow);
2219
2495
  }
2220
2496
  }
2221
2497
  const finalizedGrandTotals = events.length === 0 && grandTotals.costUsd === void 0 && grandTotals.costIncomplete !== true ? { ...grandTotals, costUsd: 0 } : grandTotals;
2498
+ const rankedGrandModels = includeModelBreakdown ? toRankedModelUsage(grandModelTotals) : { models: [], modelBreakdown: [] };
2222
2499
  const grandTotalRow = {
2223
2500
  rowType: "grand_total",
2224
2501
  periodKey: "ALL",
2225
2502
  source: "combined",
2226
- models: includeModelBreakdown ? normalizeModelList(grandModelTotals.keys()) : [],
2227
- modelBreakdown: includeModelBreakdown ? toModelUsageBreakdown(grandModelTotals) : [],
2503
+ models: rankedGrandModels.models,
2504
+ modelBreakdown: rankedGrandModels.modelBreakdown,
2228
2505
  ...finalizedGrandTotals
2229
2506
  };
2230
2507
  rows.push(grandTotalRow);
@@ -2858,6 +3135,38 @@ async function attributeUsageEventsToRepo(events, repoDir, resolveRepoRoot3 = re
2858
3135
  };
2859
3136
  }
2860
3137
 
3138
+ // src/domain/normalization.ts
3139
+ function normalizeNonNegativeInteger(value) {
3140
+ if (value === null || value === void 0) {
3141
+ return 0;
3142
+ }
3143
+ const parsed = typeof value === "number" ? value : Number(value);
3144
+ if (!Number.isFinite(parsed)) {
3145
+ return 0;
3146
+ }
3147
+ return Math.max(0, Math.trunc(parsed));
3148
+ }
3149
+ function normalizeUsdCost(value) {
3150
+ if (value === null || value === void 0) {
3151
+ return void 0;
3152
+ }
3153
+ if (typeof value === "string" && value.trim() === "") {
3154
+ return void 0;
3155
+ }
3156
+ const parsed = typeof value === "number" ? value : Number(value);
3157
+ if (!Number.isFinite(parsed)) {
3158
+ return void 0;
3159
+ }
3160
+ return Math.max(0, parsed);
3161
+ }
3162
+ function normalizeTimestamp(value) {
3163
+ const date = value instanceof Date ? value : new Date(value);
3164
+ if (Number.isNaN(date.getTime())) {
3165
+ throw new Error(`Invalid timestamp: ${String(value)}`);
3166
+ }
3167
+ return date.toISOString();
3168
+ }
3169
+
2861
3170
  // src/domain/provider-normalization.ts
2862
3171
  var billingProviderAliases = /* @__PURE__ */ new Map([
2863
3172
  ["openai-codex", "openai"],
@@ -7529,9 +7838,6 @@ function toTableCells(optimizeData, options) {
7529
7838
  return options.includeNotesColumn ? [...candidateCells, styleNotesCell(row.notes, notesCell, options.useColor)] : candidateCells;
7530
7839
  });
7531
7840
  }
7532
- function toMarkdownSafeCell2(value) {
7533
- return value.replace(/\r?\n/gu, "<br>");
7534
- }
7535
7841
  function toTableRowMeta2(row) {
7536
7842
  return {
7537
7843
  periodKey: row.periodKey,
@@ -7587,7 +7893,7 @@ function renderMarkdownOptimizeReport(optimizeData) {
7587
7893
  const bodyRows = toTableCells(optimizeData, {
7588
7894
  useColor: false,
7589
7895
  includeNotesColumn
7590
- }).map((row) => row.map((cell) => toMarkdownSafeCell2(cell)));
7896
+ }).map((row) => row.map((cell) => toMarkdownSafeCell(cell)));
7591
7897
  const tableRows = [headerCells, ...bodyRows];
7592
7898
  const alignment2 = headerCells.map((_, index) => index <= 1 ? "l" : "r");
7593
7899
  return markdownTable2(tableRows, { align: alignment2 });
@@ -8916,13 +9222,59 @@ async function buildUsageData(granularity, options, deps = {}) {
8916
9222
  // src/render/markdown-table.ts
8917
9223
  import { markdownTable as markdownTable3 } from "markdown-table";
8918
9224
  var alignment = ["l", "l", "l", "r", "r", "r", "r", "r", "r", "r"];
8919
- function toMarkdownSafeCell3(value) {
8920
- return value.replace(/\r?\n/gu, "<br>");
9225
+ function boldMarkdownText(value) {
9226
+ const safeValue = toMarkdownSafeCell(value);
9227
+ return safeValue.length === 0 ? safeValue : `**${safeValue}**`;
9228
+ }
9229
+ function emphasizeMarkdownModelsCell(value) {
9230
+ const lines = splitCellLines(value);
9231
+ return lines.map((line, index) => {
9232
+ if (line.length === 0) {
9233
+ return "";
9234
+ }
9235
+ if (line === "\u03A3 TOTAL") {
9236
+ return boldMarkdownText(line);
9237
+ }
9238
+ if (index === 0 && lines.length > 1) {
9239
+ return boldMarkdownText(line);
9240
+ }
9241
+ return toMarkdownSafeCell(line);
9242
+ }).join("<br>");
9243
+ }
9244
+ function emphasizeMarkdownSummaryMetricCell(value) {
9245
+ const lines = splitCellLines(value);
9246
+ if (lines.length <= 1) {
9247
+ return boldMarkdownText(value);
9248
+ }
9249
+ return lines.map(
9250
+ (line, index) => index === lines.length - 1 ? boldMarkdownText(line) : toMarkdownSafeCell(line)
9251
+ ).join("<br>");
9252
+ }
9253
+ function emphasizeMarkdownRow(row, cells) {
9254
+ const styledCells = cells.map((cell) => toMarkdownSafeCell(cell));
9255
+ if (styledCells.length > 2) {
9256
+ styledCells[2] = emphasizeMarkdownModelsCell(cells[2]);
9257
+ }
9258
+ if (row.rowType !== "period_source") {
9259
+ if (styledCells.length > 1) {
9260
+ styledCells[1] = boldMarkdownText(cells[1]);
9261
+ }
9262
+ if (styledCells.length > 8) {
9263
+ styledCells[8] = emphasizeMarkdownSummaryMetricCell(cells[8]);
9264
+ }
9265
+ if (styledCells.length > 9) {
9266
+ styledCells[9] = emphasizeMarkdownSummaryMetricCell(cells[9]);
9267
+ }
9268
+ }
9269
+ if (row.rowType === "grand_total" && styledCells.length > 0) {
9270
+ styledCells[0] = boldMarkdownText(cells[0]);
9271
+ }
9272
+ return styledCells;
8921
9273
  }
8922
9274
  function renderMarkdownTable(rows, options = {}) {
8923
9275
  const tableLayout = options.tableLayout ?? "compact";
8924
9276
  const bodyRows = toUsageTableCells(rows, { layout: tableLayout }).map(
8925
- (row) => row.map((cell) => toMarkdownSafeCell3(cell))
9277
+ (cells, index) => emphasizeMarkdownRow(rows[index], cells)
8926
9278
  );
8927
9279
  const tableRows = [Array.from(usageTableHeaders), ...bodyRows];
8928
9280
  return markdownTable3(tableRows, {