llm-usage-metrics 0.4.1 → 0.4.3

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
@@ -2580,7 +2580,7 @@ function renderCommitBarLabels(monthlyRows, chartLeft, stepX, maxCommits, chartT
2580
2580
  return monthlyRows.map((row, i) => {
2581
2581
  const x = chartLeft + i * stepX;
2582
2582
  const yTop = scaleY(row.commitCount, maxCommits, chartTop, chartBottom);
2583
- return `<text x="${x.toFixed(2)}" y="${(yTop - 8).toFixed(0)}" text-anchor="middle" font-size="12" font-weight="600" fill="${shareTheme.textSecondary}" font-family="${shareTheme.font}">${formatInteger(row.commitCount)}</text>`;
2583
+ return `<text x="${x.toFixed(2)}" y="${(yTop - 8).toFixed(0)}" text-anchor="middle" font-size="12" font-weight="600" fill="${shareTheme.textSecondary}" font-family="${shareTheme.font}">${escapeSvg(formatInteger(row.commitCount))}</text>`;
2584
2584
  }).join("\n");
2585
2585
  }
2586
2586
  function renderUsdDataLabels(monthlyRows, usdPoints) {
@@ -5337,6 +5337,7 @@ function emitEnvVarOverrides(activeEnvOverrides, diagnosticsLogger) {
5337
5337
  }
5338
5338
 
5339
5339
  // src/cli/share-artifact.ts
5340
+ import { spawn as spawn3 } from "child_process";
5340
5341
  import { writeFile as writeFile4 } from "fs/promises";
5341
5342
  import path13 from "path";
5342
5343
  async function writeShareSvgFile(fileName, svgContent) {
@@ -5344,6 +5345,78 @@ async function writeShareSvgFile(fileName, svgContent) {
5344
5345
  await writeFile4(outputPath, svgContent, "utf8");
5345
5346
  return outputPath;
5346
5347
  }
5348
+ function stringifyError(error) {
5349
+ if (error instanceof Error) {
5350
+ return error.message;
5351
+ }
5352
+ return String(error);
5353
+ }
5354
+ function resolveOpenCommand(filePath, platform) {
5355
+ if (platform === "win32") {
5356
+ return {
5357
+ command: "cmd",
5358
+ args: ["/c", "start", "", filePath]
5359
+ };
5360
+ }
5361
+ if (platform === "darwin") {
5362
+ return {
5363
+ command: "open",
5364
+ args: [filePath]
5365
+ };
5366
+ }
5367
+ return {
5368
+ command: "xdg-open",
5369
+ args: [filePath]
5370
+ };
5371
+ }
5372
+ async function spawnDetached(command, args) {
5373
+ await new Promise((resolve, reject) => {
5374
+ const child = spawn3(command, args, {
5375
+ detached: true,
5376
+ stdio: "ignore",
5377
+ windowsHide: true
5378
+ });
5379
+ const cleanup = () => {
5380
+ child.removeListener("error", onError);
5381
+ child.removeListener("spawn", onSpawn);
5382
+ };
5383
+ const onError = (error) => {
5384
+ cleanup();
5385
+ reject(error);
5386
+ };
5387
+ const onSpawn = () => {
5388
+ cleanup();
5389
+ child.unref();
5390
+ resolve();
5391
+ };
5392
+ child.once("error", onError);
5393
+ child.once("spawn", onSpawn);
5394
+ });
5395
+ }
5396
+ async function openShareSvgFile(filePath, deps = {}) {
5397
+ const platform = deps.platform ?? process.platform;
5398
+ const runDetached = deps.spawnDetached ?? spawnDetached;
5399
+ const { command, args } = resolveOpenCommand(filePath, platform);
5400
+ await runDetached(command, args);
5401
+ }
5402
+ async function writeAndOpenShareSvgFile(fileName, svgContent, deps = {}) {
5403
+ const writeShareSvg = deps.writeShareSvgFileFn ?? writeShareSvgFile;
5404
+ const openShareSvg = deps.openShareSvgFileFn ?? openShareSvgFile;
5405
+ const outputPath = await writeShareSvg(fileName, svgContent);
5406
+ try {
5407
+ await openShareSvg(outputPath);
5408
+ return {
5409
+ outputPath,
5410
+ opened: true
5411
+ };
5412
+ } catch (error) {
5413
+ return {
5414
+ outputPath,
5415
+ opened: false,
5416
+ openErrorMessage: stringifyError(error)
5417
+ };
5418
+ }
5419
+ }
5347
5420
 
5348
5421
  // src/render/table-text-layout.ts
5349
5422
  var ansiEscapePattern = new RegExp(String.raw`\u001B\[[0-9;]*m`, "gu");
@@ -6454,11 +6527,18 @@ async function runEfficiencyReport(granularity, options) {
6454
6527
  });
6455
6528
  }
6456
6529
  if (preparedReport.shareSvg) {
6457
- const outputPath = await writeShareSvgFile(
6530
+ const shareResult = await writeAndOpenShareSvgFile(
6458
6531
  "efficiency-monthly-share.svg",
6459
6532
  preparedReport.shareSvg
6460
6533
  );
6461
- logger.info(`Wrote efficiency share SVG: ${outputPath}`);
6534
+ logger.info(`Wrote efficiency share SVG: ${shareResult.outputPath}`);
6535
+ if (shareResult.opened) {
6536
+ logger.info(`Opened efficiency share SVG: ${shareResult.outputPath}`);
6537
+ } else {
6538
+ logger.warn(
6539
+ `Could not open efficiency share SVG: ${shareResult.outputPath} (${shareResult.openErrorMessage})`
6540
+ );
6541
+ }
6462
6542
  }
6463
6543
  console.log(preparedReport.output);
6464
6544
  }
@@ -7227,11 +7307,18 @@ async function runOptimizeReport(granularity, options) {
7227
7307
  });
7228
7308
  }
7229
7309
  if (preparedReport.shareSvg) {
7230
- const outputPath = await writeShareSvgFile(
7310
+ const shareResult = await writeAndOpenShareSvgFile(
7231
7311
  "optimize-monthly-share.svg",
7232
7312
  preparedReport.shareSvg
7233
7313
  );
7234
- logger.info(`Wrote optimize share SVG: ${outputPath}`);
7314
+ logger.info(`Wrote optimize share SVG: ${shareResult.outputPath}`);
7315
+ if (shareResult.opened) {
7316
+ logger.info(`Opened optimize share SVG: ${shareResult.outputPath}`);
7317
+ } else {
7318
+ logger.warn(
7319
+ `Could not open optimize share SVG: ${shareResult.outputPath} (${shareResult.openErrorMessage})`
7320
+ );
7321
+ }
7235
7322
  }
7236
7323
  console.log(preparedReport.output);
7237
7324
  }
@@ -7296,6 +7383,11 @@ var H3 = 560;
7296
7383
  var ACCENT_H3 = 4;
7297
7384
  var FOOTER_H3 = 36;
7298
7385
  var pad3 = { top: 140, right: 80, bottom: 60 + FOOTER_H3, left: 200 };
7386
+ var STAT_X = 60;
7387
+ var STAT_VALUE_FONT_SIZE = 52;
7388
+ var STAT_VALUE_WIDTH_FACTOR = 0.6;
7389
+ var SOURCE_PILLS_MIN_X = pad3.left + 10;
7390
+ var SOURCE_PILLS_STAT_GAP = 24;
7299
7391
  function extractPeriodSourceRows(rows) {
7300
7392
  return rows.filter((r) => r.rowType === "period_source");
7301
7393
  }
@@ -7333,16 +7425,10 @@ function buildStackedValues(series) {
7333
7425
  return stacked;
7334
7426
  }
7335
7427
  function renderAccentBar() {
7336
- return [
7337
- `<defs><linearGradient id="accent-grad" x1="0" y1="0" x2="1" y2="0">`,
7338
- ` <stop offset="0%" stop-color="#10b981"/>`,
7339
- ` <stop offset="100%" stop-color="#06b6d4"/>`,
7340
- `</linearGradient></defs>`,
7341
- `<rect width="${W3}" height="${ACCENT_H3}" fill="url(#accent-grad)"/>`
7342
- ].join("\n");
7428
+ return `<rect width="${W3}" height="${ACCENT_H3}" fill="url(#accent-grad)"/>`;
7343
7429
  }
7344
7430
  function renderStatColumn(totalTokens, costUsd, sourceCount) {
7345
- const x = 60;
7431
+ const x = STAT_X;
7346
7432
  const baseY = ACCENT_H3 + 48;
7347
7433
  let svg = "";
7348
7434
  svg += `<text x="${x}" y="${baseY}" fill="${shareTheme.textPrimary}" font-family="${shareTheme.font}" font-size="52" font-weight="800">${escapeSvg(formatCompact(totalTokens))}</text>
@@ -7357,9 +7443,9 @@ function renderStatColumn(totalTokens, costUsd, sourceCount) {
7357
7443
  `;
7358
7444
  return svg;
7359
7445
  }
7360
- function renderSourcePills(series) {
7446
+ function renderSourcePills(series, startX) {
7361
7447
  let svg = "";
7362
- let cx = pad3.left + 10;
7448
+ let cx = Math.max(SOURCE_PILLS_MIN_X, startX);
7363
7449
  const pillY = ACCENT_H3 + 30;
7364
7450
  for (const s of series) {
7365
7451
  const label = `${s.source} ${formatCompact(s.total)}`;
@@ -7376,6 +7462,10 @@ function renderSourcePills(series) {
7376
7462
  }
7377
7463
  return svg;
7378
7464
  }
7465
+ function estimateStatValueRightEdge(totalTokens) {
7466
+ const value = formatCompact(totalTokens);
7467
+ return STAT_X + value.length * STAT_VALUE_FONT_SIZE * STAT_VALUE_WIDTH_FACTOR;
7468
+ }
7379
7469
  function renderCommandBadge(command) {
7380
7470
  const textW = command.length * 9;
7381
7471
  const badgeW = textW + 28;
@@ -7500,6 +7590,7 @@ function renderUsageShareSvg(usageData, granularity) {
7500
7590
  const toX = (p) => chartLeft + (periodCount <= 1 ? chartW / 2 : p / (periodCount - 1) * chartW);
7501
7591
  const toChartY = (val) => scaleY(val, maxY, chartTop, chartBottom);
7502
7592
  const commandText = `llm-usage ${granularity} --share`;
7593
+ const sourcePillsStartX = estimateStatValueRightEdge(totalTokens) + SOURCE_PILLS_STAT_GAP;
7503
7594
  let chartContent;
7504
7595
  if (periodCount === 0) {
7505
7596
  chartContent = `<text x="${(W3 / 2).toFixed(0)}" y="${(H3 / 2).toFixed(0)}" text-anchor="middle" font-size="20" fill="${shareTheme.textSecondary}" font-family="${shareTheme.font}">No usage data available</text>`;
@@ -7524,6 +7615,10 @@ function renderUsageShareSvg(usageData, granularity) {
7524
7615
  }
7525
7616
  return `<svg xmlns="http://www.w3.org/2000/svg" width="${W3}" height="${H3}" viewBox="0 0 ${W3} ${H3}">
7526
7617
  <defs>
7618
+ <linearGradient id="accent-grad" x1="0" y1="0" x2="1" y2="0">
7619
+ <stop offset="0%" stop-color="#10b981"/>
7620
+ <stop offset="100%" stop-color="#06b6d4"/>
7621
+ </linearGradient>
7527
7622
  <clipPath id="chart-clip">
7528
7623
  <rect x="${chartLeft}" y="${chartTop - 4}" width="${chartW}" height="${chartH + 8}"/>
7529
7624
  </clipPath>
@@ -7532,7 +7627,7 @@ function renderUsageShareSvg(usageData, granularity) {
7532
7627
  <rect width="${W3}" height="${H3}" fill="${shareTheme.bg}"/>
7533
7628
  ${renderAccentBar()}
7534
7629
  ${renderStatColumn(totalTokens, totalCost, activeSeries.length)}
7535
- ${renderSourcePills(activeSeries)}
7630
+ ${renderSourcePills(activeSeries, sourcePillsStartX)}
7536
7631
  ${renderCommandBadge(commandText)}
7537
7632
  ${renderGridLines(chartLeft, chartRight, chartTop, chartH, maxY)}
7538
7633
  ${chartContent}
@@ -7586,11 +7681,18 @@ async function runUsageReport(granularity, options) {
7586
7681
  });
7587
7682
  }
7588
7683
  if (preparedReport.shareSvg) {
7589
- const outputPath = await writeShareSvgFile(
7684
+ const shareResult = await writeAndOpenShareSvgFile(
7590
7685
  resolveShareFileName(granularity),
7591
7686
  preparedReport.shareSvg
7592
7687
  );
7593
- logger.info(`Wrote usage share SVG: ${outputPath}`);
7688
+ logger.info(`Wrote usage share SVG: ${shareResult.outputPath}`);
7689
+ if (shareResult.opened) {
7690
+ logger.info(`Opened usage share SVG: ${shareResult.outputPath}`);
7691
+ } else {
7692
+ logger.warn(
7693
+ `Could not open usage share SVG: ${shareResult.outputPath} (${shareResult.openErrorMessage})`
7694
+ );
7695
+ }
7594
7696
  }
7595
7697
  console.log(preparedReport.output);
7596
7698
  }
@@ -7761,7 +7863,7 @@ function loadPackageMetadataFromRuntime() {
7761
7863
  var { packageName, packageVersion } = loadPackageMetadataFromRuntime();
7762
7864
  var updateRuntimeConfig = getUpdateNotifierRuntimeConfig();
7763
7865
  var cli = createCli({ version: packageVersion });
7764
- try {
7866
+ async function main() {
7765
7867
  const updateResult = await checkForUpdatesAndMaybeRestart({
7766
7868
  packageName,
7767
7869
  currentVersion: packageVersion,
@@ -7773,9 +7875,10 @@ try {
7773
7875
  } else {
7774
7876
  await cli.parseAsync(process.argv);
7775
7877
  }
7776
- } catch (error) {
7878
+ }
7879
+ main().catch((error) => {
7777
7880
  const message = error instanceof Error ? error.message : String(error);
7778
7881
  console.error(message);
7779
7882
  process.exitCode = 1;
7780
- }
7883
+ });
7781
7884
  //# sourceMappingURL=index.js.map