llm-usage-metrics 0.12.0 → 0.12.1

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.
Files changed (2) hide show
  1. package/dist/index.js +138 -66
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -73,7 +73,13 @@ function compareByCodePoint(left, right) {
73
73
  // packages/core/src/domain/provider-normalization.ts
74
74
  var billingProviderAliases = /* @__PURE__ */ new Map([
75
75
  ["openai-codex", "openai"],
76
- ["github-copilot", "github"]
76
+ ["github-copilot", "github"],
77
+ // Built-in pi providers that bill through another vendor's API.
78
+ ["claude-bridge", "anthropic"],
79
+ ["google-gemini-cli", "google"],
80
+ ["google-antigravity", "google"],
81
+ ["deepseek-official", "deepseek"],
82
+ ["kimi-coding", "moonshot"]
77
83
  ]);
78
84
  var billingProviderPrefixAliases = [
79
85
  ["openai-", "openai"],
@@ -230,7 +236,7 @@ function normalizeTimestamp(value) {
230
236
  }
231
237
 
232
238
  // packages/core/src/domain/usage-event.ts
233
- var USAGE_EVENT_NORMALIZATION_VERSION = 3;
239
+ var USAGE_EVENT_NORMALIZATION_VERSION = 4;
234
240
  function normalizeSourceId(value) {
235
241
  if (typeof value !== "string") {
236
242
  return void 0;
@@ -1993,6 +1999,29 @@ function computeActiveMs(timestampsMs) {
1993
1999
  }
1994
2000
  return activeMs;
1995
2001
  }
2002
+ function computeUnionActiveMs(timestampLists) {
2003
+ const intervals = [];
2004
+ for (const timestamps of timestampLists) {
2005
+ const sorted = [...timestamps].sort((left, right) => left - right);
2006
+ for (let index = 1; index < sorted.length; index += 1) {
2007
+ const start = sorted[index - 1];
2008
+ const end = Math.min(sorted[index], start + IDLE_GAP_CAP_MS);
2009
+ if (end > start) {
2010
+ intervals.push([start, end]);
2011
+ }
2012
+ }
2013
+ }
2014
+ intervals.sort((left, right) => left[0] - right[0]);
2015
+ let activeMs = 0;
2016
+ let coveredUntil = Number.NEGATIVE_INFINITY;
2017
+ for (const [start, end] of intervals) {
2018
+ if (end > coveredUntil) {
2019
+ activeMs += end - Math.max(start, coveredUntil);
2020
+ coveredUntil = end;
2021
+ }
2022
+ }
2023
+ return activeMs;
2024
+ }
1996
2025
 
1997
2026
  // packages/core/src/sources/source-context.ts
1998
2027
  import os from "os";
@@ -3043,7 +3072,11 @@ function readSchemaVersion(database) {
3043
3072
  return void 0;
3044
3073
  }
3045
3074
  const schemaVersionRow = database.prepare("SELECT value FROM meta WHERE key = 'schemaVersion'").get();
3046
- return toText(schemaVersionRow?.value);
3075
+ const schemaVersion = toText(schemaVersionRow?.value);
3076
+ return schemaVersion === "5" && tableNames.has("events") && !hasEventsFileIdColumn(database) ? "4" : schemaVersion;
3077
+ }
3078
+ function hasEventsFileIdColumn(database) {
3079
+ return database.prepare("SELECT name FROM pragma_table_info(?)").all("events").some((row) => row.name === "file_id");
3047
3080
  }
3048
3081
  function createFreshSchema(database) {
3049
3082
  database.exec(createSchemaSql());
@@ -3144,6 +3177,8 @@ function migrateSchemaV4ToV5(database) {
3144
3177
  ALTER TABLE files RENAME TO v4_files;
3145
3178
  ALTER TABLE events RENAME TO v4_events;
3146
3179
  DROP INDEX IF EXISTS events_content_hash;
3180
+ DROP TABLE IF EXISTS tool_calls;
3181
+ DROP TABLE IF EXISTS messages;
3147
3182
  ${createFileTablesSql()}
3148
3183
  INSERT INTO files (source, file_path, fingerprint, skipped_rows, skipped_row_reasons, ingested_at)
3149
3184
  SELECT source, file_path, fingerprint, skipped_rows, skipped_row_reasons, ingested_at
@@ -3566,8 +3601,7 @@ async function readEventStoreSummary(filePath = getDefaultEventStorePath(), load
3566
3601
  timeout: EVENT_STORE_OPEN_TIMEOUT_MS
3567
3602
  });
3568
3603
  try {
3569
- const schemaVersionRow = database.prepare("SELECT value FROM meta WHERE key = 'schemaVersion'").get();
3570
- const schemaVersion = toText(schemaVersionRow?.value);
3604
+ const schemaVersion = readSchemaVersion(database);
3571
3605
  const countRows = (tableName) => toNonNegativeInteger(
3572
3606
  database.prepare(`SELECT COUNT(*) AS count FROM ${tableName}`).get()?.count
3573
3607
  ) ?? 0;
@@ -3713,11 +3747,12 @@ async function readEventStoreEvents(filePath, window = {}, loadSqliteModule = lo
3713
3747
  try {
3714
3748
  assertSupportedSchemaVersion(database);
3715
3749
  const meta = readMetaValues(database);
3750
+ const schemaVersion = readSchemaVersion(database);
3716
3751
  const fromTimestamp = window.fromTimestamp ?? "";
3717
3752
  const toTimestamp = window.toTimestamp ?? "\uFFFF";
3718
3753
  const wideWindow = isWideWindow(database, fromTimestamp, toTimestamp);
3719
3754
  const statement = database.prepare(
3720
- hasSchemaVersion(meta.get("schemaVersion"), 5) ? selectWindowEventsSql(wideWindow) : selectPreV5WindowEventsSql(meta.get("schemaVersion"), wideWindow)
3755
+ hasSchemaVersion(schemaVersion, 5) ? selectWindowEventsSql(wideWindow) : selectPreV5WindowEventsSql(schemaVersion, wideWindow)
3721
3756
  );
3722
3757
  statement.setReturnArrays(true);
3723
3758
  const rows = statement.all(fromTimestamp, toTimestamp);
@@ -3744,7 +3779,7 @@ async function readStoredSessionEvents(filePath, files, kinds, loadSqliteModule
3744
3779
  timeout: EVENT_STORE_OPEN_TIMEOUT_MS
3745
3780
  });
3746
3781
  try {
3747
- const schemaVersion = readMetaValues(database).get("schemaVersion");
3782
+ const schemaVersion = readSchemaVersion(database);
3748
3783
  if (!hasSchemaVersion(schemaVersion, 5)) {
3749
3784
  throw new EventStoreSchemaVersionError(schemaVersion);
3750
3785
  }
@@ -6183,8 +6218,22 @@ function stripTrailingSeparators(value) {
6183
6218
  }
6184
6219
  return value.slice(0, end);
6185
6220
  }
6221
+ function stripDependencyPath(value) {
6222
+ let cut = value.length;
6223
+ for (const separator of ["/", "\\"]) {
6224
+ const needle = `${separator}node_modules`;
6225
+ for (let index = value.indexOf(needle); index >= 0; index = value.indexOf(needle, index + 1)) {
6226
+ const end = index + needle.length;
6227
+ if (end === value.length || isSeparator(value[end])) {
6228
+ cut = Math.min(cut, index);
6229
+ break;
6230
+ }
6231
+ }
6232
+ }
6233
+ return cut < value.length && isBareRoot(value.slice(0, cut + 1)) ? value.slice(0, cut + 1) : value.slice(0, cut);
6234
+ }
6186
6235
  function toProjectKey(repoRoot) {
6187
- return repoRoot === void 0 ? void 0 : stripTrailingSeparators(repoRoot);
6236
+ return repoRoot === void 0 ? void 0 : stripDependencyPath(stripTrailingSeparators(repoRoot));
6188
6237
  }
6189
6238
  function toProjectLabel(repoRoot, labels) {
6190
6239
  return labels.get(toProjectKey(repoRoot)) ?? UNATTRIBUTED_PROJECT_LABEL;
@@ -42892,13 +42941,15 @@ async function buildUsageEventDataset(options, deps = {}, request = {}) {
42892
42941
  (failure.stale ? machineWarnings : machineNotes).push(failure.text);
42893
42942
  }
42894
42943
  }
42895
- machineNotes.push(
42896
- formatMachinesNote(
42897
- machineUsage.machines,
42898
- nowMs(),
42899
- loadPackageMetadataFromRuntime().packageVersion
42900
- )
42901
- );
42944
+ if (machineUsage.machines.length > 0) {
42945
+ machineNotes.push(
42946
+ formatMachinesNote(
42947
+ machineUsage.machines,
42948
+ nowMs(),
42949
+ loadPackageMetadataFromRuntime().packageVersion
42950
+ )
42951
+ );
42952
+ }
42902
42953
  }
42903
42954
  const sessionCopyingSources = getSessionCopyingSourceIds(adaptersToParse);
42904
42955
  parseResultsForFiltering = measureRuntimeProfileStageSync(
@@ -44337,7 +44388,7 @@ function formatBurnRate(active, options) {
44337
44388
  }
44338
44389
  function buildFooterLines(data, options) {
44339
44390
  const lines = [
44340
- `Blocks are estimated: each starts at the UTC hour of a source's first event after the previous block and lasts ${String(data.blockHours)} hours (times in ${data.timezone}), across machines.`
44391
+ `Blocks are estimated: each starts on the whole UTC hour at or before a source's first event after the previous block and lasts ${String(data.blockHours)} hours, across machines. Times are in ${data.timezone}.`
44341
44392
  ];
44342
44393
  if (data.blocks.some((row) => row.startUncertain)) {
44343
44394
  lines.push(
@@ -46716,7 +46767,7 @@ function formatDeltaUsd(value, options = {}) {
46716
46767
  }
46717
46768
  const sign = options.signed && value !== 0 ? value > 0 ? "+" : "-" : "";
46718
46769
  const formatted = formatUsd(Math.abs(value));
46719
- return `${options.approximate ? "~" : ""}${sign}${formatted}`;
46770
+ return `${sign}${options.approximate ? "~" : ""}${formatted}`;
46720
46771
  }
46721
46772
  function formatPercent(value) {
46722
46773
  if (value === void 0) {
@@ -47901,7 +47952,7 @@ function toPressureCells(row) {
47901
47952
  ];
47902
47953
  }
47903
47954
  function formatOptionalInteger(value) {
47904
- return value === void 0 ? "" : formatInteger(value);
47955
+ return value === void 0 ? "-" : formatInteger(value);
47905
47956
  }
47906
47957
  function buildModelTable(rows) {
47907
47958
  return {
@@ -48835,12 +48886,19 @@ async function isGitRepository(repoDir, runCommand) {
48835
48886
  throw error;
48836
48887
  }
48837
48888
  );
48838
- return gitRepoResult.exitCode === 0;
48889
+ return gitRepoResult.exitCode === 0 && gitRepoResult.lines[0]?.trim() === "true";
48839
48890
  }
48840
48891
  function notGitRepositoryMessage(repoDir, repoDirOption) {
48841
48892
  const hint = repoDirOption === void 0 ? "<path>" : "<path to a git repository>";
48842
48893
  return `${repoDir} is not a git repository; pass --repo-dir ${hint}`;
48843
48894
  }
48895
+ async function assertGitRepositoryDir(repoDirOption) {
48896
+ const repoDir = resolveRepoDir(repoDirOption);
48897
+ await assertRepoDirReadable(repoDir);
48898
+ if (!await isGitRepository(repoDir, runGitCommand)) {
48899
+ throw new Error(notGitRepositoryMessage(repoDir, repoDirOption));
48900
+ }
48901
+ }
48844
48902
  function isNoCommitHistoryFailure(result) {
48845
48903
  if (result.exitCode !== 128) {
48846
48904
  return false;
@@ -49111,21 +49169,11 @@ async function collectGitOutcomes(options, deps = {}) {
49111
49169
  const repoDir = resolveRepoDir(options.repoDir);
49112
49170
  const includeMergeCommits = options.includeMergeCommits ?? false;
49113
49171
  const runCommand = deps.runGitCommand ?? runGitCommand;
49114
- if (options.activeUsageDays?.size === 0) {
49115
- const emptyCollection = createEmptyOutcomeCollection(repoDir, includeMergeCommits);
49116
- if (!deps.runGitCommand) {
49117
- await assertRepoDirReadable(repoDir);
49118
- if (!await isGitRepository(repoDir, runCommand)) {
49119
- emptyCollection.warning = notGitRepositoryMessage(repoDir, options.repoDir);
49120
- }
49121
- }
49122
- return emptyCollection;
49123
- }
49124
49172
  if (!deps.runGitCommand) {
49125
- await assertRepoDirReadable(repoDir);
49126
- if (!await isGitRepository(repoDir, runCommand)) {
49127
- throw new Error(notGitRepositoryMessage(repoDir, options.repoDir));
49128
- }
49173
+ await assertGitRepositoryDir(options.repoDir);
49174
+ }
49175
+ if (options.activeUsageDays?.size === 0) {
49176
+ return createEmptyOutcomeCollection(repoDir, includeMergeCommits);
49129
49177
  }
49130
49178
  let authorEmail;
49131
49179
  try {
@@ -49293,6 +49341,9 @@ async function buildEfficiencyData(granularity, options, deps = {}) {
49293
49341
  if (options.repoDir !== void 0 && !repoDir) {
49294
49342
  throw new Error("--repo-dir must be a non-empty path");
49295
49343
  }
49344
+ if (!deps.collectGitOutcomes) {
49345
+ await assertGitRepositoryDir(repoDir);
49346
+ }
49296
49347
  const dataset = await measureRuntimeProfileStage(
49297
49348
  deps.runtimeProfile,
49298
49349
  "efficiency.dataset.total",
@@ -49351,7 +49402,7 @@ async function buildEfficiencyData(granularity, options, deps = {}) {
49351
49402
  sourceFailures: dataset.sourceFailures,
49352
49403
  pricingOrigin,
49353
49404
  pricingWarning,
49354
- warnings: gitOutcomes.warning ? [...dataset.warnings, gitOutcomes.warning] : dataset.warnings,
49405
+ warnings: dataset.warnings,
49355
49406
  // The pricing stats are keyed by the priced events, not attribution's copies.
49356
49407
  notes: withPricingNotes(
49357
49408
  dataset.notes,
@@ -49438,7 +49489,7 @@ function emitEfficiencyReportDiagnostics(diagnostics) {
49438
49489
  `Git outcomes (${mergeModeLabel}): ${pluralize(diagnostics.gitCommitCount, "commit")}, +${diagnostics.gitLinesAdded}/-${diagnostics.gitLinesDeleted} lines (${diagnostics.repoDir})`
49439
49490
  );
49440
49491
  logger.info(
49441
- `Repo-attributed usage events: ${diagnostics.repoMatchedUsageEvents} matched, ${diagnostics.repoExcludedUsageEvents} excluded, ${diagnostics.repoUnattributedUsageEvents} unattributed`
49492
+ `Repo-attributed usage events: ${formatInteger(diagnostics.repoMatchedUsageEvents)} matched, ${formatInteger(diagnostics.repoExcludedUsageEvents)} excluded, ${formatInteger(diagnostics.repoUnattributedUsageEvents)} unattributed`
49442
49493
  );
49443
49494
  if (diagnostics.scopeNote) {
49444
49495
  logger.warn(diagnostics.scopeNote);
@@ -51371,6 +51422,15 @@ function toCounts(accumulator) {
51371
51422
  sessions: accumulator.sessions.size
51372
51423
  };
51373
51424
  }
51425
+ function findDisplayName(callsByName) {
51426
+ let top;
51427
+ for (const entry of callsByName) {
51428
+ if (top === void 0 || entry[1] > top[1] || entry[1] === top[1] && compareByCodePoint(entry[0], top[0]) < 0) {
51429
+ top = entry;
51430
+ }
51431
+ }
51432
+ return top?.[0] ?? "";
51433
+ }
51374
51434
  function findTopRepoRoot(callsByRepoRoot) {
51375
51435
  let top;
51376
51436
  for (const entry of callsByRepoRoot) {
@@ -51398,12 +51458,13 @@ function aggregateTools(calls, options) {
51398
51458
  const repoRoot = toProjectKey(options.sessionRepoRoots.get(sessionKey)) ?? null;
51399
51459
  const source = options.bySource ? call.source : void 0;
51400
51460
  const rowRepoRoot = options.byRepo ? repoRoot : void 0;
51401
- const key = [call.name, source ?? "", rowRepoRoot ?? ""].join("\0");
51461
+ const foldedName = call.name.toLowerCase();
51462
+ const key = [foldedName, source ?? "", rowRepoRoot ?? ""].join("\0");
51402
51463
  let row = rows.get(key);
51403
51464
  if (!row) {
51404
51465
  row = {
51405
51466
  ...createCountAccumulator(),
51406
- name: call.name,
51467
+ callsByName: /* @__PURE__ */ new Map(),
51407
51468
  ...source === void 0 ? {} : { source },
51408
51469
  ...rowRepoRoot === void 0 ? {} : { repoRoot: rowRepoRoot },
51409
51470
  callsByRepoRoot: /* @__PURE__ */ new Map()
@@ -51411,12 +51472,13 @@ function aggregateTools(calls, options) {
51411
51472
  rows.set(key, row);
51412
51473
  }
51413
51474
  addCall(row, call, sessionKey);
51475
+ row.callsByName.set(call.name, (row.callsByName.get(call.name) ?? 0) + 1);
51414
51476
  row.callsByRepoRoot.set(repoRoot, (row.callsByRepoRoot.get(repoRoot) ?? 0) + 1);
51415
51477
  addCall(totals, call, sessionKey);
51416
- names.add(call.name);
51478
+ names.add(foldedName);
51417
51479
  }
51418
51480
  const toolRows = [...rows.values()].map((row) => ({
51419
- name: row.name,
51481
+ name: findDisplayName(row.callsByName),
51420
51482
  ...row.source === void 0 ? {} : { source: row.source },
51421
51483
  ...row.repoRoot === void 0 ? {} : { repoRoot: row.repoRoot },
51422
51484
  ...toCounts(row),
@@ -53310,38 +53372,49 @@ function sumActiveMsByDay(events, timezone, groupOf) {
53310
53372
  sessionsByDate.set(dateKey, sessions);
53311
53373
  }
53312
53374
  const rows = [];
53375
+ const combinedByDay = /* @__PURE__ */ new Map();
53313
53376
  for (const [periodKey, sessions] of sessionsByDate) {
53314
- const activeMsByGroup = /* @__PURE__ */ new Map();
53377
+ const runsByGroup = /* @__PURE__ */ new Map();
53378
+ const sessionTimestamps = [];
53315
53379
  for (const sessionEvents of sessions.values()) {
53316
53380
  sessionEvents.sort((left, right) => left.timestampMs - right.timestampMs);
53381
+ sessionTimestamps.push(sessionEvents.map((entry) => entry.timestampMs));
53317
53382
  let runStart = 0;
53318
53383
  for (let index = 1; index <= sessionEvents.length; index += 1) {
53319
53384
  const group = sessionEvents[runStart].group;
53320
53385
  if (index < sessionEvents.length && sessionEvents[index].group === group) {
53321
53386
  continue;
53322
53387
  }
53323
- const runMs = computeActiveMs(
53324
- sessionEvents.slice(runStart, index).map((entry) => entry.timestampMs)
53325
- );
53326
- activeMsByGroup.set(group, (activeMsByGroup.get(group) ?? 0) + runMs);
53388
+ const runs = runsByGroup.get(group) ?? [];
53389
+ runs.push(sessionEvents.slice(runStart, index).map((entry) => entry.timestampMs));
53390
+ runsByGroup.set(group, runs);
53327
53391
  runStart = index;
53328
53392
  }
53329
53393
  }
53330
- for (const [group, value] of activeMsByGroup) {
53331
- rows.push({ periodKey, group, value });
53394
+ for (const [group, runs] of runsByGroup) {
53395
+ rows.push({ periodKey, group, value: computeUnionActiveMs(runs) });
53332
53396
  }
53397
+ combinedByDay.set(periodKey, computeUnionActiveMs(sessionTimestamps));
53333
53398
  }
53334
- return rows;
53399
+ return { rows, combinedByDay };
53335
53400
  }
53336
53401
  function toActiveTimeValueRows(events, timezone) {
53337
- return sumActiveMsByDay(events, timezone, (event) => event.source).map((row) => ({
53402
+ const { rows, combinedByDay } = sumActiveMsByDay(events, timezone, (event) => event.source);
53403
+ const sourceRows = rows.map((row) => ({
53338
53404
  periodKey: row.periodKey,
53339
53405
  source: row.group,
53340
53406
  value: row.value
53341
53407
  }));
53408
+ const combinedRows = [...combinedByDay].map(([periodKey, value]) => ({
53409
+ periodKey,
53410
+ source: "combined",
53411
+ value
53412
+ }));
53413
+ return [...sourceRows, ...combinedRows];
53342
53414
  }
53343
53415
  function toActiveTimeProjectValueRows(events, timezone, projectLabels) {
53344
- return sumActiveMsByDay(events, timezone, (event) => toProjectKey(event.repoRoot)).map((row) => {
53416
+ const { rows } = sumActiveMsByDay(events, timezone, (event) => toProjectKey(event.repoRoot));
53417
+ return rows.map((row) => {
53345
53418
  const project = projectLabels.get(row.group);
53346
53419
  if (project === void 0) {
53347
53420
  throw new Error(`No project label for ${row.group ?? "unattributed usage"}`);
@@ -55118,9 +55191,10 @@ function toCsvLine(event) {
55118
55191
  event.cacheWrite1hTokens
55119
55192
  ].map(toCsvField).join(",");
55120
55193
  }
55121
- async function runEventsReport(options, deps = {}) {
55122
- const format = resolveEventsFormat(options);
55123
- const pushTarget = resolveOtlpPushTarget(options);
55194
+ async function runEventsReport(commandOptions, commandDeps = {}) {
55195
+ const format = resolveEventsFormat(commandOptions);
55196
+ const pushTarget = resolveOtlpPushTarget(commandOptions);
55197
+ const { options, deps } = commandOptions.last === void 0 ? { options: commandOptions, deps: commandDeps } : await resolveUsageDataRequest("daily", commandOptions, commandDeps);
55124
55198
  const dataset = await buildUsageEventDataset(options, deps);
55125
55199
  const { pricedEvents, pricingOrigin, pricingWarning } = await applyPricingToUsageEventDataset(
55126
55200
  dataset,
@@ -55156,16 +55230,16 @@ async function runEventsReport(options, deps = {}) {
55156
55230
  timezone: dataset.normalizedInputs.timezone,
55157
55231
  nowMs: (deps.now?.() ?? /* @__PURE__ */ new Date()).getTime()
55158
55232
  });
55159
- await writeOtlp({ kind: "metrics", lines }, pushTarget, deps);
55233
+ await writeOtlp({ kind: "metrics", lines }, pushTarget, commandDeps);
55160
55234
  return;
55161
55235
  }
55162
55236
  if (toolCalls) {
55163
55237
  const lines = buildOtlpTraces({
55164
55238
  usageEvents: sortedEvents,
55165
55239
  toolCalls: dedupeToolCalls(toolCalls.events),
55166
- includePaths: options.otelIncludePaths
55240
+ includePaths: commandOptions.otelIncludePaths
55167
55241
  });
55168
- await writeOtlp({ kind: "traces", lines }, pushTarget, deps);
55242
+ await writeOtlp({ kind: "traces", lines }, pushTarget, commandDeps);
55169
55243
  return;
55170
55244
  }
55171
55245
  if (format === "csv") {
@@ -55609,10 +55683,7 @@ function aggregateWrapped(events, options) {
55609
55683
  }
55610
55684
  }
55611
55685
  const sortedDateKeys = [...activeDateKeys].sort(compareByCodePoint);
55612
- let activeMs = 0;
55613
- for (const timestamps of sessionTimestamps.values()) {
55614
- activeMs += computeActiveMs(timestamps);
55615
- }
55686
+ const activeMs = computeUnionActiveMs(sessionTimestamps.values());
55616
55687
  let peakHour;
55617
55688
  for (let hour = 0; hour < hourTokens.length; hour += 1) {
55618
55689
  if (hourTokens[hour] > 0 && (peakHour === void 0 || hourTokens[hour] > peakHour.totalTokens)) {
@@ -57040,7 +57111,7 @@ var eventsReportDefinition = {
57040
57111
  ).option(
57041
57112
  "--otlp-endpoint <url>",
57042
57113
  "With an OTLP format: POST to <url>/v1/traces or /v1/metrics (unless <url> already ends with it) instead of printing; headers from OTEL_EXPORTER_OTLP_HEADERS"
57043
- );
57114
+ ).option("--last <n>", "Export the last N days, counting today");
57044
57115
  addFullHistoryOption(command);
57045
57116
  addRepoFilterOption(command).action(
57046
57117
  (options) => runEventsReport(options)
@@ -58177,6 +58248,7 @@ async function runMachineList(deps = {}) {
58177
58248
  }
58178
58249
  async function runSync(names, options, deps = {}) {
58179
58250
  const print = deps.print ?? console.log;
58251
+ const printError = deps.printError ?? console.error;
58180
58252
  const machines = await loadConfiguredMachines();
58181
58253
  const unknownNames = names.filter((name) => !Object.hasOwn(machines, name));
58182
58254
  if (unknownNames.length > 0) {
@@ -58203,7 +58275,7 @@ async function runSync(names, options, deps = {}) {
58203
58275
  now: deps.now
58204
58276
  });
58205
58277
  for (const line of renderSyncOutcome(outcome, (deps.now ?? Date.now)())) {
58206
- print(line);
58278
+ (outcome.ok ? print : printError)(line);
58207
58279
  }
58208
58280
  return outcome;
58209
58281
  })
@@ -63125,23 +63197,23 @@ var schemaDocuments = {
63125
63197
  // src/cli/create-schema-command.ts
63126
63198
  var schemaNames = Object.keys(schemaDocuments).sort();
63127
63199
  function createSchemaCommand() {
63128
- return new Command5("schema").description("Print a bundled report JSON Schema").argument("[name]", `Schema name: ${schemaNames.join(", ")}`).option("--list", "List available schema names").addHelpText(
63200
+ return new Command5("schema").description("Print a bundled report JSON Schema").argument("[name]", `Schema name: ${schemaNames.join(", ")}`).option("--list", "List available schema names (the default without a name)").addHelpText(
63129
63201
  "after",
63130
63202
  formatHelpExamples([
63131
63203
  "llm-usage schema --list",
63132
63204
  "llm-usage schema summary > summary.schema.json"
63133
63205
  ])
63134
63206
  ).action((name, options) => {
63135
- if (options.list) {
63207
+ if (options.list || name === void 0) {
63136
63208
  for (const schemaName of schemaNames) {
63137
63209
  console.log(schemaName);
63138
63210
  }
63139
63211
  return;
63140
63212
  }
63141
- const schema = name === void 0 ? void 0 : schemaDocuments[name];
63213
+ const schema = schemaDocuments[name];
63142
63214
  if (schema === void 0) {
63143
63215
  throw new Error(
63144
- `Unknown schema "${name ?? ""}"; valid names: ${schemaNames.join(", ")} (or --list)`
63216
+ `Unknown schema "${name}"; valid names: ${schemaNames.join(", ")} (or --list)`
63145
63217
  );
63146
63218
  }
63147
63219
  console.log(JSON.stringify(schema, null, 2));
@@ -63314,7 +63386,7 @@ var currentWorkerData = workerData;
63314
63386
  var mainPromise = !isMainThread && isParseWorkerRequest(currentWorkerData) ? runParseWorker() : runCli();
63315
63387
  mainPromise.catch((error) => {
63316
63388
  const message = error instanceof Error ? error.message : String(error);
63317
- console.error(message);
63389
+ console.error(`error: ${message}`);
63318
63390
  process.exitCode = 1;
63319
63391
  });
63320
63392
  /*! Bundled license information:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llm-usage-metrics",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "description": "CLI for aggregating local LLM usage metrics from pi, codex, gemini, droid, opencode, openclaw, claude, copilot, goose, amp, qwen, kimi, cline, roocode, kilocode, antigravity, and deepseek harness sessions",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@12.9.1",