llm-usage-metrics 0.1.2 → 0.1.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/README.md CHANGED
@@ -20,9 +20,11 @@ npm install -g llm-usage-metrics
20
20
  Or run without global install:
21
21
 
22
22
  ```bash
23
- npx llm-usage-metrics daily
23
+ npx --yes llm-usage-metrics daily
24
24
  ```
25
25
 
26
+ (`npx llm-usage daily` works when the project is already installed locally.)
27
+
26
28
  ## Usage
27
29
 
28
30
  ### Daily report (default terminal table)
package/dist/index.js CHANGED
@@ -116,6 +116,10 @@ function getPeriodKey(timestampIso, granularity, timezone) {
116
116
  }
117
117
 
118
118
  // src/aggregate/aggregate-usage.ts
119
+ var USD_PRECISION_SCALE = 1e12;
120
+ function addUsd(left, right) {
121
+ return Math.round((left + right) * USD_PRECISION_SCALE) / USD_PRECISION_SCALE;
122
+ }
119
123
  function createEmptyTotals() {
120
124
  return {
121
125
  inputTokens: 0,
@@ -140,7 +144,7 @@ function addEventToAccumulator(accumulator, event) {
140
144
  accumulator.totals.cacheReadTokens += event.cacheReadTokens;
141
145
  accumulator.totals.cacheWriteTokens += event.cacheWriteTokens;
142
146
  accumulator.totals.totalTokens += event.totalTokens;
143
- accumulator.totals.costUsd += event.costUsd ?? 0;
147
+ accumulator.totals.costUsd = addUsd(accumulator.totals.costUsd, event.costUsd ?? 0);
144
148
  if (event.model) {
145
149
  accumulator.modelSet.add(event.model);
146
150
  }
@@ -152,7 +156,7 @@ function addTotals(target, source) {
152
156
  target.cacheReadTokens += source.cacheReadTokens;
153
157
  target.cacheWriteTokens += source.cacheWriteTokens;
154
158
  target.totalTokens += source.totalTokens;
155
- target.costUsd += source.costUsd;
159
+ target.costUsd = addUsd(target.costUsd, source.costUsd);
156
160
  }
157
161
  function sourceSortComparator(left, right) {
158
162
  const sourceOrder = {
@@ -480,15 +484,18 @@ var LiteLLMPricingFetcher = class {
480
484
  }
481
485
  resolvePrefixModelMatch(normalizedModel) {
482
486
  const candidates = [normalizedModel, stripProviderPrefix(normalizedModel)];
483
- const modelNames = [...this.pricingByModel.keys()];
484
487
  for (const candidate of candidates) {
485
- const prefixMatches = modelNames.filter((modelName) => {
486
- return candidate.startsWith(modelName);
487
- });
488
- if (prefixMatches.length > 0) {
489
- return prefixMatches.sort(
490
- (left, right) => right.length - left.length || left.localeCompare(right)
491
- )[0];
488
+ let bestMatch;
489
+ for (const modelName of this.pricingByModel.keys()) {
490
+ if (!candidate.startsWith(modelName)) {
491
+ continue;
492
+ }
493
+ if (!bestMatch || modelName.length > bestMatch.length || modelName.length === bestMatch.length && modelName.localeCompare(bestMatch) < 0) {
494
+ bestMatch = modelName;
495
+ }
496
+ }
497
+ if (bestMatch) {
498
+ return bestMatch;
492
499
  }
493
500
  }
494
501
  return void 0;
@@ -846,7 +853,6 @@ function renderTerminalTable(rows, options = {}) {
846
853
  }
847
854
 
848
855
  // src/sources/codex/codex-source-adapter.ts
849
- import { readFile as readFile2 } from "fs/promises";
850
856
  import os2 from "os";
851
857
  import path3 from "path";
852
858
 
@@ -933,6 +939,50 @@ async function discoverJsonlFiles(rootDir) {
933
939
  return files;
934
940
  }
935
941
 
942
+ // src/utils/read-jsonl-objects.ts
943
+ import { createReadStream } from "fs";
944
+ import { createInterface } from "readline";
945
+ function asObject(value) {
946
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
947
+ return void 0;
948
+ }
949
+ return value;
950
+ }
951
+ async function* readJsonlObjects(filePath) {
952
+ const stream = createReadStream(filePath, {
953
+ encoding: "utf8"
954
+ });
955
+ const lineReader = createInterface({
956
+ input: stream,
957
+ crlfDelay: Infinity
958
+ });
959
+ let isFirstLine = true;
960
+ try {
961
+ for await (const rawLine of lineReader) {
962
+ const normalizedLine = isFirstLine ? rawLine.replace(/^\uFEFF/u, "") : rawLine;
963
+ isFirstLine = false;
964
+ const lineText = normalizedLine.trim();
965
+ if (!lineText) {
966
+ continue;
967
+ }
968
+ let parsed;
969
+ try {
970
+ parsed = JSON.parse(lineText);
971
+ } catch {
972
+ continue;
973
+ }
974
+ const parsedObject = asObject(parsed);
975
+ if (!parsedObject) {
976
+ continue;
977
+ }
978
+ yield parsedObject;
979
+ }
980
+ } finally {
981
+ lineReader.close();
982
+ stream.destroy();
983
+ }
984
+ }
985
+
936
986
  // src/sources/codex/codex-source-adapter.ts
937
987
  var defaultSessionsDir = path3.join(os2.homedir(), ".codex", "sessions");
938
988
  var LEGACY_CODEX_MODEL_FALLBACK = "legacy-codex-unknown";
@@ -1015,27 +1065,12 @@ var CodexSourceAdapter = class {
1015
1065
  return discoverJsonlFiles(this.sessionsDir);
1016
1066
  }
1017
1067
  async parseFile(filePath) {
1018
- const content = await readFile2(filePath, "utf8");
1019
1068
  const events = [];
1020
1069
  const state = {
1021
1070
  sessionId: getFallbackSessionId(filePath),
1022
1071
  provider: "openai"
1023
1072
  };
1024
- for (const rawLine of content.split(/\r?\n/u)) {
1025
- const lineText = rawLine.trim();
1026
- if (!lineText) {
1027
- continue;
1028
- }
1029
- let parsedLine;
1030
- try {
1031
- parsedLine = JSON.parse(lineText);
1032
- } catch {
1033
- continue;
1034
- }
1035
- const line = asRecord2(parsedLine);
1036
- if (!line) {
1037
- continue;
1038
- }
1073
+ for await (const line of readJsonlObjects(filePath)) {
1039
1074
  if (line.type === "session_meta") {
1040
1075
  const payload2 = asRecord2(line.payload);
1041
1076
  state.sessionId = asText(payload2?.id) ?? state.sessionId;
@@ -1101,7 +1136,6 @@ var CodexSourceAdapter = class {
1101
1136
  };
1102
1137
 
1103
1138
  // src/sources/pi/pi-source-adapter.ts
1104
- import { readFile as readFile3 } from "fs/promises";
1105
1139
  import os3 from "os";
1106
1140
  import path4 from "path";
1107
1141
  var defaultSessionsDir2 = path4.join(os3.homedir(), ".pi", "agent", "sessions");
@@ -1131,7 +1165,12 @@ function resolveTimestamp(line, message, state) {
1131
1165
  const candidates = [line.timestamp, message?.timestamp, state.sessionTimestamp];
1132
1166
  for (const candidate of candidates) {
1133
1167
  if (typeof candidate === "number" && Number.isFinite(candidate)) {
1134
- return new Date(candidate).toISOString();
1168
+ const timestampMs = Math.abs(candidate) < 1e12 ? candidate * 1e3 : candidate;
1169
+ const date = new Date(timestampMs);
1170
+ if (!Number.isNaN(date.getTime())) {
1171
+ return date.toISOString();
1172
+ }
1173
+ continue;
1135
1174
  }
1136
1175
  if (typeof candidate === "string" && candidate.trim()) {
1137
1176
  return candidate;
@@ -1174,24 +1213,9 @@ var PiSourceAdapter = class {
1174
1213
  return discoverJsonlFiles(this.sessionsDir);
1175
1214
  }
1176
1215
  async parseFile(filePath) {
1177
- const content = await readFile3(filePath, "utf8");
1178
1216
  const events = [];
1179
1217
  const state = { sessionId: getFallbackSessionId2(filePath) };
1180
- for (const rawLine of content.split(/\r?\n/u)) {
1181
- const lineText = rawLine.trim();
1182
- if (!lineText) {
1183
- continue;
1184
- }
1185
- let parsedLine;
1186
- try {
1187
- parsedLine = JSON.parse(lineText);
1188
- } catch {
1189
- continue;
1190
- }
1191
- const line = asRecord3(parsedLine);
1192
- if (!line) {
1193
- continue;
1194
- }
1218
+ for await (const line of readJsonlObjects(filePath)) {
1195
1219
  if (line.type === "session") {
1196
1220
  state.sessionId = asText2(line.id) ?? state.sessionId;
1197
1221
  state.sessionTimestamp = asText2(line.timestamp) ?? state.sessionTimestamp;
@@ -1239,6 +1263,7 @@ var PiSourceAdapter = class {
1239
1263
  };
1240
1264
 
1241
1265
  // src/cli/run-usage-report.ts
1266
+ var MAX_PARALLEL_FILE_PARSING = 8;
1242
1267
  function validateDateInput(value, flagName) {
1243
1268
  if (!/^\d{4}-\d{2}-\d{2}$/u.test(value)) {
1244
1269
  throw new Error(`${flagName} must use format YYYY-MM-DD`);
@@ -1273,21 +1298,45 @@ function normalizeSourceFilter(source) {
1273
1298
  }
1274
1299
  return new Set(normalizedSources);
1275
1300
  }
1301
+ function validateSourceFilterValues(sourceFilter, availableSourceIds) {
1302
+ if (!sourceFilter) {
1303
+ return;
1304
+ }
1305
+ const unknownSources = [...sourceFilter].filter((source) => !availableSourceIds.has(source));
1306
+ if (unknownSources.length === 0) {
1307
+ return;
1308
+ }
1309
+ const allowedSources = [...availableSourceIds].sort((left, right) => left.localeCompare(right));
1310
+ throw new Error(
1311
+ `Unknown --source value(s): ${unknownSources.join(", ")}. Allowed values: ${allowedSources.join(", ")}`
1312
+ );
1313
+ }
1276
1314
  function matchesProvider(provider, providerFilter) {
1277
1315
  if (!providerFilter) {
1278
1316
  return true;
1279
1317
  }
1280
1318
  return provider?.toLowerCase().includes(providerFilter) ?? false;
1281
1319
  }
1282
- function matchesSource(source, sourceFilter) {
1283
- if (!sourceFilter) {
1284
- return true;
1285
- }
1286
- return sourceFilter.has(source.toLowerCase());
1287
- }
1288
1320
  async function parseAdapterEvents(adapter) {
1289
1321
  const files = await adapter.discoverFiles();
1290
- const parsedByFile = await Promise.all(files.map((filePath) => adapter.parseFile(filePath)));
1322
+ if (files.length === 0) {
1323
+ return [];
1324
+ }
1325
+ const parsedByFile = Array.from({ length: files.length }, () => []);
1326
+ const workerCount = Math.min(MAX_PARALLEL_FILE_PARSING, files.length);
1327
+ let nextFileIndex = 0;
1328
+ const workers = Array.from({ length: workerCount }, async () => {
1329
+ while (nextFileIndex < files.length) {
1330
+ const fileIndex = nextFileIndex;
1331
+ nextFileIndex += 1;
1332
+ const filePath = files[fileIndex];
1333
+ if (!filePath) {
1334
+ break;
1335
+ }
1336
+ parsedByFile[fileIndex] = await adapter.parseFile(filePath);
1337
+ }
1338
+ });
1339
+ await Promise.all(workers);
1291
1340
  return parsedByFile.flat();
1292
1341
  }
1293
1342
  function filterEventsByDateRange(events, timezone, since, until) {
@@ -1335,6 +1384,18 @@ async function resolvePricingSource(options) {
1335
1384
  return fallbackPricingSource;
1336
1385
  }
1337
1386
  }
1387
+ function eventNeedsPricingLookup(event) {
1388
+ if (!event.model) {
1389
+ return false;
1390
+ }
1391
+ return event.costMode !== "explicit" || event.costUsd === void 0;
1392
+ }
1393
+ function shouldLoadPricingSource(options, events) {
1394
+ if (options.pricingUrl || options.pricingOffline) {
1395
+ return true;
1396
+ }
1397
+ return events.some((event) => eventNeedsPricingLookup(event));
1398
+ }
1338
1399
  async function buildUsageReport(granularity, options) {
1339
1400
  if (options.markdown && options.json) {
1340
1401
  throw new Error("Choose either --markdown or --json, not both");
@@ -1354,28 +1415,29 @@ async function buildUsageReport(granularity, options) {
1354
1415
  const providerFilter = normalizeProviderFilter(options.provider);
1355
1416
  const sourceFilter = normalizeSourceFilter(options.source);
1356
1417
  const effectiveProviderFilter = providerFilter ?? "openai";
1357
- const pricingSource = await resolvePricingSource(options);
1358
- const piAdapter = new PiSourceAdapter({
1359
- sessionsDir: options.piDir,
1360
- providerFilter: (provider) => matchesProvider(provider, effectiveProviderFilter)
1361
- });
1362
- const codexAdapter = new CodexSourceAdapter({
1363
- sessionsDir: options.codexDir
1364
- });
1365
- const [piEvents, codexEvents] = await Promise.all([
1366
- parseAdapterEvents(piAdapter),
1367
- parseAdapterEvents(codexAdapter)
1368
- ]);
1369
- const providerAndSourceFilteredEvents = [...piEvents, ...codexEvents].filter(
1370
- (event) => matchesProvider(event.provider, effectiveProviderFilter) && matchesSource(event.source, sourceFilter)
1418
+ const adapters = [
1419
+ new PiSourceAdapter({
1420
+ sessionsDir: options.piDir,
1421
+ providerFilter: (provider) => matchesProvider(provider, effectiveProviderFilter)
1422
+ }),
1423
+ new CodexSourceAdapter({
1424
+ sessionsDir: options.codexDir
1425
+ })
1426
+ ];
1427
+ const availableSourceIds = new Set(adapters.map((adapter) => adapter.id.toLowerCase()));
1428
+ validateSourceFilterValues(sourceFilter, availableSourceIds);
1429
+ const adaptersToParse = sourceFilter ? adapters.filter((adapter) => sourceFilter.has(adapter.id.toLowerCase())) : adapters;
1430
+ const parsedEventsByAdapter = await Promise.all(
1431
+ adaptersToParse.map((adapter) => parseAdapterEvents(adapter))
1371
1432
  );
1433
+ const providerFilteredEvents = parsedEventsByAdapter.flat().filter((event) => matchesProvider(event.provider, effectiveProviderFilter));
1372
1434
  const dateFilteredEvents = filterEventsByDateRange(
1373
- providerAndSourceFilteredEvents,
1435
+ providerFilteredEvents,
1374
1436
  timezone,
1375
1437
  options.since,
1376
1438
  options.until
1377
1439
  );
1378
- const pricedEvents = applyPricingToEvents(dateFilteredEvents, pricingSource);
1440
+ const pricedEvents = shouldLoadPricingSource(options, dateFilteredEvents) ? applyPricingToEvents(dateFilteredEvents, await resolvePricingSource(options)) : dateFilteredEvents;
1379
1441
  const rows = aggregateUsage(pricedEvents, {
1380
1442
  granularity,
1381
1443
  timezone
@@ -1401,7 +1463,7 @@ function collectSourceOption(value, previous) {
1401
1463
  function addSharedOptions(command) {
1402
1464
  return command.option("--pi-dir <path>", "Path to .pi sessions directory").option("--codex-dir <path>", "Path to .codex sessions directory").option(
1403
1465
  "--source <name>",
1404
- "Filter by source id (repeatable or comma-separated, e.g. --source codex or --source pi,codex)",
1466
+ "Filter by source id (repeatable or comma-separated, allowed: pi,codex)",
1405
1467
  collectSourceOption,
1406
1468
  []
1407
1469
  ).option("--since <YYYY-MM-DD>", "Inclusive start date filter").option("--until <YYYY-MM-DD>", "Inclusive end date filter").option("--timezone <iana>", "Timezone for bucketing", defaultTimezone).option("--provider <name>", "Provider filter (defaults to openai behavior)").option("--pricing-url <url>", "Override LiteLLM pricing source URL").option("--pricing-offline", "Use cached LiteLLM pricing only (no network fetch)").option("--markdown", "Render output as markdown table").option("--json", "Render output as JSON");
@@ -1423,9 +1485,20 @@ function createCommand(granularity) {
1423
1485
  });
1424
1486
  return command;
1425
1487
  }
1488
+ function rootDescription() {
1489
+ return [
1490
+ "Aggregate local LLM usage metrics from pi and codex sessions",
1491
+ "",
1492
+ "Examples:",
1493
+ " $ llm-usage daily",
1494
+ " $ llm-usage weekly --timezone Europe/Paris",
1495
+ " $ llm-usage monthly --since 2026-01-01 --until 2026-01-31 --source codex --json",
1496
+ " $ npx --yes llm-usage-metrics daily"
1497
+ ].join("\n");
1498
+ }
1426
1499
  function createCli() {
1427
1500
  const program = new Command();
1428
- program.name("llm-usage").description("Aggregate local LLM usage metrics from pi and codex sessions").showHelpAfterError().addCommand(createCommand("daily")).addCommand(createCommand("weekly")).addCommand(createCommand("monthly"));
1501
+ program.name("llm-usage").description(rootDescription()).showHelpAfterError().addCommand(createCommand("daily")).addCommand(createCommand("weekly")).addCommand(createCommand("monthly"));
1429
1502
  return program;
1430
1503
  }
1431
1504
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli/create-cli.ts","../src/domain/normalization.ts","../src/utils/time-buckets.ts","../src/aggregate/aggregate-usage.ts","../src/pricing/cost-engine.ts","../src/pricing/litellm-pricing-fetcher.ts","../src/pricing/static-pricing-source.ts","../src/render/markdown-table.ts","../src/render/row-cells.ts","../src/render/terminal-table.ts","../src/sources/codex/codex-source-adapter.ts","../src/domain/usage-event.ts","../src/utils/discover-jsonl-files.ts","../src/sources/pi/pi-source-adapter.ts","../src/cli/run-usage-report.ts","../src/cli/index.ts"],"sourcesContent":["import { Command } from 'commander';\n\nimport { runUsageReport } from './run-usage-report.js';\n\nexport type UsageGranularity = 'daily' | 'weekly' | 'monthly';\n\ntype SharedOptions = {\n piDir?: string;\n codexDir?: string;\n source?: string[];\n since?: string;\n until?: string;\n timezone?: string;\n provider?: string;\n markdown?: boolean;\n json?: boolean;\n pricingUrl?: string;\n pricingOffline?: boolean;\n};\n\nconst defaultTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';\n\nfunction collectSourceOption(value: string, previous: string[]): string[] {\n return [...previous, ...value.split(',')];\n}\n\nfunction addSharedOptions(command: Command): Command {\n return command\n .option('--pi-dir <path>', 'Path to .pi sessions directory')\n .option('--codex-dir <path>', 'Path to .codex sessions directory')\n .option(\n '--source <name>',\n 'Filter by source id (repeatable or comma-separated, e.g. --source codex or --source pi,codex)',\n collectSourceOption,\n [],\n )\n .option('--since <YYYY-MM-DD>', 'Inclusive start date filter')\n .option('--until <YYYY-MM-DD>', 'Inclusive end date filter')\n .option('--timezone <iana>', 'Timezone for bucketing', defaultTimezone)\n .option('--provider <name>', 'Provider filter (defaults to openai behavior)')\n .option('--pricing-url <url>', 'Override LiteLLM pricing source URL')\n .option('--pricing-offline', 'Use cached LiteLLM pricing only (no network fetch)')\n .option('--markdown', 'Render output as markdown table')\n .option('--json', 'Render output as JSON');\n}\n\nfunction commandDescription(granularity: UsageGranularity): string {\n switch (granularity) {\n case 'daily':\n return 'Show daily usage report';\n case 'weekly':\n return 'Show weekly usage report (week starts Monday)';\n case 'monthly':\n return 'Show monthly usage report';\n }\n}\n\nfunction createCommand(granularity: UsageGranularity): Command {\n const command = new Command(granularity);\n\n addSharedOptions(command)\n .description(commandDescription(granularity))\n .action(async (options: SharedOptions) => {\n await runUsageReport(granularity, options);\n });\n\n return command;\n}\n\nexport function createCli(): Command {\n const program = new Command();\n\n program\n .name('llm-usage')\n .description('Aggregate local LLM usage metrics from pi and codex sessions')\n .showHelpAfterError()\n .addCommand(createCommand('daily'))\n .addCommand(createCommand('weekly'))\n .addCommand(createCommand('monthly'));\n\n return program;\n}\n","export type NumberLike = number | string | null | undefined;\n\nexport function normalizeNonNegativeInteger(value: NumberLike): number {\n if (value === null || value === undefined) {\n return 0;\n }\n\n const parsed = typeof value === 'number' ? value : Number(value);\n\n if (!Number.isFinite(parsed)) {\n return 0;\n }\n\n return Math.max(0, Math.trunc(parsed));\n}\n\nexport function normalizeUsdCost(value: NumberLike): number | undefined {\n if (value === null || value === undefined) {\n return undefined;\n }\n\n if (typeof value === 'string' && value.trim() === '') {\n return undefined;\n }\n\n const parsed = typeof value === 'number' ? value : Number(value);\n\n if (!Number.isFinite(parsed)) {\n return undefined;\n }\n\n return Math.max(0, parsed);\n}\n\nexport function normalizeTimestamp(value: string | Date): string {\n const date = value instanceof Date ? value : new Date(value);\n\n if (Number.isNaN(date.getTime())) {\n throw new Error(`Invalid timestamp: ${String(value)}`);\n }\n\n return date.toISOString();\n}\n\nexport function normalizeModelList(models: Iterable<string | null | undefined>): string[] {\n const deduplicated = new Set<string>();\n\n for (const model of models) {\n if (!model) {\n continue;\n }\n\n const normalized = model.trim();\n\n if (!normalized) {\n continue;\n }\n\n deduplicated.add(normalized);\n }\n\n return [...deduplicated].sort((left, right) => left.localeCompare(right));\n}\n","export type ReportGranularity = 'daily' | 'weekly' | 'monthly';\n\ntype LocalDateParts = {\n year: number;\n month: number;\n day: number;\n};\n\nconst formatterCache = new Map<string, Intl.DateTimeFormat>();\n\nfunction getDateFormatter(timezone: string): Intl.DateTimeFormat {\n const cachedFormatter = formatterCache.get(timezone);\n\n if (cachedFormatter) {\n return cachedFormatter;\n }\n\n const formatter = new Intl.DateTimeFormat('en-CA', {\n timeZone: timezone,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n });\n formatterCache.set(timezone, formatter);\n return formatter;\n}\n\nfunction extractLocalDateParts(timestampIso: string, timezone: string): LocalDateParts {\n const date = new Date(timestampIso);\n\n if (Number.isNaN(date.getTime())) {\n throw new Error(`Invalid event timestamp: ${timestampIso}`);\n }\n\n const formatter = getDateFormatter(timezone);\n const parts = formatter.formatToParts(date);\n\n const year = Number(parts.find((part) => part.type === 'year')?.value);\n const month = Number(parts.find((part) => part.type === 'month')?.value);\n const day = Number(parts.find((part) => part.type === 'day')?.value);\n\n if (!year || !month || !day) {\n throw new Error(`Could not resolve local date parts for timestamp: ${timestampIso}`);\n }\n\n return { year, month, day };\n}\n\nfunction createUtcDate(localDate: LocalDateParts): Date {\n return new Date(Date.UTC(localDate.year, localDate.month - 1, localDate.day));\n}\n\nfunction addDays(date: Date, days: number): Date {\n return new Date(date.getTime() + days * 24 * 60 * 60 * 1000);\n}\n\nfunction toIsoDayOfWeek(date: Date): number {\n const utcDay = date.getUTCDay();\n return utcDay === 0 ? 7 : utcDay;\n}\n\nfunction getIsoWeekParts(localDate: LocalDateParts): { weekYear: number; weekNumber: number } {\n const localUtcDate = createUtcDate(localDate);\n const isoDay = toIsoDayOfWeek(localUtcDate);\n\n const currentWeekMonday = addDays(localUtcDate, -(isoDay - 1));\n const currentWeekThursday = addDays(localUtcDate, 4 - isoDay);\n const weekYear = currentWeekThursday.getUTCFullYear();\n\n const jan4 = new Date(Date.UTC(weekYear, 0, 4));\n const jan4IsoDay = toIsoDayOfWeek(jan4);\n const firstWeekMonday = addDays(jan4, -(jan4IsoDay - 1));\n\n const diffMs = currentWeekMonday.getTime() - firstWeekMonday.getTime();\n const weekNumber = Math.floor(diffMs / (7 * 24 * 60 * 60 * 1000)) + 1;\n\n return { weekYear, weekNumber };\n}\n\nexport function getPeriodKey(\n timestampIso: string,\n granularity: ReportGranularity,\n timezone: string,\n): string {\n const localDate = extractLocalDateParts(timestampIso, timezone);\n\n if (granularity === 'daily') {\n return `${localDate.year}-${String(localDate.month).padStart(2, '0')}-${String(localDate.day).padStart(2, '0')}`;\n }\n\n if (granularity === 'monthly') {\n return `${localDate.year}-${String(localDate.month).padStart(2, '0')}`;\n }\n\n const isoWeek = getIsoWeekParts(localDate);\n return `${isoWeek.weekYear}-W${String(isoWeek.weekNumber).padStart(2, '0')}`;\n}\n","import type { UsageEvent } from '../domain/usage-event.js';\nimport type {\n GrandTotalRow,\n PeriodCombinedRow,\n PeriodSourceRow,\n UsageReportRow,\n UsageTotals,\n} from '../domain/usage-report-row.js';\nimport { normalizeModelList } from '../domain/normalization.js';\nimport { getPeriodKey, type ReportGranularity } from '../utils/time-buckets.js';\n\nexport type AggregateUsageOptions = {\n granularity: ReportGranularity;\n timezone: string;\n};\n\ntype RowAccumulator = {\n totals: UsageTotals;\n modelSet: Set<string>;\n};\n\nfunction createEmptyTotals(): UsageTotals {\n return {\n inputTokens: 0,\n outputTokens: 0,\n reasoningTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n totalTokens: 0,\n costUsd: 0,\n };\n}\n\nfunction createRowAccumulator(): RowAccumulator {\n return {\n totals: createEmptyTotals(),\n modelSet: new Set<string>(),\n };\n}\n\nfunction addEventToAccumulator(accumulator: RowAccumulator, event: UsageEvent): void {\n accumulator.totals.inputTokens += event.inputTokens;\n accumulator.totals.outputTokens += event.outputTokens;\n accumulator.totals.reasoningTokens += event.reasoningTokens;\n accumulator.totals.cacheReadTokens += event.cacheReadTokens;\n accumulator.totals.cacheWriteTokens += event.cacheWriteTokens;\n accumulator.totals.totalTokens += event.totalTokens;\n accumulator.totals.costUsd += event.costUsd ?? 0;\n\n if (event.model) {\n accumulator.modelSet.add(event.model);\n }\n}\n\nfunction addTotals(target: UsageTotals, source: UsageTotals): void {\n target.inputTokens += source.inputTokens;\n target.outputTokens += source.outputTokens;\n target.reasoningTokens += source.reasoningTokens;\n target.cacheReadTokens += source.cacheReadTokens;\n target.cacheWriteTokens += source.cacheWriteTokens;\n target.totalTokens += source.totalTokens;\n target.costUsd += source.costUsd;\n}\n\nfunction sourceSortComparator(left: string, right: string): number {\n const sourceOrder: Record<string, number> = {\n pi: 0,\n codex: 1,\n };\n\n const leftWeight = sourceOrder[left] ?? Number.MAX_SAFE_INTEGER;\n const rightWeight = sourceOrder[right] ?? Number.MAX_SAFE_INTEGER;\n\n if (leftWeight !== rightWeight) {\n return leftWeight - rightWeight;\n }\n\n return left.localeCompare(right);\n}\n\nexport function aggregateUsage(\n events: UsageEvent[],\n options: AggregateUsageOptions,\n): UsageReportRow[] {\n const periodMap = new Map<string, Map<string, RowAccumulator>>();\n\n for (const event of events) {\n const periodKey = getPeriodKey(event.timestamp, options.granularity, options.timezone);\n const periodSources = periodMap.get(periodKey) ?? new Map<string, RowAccumulator>();\n periodMap.set(periodKey, periodSources);\n\n const rowAccumulator = periodSources.get(event.source) ?? createRowAccumulator();\n periodSources.set(event.source, rowAccumulator);\n\n addEventToAccumulator(rowAccumulator, event);\n }\n\n const sortedPeriodKeys = [...periodMap.keys()].sort((left, right) => left.localeCompare(right));\n const rows: UsageReportRow[] = [];\n const grandTotals = createEmptyTotals();\n const grandModels = new Set<string>();\n\n for (const periodKey of sortedPeriodKeys) {\n const sourceMap = periodMap.get(periodKey);\n\n if (!sourceMap) {\n continue;\n }\n\n const periodCombinedTotals = createEmptyTotals();\n const periodModels = new Set<string>();\n\n const sortedSources = [...sourceMap.keys()].sort(sourceSortComparator);\n\n for (const source of sortedSources) {\n const accumulator = sourceMap.get(source);\n\n if (!accumulator) {\n continue;\n }\n\n const sourceRow: PeriodSourceRow = {\n rowType: 'period_source',\n periodKey,\n source,\n models: normalizeModelList(accumulator.modelSet),\n ...accumulator.totals,\n };\n\n rows.push(sourceRow);\n\n addTotals(periodCombinedTotals, accumulator.totals);\n addTotals(grandTotals, accumulator.totals);\n\n for (const model of accumulator.modelSet) {\n periodModels.add(model);\n grandModels.add(model);\n }\n }\n\n if (sortedSources.length > 1) {\n const combinedRow: PeriodCombinedRow = {\n rowType: 'period_combined',\n periodKey,\n source: 'combined',\n models: normalizeModelList(periodModels),\n ...periodCombinedTotals,\n };\n\n rows.push(combinedRow);\n }\n }\n\n const grandTotalRow: GrandTotalRow = {\n rowType: 'grand_total',\n periodKey: 'ALL',\n source: 'combined',\n models: normalizeModelList(grandModels),\n ...grandTotals,\n };\n\n rows.push(grandTotalRow);\n\n return rows;\n}\n","import type { UsageEvent } from '../domain/usage-event.js';\nimport type { ModelPricing, PricingSource } from './types.js';\n\nconst ONE_MILLION = 1_000_000;\n\nfunction estimateTokenGroupCost(tokens: number, per1MUsd: number | undefined): number {\n if (!per1MUsd || tokens <= 0) {\n return 0;\n }\n\n return (tokens / ONE_MILLION) * per1MUsd;\n}\n\nexport function calculateEstimatedCostUsd(event: UsageEvent, pricing: ModelPricing): number {\n const reasoningBilling = pricing.reasoningBilling ?? 'included-in-output';\n\n const inputCost = estimateTokenGroupCost(event.inputTokens, pricing.inputPer1MUsd);\n const outputCost = estimateTokenGroupCost(event.outputTokens, pricing.outputPer1MUsd);\n const cacheReadCost = estimateTokenGroupCost(event.cacheReadTokens, pricing.cacheReadPer1MUsd);\n const cacheWriteCost = estimateTokenGroupCost(event.cacheWriteTokens, pricing.cacheWritePer1MUsd);\n\n const reasoningCost =\n reasoningBilling === 'separate'\n ? estimateTokenGroupCost(event.reasoningTokens, pricing.reasoningPer1MUsd)\n : 0;\n\n return inputCost + outputCost + cacheReadCost + cacheWriteCost + reasoningCost;\n}\n\nexport function applyPricingToEvent(event: UsageEvent, pricingSource: PricingSource): UsageEvent {\n if (event.costMode === 'explicit' && event.costUsd !== undefined) {\n return event;\n }\n\n if (!event.model) {\n return { ...event, costMode: 'estimated' };\n }\n\n const pricing = pricingSource.getPricing(event.model);\n\n if (!pricing) {\n return { ...event, costMode: 'estimated' };\n }\n\n return {\n ...event,\n costUsd: calculateEstimatedCostUsd(event, pricing),\n costMode: 'estimated',\n };\n}\n\nexport function applyPricingToEvents(\n events: UsageEvent[],\n pricingSource: PricingSource,\n): UsageEvent[] {\n return events.map((event) => applyPricingToEvent(event, pricingSource));\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport type { ModelPricing, PricingSource } from './types.js';\n\nconst ONE_MILLION = 1_000_000;\nconst DEFAULT_CACHE_TTL_MS = 24 * 60 * 60 * 1000;\nconst DEFAULT_FETCH_TIMEOUT_MS = 4000;\n\nexport const DEFAULT_LITELLM_PRICING_URL =\n 'https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json';\n\ntype LiteLLMCachePayload = {\n fetchedAt: number;\n sourceUrl: string;\n pricingByModel: Record<string, ModelPricing>;\n};\n\nexport type LiteLLMPricingFetcherOptions = {\n sourceUrl?: string;\n cacheFilePath?: string;\n cacheTtlMs?: number;\n fetchTimeoutMs?: number;\n offline?: boolean;\n fetchImpl?: typeof fetch;\n now?: () => number;\n};\n\nfunction normalizeKey(value: string): string {\n return value.trim().toLowerCase();\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n if (!value || typeof value !== 'object') {\n return undefined;\n }\n\n return value as Record<string, unknown>;\n}\n\nfunction toNonNegativeNumber(value: unknown): number | undefined {\n if (typeof value === 'number') {\n if (!Number.isFinite(value) || value < 0) {\n return undefined;\n }\n\n return value;\n }\n\n if (typeof value === 'string') {\n if (value.trim() === '') {\n return undefined;\n }\n\n const parsedValue = Number(value);\n\n if (!Number.isFinite(parsedValue) || parsedValue < 0) {\n return undefined;\n }\n\n return parsedValue;\n }\n\n return undefined;\n}\n\nfunction normalizeModelPricing(rawModelPricing: Record<string, unknown>): ModelPricing | undefined {\n const inputPerToken =\n toNonNegativeNumber(rawModelPricing.input_cost_per_token) ??\n toNonNegativeNumber(rawModelPricing.input_cost_per_token_priority);\n const outputPerToken =\n toNonNegativeNumber(rawModelPricing.output_cost_per_token) ??\n toNonNegativeNumber(rawModelPricing.output_cost_per_token_priority);\n\n if (inputPerToken === undefined || outputPerToken === undefined) {\n return undefined;\n }\n\n const cacheReadPerToken =\n toNonNegativeNumber(rawModelPricing.cache_read_input_token_cost) ??\n toNonNegativeNumber(rawModelPricing.cache_read_input_token_cost_priority);\n const cacheWritePerToken = toNonNegativeNumber(rawModelPricing.cache_creation_input_token_cost);\n const reasoningPerToken = toNonNegativeNumber(rawModelPricing.output_cost_per_reasoning_token);\n\n const modelPricing: ModelPricing = {\n inputPer1MUsd: inputPerToken * ONE_MILLION,\n outputPer1MUsd: outputPerToken * ONE_MILLION,\n };\n\n if (cacheReadPerToken !== undefined) {\n modelPricing.cacheReadPer1MUsd = cacheReadPerToken * ONE_MILLION;\n }\n\n if (cacheWritePerToken !== undefined) {\n modelPricing.cacheWritePer1MUsd = cacheWritePerToken * ONE_MILLION;\n }\n\n if (reasoningPerToken !== undefined) {\n modelPricing.reasoningPer1MUsd = reasoningPerToken * ONE_MILLION;\n modelPricing.reasoningBilling = 'separate';\n }\n\n return modelPricing;\n}\n\nfunction normalizeLitellmPricingPayload(payload: unknown): Map<string, ModelPricing> {\n const payloadRecord = asRecord(payload);\n\n if (!payloadRecord) {\n throw new Error('LiteLLM pricing payload must be a JSON object');\n }\n\n const normalizedPricing = new Map<string, ModelPricing>();\n\n for (const [modelName, rawModelPricing] of Object.entries(payloadRecord)) {\n const modelPricingRecord = asRecord(rawModelPricing);\n\n if (!modelPricingRecord) {\n continue;\n }\n\n const normalizedModelPricing = normalizeModelPricing(modelPricingRecord);\n\n if (!normalizedModelPricing) {\n continue;\n }\n\n normalizedPricing.set(normalizeKey(modelName), normalizedModelPricing);\n }\n\n if (normalizedPricing.size === 0) {\n throw new Error('LiteLLM pricing payload did not contain any usable model pricing entries');\n }\n\n return normalizedPricing;\n}\n\nfunction getCacheRootDir(): string {\n const xdgCacheDir = process.env.XDG_CACHE_HOME;\n\n if (xdgCacheDir) {\n return xdgCacheDir;\n }\n\n if (process.platform === 'win32') {\n const localAppData = process.env.LOCALAPPDATA;\n\n if (localAppData) {\n return localAppData;\n }\n }\n\n return path.join(os.homedir(), '.cache');\n}\n\nexport function getDefaultLiteLLMPricingCachePath(): string {\n return path.join(getCacheRootDir(), 'llm-usage-metrics', 'litellm-pricing-cache.json');\n}\n\nfunction stripProviderPrefix(model: string): string {\n const slashIndex = model.lastIndexOf('/');\n\n if (slashIndex === -1) {\n return model;\n }\n\n return model.slice(slashIndex + 1);\n}\n\nfunction canonicalizeForFuzzy(value: string): string {\n return value.replace(/[^a-z0-9]/gu, '');\n}\n\nfunction levenshteinDistance(left: string, right: string): number {\n const leftLength = left.length;\n const rightLength = right.length;\n\n const matrix = Array.from({ length: leftLength + 1 }, (_, rowIndex) => {\n return Array.from({ length: rightLength + 1 }, (_, columnIndex) => {\n if (rowIndex === 0) {\n return columnIndex;\n }\n\n if (columnIndex === 0) {\n return rowIndex;\n }\n\n return 0;\n });\n });\n\n for (let rowIndex = 1; rowIndex <= leftLength; rowIndex += 1) {\n for (let columnIndex = 1; columnIndex <= rightLength; columnIndex += 1) {\n const substitutionCost = left[rowIndex - 1] === right[columnIndex - 1] ? 0 : 1;\n\n matrix[rowIndex][columnIndex] = Math.min(\n matrix[rowIndex - 1][columnIndex] + 1,\n matrix[rowIndex][columnIndex - 1] + 1,\n matrix[rowIndex - 1][columnIndex - 1] + substitutionCost,\n );\n }\n }\n\n return matrix[leftLength][rightLength];\n}\n\nexport class LiteLLMPricingFetcher implements PricingSource {\n private readonly sourceUrl: string;\n private readonly cacheFilePath: string;\n private readonly cacheTtlMs: number;\n private readonly fetchTimeoutMs: number;\n private readonly offline: boolean;\n private readonly fetchImpl: typeof fetch;\n private readonly now: () => number;\n\n private pricingByModel = new Map<string, ModelPricing>();\n private resolvedAliasCache = new Map<string, string>();\n\n public constructor(options: LiteLLMPricingFetcherOptions = {}) {\n this.sourceUrl = options.sourceUrl ?? DEFAULT_LITELLM_PRICING_URL;\n this.cacheFilePath = options.cacheFilePath ?? getDefaultLiteLLMPricingCachePath();\n this.cacheTtlMs = options.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;\n this.fetchTimeoutMs = options.fetchTimeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;\n this.offline = options.offline ?? false;\n this.fetchImpl = options.fetchImpl ?? fetch;\n this.now = options.now ?? Date.now;\n }\n\n public async load(): Promise<void> {\n const cacheLoaded = await this.loadFromCache({ allowStale: false });\n\n if (cacheLoaded) {\n return;\n }\n\n if (this.offline) {\n const staleCacheLoaded = await this.loadFromCache({ allowStale: true });\n\n if (!staleCacheLoaded) {\n throw new Error('Offline pricing mode enabled but no cached LiteLLM pricing is available');\n }\n\n return;\n }\n\n try {\n await this.loadFromRemote();\n } catch {\n const staleCacheLoaded = await this.loadFromCache({ allowStale: true });\n\n if (!staleCacheLoaded) {\n throw new Error('Could not load LiteLLM pricing from network or cache');\n }\n }\n }\n\n public resolveModelAlias(model: string): string {\n const normalizedModel = normalizeKey(model);\n const cachedAlias = this.resolvedAliasCache.get(normalizedModel);\n\n if (cachedAlias) {\n return cachedAlias;\n }\n\n const directMatch = this.resolveDirectModelMatch(normalizedModel);\n\n if (directMatch) {\n this.resolvedAliasCache.set(normalizedModel, directMatch);\n return directMatch;\n }\n\n const prefixMatch = this.resolvePrefixModelMatch(normalizedModel);\n\n if (prefixMatch) {\n this.resolvedAliasCache.set(normalizedModel, prefixMatch);\n return prefixMatch;\n }\n\n const fuzzyMatch = this.resolveFuzzyModelMatch(normalizedModel);\n const resolvedAlias = fuzzyMatch ?? normalizedModel;\n this.resolvedAliasCache.set(normalizedModel, resolvedAlias);\n\n return resolvedAlias;\n }\n\n public getPricing(model: string): ModelPricing | undefined {\n const resolvedModel = this.resolveModelAlias(model);\n return this.pricingByModel.get(resolvedModel);\n }\n\n private resolveDirectModelMatch(normalizedModel: string): string | undefined {\n if (this.pricingByModel.has(normalizedModel)) {\n return normalizedModel;\n }\n\n const strippedModel = stripProviderPrefix(normalizedModel);\n\n if (this.pricingByModel.has(strippedModel)) {\n return strippedModel;\n }\n\n return undefined;\n }\n\n private resolvePrefixModelMatch(normalizedModel: string): string | undefined {\n const candidates = [normalizedModel, stripProviderPrefix(normalizedModel)];\n const modelNames = [...this.pricingByModel.keys()];\n\n for (const candidate of candidates) {\n const prefixMatches = modelNames.filter((modelName) => {\n return candidate.startsWith(modelName);\n });\n\n if (prefixMatches.length > 0) {\n return prefixMatches.sort(\n (left, right) => right.length - left.length || left.localeCompare(right),\n )[0];\n }\n }\n\n return undefined;\n }\n\n private resolveFuzzyModelMatch(normalizedModel: string): string | undefined {\n const fuzzyTarget = canonicalizeForFuzzy(stripProviderPrefix(normalizedModel));\n\n if (!fuzzyTarget) {\n return undefined;\n }\n\n let bestMatch: { modelName: string; distance: number } | undefined;\n\n for (const modelName of this.pricingByModel.keys()) {\n const fuzzyModelName = canonicalizeForFuzzy(modelName);\n\n if (!fuzzyModelName) {\n continue;\n }\n\n const distance = levenshteinDistance(fuzzyTarget, fuzzyModelName);\n\n if (!bestMatch || distance < bestMatch.distance) {\n bestMatch = { modelName, distance };\n }\n }\n\n if (!bestMatch) {\n return undefined;\n }\n\n const maxDistance = Math.max(2, Math.floor(fuzzyTarget.length * 0.2));\n\n if (bestMatch.distance > maxDistance) {\n return undefined;\n }\n\n return bestMatch.modelName;\n }\n\n private async loadFromRemote(): Promise<void> {\n const response = await this.fetchImpl(this.sourceUrl, {\n signal: AbortSignal.timeout(this.fetchTimeoutMs),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch LiteLLM pricing: HTTP ${response.status}`);\n }\n\n const payload = (await response.json()) as unknown;\n const normalizedPricing = normalizeLitellmPricingPayload(payload);\n\n this.pricingByModel = normalizedPricing;\n this.resolvedAliasCache.clear();\n\n try {\n await this.writeCache();\n } catch {\n // Cache writes are best-effort. A successful remote fetch must still be usable.\n }\n }\n\n private async loadFromCache(options: { allowStale: boolean }): Promise<boolean> {\n const cacheFileContent = await this.readCachePayload();\n\n if (!cacheFileContent) {\n return false;\n }\n\n if (cacheFileContent.sourceUrl !== this.sourceUrl) {\n return false;\n }\n\n const isStale = this.now() - cacheFileContent.fetchedAt > this.cacheTtlMs;\n\n if (isStale && !options.allowStale) {\n return false;\n }\n\n this.pricingByModel = new Map(\n Object.entries(cacheFileContent.pricingByModel).map(([modelName, pricing]) => [\n normalizeKey(modelName),\n pricing,\n ]),\n );\n this.resolvedAliasCache.clear();\n\n return this.pricingByModel.size > 0;\n }\n\n private normalizeCachedPricing(rawPricing: unknown): ModelPricing | undefined {\n const pricingRecord = asRecord(rawPricing);\n\n if (!pricingRecord) {\n return undefined;\n }\n\n const inputPer1MUsd = toNonNegativeNumber(pricingRecord.inputPer1MUsd);\n const outputPer1MUsd = toNonNegativeNumber(pricingRecord.outputPer1MUsd);\n\n if (inputPer1MUsd === undefined || outputPer1MUsd === undefined) {\n return undefined;\n }\n\n const modelPricing: ModelPricing = {\n inputPer1MUsd,\n outputPer1MUsd,\n };\n\n const cacheReadPer1MUsd = toNonNegativeNumber(pricingRecord.cacheReadPer1MUsd);\n\n if (cacheReadPer1MUsd !== undefined) {\n modelPricing.cacheReadPer1MUsd = cacheReadPer1MUsd;\n }\n\n const cacheWritePer1MUsd = toNonNegativeNumber(pricingRecord.cacheWritePer1MUsd);\n\n if (cacheWritePer1MUsd !== undefined) {\n modelPricing.cacheWritePer1MUsd = cacheWritePer1MUsd;\n }\n\n const reasoningPer1MUsd = toNonNegativeNumber(pricingRecord.reasoningPer1MUsd);\n\n if (reasoningPer1MUsd !== undefined) {\n modelPricing.reasoningPer1MUsd = reasoningPer1MUsd;\n modelPricing.reasoningBilling = 'separate';\n }\n\n return modelPricing;\n }\n\n private async readCachePayload(): Promise<LiteLLMCachePayload | undefined> {\n let content: string;\n\n try {\n content = await readFile(this.cacheFilePath, 'utf8');\n } catch {\n return undefined;\n }\n\n let parsedPayload: unknown;\n\n try {\n parsedPayload = JSON.parse(content);\n } catch {\n return undefined;\n }\n\n const payloadRecord = asRecord(parsedPayload);\n\n if (!payloadRecord) {\n return undefined;\n }\n\n const fetchedAt = toNonNegativeNumber(payloadRecord.fetchedAt);\n const sourceUrl =\n typeof payloadRecord.sourceUrl === 'string' ? payloadRecord.sourceUrl : undefined;\n const pricingByModelRecord = asRecord(payloadRecord.pricingByModel);\n\n if (fetchedAt === undefined || !sourceUrl || !pricingByModelRecord) {\n return undefined;\n }\n\n const pricingByModel: Record<string, ModelPricing> = {};\n\n for (const [modelName, rawPricing] of Object.entries(pricingByModelRecord)) {\n const pricing = this.normalizeCachedPricing(rawPricing);\n\n if (!pricing) {\n continue;\n }\n\n pricingByModel[modelName] = pricing;\n }\n\n return {\n fetchedAt,\n sourceUrl,\n pricingByModel,\n };\n }\n\n private async writeCache(): Promise<void> {\n const directoryPath = path.dirname(this.cacheFilePath);\n await mkdir(directoryPath, { recursive: true });\n\n const payload: LiteLLMCachePayload = {\n fetchedAt: this.now(),\n sourceUrl: this.sourceUrl,\n pricingByModel: Object.fromEntries(this.pricingByModel.entries()),\n };\n\n await writeFile(this.cacheFilePath, JSON.stringify(payload), 'utf8');\n }\n}\n","import type { ModelPricing, PricingSource } from './types.js';\n\nexport type StaticPricingSourceOptions = {\n pricingByModel: Record<string, ModelPricing>;\n modelAliases?: Record<string, string>;\n};\n\nfunction normalizeKey(key: string): string {\n return key.trim().toLowerCase();\n}\n\nexport class StaticPricingSource implements PricingSource {\n private readonly pricingByModel: Map<string, ModelPricing>;\n private readonly modelAliases: Map<string, string>;\n\n public constructor(options: StaticPricingSourceOptions) {\n this.pricingByModel = new Map(\n Object.entries(options.pricingByModel).map(([model, pricing]) => [\n normalizeKey(model),\n pricing,\n ]),\n );\n this.modelAliases = new Map(\n Object.entries(options.modelAliases ?? {}).map(([alias, target]) => [\n normalizeKey(alias),\n normalizeKey(target),\n ]),\n );\n }\n\n public resolveModelAlias(model: string): string {\n const normalizedModel = normalizeKey(model);\n return this.modelAliases.get(normalizedModel) ?? normalizedModel;\n }\n\n public getPricing(model: string): ModelPricing | undefined {\n const resolvedModel = this.resolveModelAlias(model);\n return this.pricingByModel.get(resolvedModel);\n }\n}\n\nexport function createDefaultOpenAiPricingSource(): StaticPricingSource {\n return new StaticPricingSource({\n pricingByModel: {\n 'gpt-5-codex': {\n inputPer1MUsd: 1.5,\n outputPer1MUsd: 10,\n cacheReadPer1MUsd: 0.15,\n },\n 'gpt-4.1': {\n inputPer1MUsd: 2,\n outputPer1MUsd: 8,\n cacheReadPer1MUsd: 0.5,\n },\n },\n modelAliases: {\n 'gpt-5.1-codex': 'gpt-5-codex',\n 'gpt-5.2-codex': 'gpt-5-codex',\n 'gpt-5.3-codex': 'gpt-5-codex',\n },\n });\n}\n","import { markdownTable } from 'markdown-table';\n\nimport type { UsageReportRow } from '../domain/usage-report-row.js';\nimport { toUsageTableCells, usageTableHeaders } from './row-cells.js';\n\nconst alignment: ('l' | 'r')[] = ['l', 'l', 'l', 'r', 'r', 'r', 'r', 'r', 'r', 'r'];\n\nexport function renderMarkdownTable(rows: UsageReportRow[]): string {\n const bodyRows = toUsageTableCells(rows);\n const tableRows = [Array.from(usageTableHeaders), ...bodyRows];\n\n return markdownTable(tableRows, {\n align: alignment,\n });\n}\n","import type { UsageReportRow } from '../domain/usage-report-row.js';\n\nexport const usageTableHeaders = [\n 'Period',\n 'Source',\n 'Models',\n 'Input',\n 'Output',\n 'Reasoning',\n 'Cache Read',\n 'Cache Write',\n 'Total Tokens',\n 'Cost (USD)',\n] as const;\n\nconst integerFormatter = new Intl.NumberFormat('en-US');\nconst usdFormatter = new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: 'USD',\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n});\n\nfunction formatSource(row: UsageReportRow): string {\n if (row.rowType === 'grand_total') {\n return 'TOTAL';\n }\n\n return row.source;\n}\n\nfunction formatModels(models: string[]): string {\n return models.length > 0 ? models.join(', ') : '-';\n}\n\nfunction formatTokenCount(value: number): string {\n return integerFormatter.format(value);\n}\n\nfunction formatUsd(value: number): string {\n return usdFormatter.format(value);\n}\n\nexport function toUsageTableCells(rows: UsageReportRow[]): string[][] {\n return rows.map((row) => [\n row.periodKey,\n formatSource(row),\n formatModels(row.models),\n formatTokenCount(row.inputTokens),\n formatTokenCount(row.outputTokens),\n formatTokenCount(row.reasoningTokens),\n formatTokenCount(row.cacheReadTokens),\n formatTokenCount(row.cacheWriteTokens),\n formatTokenCount(row.totalTokens),\n formatUsd(row.costUsd),\n ]);\n}\n","import pc from 'picocolors';\nimport { getBorderCharacters, table, type TableUserConfig } from 'table';\n\nimport type { UsageReportRow } from '../domain/usage-report-row.js';\nimport { toUsageTableCells, usageTableHeaders } from './row-cells.js';\n\nconst modelsColumnIndex = 2;\n\ntype TerminalRenderOptions = {\n useColor?: boolean;\n};\n\nfunction shouldDrawHorizontalLine(index: number, rowCount: number, rows: string[][]): boolean {\n if (index === 0 || index === 1 || index === rowCount) {\n return true;\n }\n\n const previousRow = rows[index - 1];\n const nextRow = rows[index];\n\n const previousSource = previousRow[1];\n const nextSource = nextRow[1];\n const previousPeriod = previousRow[0];\n const nextPeriod = nextRow[0];\n\n return previousSource === 'combined' || nextSource === 'TOTAL' || previousPeriod !== nextPeriod;\n}\n\nfunction createTableConfig(rows: string[][]): TableUserConfig {\n return {\n border: getBorderCharacters('norc'),\n drawHorizontalLine: (index, rowCount) => shouldDrawHorizontalLine(index, rowCount, rows),\n columnDefault: {\n paddingLeft: 1,\n paddingRight: 1,\n verticalAlignment: 'middle',\n },\n columns: {\n 0: { alignment: 'left' },\n 1: { alignment: 'left' },\n [modelsColumnIndex]: {\n alignment: 'left',\n width: 34,\n wrapWord: true,\n },\n 3: { alignment: 'right' },\n 4: { alignment: 'right' },\n 5: { alignment: 'right' },\n 6: { alignment: 'right' },\n 7: { alignment: 'right' },\n 8: { alignment: 'right' },\n 9: { alignment: 'right' },\n },\n };\n}\n\nfunction shouldUseColorByDefault(): boolean {\n if (process.env.NO_COLOR !== undefined) {\n return false;\n }\n\n if (process.env.FORCE_COLOR !== undefined && process.env.FORCE_COLOR !== '0') {\n return true;\n }\n\n return process.stdout.isTTY;\n}\n\nfunction colorSource(source: string): (text: string) => string {\n switch (source) {\n case 'pi':\n return pc.cyan;\n case 'codex':\n return pc.magenta;\n case 'combined':\n return pc.yellow;\n case 'TOTAL':\n return (text) => pc.bold(pc.green(text));\n default:\n return (text) => text;\n }\n}\n\nfunction colorizeBodyRows(\n bodyRows: string[][],\n rows: UsageReportRow[],\n useColor: boolean,\n): string[][] {\n if (!useColor) {\n return bodyRows;\n }\n\n return rows.map((row, index) => {\n const styledCells = [...(bodyRows[index] ?? [])];\n const sourceStyler = colorSource(styledCells[1] ?? row.source);\n\n styledCells[1] = sourceStyler(styledCells[1] ?? row.source);\n\n if (row.rowType === 'grand_total') {\n return styledCells.map((cell) => pc.bold(cell));\n }\n\n if (row.rowType === 'period_combined') {\n return styledCells.map((cell, cellIndex) => (cellIndex === 1 ? pc.bold(cell) : pc.dim(cell)));\n }\n\n return styledCells;\n });\n}\n\nfunction colorizeHeader(useColor: boolean): string[] {\n const headerCells = Array.from(usageTableHeaders);\n\n if (!useColor) {\n return headerCells;\n }\n\n return headerCells.map((header) => pc.bold(pc.white(header)));\n}\n\nexport function renderTerminalTable(\n rows: UsageReportRow[],\n options: TerminalRenderOptions = {},\n): string {\n const useColor = options.useColor ?? shouldUseColorByDefault();\n const bodyRows = colorizeBodyRows(toUsageTableCells(rows), rows, useColor);\n const displayRows = [colorizeHeader(useColor), ...bodyRows];\n\n return table(displayRows, createTableConfig(displayRows));\n}\n","import { readFile } from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { createUsageEvent } from '../../domain/usage-event.js';\nimport type { UsageEvent } from '../../domain/usage-event.js';\nimport { normalizeNonNegativeInteger } from '../../domain/normalization.js';\nimport type { NumberLike } from '../../domain/normalization.js';\nimport { discoverJsonlFiles } from '../../utils/discover-jsonl-files.js';\nimport type { SourceAdapter } from '../source-adapter.js';\n\nconst defaultSessionsDir = path.join(os.homedir(), '.codex', 'sessions');\n\nexport const LEGACY_CODEX_MODEL_FALLBACK = 'legacy-codex-unknown';\n\ntype CodexUsage = {\n inputTokens: number;\n cacheReadTokens: number;\n outputTokens: number;\n reasoningTokens: number;\n totalTokens: number;\n};\n\ntype CodexSessionState = {\n sessionId: string;\n provider?: string;\n model?: string;\n previousTotalUsage?: CodexUsage;\n};\n\nexport type CodexSourceAdapterOptions = {\n sessionsDir?: string;\n};\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n if (!value || typeof value !== 'object') {\n return undefined;\n }\n\n return value as Record<string, unknown>;\n}\n\nfunction asText(value: unknown): string | undefined {\n if (typeof value !== 'string') {\n return undefined;\n }\n\n const normalized = value.trim();\n return normalized || undefined;\n}\n\nfunction toUsage(value: unknown): CodexUsage | undefined {\n const usage = asRecord(value);\n\n if (!usage) {\n return undefined;\n }\n\n const rawInputTokens = normalizeNonNegativeInteger(usage.input_tokens as NumberLike);\n const cacheReadTokens = normalizeNonNegativeInteger(usage.cached_input_tokens as NumberLike);\n const outputTokens = normalizeNonNegativeInteger(usage.output_tokens as NumberLike);\n\n const inputTokens = Math.max(0, rawInputTokens - cacheReadTokens);\n\n return {\n // Codex input_tokens includes cached_input_tokens. We store net input separately\n // to avoid double counting input + cache read in reports and estimated pricing.\n inputTokens,\n cacheReadTokens,\n outputTokens,\n reasoningTokens: normalizeNonNegativeInteger(usage.reasoning_output_tokens as NumberLike),\n // Match ccusage semantics: billable total excludes reasoning breakdown.\n totalTokens: inputTokens + outputTokens + cacheReadTokens,\n };\n}\n\nfunction subtractUsage(current: CodexUsage, previous: CodexUsage): CodexUsage {\n return {\n inputTokens: Math.max(0, current.inputTokens - previous.inputTokens),\n cacheReadTokens: Math.max(0, current.cacheReadTokens - previous.cacheReadTokens),\n outputTokens: Math.max(0, current.outputTokens - previous.outputTokens),\n reasoningTokens: Math.max(0, current.reasoningTokens - previous.reasoningTokens),\n totalTokens: Math.max(0, current.totalTokens - previous.totalTokens),\n };\n}\n\nfunction addUsage(left: CodexUsage, right: CodexUsage): CodexUsage {\n return {\n inputTokens: left.inputTokens + right.inputTokens,\n cacheReadTokens: left.cacheReadTokens + right.cacheReadTokens,\n outputTokens: left.outputTokens + right.outputTokens,\n reasoningTokens: left.reasoningTokens + right.reasoningTokens,\n totalTokens: left.totalTokens + right.totalTokens,\n };\n}\n\nfunction hasUsageSignal(usage: CodexUsage): boolean {\n return (\n usage.inputTokens > 0 ||\n usage.cacheReadTokens > 0 ||\n usage.outputTokens > 0 ||\n usage.reasoningTokens > 0 ||\n usage.totalTokens > 0\n );\n}\n\nfunction deriveDeltaUsage(\n info: Record<string, unknown>,\n previousTotalUsage: CodexUsage | undefined,\n): { deltaUsage?: CodexUsage; latestTotalUsage?: CodexUsage } {\n const totalUsage = toUsage(info.total_token_usage);\n const lastUsage = toUsage(info.last_token_usage);\n\n if (lastUsage) {\n return { deltaUsage: lastUsage, latestTotalUsage: totalUsage };\n }\n\n if (!totalUsage) {\n return {};\n }\n\n const deltaUsage = previousTotalUsage\n ? subtractUsage(totalUsage, previousTotalUsage)\n : totalUsage;\n\n return { deltaUsage, latestTotalUsage: totalUsage };\n}\n\nfunction getFallbackSessionId(filePath: string): string {\n return path.basename(filePath, '.jsonl');\n}\n\nexport class CodexSourceAdapter implements SourceAdapter {\n public readonly id = 'codex' as const;\n\n private readonly sessionsDir: string;\n\n public constructor(options: CodexSourceAdapterOptions = {}) {\n this.sessionsDir = options.sessionsDir ?? defaultSessionsDir;\n }\n\n public async discoverFiles(): Promise<string[]> {\n return discoverJsonlFiles(this.sessionsDir);\n }\n\n public async parseFile(filePath: string): Promise<UsageEvent[]> {\n const content = await readFile(filePath, 'utf8');\n const events: UsageEvent[] = [];\n\n const state: CodexSessionState = {\n sessionId: getFallbackSessionId(filePath),\n provider: 'openai',\n };\n\n for (const rawLine of content.split(/\\r?\\n/u)) {\n const lineText = rawLine.trim();\n\n if (!lineText) {\n continue;\n }\n\n let parsedLine: unknown;\n\n try {\n parsedLine = JSON.parse(lineText);\n } catch {\n continue;\n }\n\n const line = asRecord(parsedLine);\n\n if (!line) {\n continue;\n }\n\n if (line.type === 'session_meta') {\n const payload = asRecord(line.payload);\n state.sessionId = asText(payload?.id) ?? state.sessionId;\n state.provider = asText(payload?.model_provider) ?? state.provider;\n continue;\n }\n\n if (line.type === 'turn_context') {\n const payload = asRecord(line.payload);\n state.model = asText(payload?.model) ?? state.model;\n continue;\n }\n\n if (line.type !== 'event_msg') {\n continue;\n }\n\n const payload = asRecord(line.payload);\n\n if (payload?.type !== 'token_count') {\n continue;\n }\n\n const info = asRecord(payload.info);\n\n if (!info) {\n continue;\n }\n\n const { deltaUsage, latestTotalUsage } = deriveDeltaUsage(info, state.previousTotalUsage);\n\n if (!deltaUsage || !hasUsageSignal(deltaUsage)) {\n state.previousTotalUsage = latestTotalUsage ?? state.previousTotalUsage;\n continue;\n }\n\n const timestamp = asText(line.timestamp);\n\n if (!timestamp) {\n state.previousTotalUsage = latestTotalUsage ?? state.previousTotalUsage;\n continue;\n }\n\n const model = state.model ?? LEGACY_CODEX_MODEL_FALLBACK;\n\n try {\n events.push(\n createUsageEvent({\n source: this.id,\n sessionId: state.sessionId,\n timestamp,\n provider: state.provider,\n model,\n inputTokens: deltaUsage.inputTokens,\n outputTokens: deltaUsage.outputTokens,\n reasoningTokens: deltaUsage.reasoningTokens,\n cacheReadTokens: deltaUsage.cacheReadTokens,\n cacheWriteTokens: 0,\n totalTokens: deltaUsage.totalTokens,\n costMode: 'estimated',\n }),\n );\n } catch {\n // no-op: malformed lines are ignored by design\n }\n\n if (latestTotalUsage) {\n state.previousTotalUsage = latestTotalUsage;\n } else if (state.previousTotalUsage) {\n state.previousTotalUsage = addUsage(state.previousTotalUsage, deltaUsage);\n } else {\n state.previousTotalUsage = deltaUsage;\n }\n }\n\n return events;\n }\n}\n\nexport function getDefaultCodexSessionsDir(): string {\n return defaultSessionsDir;\n}\n","import {\n normalizeNonNegativeInteger,\n normalizeTimestamp,\n normalizeUsdCost,\n type NumberLike,\n} from './normalization.js';\n\nexport type SourceId = 'pi' | 'codex' | (string & {});\n\nexport type CostMode = 'explicit' | 'estimated';\n\nexport type UsageEvent = {\n source: SourceId;\n sessionId: string;\n timestamp: string;\n provider?: string;\n model?: string;\n\n inputTokens: number;\n outputTokens: number;\n reasoningTokens: number;\n cacheReadTokens: number;\n cacheWriteTokens: number;\n totalTokens: number;\n\n costUsd?: number;\n costMode: CostMode;\n};\n\nexport type UsageEventInput = {\n source: SourceId;\n sessionId: string;\n timestamp: string | Date;\n provider?: string;\n model?: string;\n\n inputTokens?: NumberLike;\n outputTokens?: NumberLike;\n reasoningTokens?: NumberLike;\n cacheReadTokens?: NumberLike;\n cacheWriteTokens?: NumberLike;\n totalTokens?: NumberLike;\n\n costUsd?: NumberLike;\n costMode?: CostMode;\n};\n\nfunction requireText(value: string, fieldName: string): string {\n const normalized = value.trim();\n\n if (!normalized) {\n throw new Error(`UsageEvent ${fieldName} must be a non-empty string`);\n }\n\n return normalized;\n}\n\nfunction normalizeOptionalText(value: string | undefined): string | undefined {\n if (!value) {\n return undefined;\n }\n\n const normalized = value.trim();\n return normalized || undefined;\n}\n\nfunction resolveCostMode(costMode: CostMode | undefined, costUsd: number | undefined): CostMode {\n if (costMode === 'explicit' && costUsd === undefined) {\n throw new Error('UsageEvent with costMode \"explicit\" requires costUsd');\n }\n\n if (costMode) {\n return costMode;\n }\n\n return costUsd === undefined ? 'estimated' : 'explicit';\n}\n\nexport function createUsageEvent(input: UsageEventInput): UsageEvent {\n const inputTokens = normalizeNonNegativeInteger(input.inputTokens);\n const outputTokens = normalizeNonNegativeInteger(input.outputTokens);\n const reasoningTokens = normalizeNonNegativeInteger(input.reasoningTokens);\n const cacheReadTokens = normalizeNonNegativeInteger(input.cacheReadTokens);\n const cacheWriteTokens = normalizeNonNegativeInteger(input.cacheWriteTokens);\n const declaredTotalTokens = normalizeNonNegativeInteger(input.totalTokens);\n const componentTotalTokens =\n inputTokens + outputTokens + reasoningTokens + cacheReadTokens + cacheWriteTokens;\n const totalTokens = declaredTotalTokens > 0 ? declaredTotalTokens : componentTotalTokens;\n\n const costUsd = normalizeUsdCost(input.costUsd);\n const costMode = resolveCostMode(input.costMode, costUsd);\n\n return {\n source: requireText(input.source, 'source') as SourceId,\n sessionId: requireText(input.sessionId, 'sessionId'),\n timestamp: normalizeTimestamp(input.timestamp),\n provider: normalizeOptionalText(input.provider),\n model: normalizeOptionalText(input.model),\n inputTokens,\n outputTokens,\n reasoningTokens,\n cacheReadTokens,\n cacheWriteTokens,\n totalTokens,\n costUsd,\n costMode,\n };\n}\n","import { readdir } from 'node:fs/promises';\nimport path from 'node:path';\n\nasync function walkDirectory(rootDir: string, acc: string[]): Promise<void> {\n const entries = await readdir(rootDir, { withFileTypes: true });\n entries.sort((left, right) => left.name.localeCompare(right.name));\n\n for (const entry of entries) {\n const entryPath = path.join(rootDir, entry.name);\n\n if (entry.isDirectory()) {\n await walkDirectory(entryPath, acc);\n continue;\n }\n\n if (entry.isFile() && entry.name.endsWith('.jsonl')) {\n acc.push(entryPath);\n }\n }\n}\n\nexport async function discoverJsonlFiles(rootDir: string): Promise<string[]> {\n const files: string[] = [];\n\n try {\n await walkDirectory(rootDir, files);\n } catch (error) {\n const nodeError = error as NodeJS.ErrnoException;\n\n if (nodeError.code === 'ENOENT') {\n return [];\n }\n\n throw error;\n }\n\n return files;\n}\n","import { readFile } from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { createUsageEvent } from '../../domain/usage-event.js';\nimport type { UsageEvent } from '../../domain/usage-event.js';\nimport type { NumberLike } from '../../domain/normalization.js';\nimport type { SourceAdapter } from '../source-adapter.js';\nimport { discoverJsonlFiles } from '../../utils/discover-jsonl-files.js';\n\nconst defaultSessionsDir = path.join(os.homedir(), '.pi', 'agent', 'sessions');\n\ntype ProviderFilter = (provider: string | undefined) => boolean;\n\ntype PiSessionState = {\n sessionId?: string;\n sessionTimestamp?: string;\n provider?: string;\n model?: string;\n};\n\ntype PiUsageExtract = {\n inputTokens?: NumberLike;\n outputTokens?: NumberLike;\n reasoningTokens?: NumberLike;\n cacheReadTokens?: NumberLike;\n cacheWriteTokens?: NumberLike;\n totalTokens?: NumberLike;\n costUsd?: NumberLike;\n};\n\nexport type PiSourceAdapterOptions = {\n sessionsDir?: string;\n providerFilter?: ProviderFilter;\n};\n\nexport function isOpenAiProvider(provider: string | undefined): boolean {\n return provider?.toLowerCase().includes('openai') ?? false;\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n if (!value || typeof value !== 'object') {\n return undefined;\n }\n\n return value as Record<string, unknown>;\n}\n\nfunction asText(value: unknown): string | undefined {\n if (typeof value !== 'string') {\n return undefined;\n }\n\n const normalized = value.trim();\n return normalized || undefined;\n}\n\nfunction asNumberLike(value: unknown): NumberLike {\n if (\n value === null ||\n value === undefined ||\n typeof value === 'number' ||\n typeof value === 'string'\n ) {\n return value;\n }\n\n return undefined;\n}\n\nfunction resolveTimestamp(\n line: Record<string, unknown>,\n message: Record<string, unknown> | undefined,\n state: PiSessionState,\n): string | undefined {\n const candidates = [line.timestamp, message?.timestamp, state.sessionTimestamp];\n\n for (const candidate of candidates) {\n if (typeof candidate === 'number' && Number.isFinite(candidate)) {\n return new Date(candidate).toISOString();\n }\n\n if (typeof candidate === 'string' && candidate.trim()) {\n return candidate;\n }\n }\n\n return undefined;\n}\n\nfunction extractUsage(line: Record<string, unknown>, message: Record<string, unknown> | undefined) {\n const usage = asRecord(line.usage) ?? asRecord(message?.usage);\n\n if (!usage) {\n return undefined;\n }\n\n const cost = asRecord(usage.cost);\n\n const extracted: PiUsageExtract = {\n inputTokens: asNumberLike(usage.input),\n outputTokens: asNumberLike(usage.output),\n reasoningTokens: asNumberLike(\n usage.reasoning ?? usage.reasoningTokens ?? usage.reasoningOutput ?? usage.outputReasoning,\n ),\n cacheReadTokens: asNumberLike(usage.cacheRead),\n cacheWriteTokens: asNumberLike(usage.cacheWrite),\n totalTokens: asNumberLike(usage.totalTokens),\n costUsd: asNumberLike(cost?.total),\n };\n\n const hasKnownUsageField =\n extracted.inputTokens !== undefined ||\n extracted.outputTokens !== undefined ||\n extracted.reasoningTokens !== undefined ||\n extracted.cacheReadTokens !== undefined ||\n extracted.cacheWriteTokens !== undefined ||\n extracted.totalTokens !== undefined ||\n extracted.costUsd !== undefined;\n\n return hasKnownUsageField ? extracted : undefined;\n}\n\nfunction getFallbackSessionId(filePath: string): string {\n return path.basename(filePath, '.jsonl');\n}\n\nexport class PiSourceAdapter implements SourceAdapter {\n public readonly id = 'pi' as const;\n\n private readonly sessionsDir: string;\n private readonly providerFilter: ProviderFilter;\n\n public constructor(options: PiSourceAdapterOptions = {}) {\n this.sessionsDir = options.sessionsDir ?? defaultSessionsDir;\n this.providerFilter = options.providerFilter ?? isOpenAiProvider;\n }\n\n public async discoverFiles(): Promise<string[]> {\n return discoverJsonlFiles(this.sessionsDir);\n }\n\n public async parseFile(filePath: string): Promise<UsageEvent[]> {\n const content = await readFile(filePath, 'utf8');\n const events: UsageEvent[] = [];\n const state: PiSessionState = { sessionId: getFallbackSessionId(filePath) };\n\n for (const rawLine of content.split(/\\r?\\n/u)) {\n const lineText = rawLine.trim();\n\n if (!lineText) {\n continue;\n }\n\n let parsedLine: unknown;\n\n try {\n parsedLine = JSON.parse(lineText);\n } catch {\n continue;\n }\n\n const line = asRecord(parsedLine);\n\n if (!line) {\n continue;\n }\n\n if (line.type === 'session') {\n state.sessionId = asText(line.id) ?? state.sessionId;\n state.sessionTimestamp = asText(line.timestamp) ?? state.sessionTimestamp;\n continue;\n }\n\n if (line.type === 'model_change') {\n state.provider = asText(line.provider) ?? state.provider;\n state.model = asText(line.modelId) ?? asText(line.model) ?? state.model;\n continue;\n }\n\n if (line.type !== 'message') {\n continue;\n }\n\n const message = asRecord(line.message);\n const usage = extractUsage(line, message);\n\n if (!usage) {\n continue;\n }\n\n const provider = asText(line.provider) ?? asText(message?.provider) ?? state.provider;\n\n if (!this.providerFilter(provider)) {\n continue;\n }\n\n const timestamp = resolveTimestamp(line, message, state);\n\n if (!timestamp || !state.sessionId) {\n continue;\n }\n\n const model =\n asText(line.model) ?? asText(line.modelId) ?? asText(message?.model) ?? state.model;\n\n try {\n events.push(\n createUsageEvent({\n source: this.id,\n sessionId: state.sessionId,\n timestamp,\n provider,\n model,\n ...usage,\n }),\n );\n } catch {\n continue;\n }\n }\n\n return events;\n }\n}\n\nexport function getDefaultPiSessionsDir(): string {\n return defaultSessionsDir;\n}\n","import { aggregateUsage } from '../aggregate/aggregate-usage.js';\nimport type { UsageEvent } from '../domain/usage-event.js';\nimport { applyPricingToEvents } from '../pricing/cost-engine.js';\nimport { LiteLLMPricingFetcher } from '../pricing/litellm-pricing-fetcher.js';\nimport { createDefaultOpenAiPricingSource } from '../pricing/static-pricing-source.js';\nimport type { PricingSource } from '../pricing/types.js';\nimport { renderMarkdownTable } from '../render/markdown-table.js';\nimport { renderTerminalTable } from '../render/terminal-table.js';\nimport { CodexSourceAdapter } from '../sources/codex/codex-source-adapter.js';\nimport { PiSourceAdapter } from '../sources/pi/pi-source-adapter.js';\nimport type { SourceAdapter } from '../sources/source-adapter.js';\nimport { getPeriodKey, type ReportGranularity } from '../utils/time-buckets.js';\n\nexport type ReportCommandOptions = {\n piDir?: string;\n codexDir?: string;\n source?: string | string[];\n since?: string;\n until?: string;\n timezone?: string;\n provider?: string;\n markdown?: boolean;\n json?: boolean;\n pricingUrl?: string;\n pricingOffline?: boolean;\n};\n\nfunction validateDateInput(value: string, flagName: '--since' | '--until'): void {\n if (!/^\\d{4}-\\d{2}-\\d{2}$/u.test(value)) {\n throw new Error(`${flagName} must use format YYYY-MM-DD`);\n }\n\n const parsed = new Date(`${value}T00:00:00.000Z`);\n\n if (Number.isNaN(parsed.getTime()) || parsed.toISOString().slice(0, 10) !== value) {\n throw new Error(`${flagName} has an invalid calendar date`);\n }\n}\n\nfunction validateTimezone(timezone: string): void {\n try {\n new Intl.DateTimeFormat('en-US', { timeZone: timezone }).format(new Date());\n } catch {\n throw new Error(`Invalid timezone: ${timezone}`);\n }\n}\n\nfunction normalizeProviderFilter(provider: string | undefined): string | undefined {\n if (!provider) {\n return undefined;\n }\n\n const normalized = provider.trim().toLowerCase();\n return normalized || undefined;\n}\n\nfunction normalizeSourceFilter(source: string | string[] | undefined): Set<string> | undefined {\n if (!source || (Array.isArray(source) && source.length === 0)) {\n return undefined;\n }\n\n const sourceCandidates = Array.isArray(source) ? source : [source];\n const normalizedSources = sourceCandidates\n .flatMap((candidate) => candidate.split(','))\n .map((candidate) => candidate.trim().toLowerCase())\n .filter((candidate) => candidate.length > 0);\n\n if (normalizedSources.length === 0) {\n throw new Error('--source must contain at least one non-empty source id');\n }\n\n return new Set(normalizedSources);\n}\n\nfunction matchesProvider(\n provider: string | undefined,\n providerFilter: string | undefined,\n): boolean {\n if (!providerFilter) {\n return true;\n }\n\n return provider?.toLowerCase().includes(providerFilter) ?? false;\n}\n\nfunction matchesSource(source: string, sourceFilter: Set<string> | undefined): boolean {\n if (!sourceFilter) {\n return true;\n }\n\n return sourceFilter.has(source.toLowerCase());\n}\n\nasync function parseAdapterEvents(adapter: SourceAdapter): Promise<UsageEvent[]> {\n const files = await adapter.discoverFiles();\n const parsedByFile = await Promise.all(files.map((filePath) => adapter.parseFile(filePath)));\n\n return parsedByFile.flat();\n}\n\nfunction filterEventsByDateRange(\n events: UsageEvent[],\n timezone: string,\n since: string | undefined,\n until: string | undefined,\n): UsageEvent[] {\n return events.filter((event) => {\n const eventDate = getPeriodKey(event.timestamp, 'daily', timezone);\n\n if (since && eventDate < since) {\n return false;\n }\n\n if (until && eventDate > until) {\n return false;\n }\n\n return true;\n });\n}\n\nfunction validatePricingUrl(pricingUrl: string | undefined): void {\n if (!pricingUrl) {\n return;\n }\n\n try {\n const parsedUrl = new URL(pricingUrl);\n\n if (!['http:', 'https:'].includes(parsedUrl.protocol)) {\n throw new Error('Unsupported protocol');\n }\n } catch {\n throw new Error('--pricing-url must be a valid http(s) URL');\n }\n}\n\nasync function resolvePricingSource(options: ReportCommandOptions): Promise<PricingSource> {\n const fallbackPricingSource = createDefaultOpenAiPricingSource();\n const litellmPricingFetcher = new LiteLLMPricingFetcher({\n sourceUrl: options.pricingUrl,\n offline: options.pricingOffline,\n });\n\n try {\n await litellmPricingFetcher.load();\n return litellmPricingFetcher;\n } catch (error) {\n if (options.pricingOffline) {\n throw new Error('Offline pricing mode enabled but cached pricing is unavailable');\n }\n\n if (options.pricingUrl) {\n const reason = error instanceof Error ? error.message : String(error);\n throw new Error(`Could not load pricing from --pricing-url: ${reason}`);\n }\n\n return fallbackPricingSource;\n }\n}\n\nexport async function buildUsageReport(\n granularity: ReportGranularity,\n options: ReportCommandOptions,\n): Promise<string> {\n if (options.markdown && options.json) {\n throw new Error('Choose either --markdown or --json, not both');\n }\n\n if (options.since) {\n validateDateInput(options.since, '--since');\n }\n\n if (options.until) {\n validateDateInput(options.until, '--until');\n }\n\n if (options.since && options.until && options.since > options.until) {\n throw new Error('--since must be less than or equal to --until');\n }\n\n validatePricingUrl(options.pricingUrl);\n\n const timezone = options.timezone ?? Intl.DateTimeFormat().resolvedOptions().timeZone;\n validateTimezone(timezone);\n\n const providerFilter = normalizeProviderFilter(options.provider);\n const sourceFilter = normalizeSourceFilter(options.source);\n const effectiveProviderFilter = providerFilter ?? 'openai';\n const pricingSource = await resolvePricingSource(options);\n\n const piAdapter = new PiSourceAdapter({\n sessionsDir: options.piDir,\n providerFilter: (provider) => matchesProvider(provider, effectiveProviderFilter),\n });\n const codexAdapter = new CodexSourceAdapter({\n sessionsDir: options.codexDir,\n });\n\n const [piEvents, codexEvents] = await Promise.all([\n parseAdapterEvents(piAdapter),\n parseAdapterEvents(codexAdapter),\n ]);\n\n const providerAndSourceFilteredEvents = [...piEvents, ...codexEvents].filter(\n (event) =>\n matchesProvider(event.provider, effectiveProviderFilter) &&\n matchesSource(event.source, sourceFilter),\n );\n\n const dateFilteredEvents = filterEventsByDateRange(\n providerAndSourceFilteredEvents,\n timezone,\n options.since,\n options.until,\n );\n\n const pricedEvents = applyPricingToEvents(dateFilteredEvents, pricingSource);\n const rows = aggregateUsage(pricedEvents, {\n granularity,\n timezone,\n });\n\n if (options.json) {\n return JSON.stringify(rows, null, 2);\n }\n\n if (options.markdown) {\n return renderMarkdownTable(rows);\n }\n\n return renderTerminalTable(rows);\n}\n\nexport async function runUsageReport(\n granularity: ReportGranularity,\n options: ReportCommandOptions,\n): Promise<void> {\n const output = await buildUsageReport(granularity, options);\n console.log(output);\n}\n","#!/usr/bin/env node\n\nimport { createCli } from './create-cli.js';\n\nconst cli = createCli();\n\ntry {\n await cli.parseAsync(process.argv);\n} catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(message);\n process.exitCode = 1;\n}\n"],"mappings":"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","names":["ONE_MILLION","_","normalizeKey","readFile","os","path","path","path","os","asRecord","readFile","payload","readFile","os","path","defaultSessionsDir","path","os","asRecord","asText","getFallbackSessionId","readFile"]}
1
+ {"version":3,"sources":["../src/cli/create-cli.ts","../src/domain/normalization.ts","../src/utils/time-buckets.ts","../src/aggregate/aggregate-usage.ts","../src/pricing/cost-engine.ts","../src/pricing/litellm-pricing-fetcher.ts","../src/pricing/static-pricing-source.ts","../src/render/markdown-table.ts","../src/render/row-cells.ts","../src/render/terminal-table.ts","../src/sources/codex/codex-source-adapter.ts","../src/domain/usage-event.ts","../src/utils/discover-jsonl-files.ts","../src/utils/read-jsonl-objects.ts","../src/sources/pi/pi-source-adapter.ts","../src/cli/run-usage-report.ts","../src/cli/index.ts"],"sourcesContent":["import { Command } from 'commander';\n\nimport { runUsageReport } from './run-usage-report.js';\n\nexport type UsageGranularity = 'daily' | 'weekly' | 'monthly';\n\ntype SharedOptions = {\n piDir?: string;\n codexDir?: string;\n source?: string[];\n since?: string;\n until?: string;\n timezone?: string;\n provider?: string;\n markdown?: boolean;\n json?: boolean;\n pricingUrl?: string;\n pricingOffline?: boolean;\n};\n\nconst defaultTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';\n\nfunction collectSourceOption(value: string, previous: string[]): string[] {\n return [...previous, ...value.split(',')];\n}\n\nfunction addSharedOptions(command: Command): Command {\n return command\n .option('--pi-dir <path>', 'Path to .pi sessions directory')\n .option('--codex-dir <path>', 'Path to .codex sessions directory')\n .option(\n '--source <name>',\n 'Filter by source id (repeatable or comma-separated, allowed: pi,codex)',\n collectSourceOption,\n [],\n )\n .option('--since <YYYY-MM-DD>', 'Inclusive start date filter')\n .option('--until <YYYY-MM-DD>', 'Inclusive end date filter')\n .option('--timezone <iana>', 'Timezone for bucketing', defaultTimezone)\n .option('--provider <name>', 'Provider filter (defaults to openai behavior)')\n .option('--pricing-url <url>', 'Override LiteLLM pricing source URL')\n .option('--pricing-offline', 'Use cached LiteLLM pricing only (no network fetch)')\n .option('--markdown', 'Render output as markdown table')\n .option('--json', 'Render output as JSON');\n}\n\nfunction commandDescription(granularity: UsageGranularity): string {\n switch (granularity) {\n case 'daily':\n return 'Show daily usage report';\n case 'weekly':\n return 'Show weekly usage report (week starts Monday)';\n case 'monthly':\n return 'Show monthly usage report';\n }\n}\n\nfunction createCommand(granularity: UsageGranularity): Command {\n const command = new Command(granularity);\n\n addSharedOptions(command)\n .description(commandDescription(granularity))\n .action(async (options: SharedOptions) => {\n await runUsageReport(granularity, options);\n });\n\n return command;\n}\n\nfunction rootDescription(): string {\n return [\n 'Aggregate local LLM usage metrics from pi and codex sessions',\n '',\n 'Examples:',\n ' $ llm-usage daily',\n ' $ llm-usage weekly --timezone Europe/Paris',\n ' $ llm-usage monthly --since 2026-01-01 --until 2026-01-31 --source codex --json',\n ' $ npx --yes llm-usage-metrics daily',\n ].join('\\n');\n}\n\nexport function createCli(): Command {\n const program = new Command();\n\n program\n .name('llm-usage')\n .description(rootDescription())\n .showHelpAfterError()\n .addCommand(createCommand('daily'))\n .addCommand(createCommand('weekly'))\n .addCommand(createCommand('monthly'));\n\n return program;\n}\n","export type NumberLike = number | string | null | undefined;\n\nexport function normalizeNonNegativeInteger(value: NumberLike): number {\n if (value === null || value === undefined) {\n return 0;\n }\n\n const parsed = typeof value === 'number' ? value : Number(value);\n\n if (!Number.isFinite(parsed)) {\n return 0;\n }\n\n return Math.max(0, Math.trunc(parsed));\n}\n\nexport function normalizeUsdCost(value: NumberLike): number | undefined {\n if (value === null || value === undefined) {\n return undefined;\n }\n\n if (typeof value === 'string' && value.trim() === '') {\n return undefined;\n }\n\n const parsed = typeof value === 'number' ? value : Number(value);\n\n if (!Number.isFinite(parsed)) {\n return undefined;\n }\n\n return Math.max(0, parsed);\n}\n\nexport function normalizeTimestamp(value: string | Date): string {\n const date = value instanceof Date ? value : new Date(value);\n\n if (Number.isNaN(date.getTime())) {\n throw new Error(`Invalid timestamp: ${String(value)}`);\n }\n\n return date.toISOString();\n}\n\nexport function normalizeModelList(models: Iterable<string | null | undefined>): string[] {\n const deduplicated = new Set<string>();\n\n for (const model of models) {\n if (!model) {\n continue;\n }\n\n const normalized = model.trim();\n\n if (!normalized) {\n continue;\n }\n\n deduplicated.add(normalized);\n }\n\n return [...deduplicated].sort((left, right) => left.localeCompare(right));\n}\n","export type ReportGranularity = 'daily' | 'weekly' | 'monthly';\n\ntype LocalDateParts = {\n year: number;\n month: number;\n day: number;\n};\n\nconst formatterCache = new Map<string, Intl.DateTimeFormat>();\n\nfunction getDateFormatter(timezone: string): Intl.DateTimeFormat {\n const cachedFormatter = formatterCache.get(timezone);\n\n if (cachedFormatter) {\n return cachedFormatter;\n }\n\n const formatter = new Intl.DateTimeFormat('en-CA', {\n timeZone: timezone,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n });\n formatterCache.set(timezone, formatter);\n return formatter;\n}\n\nfunction extractLocalDateParts(timestampIso: string, timezone: string): LocalDateParts {\n const date = new Date(timestampIso);\n\n if (Number.isNaN(date.getTime())) {\n throw new Error(`Invalid event timestamp: ${timestampIso}`);\n }\n\n const formatter = getDateFormatter(timezone);\n const parts = formatter.formatToParts(date);\n\n const year = Number(parts.find((part) => part.type === 'year')?.value);\n const month = Number(parts.find((part) => part.type === 'month')?.value);\n const day = Number(parts.find((part) => part.type === 'day')?.value);\n\n if (!year || !month || !day) {\n throw new Error(`Could not resolve local date parts for timestamp: ${timestampIso}`);\n }\n\n return { year, month, day };\n}\n\nfunction createUtcDate(localDate: LocalDateParts): Date {\n return new Date(Date.UTC(localDate.year, localDate.month - 1, localDate.day));\n}\n\nfunction addDays(date: Date, days: number): Date {\n return new Date(date.getTime() + days * 24 * 60 * 60 * 1000);\n}\n\nfunction toIsoDayOfWeek(date: Date): number {\n const utcDay = date.getUTCDay();\n return utcDay === 0 ? 7 : utcDay;\n}\n\nfunction getIsoWeekParts(localDate: LocalDateParts): { weekYear: number; weekNumber: number } {\n const localUtcDate = createUtcDate(localDate);\n const isoDay = toIsoDayOfWeek(localUtcDate);\n\n const currentWeekMonday = addDays(localUtcDate, -(isoDay - 1));\n const currentWeekThursday = addDays(localUtcDate, 4 - isoDay);\n const weekYear = currentWeekThursday.getUTCFullYear();\n\n const jan4 = new Date(Date.UTC(weekYear, 0, 4));\n const jan4IsoDay = toIsoDayOfWeek(jan4);\n const firstWeekMonday = addDays(jan4, -(jan4IsoDay - 1));\n\n const diffMs = currentWeekMonday.getTime() - firstWeekMonday.getTime();\n const weekNumber = Math.floor(diffMs / (7 * 24 * 60 * 60 * 1000)) + 1;\n\n return { weekYear, weekNumber };\n}\n\nexport function getPeriodKey(\n timestampIso: string,\n granularity: ReportGranularity,\n timezone: string,\n): string {\n const localDate = extractLocalDateParts(timestampIso, timezone);\n\n if (granularity === 'daily') {\n return `${localDate.year}-${String(localDate.month).padStart(2, '0')}-${String(localDate.day).padStart(2, '0')}`;\n }\n\n if (granularity === 'monthly') {\n return `${localDate.year}-${String(localDate.month).padStart(2, '0')}`;\n }\n\n const isoWeek = getIsoWeekParts(localDate);\n return `${isoWeek.weekYear}-W${String(isoWeek.weekNumber).padStart(2, '0')}`;\n}\n","import type { UsageEvent } from '../domain/usage-event.js';\nimport type {\n GrandTotalRow,\n PeriodCombinedRow,\n PeriodSourceRow,\n UsageReportRow,\n UsageTotals,\n} from '../domain/usage-report-row.js';\nimport { normalizeModelList } from '../domain/normalization.js';\nimport { getPeriodKey, type ReportGranularity } from '../utils/time-buckets.js';\n\nexport type AggregateUsageOptions = {\n granularity: ReportGranularity;\n timezone: string;\n};\n\ntype RowAccumulator = {\n totals: UsageTotals;\n modelSet: Set<string>;\n};\n\nconst USD_PRECISION_SCALE = 1_000_000_000_000;\n\nfunction addUsd(left: number, right: number): number {\n return Math.round((left + right) * USD_PRECISION_SCALE) / USD_PRECISION_SCALE;\n}\n\nfunction createEmptyTotals(): UsageTotals {\n return {\n inputTokens: 0,\n outputTokens: 0,\n reasoningTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n totalTokens: 0,\n costUsd: 0,\n };\n}\n\nfunction createRowAccumulator(): RowAccumulator {\n return {\n totals: createEmptyTotals(),\n modelSet: new Set<string>(),\n };\n}\n\nfunction addEventToAccumulator(accumulator: RowAccumulator, event: UsageEvent): void {\n accumulator.totals.inputTokens += event.inputTokens;\n accumulator.totals.outputTokens += event.outputTokens;\n accumulator.totals.reasoningTokens += event.reasoningTokens;\n accumulator.totals.cacheReadTokens += event.cacheReadTokens;\n accumulator.totals.cacheWriteTokens += event.cacheWriteTokens;\n accumulator.totals.totalTokens += event.totalTokens;\n accumulator.totals.costUsd = addUsd(accumulator.totals.costUsd, event.costUsd ?? 0);\n\n if (event.model) {\n accumulator.modelSet.add(event.model);\n }\n}\n\nfunction addTotals(target: UsageTotals, source: UsageTotals): void {\n target.inputTokens += source.inputTokens;\n target.outputTokens += source.outputTokens;\n target.reasoningTokens += source.reasoningTokens;\n target.cacheReadTokens += source.cacheReadTokens;\n target.cacheWriteTokens += source.cacheWriteTokens;\n target.totalTokens += source.totalTokens;\n target.costUsd = addUsd(target.costUsd, source.costUsd);\n}\n\nfunction sourceSortComparator(left: string, right: string): number {\n const sourceOrder: Record<string, number> = {\n pi: 0,\n codex: 1,\n };\n\n const leftWeight = sourceOrder[left] ?? Number.MAX_SAFE_INTEGER;\n const rightWeight = sourceOrder[right] ?? Number.MAX_SAFE_INTEGER;\n\n if (leftWeight !== rightWeight) {\n return leftWeight - rightWeight;\n }\n\n return left.localeCompare(right);\n}\n\nexport function aggregateUsage(\n events: UsageEvent[],\n options: AggregateUsageOptions,\n): UsageReportRow[] {\n const periodMap = new Map<string, Map<string, RowAccumulator>>();\n\n for (const event of events) {\n const periodKey = getPeriodKey(event.timestamp, options.granularity, options.timezone);\n const periodSources = periodMap.get(periodKey) ?? new Map<string, RowAccumulator>();\n periodMap.set(periodKey, periodSources);\n\n const rowAccumulator = periodSources.get(event.source) ?? createRowAccumulator();\n periodSources.set(event.source, rowAccumulator);\n\n addEventToAccumulator(rowAccumulator, event);\n }\n\n const sortedPeriodKeys = [...periodMap.keys()].sort((left, right) => left.localeCompare(right));\n const rows: UsageReportRow[] = [];\n const grandTotals = createEmptyTotals();\n const grandModels = new Set<string>();\n\n for (const periodKey of sortedPeriodKeys) {\n const sourceMap = periodMap.get(periodKey);\n\n if (!sourceMap) {\n continue;\n }\n\n const periodCombinedTotals = createEmptyTotals();\n const periodModels = new Set<string>();\n\n const sortedSources = [...sourceMap.keys()].sort(sourceSortComparator);\n\n for (const source of sortedSources) {\n const accumulator = sourceMap.get(source);\n\n if (!accumulator) {\n continue;\n }\n\n const sourceRow: PeriodSourceRow = {\n rowType: 'period_source',\n periodKey,\n source,\n models: normalizeModelList(accumulator.modelSet),\n ...accumulator.totals,\n };\n\n rows.push(sourceRow);\n\n addTotals(periodCombinedTotals, accumulator.totals);\n addTotals(grandTotals, accumulator.totals);\n\n for (const model of accumulator.modelSet) {\n periodModels.add(model);\n grandModels.add(model);\n }\n }\n\n if (sortedSources.length > 1) {\n const combinedRow: PeriodCombinedRow = {\n rowType: 'period_combined',\n periodKey,\n source: 'combined',\n models: normalizeModelList(periodModels),\n ...periodCombinedTotals,\n };\n\n rows.push(combinedRow);\n }\n }\n\n const grandTotalRow: GrandTotalRow = {\n rowType: 'grand_total',\n periodKey: 'ALL',\n source: 'combined',\n models: normalizeModelList(grandModels),\n ...grandTotals,\n };\n\n rows.push(grandTotalRow);\n\n return rows;\n}\n","import type { UsageEvent } from '../domain/usage-event.js';\nimport type { ModelPricing, PricingSource } from './types.js';\n\nconst ONE_MILLION = 1_000_000;\n\nfunction estimateTokenGroupCost(tokens: number, per1MUsd: number | undefined): number {\n if (!per1MUsd || tokens <= 0) {\n return 0;\n }\n\n return (tokens / ONE_MILLION) * per1MUsd;\n}\n\nexport function calculateEstimatedCostUsd(event: UsageEvent, pricing: ModelPricing): number {\n const reasoningBilling = pricing.reasoningBilling ?? 'included-in-output';\n\n const inputCost = estimateTokenGroupCost(event.inputTokens, pricing.inputPer1MUsd);\n const outputCost = estimateTokenGroupCost(event.outputTokens, pricing.outputPer1MUsd);\n const cacheReadCost = estimateTokenGroupCost(event.cacheReadTokens, pricing.cacheReadPer1MUsd);\n const cacheWriteCost = estimateTokenGroupCost(event.cacheWriteTokens, pricing.cacheWritePer1MUsd);\n\n const reasoningCost =\n reasoningBilling === 'separate'\n ? estimateTokenGroupCost(event.reasoningTokens, pricing.reasoningPer1MUsd)\n : 0;\n\n return inputCost + outputCost + cacheReadCost + cacheWriteCost + reasoningCost;\n}\n\nexport function applyPricingToEvent(event: UsageEvent, pricingSource: PricingSource): UsageEvent {\n if (event.costMode === 'explicit' && event.costUsd !== undefined) {\n return event;\n }\n\n if (!event.model) {\n return { ...event, costMode: 'estimated' };\n }\n\n const pricing = pricingSource.getPricing(event.model);\n\n if (!pricing) {\n return { ...event, costMode: 'estimated' };\n }\n\n return {\n ...event,\n costUsd: calculateEstimatedCostUsd(event, pricing),\n costMode: 'estimated',\n };\n}\n\nexport function applyPricingToEvents(\n events: UsageEvent[],\n pricingSource: PricingSource,\n): UsageEvent[] {\n return events.map((event) => applyPricingToEvent(event, pricingSource));\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport type { ModelPricing, PricingSource } from './types.js';\n\nconst ONE_MILLION = 1_000_000;\nconst DEFAULT_CACHE_TTL_MS = 24 * 60 * 60 * 1000;\nconst DEFAULT_FETCH_TIMEOUT_MS = 4000;\n\nexport const DEFAULT_LITELLM_PRICING_URL =\n 'https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json';\n\ntype LiteLLMCachePayload = {\n fetchedAt: number;\n sourceUrl: string;\n pricingByModel: Record<string, ModelPricing>;\n};\n\nexport type LiteLLMPricingFetcherOptions = {\n sourceUrl?: string;\n cacheFilePath?: string;\n cacheTtlMs?: number;\n fetchTimeoutMs?: number;\n offline?: boolean;\n fetchImpl?: typeof fetch;\n now?: () => number;\n};\n\nfunction normalizeKey(value: string): string {\n return value.trim().toLowerCase();\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n if (!value || typeof value !== 'object') {\n return undefined;\n }\n\n return value as Record<string, unknown>;\n}\n\nfunction toNonNegativeNumber(value: unknown): number | undefined {\n if (typeof value === 'number') {\n if (!Number.isFinite(value) || value < 0) {\n return undefined;\n }\n\n return value;\n }\n\n if (typeof value === 'string') {\n if (value.trim() === '') {\n return undefined;\n }\n\n const parsedValue = Number(value);\n\n if (!Number.isFinite(parsedValue) || parsedValue < 0) {\n return undefined;\n }\n\n return parsedValue;\n }\n\n return undefined;\n}\n\nfunction normalizeModelPricing(rawModelPricing: Record<string, unknown>): ModelPricing | undefined {\n const inputPerToken =\n toNonNegativeNumber(rawModelPricing.input_cost_per_token) ??\n toNonNegativeNumber(rawModelPricing.input_cost_per_token_priority);\n const outputPerToken =\n toNonNegativeNumber(rawModelPricing.output_cost_per_token) ??\n toNonNegativeNumber(rawModelPricing.output_cost_per_token_priority);\n\n if (inputPerToken === undefined || outputPerToken === undefined) {\n return undefined;\n }\n\n const cacheReadPerToken =\n toNonNegativeNumber(rawModelPricing.cache_read_input_token_cost) ??\n toNonNegativeNumber(rawModelPricing.cache_read_input_token_cost_priority);\n const cacheWritePerToken = toNonNegativeNumber(rawModelPricing.cache_creation_input_token_cost);\n const reasoningPerToken = toNonNegativeNumber(rawModelPricing.output_cost_per_reasoning_token);\n\n const modelPricing: ModelPricing = {\n inputPer1MUsd: inputPerToken * ONE_MILLION,\n outputPer1MUsd: outputPerToken * ONE_MILLION,\n };\n\n if (cacheReadPerToken !== undefined) {\n modelPricing.cacheReadPer1MUsd = cacheReadPerToken * ONE_MILLION;\n }\n\n if (cacheWritePerToken !== undefined) {\n modelPricing.cacheWritePer1MUsd = cacheWritePerToken * ONE_MILLION;\n }\n\n if (reasoningPerToken !== undefined) {\n modelPricing.reasoningPer1MUsd = reasoningPerToken * ONE_MILLION;\n modelPricing.reasoningBilling = 'separate';\n }\n\n return modelPricing;\n}\n\nfunction normalizeLitellmPricingPayload(payload: unknown): Map<string, ModelPricing> {\n const payloadRecord = asRecord(payload);\n\n if (!payloadRecord) {\n throw new Error('LiteLLM pricing payload must be a JSON object');\n }\n\n const normalizedPricing = new Map<string, ModelPricing>();\n\n for (const [modelName, rawModelPricing] of Object.entries(payloadRecord)) {\n const modelPricingRecord = asRecord(rawModelPricing);\n\n if (!modelPricingRecord) {\n continue;\n }\n\n const normalizedModelPricing = normalizeModelPricing(modelPricingRecord);\n\n if (!normalizedModelPricing) {\n continue;\n }\n\n normalizedPricing.set(normalizeKey(modelName), normalizedModelPricing);\n }\n\n if (normalizedPricing.size === 0) {\n throw new Error('LiteLLM pricing payload did not contain any usable model pricing entries');\n }\n\n return normalizedPricing;\n}\n\nfunction getCacheRootDir(): string {\n const xdgCacheDir = process.env.XDG_CACHE_HOME;\n\n if (xdgCacheDir) {\n return xdgCacheDir;\n }\n\n if (process.platform === 'win32') {\n const localAppData = process.env.LOCALAPPDATA;\n\n if (localAppData) {\n return localAppData;\n }\n }\n\n return path.join(os.homedir(), '.cache');\n}\n\nexport function getDefaultLiteLLMPricingCachePath(): string {\n return path.join(getCacheRootDir(), 'llm-usage-metrics', 'litellm-pricing-cache.json');\n}\n\nfunction stripProviderPrefix(model: string): string {\n const slashIndex = model.lastIndexOf('/');\n\n if (slashIndex === -1) {\n return model;\n }\n\n return model.slice(slashIndex + 1);\n}\n\nfunction canonicalizeForFuzzy(value: string): string {\n return value.replace(/[^a-z0-9]/gu, '');\n}\n\nfunction levenshteinDistance(left: string, right: string): number {\n const leftLength = left.length;\n const rightLength = right.length;\n\n const matrix = Array.from({ length: leftLength + 1 }, (_, rowIndex) => {\n return Array.from({ length: rightLength + 1 }, (_, columnIndex) => {\n if (rowIndex === 0) {\n return columnIndex;\n }\n\n if (columnIndex === 0) {\n return rowIndex;\n }\n\n return 0;\n });\n });\n\n for (let rowIndex = 1; rowIndex <= leftLength; rowIndex += 1) {\n for (let columnIndex = 1; columnIndex <= rightLength; columnIndex += 1) {\n const substitutionCost = left[rowIndex - 1] === right[columnIndex - 1] ? 0 : 1;\n\n matrix[rowIndex][columnIndex] = Math.min(\n matrix[rowIndex - 1][columnIndex] + 1,\n matrix[rowIndex][columnIndex - 1] + 1,\n matrix[rowIndex - 1][columnIndex - 1] + substitutionCost,\n );\n }\n }\n\n return matrix[leftLength][rightLength];\n}\n\nexport class LiteLLMPricingFetcher implements PricingSource {\n private readonly sourceUrl: string;\n private readonly cacheFilePath: string;\n private readonly cacheTtlMs: number;\n private readonly fetchTimeoutMs: number;\n private readonly offline: boolean;\n private readonly fetchImpl: typeof fetch;\n private readonly now: () => number;\n\n private pricingByModel = new Map<string, ModelPricing>();\n private resolvedAliasCache = new Map<string, string>();\n\n public constructor(options: LiteLLMPricingFetcherOptions = {}) {\n this.sourceUrl = options.sourceUrl ?? DEFAULT_LITELLM_PRICING_URL;\n this.cacheFilePath = options.cacheFilePath ?? getDefaultLiteLLMPricingCachePath();\n this.cacheTtlMs = options.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;\n this.fetchTimeoutMs = options.fetchTimeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;\n this.offline = options.offline ?? false;\n this.fetchImpl = options.fetchImpl ?? fetch;\n this.now = options.now ?? Date.now;\n }\n\n public async load(): Promise<void> {\n const cacheLoaded = await this.loadFromCache({ allowStale: false });\n\n if (cacheLoaded) {\n return;\n }\n\n if (this.offline) {\n const staleCacheLoaded = await this.loadFromCache({ allowStale: true });\n\n if (!staleCacheLoaded) {\n throw new Error('Offline pricing mode enabled but no cached LiteLLM pricing is available');\n }\n\n return;\n }\n\n try {\n await this.loadFromRemote();\n } catch {\n const staleCacheLoaded = await this.loadFromCache({ allowStale: true });\n\n if (!staleCacheLoaded) {\n throw new Error('Could not load LiteLLM pricing from network or cache');\n }\n }\n }\n\n public resolveModelAlias(model: string): string {\n const normalizedModel = normalizeKey(model);\n const cachedAlias = this.resolvedAliasCache.get(normalizedModel);\n\n if (cachedAlias) {\n return cachedAlias;\n }\n\n const directMatch = this.resolveDirectModelMatch(normalizedModel);\n\n if (directMatch) {\n this.resolvedAliasCache.set(normalizedModel, directMatch);\n return directMatch;\n }\n\n const prefixMatch = this.resolvePrefixModelMatch(normalizedModel);\n\n if (prefixMatch) {\n this.resolvedAliasCache.set(normalizedModel, prefixMatch);\n return prefixMatch;\n }\n\n const fuzzyMatch = this.resolveFuzzyModelMatch(normalizedModel);\n const resolvedAlias = fuzzyMatch ?? normalizedModel;\n this.resolvedAliasCache.set(normalizedModel, resolvedAlias);\n\n return resolvedAlias;\n }\n\n public getPricing(model: string): ModelPricing | undefined {\n const resolvedModel = this.resolveModelAlias(model);\n return this.pricingByModel.get(resolvedModel);\n }\n\n private resolveDirectModelMatch(normalizedModel: string): string | undefined {\n if (this.pricingByModel.has(normalizedModel)) {\n return normalizedModel;\n }\n\n const strippedModel = stripProviderPrefix(normalizedModel);\n\n if (this.pricingByModel.has(strippedModel)) {\n return strippedModel;\n }\n\n return undefined;\n }\n\n private resolvePrefixModelMatch(normalizedModel: string): string | undefined {\n const candidates = [normalizedModel, stripProviderPrefix(normalizedModel)];\n\n for (const candidate of candidates) {\n let bestMatch: string | undefined;\n\n for (const modelName of this.pricingByModel.keys()) {\n if (!candidate.startsWith(modelName)) {\n continue;\n }\n\n if (\n !bestMatch ||\n modelName.length > bestMatch.length ||\n (modelName.length === bestMatch.length && modelName.localeCompare(bestMatch) < 0)\n ) {\n bestMatch = modelName;\n }\n }\n\n if (bestMatch) {\n return bestMatch;\n }\n }\n\n return undefined;\n }\n\n private resolveFuzzyModelMatch(normalizedModel: string): string | undefined {\n const fuzzyTarget = canonicalizeForFuzzy(stripProviderPrefix(normalizedModel));\n\n if (!fuzzyTarget) {\n return undefined;\n }\n\n let bestMatch: { modelName: string; distance: number } | undefined;\n\n for (const modelName of this.pricingByModel.keys()) {\n const fuzzyModelName = canonicalizeForFuzzy(modelName);\n\n if (!fuzzyModelName) {\n continue;\n }\n\n const distance = levenshteinDistance(fuzzyTarget, fuzzyModelName);\n\n if (!bestMatch || distance < bestMatch.distance) {\n bestMatch = { modelName, distance };\n }\n }\n\n if (!bestMatch) {\n return undefined;\n }\n\n const maxDistance = Math.max(2, Math.floor(fuzzyTarget.length * 0.2));\n\n if (bestMatch.distance > maxDistance) {\n return undefined;\n }\n\n return bestMatch.modelName;\n }\n\n private async loadFromRemote(): Promise<void> {\n const response = await this.fetchImpl(this.sourceUrl, {\n signal: AbortSignal.timeout(this.fetchTimeoutMs),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch LiteLLM pricing: HTTP ${response.status}`);\n }\n\n const payload = (await response.json()) as unknown;\n const normalizedPricing = normalizeLitellmPricingPayload(payload);\n\n this.pricingByModel = normalizedPricing;\n this.resolvedAliasCache.clear();\n\n try {\n await this.writeCache();\n } catch {\n // Cache writes are best-effort. A successful remote fetch must still be usable.\n }\n }\n\n private async loadFromCache(options: { allowStale: boolean }): Promise<boolean> {\n const cacheFileContent = await this.readCachePayload();\n\n if (!cacheFileContent) {\n return false;\n }\n\n if (cacheFileContent.sourceUrl !== this.sourceUrl) {\n return false;\n }\n\n const isStale = this.now() - cacheFileContent.fetchedAt > this.cacheTtlMs;\n\n if (isStale && !options.allowStale) {\n return false;\n }\n\n this.pricingByModel = new Map(\n Object.entries(cacheFileContent.pricingByModel).map(([modelName, pricing]) => [\n normalizeKey(modelName),\n pricing,\n ]),\n );\n this.resolvedAliasCache.clear();\n\n return this.pricingByModel.size > 0;\n }\n\n private normalizeCachedPricing(rawPricing: unknown): ModelPricing | undefined {\n const pricingRecord = asRecord(rawPricing);\n\n if (!pricingRecord) {\n return undefined;\n }\n\n const inputPer1MUsd = toNonNegativeNumber(pricingRecord.inputPer1MUsd);\n const outputPer1MUsd = toNonNegativeNumber(pricingRecord.outputPer1MUsd);\n\n if (inputPer1MUsd === undefined || outputPer1MUsd === undefined) {\n return undefined;\n }\n\n const modelPricing: ModelPricing = {\n inputPer1MUsd,\n outputPer1MUsd,\n };\n\n const cacheReadPer1MUsd = toNonNegativeNumber(pricingRecord.cacheReadPer1MUsd);\n\n if (cacheReadPer1MUsd !== undefined) {\n modelPricing.cacheReadPer1MUsd = cacheReadPer1MUsd;\n }\n\n const cacheWritePer1MUsd = toNonNegativeNumber(pricingRecord.cacheWritePer1MUsd);\n\n if (cacheWritePer1MUsd !== undefined) {\n modelPricing.cacheWritePer1MUsd = cacheWritePer1MUsd;\n }\n\n const reasoningPer1MUsd = toNonNegativeNumber(pricingRecord.reasoningPer1MUsd);\n\n if (reasoningPer1MUsd !== undefined) {\n modelPricing.reasoningPer1MUsd = reasoningPer1MUsd;\n modelPricing.reasoningBilling = 'separate';\n }\n\n return modelPricing;\n }\n\n private async readCachePayload(): Promise<LiteLLMCachePayload | undefined> {\n let content: string;\n\n try {\n content = await readFile(this.cacheFilePath, 'utf8');\n } catch {\n return undefined;\n }\n\n let parsedPayload: unknown;\n\n try {\n parsedPayload = JSON.parse(content);\n } catch {\n return undefined;\n }\n\n const payloadRecord = asRecord(parsedPayload);\n\n if (!payloadRecord) {\n return undefined;\n }\n\n const fetchedAt = toNonNegativeNumber(payloadRecord.fetchedAt);\n const sourceUrl =\n typeof payloadRecord.sourceUrl === 'string' ? payloadRecord.sourceUrl : undefined;\n const pricingByModelRecord = asRecord(payloadRecord.pricingByModel);\n\n if (fetchedAt === undefined || !sourceUrl || !pricingByModelRecord) {\n return undefined;\n }\n\n const pricingByModel: Record<string, ModelPricing> = {};\n\n for (const [modelName, rawPricing] of Object.entries(pricingByModelRecord)) {\n const pricing = this.normalizeCachedPricing(rawPricing);\n\n if (!pricing) {\n continue;\n }\n\n pricingByModel[modelName] = pricing;\n }\n\n return {\n fetchedAt,\n sourceUrl,\n pricingByModel,\n };\n }\n\n private async writeCache(): Promise<void> {\n const directoryPath = path.dirname(this.cacheFilePath);\n await mkdir(directoryPath, { recursive: true });\n\n const payload: LiteLLMCachePayload = {\n fetchedAt: this.now(),\n sourceUrl: this.sourceUrl,\n pricingByModel: Object.fromEntries(this.pricingByModel.entries()),\n };\n\n await writeFile(this.cacheFilePath, JSON.stringify(payload), 'utf8');\n }\n}\n","import type { ModelPricing, PricingSource } from './types.js';\n\nexport type StaticPricingSourceOptions = {\n pricingByModel: Record<string, ModelPricing>;\n modelAliases?: Record<string, string>;\n};\n\nfunction normalizeKey(key: string): string {\n return key.trim().toLowerCase();\n}\n\nexport class StaticPricingSource implements PricingSource {\n private readonly pricingByModel: Map<string, ModelPricing>;\n private readonly modelAliases: Map<string, string>;\n\n public constructor(options: StaticPricingSourceOptions) {\n this.pricingByModel = new Map(\n Object.entries(options.pricingByModel).map(([model, pricing]) => [\n normalizeKey(model),\n pricing,\n ]),\n );\n this.modelAliases = new Map(\n Object.entries(options.modelAliases ?? {}).map(([alias, target]) => [\n normalizeKey(alias),\n normalizeKey(target),\n ]),\n );\n }\n\n public resolveModelAlias(model: string): string {\n const normalizedModel = normalizeKey(model);\n return this.modelAliases.get(normalizedModel) ?? normalizedModel;\n }\n\n public getPricing(model: string): ModelPricing | undefined {\n const resolvedModel = this.resolveModelAlias(model);\n return this.pricingByModel.get(resolvedModel);\n }\n}\n\nexport function createDefaultOpenAiPricingSource(): StaticPricingSource {\n return new StaticPricingSource({\n pricingByModel: {\n 'gpt-5-codex': {\n inputPer1MUsd: 1.5,\n outputPer1MUsd: 10,\n cacheReadPer1MUsd: 0.15,\n },\n 'gpt-4.1': {\n inputPer1MUsd: 2,\n outputPer1MUsd: 8,\n cacheReadPer1MUsd: 0.5,\n },\n },\n modelAliases: {\n 'gpt-5.1-codex': 'gpt-5-codex',\n 'gpt-5.2-codex': 'gpt-5-codex',\n 'gpt-5.3-codex': 'gpt-5-codex',\n },\n });\n}\n","import { markdownTable } from 'markdown-table';\n\nimport type { UsageReportRow } from '../domain/usage-report-row.js';\nimport { toUsageTableCells, usageTableHeaders } from './row-cells.js';\n\nconst alignment: ('l' | 'r')[] = ['l', 'l', 'l', 'r', 'r', 'r', 'r', 'r', 'r', 'r'];\n\nexport function renderMarkdownTable(rows: UsageReportRow[]): string {\n const bodyRows = toUsageTableCells(rows);\n const tableRows = [Array.from(usageTableHeaders), ...bodyRows];\n\n return markdownTable(tableRows, {\n align: alignment,\n });\n}\n","import type { UsageReportRow } from '../domain/usage-report-row.js';\n\nexport const usageTableHeaders = [\n 'Period',\n 'Source',\n 'Models',\n 'Input',\n 'Output',\n 'Reasoning',\n 'Cache Read',\n 'Cache Write',\n 'Total Tokens',\n 'Cost (USD)',\n] as const;\n\nconst integerFormatter = new Intl.NumberFormat('en-US');\nconst usdFormatter = new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: 'USD',\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n});\n\nfunction formatSource(row: UsageReportRow): string {\n if (row.rowType === 'grand_total') {\n return 'TOTAL';\n }\n\n return row.source;\n}\n\nfunction formatModels(models: string[]): string {\n return models.length > 0 ? models.join(', ') : '-';\n}\n\nfunction formatTokenCount(value: number): string {\n return integerFormatter.format(value);\n}\n\nfunction formatUsd(value: number): string {\n return usdFormatter.format(value);\n}\n\nexport function toUsageTableCells(rows: UsageReportRow[]): string[][] {\n return rows.map((row) => [\n row.periodKey,\n formatSource(row),\n formatModels(row.models),\n formatTokenCount(row.inputTokens),\n formatTokenCount(row.outputTokens),\n formatTokenCount(row.reasoningTokens),\n formatTokenCount(row.cacheReadTokens),\n formatTokenCount(row.cacheWriteTokens),\n formatTokenCount(row.totalTokens),\n formatUsd(row.costUsd),\n ]);\n}\n","import pc from 'picocolors';\nimport { getBorderCharacters, table, type TableUserConfig } from 'table';\n\nimport type { UsageReportRow } from '../domain/usage-report-row.js';\nimport { toUsageTableCells, usageTableHeaders } from './row-cells.js';\n\nconst modelsColumnIndex = 2;\n\ntype TerminalRenderOptions = {\n useColor?: boolean;\n};\n\nfunction shouldDrawHorizontalLine(index: number, rowCount: number, rows: string[][]): boolean {\n if (index === 0 || index === 1 || index === rowCount) {\n return true;\n }\n\n const previousRow = rows[index - 1];\n const nextRow = rows[index];\n\n const previousSource = previousRow[1];\n const nextSource = nextRow[1];\n const previousPeriod = previousRow[0];\n const nextPeriod = nextRow[0];\n\n return previousSource === 'combined' || nextSource === 'TOTAL' || previousPeriod !== nextPeriod;\n}\n\nfunction createTableConfig(rows: string[][]): TableUserConfig {\n return {\n border: getBorderCharacters('norc'),\n drawHorizontalLine: (index, rowCount) => shouldDrawHorizontalLine(index, rowCount, rows),\n columnDefault: {\n paddingLeft: 1,\n paddingRight: 1,\n verticalAlignment: 'middle',\n },\n columns: {\n 0: { alignment: 'left' },\n 1: { alignment: 'left' },\n [modelsColumnIndex]: {\n alignment: 'left',\n width: 34,\n wrapWord: true,\n },\n 3: { alignment: 'right' },\n 4: { alignment: 'right' },\n 5: { alignment: 'right' },\n 6: { alignment: 'right' },\n 7: { alignment: 'right' },\n 8: { alignment: 'right' },\n 9: { alignment: 'right' },\n },\n };\n}\n\nfunction shouldUseColorByDefault(): boolean {\n if (process.env.NO_COLOR !== undefined) {\n return false;\n }\n\n if (process.env.FORCE_COLOR !== undefined && process.env.FORCE_COLOR !== '0') {\n return true;\n }\n\n return process.stdout.isTTY;\n}\n\nfunction colorSource(source: string): (text: string) => string {\n switch (source) {\n case 'pi':\n return pc.cyan;\n case 'codex':\n return pc.magenta;\n case 'combined':\n return pc.yellow;\n case 'TOTAL':\n return (text) => pc.bold(pc.green(text));\n default:\n return (text) => text;\n }\n}\n\nfunction colorizeBodyRows(\n bodyRows: string[][],\n rows: UsageReportRow[],\n useColor: boolean,\n): string[][] {\n if (!useColor) {\n return bodyRows;\n }\n\n return rows.map((row, index) => {\n const styledCells = [...(bodyRows[index] ?? [])];\n const sourceStyler = colorSource(styledCells[1] ?? row.source);\n\n styledCells[1] = sourceStyler(styledCells[1] ?? row.source);\n\n if (row.rowType === 'grand_total') {\n return styledCells.map((cell) => pc.bold(cell));\n }\n\n if (row.rowType === 'period_combined') {\n return styledCells.map((cell, cellIndex) => (cellIndex === 1 ? pc.bold(cell) : pc.dim(cell)));\n }\n\n return styledCells;\n });\n}\n\nfunction colorizeHeader(useColor: boolean): string[] {\n const headerCells = Array.from(usageTableHeaders);\n\n if (!useColor) {\n return headerCells;\n }\n\n return headerCells.map((header) => pc.bold(pc.white(header)));\n}\n\nexport function renderTerminalTable(\n rows: UsageReportRow[],\n options: TerminalRenderOptions = {},\n): string {\n const useColor = options.useColor ?? shouldUseColorByDefault();\n const bodyRows = colorizeBodyRows(toUsageTableCells(rows), rows, useColor);\n const displayRows = [colorizeHeader(useColor), ...bodyRows];\n\n return table(displayRows, createTableConfig(displayRows));\n}\n","import os from 'node:os';\nimport path from 'node:path';\n\nimport { createUsageEvent } from '../../domain/usage-event.js';\nimport type { UsageEvent } from '../../domain/usage-event.js';\nimport { normalizeNonNegativeInteger } from '../../domain/normalization.js';\nimport type { NumberLike } from '../../domain/normalization.js';\nimport { discoverJsonlFiles } from '../../utils/discover-jsonl-files.js';\nimport { readJsonlObjects } from '../../utils/read-jsonl-objects.js';\nimport type { SourceAdapter } from '../source-adapter.js';\n\nconst defaultSessionsDir = path.join(os.homedir(), '.codex', 'sessions');\n\nexport const LEGACY_CODEX_MODEL_FALLBACK = 'legacy-codex-unknown';\n\ntype CodexUsage = {\n inputTokens: number;\n cacheReadTokens: number;\n outputTokens: number;\n reasoningTokens: number;\n totalTokens: number;\n};\n\ntype CodexSessionState = {\n sessionId: string;\n provider?: string;\n model?: string;\n previousTotalUsage?: CodexUsage;\n};\n\nexport type CodexSourceAdapterOptions = {\n sessionsDir?: string;\n};\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n if (!value || typeof value !== 'object') {\n return undefined;\n }\n\n return value as Record<string, unknown>;\n}\n\nfunction asText(value: unknown): string | undefined {\n if (typeof value !== 'string') {\n return undefined;\n }\n\n const normalized = value.trim();\n return normalized || undefined;\n}\n\nfunction toUsage(value: unknown): CodexUsage | undefined {\n const usage = asRecord(value);\n\n if (!usage) {\n return undefined;\n }\n\n const rawInputTokens = normalizeNonNegativeInteger(usage.input_tokens as NumberLike);\n const cacheReadTokens = normalizeNonNegativeInteger(usage.cached_input_tokens as NumberLike);\n const outputTokens = normalizeNonNegativeInteger(usage.output_tokens as NumberLike);\n\n const inputTokens = Math.max(0, rawInputTokens - cacheReadTokens);\n\n return {\n // Codex input_tokens includes cached_input_tokens. We store net input separately\n // to avoid double counting input + cache read in reports and estimated pricing.\n inputTokens,\n cacheReadTokens,\n outputTokens,\n reasoningTokens: normalizeNonNegativeInteger(usage.reasoning_output_tokens as NumberLike),\n // Match ccusage semantics: billable total excludes reasoning breakdown.\n totalTokens: inputTokens + outputTokens + cacheReadTokens,\n };\n}\n\nfunction subtractUsage(current: CodexUsage, previous: CodexUsage): CodexUsage {\n return {\n inputTokens: Math.max(0, current.inputTokens - previous.inputTokens),\n cacheReadTokens: Math.max(0, current.cacheReadTokens - previous.cacheReadTokens),\n outputTokens: Math.max(0, current.outputTokens - previous.outputTokens),\n reasoningTokens: Math.max(0, current.reasoningTokens - previous.reasoningTokens),\n totalTokens: Math.max(0, current.totalTokens - previous.totalTokens),\n };\n}\n\nfunction addUsage(left: CodexUsage, right: CodexUsage): CodexUsage {\n return {\n inputTokens: left.inputTokens + right.inputTokens,\n cacheReadTokens: left.cacheReadTokens + right.cacheReadTokens,\n outputTokens: left.outputTokens + right.outputTokens,\n reasoningTokens: left.reasoningTokens + right.reasoningTokens,\n totalTokens: left.totalTokens + right.totalTokens,\n };\n}\n\nfunction hasUsageSignal(usage: CodexUsage): boolean {\n return (\n usage.inputTokens > 0 ||\n usage.cacheReadTokens > 0 ||\n usage.outputTokens > 0 ||\n usage.reasoningTokens > 0 ||\n usage.totalTokens > 0\n );\n}\n\nfunction deriveDeltaUsage(\n info: Record<string, unknown>,\n previousTotalUsage: CodexUsage | undefined,\n): { deltaUsage?: CodexUsage; latestTotalUsage?: CodexUsage } {\n const totalUsage = toUsage(info.total_token_usage);\n const lastUsage = toUsage(info.last_token_usage);\n\n if (lastUsage) {\n return { deltaUsage: lastUsage, latestTotalUsage: totalUsage };\n }\n\n if (!totalUsage) {\n return {};\n }\n\n const deltaUsage = previousTotalUsage\n ? subtractUsage(totalUsage, previousTotalUsage)\n : totalUsage;\n\n return { deltaUsage, latestTotalUsage: totalUsage };\n}\n\nfunction getFallbackSessionId(filePath: string): string {\n return path.basename(filePath, '.jsonl');\n}\n\nexport class CodexSourceAdapter implements SourceAdapter {\n public readonly id = 'codex' as const;\n\n private readonly sessionsDir: string;\n\n public constructor(options: CodexSourceAdapterOptions = {}) {\n this.sessionsDir = options.sessionsDir ?? defaultSessionsDir;\n }\n\n public async discoverFiles(): Promise<string[]> {\n return discoverJsonlFiles(this.sessionsDir);\n }\n\n public async parseFile(filePath: string): Promise<UsageEvent[]> {\n const events: UsageEvent[] = [];\n\n const state: CodexSessionState = {\n sessionId: getFallbackSessionId(filePath),\n provider: 'openai',\n };\n\n for await (const line of readJsonlObjects(filePath)) {\n if (line.type === 'session_meta') {\n const payload = asRecord(line.payload);\n state.sessionId = asText(payload?.id) ?? state.sessionId;\n state.provider = asText(payload?.model_provider) ?? state.provider;\n continue;\n }\n\n if (line.type === 'turn_context') {\n const payload = asRecord(line.payload);\n state.model = asText(payload?.model) ?? state.model;\n continue;\n }\n\n if (line.type !== 'event_msg') {\n continue;\n }\n\n const payload = asRecord(line.payload);\n\n if (payload?.type !== 'token_count') {\n continue;\n }\n\n const info = asRecord(payload.info);\n\n if (!info) {\n continue;\n }\n\n const { deltaUsage, latestTotalUsage } = deriveDeltaUsage(info, state.previousTotalUsage);\n\n if (!deltaUsage || !hasUsageSignal(deltaUsage)) {\n state.previousTotalUsage = latestTotalUsage ?? state.previousTotalUsage;\n continue;\n }\n\n const timestamp = asText(line.timestamp);\n\n if (!timestamp) {\n state.previousTotalUsage = latestTotalUsage ?? state.previousTotalUsage;\n continue;\n }\n\n const model = state.model ?? LEGACY_CODEX_MODEL_FALLBACK;\n\n try {\n events.push(\n createUsageEvent({\n source: this.id,\n sessionId: state.sessionId,\n timestamp,\n provider: state.provider,\n model,\n inputTokens: deltaUsage.inputTokens,\n outputTokens: deltaUsage.outputTokens,\n reasoningTokens: deltaUsage.reasoningTokens,\n cacheReadTokens: deltaUsage.cacheReadTokens,\n cacheWriteTokens: 0,\n totalTokens: deltaUsage.totalTokens,\n costMode: 'estimated',\n }),\n );\n } catch {\n // no-op: malformed lines are ignored by design\n }\n\n if (latestTotalUsage) {\n state.previousTotalUsage = latestTotalUsage;\n } else if (state.previousTotalUsage) {\n state.previousTotalUsage = addUsage(state.previousTotalUsage, deltaUsage);\n } else {\n state.previousTotalUsage = deltaUsage;\n }\n }\n\n return events;\n }\n}\n\nexport function getDefaultCodexSessionsDir(): string {\n return defaultSessionsDir;\n}\n","import {\n normalizeNonNegativeInteger,\n normalizeTimestamp,\n normalizeUsdCost,\n type NumberLike,\n} from './normalization.js';\n\nexport type SourceId = 'pi' | 'codex' | (string & {});\n\nexport type CostMode = 'explicit' | 'estimated';\n\nexport type UsageEvent = {\n source: SourceId;\n sessionId: string;\n timestamp: string;\n provider?: string;\n model?: string;\n\n inputTokens: number;\n outputTokens: number;\n reasoningTokens: number;\n cacheReadTokens: number;\n cacheWriteTokens: number;\n totalTokens: number;\n\n costUsd?: number;\n costMode: CostMode;\n};\n\nexport type UsageEventInput = {\n source: SourceId;\n sessionId: string;\n timestamp: string | Date;\n provider?: string;\n model?: string;\n\n inputTokens?: NumberLike;\n outputTokens?: NumberLike;\n reasoningTokens?: NumberLike;\n cacheReadTokens?: NumberLike;\n cacheWriteTokens?: NumberLike;\n totalTokens?: NumberLike;\n\n costUsd?: NumberLike;\n costMode?: CostMode;\n};\n\nfunction requireText(value: string, fieldName: string): string {\n const normalized = value.trim();\n\n if (!normalized) {\n throw new Error(`UsageEvent ${fieldName} must be a non-empty string`);\n }\n\n return normalized;\n}\n\nfunction normalizeOptionalText(value: string | undefined): string | undefined {\n if (!value) {\n return undefined;\n }\n\n const normalized = value.trim();\n return normalized || undefined;\n}\n\nfunction resolveCostMode(costMode: CostMode | undefined, costUsd: number | undefined): CostMode {\n if (costMode === 'explicit' && costUsd === undefined) {\n throw new Error('UsageEvent with costMode \"explicit\" requires costUsd');\n }\n\n if (costMode) {\n return costMode;\n }\n\n return costUsd === undefined ? 'estimated' : 'explicit';\n}\n\nexport function createUsageEvent(input: UsageEventInput): UsageEvent {\n const inputTokens = normalizeNonNegativeInteger(input.inputTokens);\n const outputTokens = normalizeNonNegativeInteger(input.outputTokens);\n const reasoningTokens = normalizeNonNegativeInteger(input.reasoningTokens);\n const cacheReadTokens = normalizeNonNegativeInteger(input.cacheReadTokens);\n const cacheWriteTokens = normalizeNonNegativeInteger(input.cacheWriteTokens);\n const declaredTotalTokens = normalizeNonNegativeInteger(input.totalTokens);\n const componentTotalTokens =\n inputTokens + outputTokens + reasoningTokens + cacheReadTokens + cacheWriteTokens;\n const totalTokens = declaredTotalTokens > 0 ? declaredTotalTokens : componentTotalTokens;\n\n const costUsd = normalizeUsdCost(input.costUsd);\n const costMode = resolveCostMode(input.costMode, costUsd);\n\n return {\n source: requireText(input.source, 'source') as SourceId,\n sessionId: requireText(input.sessionId, 'sessionId'),\n timestamp: normalizeTimestamp(input.timestamp),\n provider: normalizeOptionalText(input.provider),\n model: normalizeOptionalText(input.model),\n inputTokens,\n outputTokens,\n reasoningTokens,\n cacheReadTokens,\n cacheWriteTokens,\n totalTokens,\n costUsd,\n costMode,\n };\n}\n","import { readdir } from 'node:fs/promises';\nimport path from 'node:path';\n\nasync function walkDirectory(rootDir: string, acc: string[]): Promise<void> {\n const entries = await readdir(rootDir, { withFileTypes: true });\n entries.sort((left, right) => left.name.localeCompare(right.name));\n\n for (const entry of entries) {\n const entryPath = path.join(rootDir, entry.name);\n\n if (entry.isDirectory()) {\n await walkDirectory(entryPath, acc);\n continue;\n }\n\n if (entry.isFile() && entry.name.endsWith('.jsonl')) {\n acc.push(entryPath);\n }\n }\n}\n\nexport async function discoverJsonlFiles(rootDir: string): Promise<string[]> {\n const files: string[] = [];\n\n try {\n await walkDirectory(rootDir, files);\n } catch (error) {\n const nodeError = error as NodeJS.ErrnoException;\n\n if (nodeError.code === 'ENOENT') {\n return [];\n }\n\n throw error;\n }\n\n return files;\n}\n","import { createReadStream } from 'node:fs';\nimport { createInterface } from 'node:readline';\n\nfunction asObject(value: unknown): Record<string, unknown> | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return undefined;\n }\n\n return value as Record<string, unknown>;\n}\n\nexport async function* readJsonlObjects(\n filePath: string,\n): AsyncGenerator<Record<string, unknown>, void, undefined> {\n const stream = createReadStream(filePath, {\n encoding: 'utf8',\n });\n const lineReader = createInterface({\n input: stream,\n crlfDelay: Infinity,\n });\n\n let isFirstLine = true;\n\n try {\n for await (const rawLine of lineReader) {\n const normalizedLine = isFirstLine ? rawLine.replace(/^\\uFEFF/u, '') : rawLine;\n isFirstLine = false;\n\n const lineText = normalizedLine.trim();\n\n if (!lineText) {\n continue;\n }\n\n let parsed: unknown;\n\n try {\n parsed = JSON.parse(lineText);\n } catch {\n continue;\n }\n\n const parsedObject = asObject(parsed);\n\n if (!parsedObject) {\n continue;\n }\n\n yield parsedObject;\n }\n } finally {\n lineReader.close();\n stream.destroy();\n }\n}\n","import os from 'node:os';\nimport path from 'node:path';\n\nimport { createUsageEvent } from '../../domain/usage-event.js';\nimport type { UsageEvent } from '../../domain/usage-event.js';\nimport type { NumberLike } from '../../domain/normalization.js';\nimport { discoverJsonlFiles } from '../../utils/discover-jsonl-files.js';\nimport { readJsonlObjects } from '../../utils/read-jsonl-objects.js';\nimport type { SourceAdapter } from '../source-adapter.js';\n\nconst defaultSessionsDir = path.join(os.homedir(), '.pi', 'agent', 'sessions');\n\ntype ProviderFilter = (provider: string | undefined) => boolean;\n\ntype PiSessionState = {\n sessionId?: string;\n sessionTimestamp?: string;\n provider?: string;\n model?: string;\n};\n\ntype PiUsageExtract = {\n inputTokens?: NumberLike;\n outputTokens?: NumberLike;\n reasoningTokens?: NumberLike;\n cacheReadTokens?: NumberLike;\n cacheWriteTokens?: NumberLike;\n totalTokens?: NumberLike;\n costUsd?: NumberLike;\n};\n\nexport type PiSourceAdapterOptions = {\n sessionsDir?: string;\n providerFilter?: ProviderFilter;\n};\n\nexport function isOpenAiProvider(provider: string | undefined): boolean {\n return provider?.toLowerCase().includes('openai') ?? false;\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n if (!value || typeof value !== 'object') {\n return undefined;\n }\n\n return value as Record<string, unknown>;\n}\n\nfunction asText(value: unknown): string | undefined {\n if (typeof value !== 'string') {\n return undefined;\n }\n\n const normalized = value.trim();\n return normalized || undefined;\n}\n\nfunction asNumberLike(value: unknown): NumberLike {\n if (\n value === null ||\n value === undefined ||\n typeof value === 'number' ||\n typeof value === 'string'\n ) {\n return value;\n }\n\n return undefined;\n}\n\nfunction resolveTimestamp(\n line: Record<string, unknown>,\n message: Record<string, unknown> | undefined,\n state: PiSessionState,\n): string | undefined {\n const candidates = [line.timestamp, message?.timestamp, state.sessionTimestamp];\n\n for (const candidate of candidates) {\n if (typeof candidate === 'number' && Number.isFinite(candidate)) {\n const timestampMs = Math.abs(candidate) < 1_000_000_000_000 ? candidate * 1000 : candidate;\n const date = new Date(timestampMs);\n\n if (!Number.isNaN(date.getTime())) {\n return date.toISOString();\n }\n\n continue;\n }\n\n if (typeof candidate === 'string' && candidate.trim()) {\n return candidate;\n }\n }\n\n return undefined;\n}\n\nfunction extractUsage(line: Record<string, unknown>, message: Record<string, unknown> | undefined) {\n const usage = asRecord(line.usage) ?? asRecord(message?.usage);\n\n if (!usage) {\n return undefined;\n }\n\n const cost = asRecord(usage.cost);\n\n const extracted: PiUsageExtract = {\n inputTokens: asNumberLike(usage.input),\n outputTokens: asNumberLike(usage.output),\n reasoningTokens: asNumberLike(\n usage.reasoning ?? usage.reasoningTokens ?? usage.reasoningOutput ?? usage.outputReasoning,\n ),\n cacheReadTokens: asNumberLike(usage.cacheRead),\n cacheWriteTokens: asNumberLike(usage.cacheWrite),\n totalTokens: asNumberLike(usage.totalTokens),\n costUsd: asNumberLike(cost?.total),\n };\n\n const hasKnownUsageField =\n extracted.inputTokens !== undefined ||\n extracted.outputTokens !== undefined ||\n extracted.reasoningTokens !== undefined ||\n extracted.cacheReadTokens !== undefined ||\n extracted.cacheWriteTokens !== undefined ||\n extracted.totalTokens !== undefined ||\n extracted.costUsd !== undefined;\n\n return hasKnownUsageField ? extracted : undefined;\n}\n\nfunction getFallbackSessionId(filePath: string): string {\n return path.basename(filePath, '.jsonl');\n}\n\nexport class PiSourceAdapter implements SourceAdapter {\n public readonly id = 'pi' as const;\n\n private readonly sessionsDir: string;\n private readonly providerFilter: ProviderFilter;\n\n public constructor(options: PiSourceAdapterOptions = {}) {\n this.sessionsDir = options.sessionsDir ?? defaultSessionsDir;\n this.providerFilter = options.providerFilter ?? isOpenAiProvider;\n }\n\n public async discoverFiles(): Promise<string[]> {\n return discoverJsonlFiles(this.sessionsDir);\n }\n\n public async parseFile(filePath: string): Promise<UsageEvent[]> {\n const events: UsageEvent[] = [];\n const state: PiSessionState = { sessionId: getFallbackSessionId(filePath) };\n\n for await (const line of readJsonlObjects(filePath)) {\n if (line.type === 'session') {\n state.sessionId = asText(line.id) ?? state.sessionId;\n state.sessionTimestamp = asText(line.timestamp) ?? state.sessionTimestamp;\n continue;\n }\n\n if (line.type === 'model_change') {\n state.provider = asText(line.provider) ?? state.provider;\n state.model = asText(line.modelId) ?? asText(line.model) ?? state.model;\n continue;\n }\n\n if (line.type !== 'message') {\n continue;\n }\n\n const message = asRecord(line.message);\n const usage = extractUsage(line, message);\n\n if (!usage) {\n continue;\n }\n\n const provider = asText(line.provider) ?? asText(message?.provider) ?? state.provider;\n\n if (!this.providerFilter(provider)) {\n continue;\n }\n\n const timestamp = resolveTimestamp(line, message, state);\n\n if (!timestamp || !state.sessionId) {\n continue;\n }\n\n const model =\n asText(line.model) ?? asText(line.modelId) ?? asText(message?.model) ?? state.model;\n\n try {\n events.push(\n createUsageEvent({\n source: this.id,\n sessionId: state.sessionId,\n timestamp,\n provider,\n model,\n ...usage,\n }),\n );\n } catch {\n continue;\n }\n }\n\n return events;\n }\n}\n\nexport function getDefaultPiSessionsDir(): string {\n return defaultSessionsDir;\n}\n","import { aggregateUsage } from '../aggregate/aggregate-usage.js';\nimport type { UsageEvent } from '../domain/usage-event.js';\nimport { applyPricingToEvents } from '../pricing/cost-engine.js';\nimport { LiteLLMPricingFetcher } from '../pricing/litellm-pricing-fetcher.js';\nimport { createDefaultOpenAiPricingSource } from '../pricing/static-pricing-source.js';\nimport type { PricingSource } from '../pricing/types.js';\nimport { renderMarkdownTable } from '../render/markdown-table.js';\nimport { renderTerminalTable } from '../render/terminal-table.js';\nimport { CodexSourceAdapter } from '../sources/codex/codex-source-adapter.js';\nimport { PiSourceAdapter } from '../sources/pi/pi-source-adapter.js';\nimport type { SourceAdapter } from '../sources/source-adapter.js';\nimport { getPeriodKey, type ReportGranularity } from '../utils/time-buckets.js';\n\nconst MAX_PARALLEL_FILE_PARSING = 8;\n\nexport type ReportCommandOptions = {\n piDir?: string;\n codexDir?: string;\n source?: string | string[];\n since?: string;\n until?: string;\n timezone?: string;\n provider?: string;\n markdown?: boolean;\n json?: boolean;\n pricingUrl?: string;\n pricingOffline?: boolean;\n};\n\nfunction validateDateInput(value: string, flagName: '--since' | '--until'): void {\n if (!/^\\d{4}-\\d{2}-\\d{2}$/u.test(value)) {\n throw new Error(`${flagName} must use format YYYY-MM-DD`);\n }\n\n const parsed = new Date(`${value}T00:00:00.000Z`);\n\n if (Number.isNaN(parsed.getTime()) || parsed.toISOString().slice(0, 10) !== value) {\n throw new Error(`${flagName} has an invalid calendar date`);\n }\n}\n\nfunction validateTimezone(timezone: string): void {\n try {\n new Intl.DateTimeFormat('en-US', { timeZone: timezone }).format(new Date());\n } catch {\n throw new Error(`Invalid timezone: ${timezone}`);\n }\n}\n\nfunction normalizeProviderFilter(provider: string | undefined): string | undefined {\n if (!provider) {\n return undefined;\n }\n\n const normalized = provider.trim().toLowerCase();\n return normalized || undefined;\n}\n\nfunction normalizeSourceFilter(source: string | string[] | undefined): Set<string> | undefined {\n if (!source || (Array.isArray(source) && source.length === 0)) {\n return undefined;\n }\n\n const sourceCandidates = Array.isArray(source) ? source : [source];\n const normalizedSources = sourceCandidates\n .flatMap((candidate) => candidate.split(','))\n .map((candidate) => candidate.trim().toLowerCase())\n .filter((candidate) => candidate.length > 0);\n\n if (normalizedSources.length === 0) {\n throw new Error('--source must contain at least one non-empty source id');\n }\n\n return new Set(normalizedSources);\n}\n\nfunction validateSourceFilterValues(\n sourceFilter: Set<string> | undefined,\n availableSourceIds: ReadonlySet<string>,\n): void {\n if (!sourceFilter) {\n return;\n }\n\n const unknownSources = [...sourceFilter].filter((source) => !availableSourceIds.has(source));\n\n if (unknownSources.length === 0) {\n return;\n }\n\n const allowedSources = [...availableSourceIds].sort((left, right) => left.localeCompare(right));\n\n throw new Error(\n `Unknown --source value(s): ${unknownSources.join(', ')}. Allowed values: ${allowedSources.join(', ')}`,\n );\n}\n\nfunction matchesProvider(\n provider: string | undefined,\n providerFilter: string | undefined,\n): boolean {\n if (!providerFilter) {\n return true;\n }\n\n return provider?.toLowerCase().includes(providerFilter) ?? false;\n}\n\nasync function parseAdapterEvents(adapter: SourceAdapter): Promise<UsageEvent[]> {\n const files = await adapter.discoverFiles();\n\n if (files.length === 0) {\n return [];\n }\n\n const parsedByFile: UsageEvent[][] = Array.from({ length: files.length }, () => []);\n const workerCount = Math.min(MAX_PARALLEL_FILE_PARSING, files.length);\n let nextFileIndex = 0;\n\n const workers = Array.from({ length: workerCount }, async () => {\n while (nextFileIndex < files.length) {\n const fileIndex = nextFileIndex;\n nextFileIndex += 1;\n\n const filePath = files[fileIndex];\n\n if (!filePath) {\n break;\n }\n\n parsedByFile[fileIndex] = await adapter.parseFile(filePath);\n }\n });\n\n await Promise.all(workers);\n\n return parsedByFile.flat();\n}\n\nfunction filterEventsByDateRange(\n events: UsageEvent[],\n timezone: string,\n since: string | undefined,\n until: string | undefined,\n): UsageEvent[] {\n return events.filter((event) => {\n const eventDate = getPeriodKey(event.timestamp, 'daily', timezone);\n\n if (since && eventDate < since) {\n return false;\n }\n\n if (until && eventDate > until) {\n return false;\n }\n\n return true;\n });\n}\n\nfunction validatePricingUrl(pricingUrl: string | undefined): void {\n if (!pricingUrl) {\n return;\n }\n\n try {\n const parsedUrl = new URL(pricingUrl);\n\n if (!['http:', 'https:'].includes(parsedUrl.protocol)) {\n throw new Error('Unsupported protocol');\n }\n } catch {\n throw new Error('--pricing-url must be a valid http(s) URL');\n }\n}\n\nasync function resolvePricingSource(options: ReportCommandOptions): Promise<PricingSource> {\n const fallbackPricingSource = createDefaultOpenAiPricingSource();\n const litellmPricingFetcher = new LiteLLMPricingFetcher({\n sourceUrl: options.pricingUrl,\n offline: options.pricingOffline,\n });\n\n try {\n await litellmPricingFetcher.load();\n return litellmPricingFetcher;\n } catch (error) {\n if (options.pricingOffline) {\n throw new Error('Offline pricing mode enabled but cached pricing is unavailable');\n }\n\n if (options.pricingUrl) {\n const reason = error instanceof Error ? error.message : String(error);\n throw new Error(`Could not load pricing from --pricing-url: ${reason}`);\n }\n\n return fallbackPricingSource;\n }\n}\n\nfunction eventNeedsPricingLookup(event: UsageEvent): boolean {\n if (!event.model) {\n return false;\n }\n\n return event.costMode !== 'explicit' || event.costUsd === undefined;\n}\n\nfunction shouldLoadPricingSource(options: ReportCommandOptions, events: UsageEvent[]): boolean {\n if (options.pricingUrl || options.pricingOffline) {\n return true;\n }\n\n return events.some((event) => eventNeedsPricingLookup(event));\n}\n\nexport async function buildUsageReport(\n granularity: ReportGranularity,\n options: ReportCommandOptions,\n): Promise<string> {\n if (options.markdown && options.json) {\n throw new Error('Choose either --markdown or --json, not both');\n }\n\n if (options.since) {\n validateDateInput(options.since, '--since');\n }\n\n if (options.until) {\n validateDateInput(options.until, '--until');\n }\n\n if (options.since && options.until && options.since > options.until) {\n throw new Error('--since must be less than or equal to --until');\n }\n\n validatePricingUrl(options.pricingUrl);\n\n const timezone = options.timezone ?? Intl.DateTimeFormat().resolvedOptions().timeZone;\n validateTimezone(timezone);\n\n const providerFilter = normalizeProviderFilter(options.provider);\n const sourceFilter = normalizeSourceFilter(options.source);\n const effectiveProviderFilter = providerFilter ?? 'openai';\n\n const adapters: SourceAdapter[] = [\n new PiSourceAdapter({\n sessionsDir: options.piDir,\n providerFilter: (provider) => matchesProvider(provider, effectiveProviderFilter),\n }),\n new CodexSourceAdapter({\n sessionsDir: options.codexDir,\n }),\n ];\n\n const availableSourceIds = new Set(adapters.map((adapter) => adapter.id.toLowerCase()));\n validateSourceFilterValues(sourceFilter, availableSourceIds);\n\n const adaptersToParse = sourceFilter\n ? adapters.filter((adapter) => sourceFilter.has(adapter.id.toLowerCase()))\n : adapters;\n\n const parsedEventsByAdapter = await Promise.all(\n adaptersToParse.map((adapter) => parseAdapterEvents(adapter)),\n );\n const providerFilteredEvents = parsedEventsByAdapter\n .flat()\n .filter((event) => matchesProvider(event.provider, effectiveProviderFilter));\n\n const dateFilteredEvents = filterEventsByDateRange(\n providerFilteredEvents,\n timezone,\n options.since,\n options.until,\n );\n\n const pricedEvents = shouldLoadPricingSource(options, dateFilteredEvents)\n ? applyPricingToEvents(dateFilteredEvents, await resolvePricingSource(options))\n : dateFilteredEvents;\n\n const rows = aggregateUsage(pricedEvents, {\n granularity,\n timezone,\n });\n\n if (options.json) {\n return JSON.stringify(rows, null, 2);\n }\n\n if (options.markdown) {\n return renderMarkdownTable(rows);\n }\n\n return renderTerminalTable(rows);\n}\n\nexport async function runUsageReport(\n granularity: ReportGranularity,\n options: ReportCommandOptions,\n): Promise<void> {\n const output = await buildUsageReport(granularity, options);\n console.log(output);\n}\n","#!/usr/bin/env node\n\nimport { createCli } from './create-cli.js';\n\nconst cli = createCli();\n\ntry {\n await cli.parseAsync(process.argv);\n} catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(message);\n process.exitCode = 1;\n}\n"],"mappings":";;;AAAA,SAAS,eAAe;;;ACEjB,SAAS,4BAA4B,OAA2B;AACrE,MAAI,UAAU,QAAQ,UAAU,QAAW;AACzC,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,OAAO,UAAU,WAAW,QAAQ,OAAO,KAAK;AAE/D,MAAI,CAAC,OAAO,SAAS,MAAM,GAAG;AAC5B,WAAO;AAAA,EACT;AAEA,SAAO,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,CAAC;AACvC;AAEO,SAAS,iBAAiB,OAAuC;AACtE,MAAI,UAAU,QAAQ,UAAU,QAAW;AACzC,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM,IAAI;AACpD,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,OAAO,UAAU,WAAW,QAAQ,OAAO,KAAK;AAE/D,MAAI,CAAC,OAAO,SAAS,MAAM,GAAG;AAC5B,WAAO;AAAA,EACT;AAEA,SAAO,KAAK,IAAI,GAAG,MAAM;AAC3B;AAEO,SAAS,mBAAmB,OAA8B;AAC/D,QAAM,OAAO,iBAAiB,OAAO,QAAQ,IAAI,KAAK,KAAK;AAE3D,MAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,GAAG;AAChC,UAAM,IAAI,MAAM,sBAAsB,OAAO,KAAK,CAAC,EAAE;AAAA,EACvD;AAEA,SAAO,KAAK,YAAY;AAC1B;AAEO,SAAS,mBAAmB,QAAuD;AACxF,QAAM,eAAe,oBAAI,IAAY;AAErC,aAAW,SAAS,QAAQ;AAC1B,QAAI,CAAC,OAAO;AACV;AAAA,IACF;AAEA,UAAM,aAAa,MAAM,KAAK;AAE9B,QAAI,CAAC,YAAY;AACf;AAAA,IACF;AAEA,iBAAa,IAAI,UAAU;AAAA,EAC7B;AAEA,SAAO,CAAC,GAAG,YAAY,EAAE,KAAK,CAAC,MAAM,UAAU,KAAK,cAAc,KAAK,CAAC;AAC1E;;;ACtDA,IAAM,iBAAiB,oBAAI,IAAiC;AAE5D,SAAS,iBAAiB,UAAuC;AAC/D,QAAM,kBAAkB,eAAe,IAAI,QAAQ;AAEnD,MAAI,iBAAiB;AACnB,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,IAAI,KAAK,eAAe,SAAS;AAAA,IACjD,UAAU;AAAA,IACV,MAAM;AAAA,IACN,OAAO;AAAA,IACP,KAAK;AAAA,EACP,CAAC;AACD,iBAAe,IAAI,UAAU,SAAS;AACtC,SAAO;AACT;AAEA,SAAS,sBAAsB,cAAsB,UAAkC;AACrF,QAAM,OAAO,IAAI,KAAK,YAAY;AAElC,MAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,GAAG;AAChC,UAAM,IAAI,MAAM,4BAA4B,YAAY,EAAE;AAAA,EAC5D;AAEA,QAAM,YAAY,iBAAiB,QAAQ;AAC3C,QAAM,QAAQ,UAAU,cAAc,IAAI;AAE1C,QAAM,OAAO,OAAO,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,MAAM,GAAG,KAAK;AACrE,QAAM,QAAQ,OAAO,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,OAAO,GAAG,KAAK;AACvE,QAAM,MAAM,OAAO,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,KAAK,GAAG,KAAK;AAEnE,MAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK;AAC3B,UAAM,IAAI,MAAM,qDAAqD,YAAY,EAAE;AAAA,EACrF;AAEA,SAAO,EAAE,MAAM,OAAO,IAAI;AAC5B;AAEA,SAAS,cAAc,WAAiC;AACtD,SAAO,IAAI,KAAK,KAAK,IAAI,UAAU,MAAM,UAAU,QAAQ,GAAG,UAAU,GAAG,CAAC;AAC9E;AAEA,SAAS,QAAQ,MAAY,MAAoB;AAC/C,SAAO,IAAI,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,KAAK,GAAI;AAC7D;AAEA,SAAS,eAAe,MAAoB;AAC1C,QAAM,SAAS,KAAK,UAAU;AAC9B,SAAO,WAAW,IAAI,IAAI;AAC5B;AAEA,SAAS,gBAAgB,WAAqE;AAC5F,QAAM,eAAe,cAAc,SAAS;AAC5C,QAAM,SAAS,eAAe,YAAY;AAE1C,QAAM,oBAAoB,QAAQ,cAAc,EAAE,SAAS,EAAE;AAC7D,QAAM,sBAAsB,QAAQ,cAAc,IAAI,MAAM;AAC5D,QAAM,WAAW,oBAAoB,eAAe;AAEpD,QAAM,OAAO,IAAI,KAAK,KAAK,IAAI,UAAU,GAAG,CAAC,CAAC;AAC9C,QAAM,aAAa,eAAe,IAAI;AACtC,QAAM,kBAAkB,QAAQ,MAAM,EAAE,aAAa,EAAE;AAEvD,QAAM,SAAS,kBAAkB,QAAQ,IAAI,gBAAgB,QAAQ;AACrE,QAAM,aAAa,KAAK,MAAM,UAAU,IAAI,KAAK,KAAK,KAAK,IAAK,IAAI;AAEpE,SAAO,EAAE,UAAU,WAAW;AAChC;AAEO,SAAS,aACd,cACA,aACA,UACQ;AACR,QAAM,YAAY,sBAAsB,cAAc,QAAQ;AAE9D,MAAI,gBAAgB,SAAS;AAC3B,WAAO,GAAG,UAAU,IAAI,IAAI,OAAO,UAAU,KAAK,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,OAAO,UAAU,GAAG,EAAE,SAAS,GAAG,GAAG,CAAC;AAAA,EAChH;AAEA,MAAI,gBAAgB,WAAW;AAC7B,WAAO,GAAG,UAAU,IAAI,IAAI,OAAO,UAAU,KAAK,EAAE,SAAS,GAAG,GAAG,CAAC;AAAA,EACtE;AAEA,QAAM,UAAU,gBAAgB,SAAS;AACzC,SAAO,GAAG,QAAQ,QAAQ,KAAK,OAAO,QAAQ,UAAU,EAAE,SAAS,GAAG,GAAG,CAAC;AAC5E;;;AC3EA,IAAM,sBAAsB;AAE5B,SAAS,OAAO,MAAc,OAAuB;AACnD,SAAO,KAAK,OAAO,OAAO,SAAS,mBAAmB,IAAI;AAC5D;AAEA,SAAS,oBAAiC;AACxC,SAAO;AAAA,IACL,aAAa;AAAA,IACb,cAAc;AAAA,IACd,iBAAiB;AAAA,IACjB,iBAAiB;AAAA,IACjB,kBAAkB;AAAA,IAClB,aAAa;AAAA,IACb,SAAS;AAAA,EACX;AACF;AAEA,SAAS,uBAAuC;AAC9C,SAAO;AAAA,IACL,QAAQ,kBAAkB;AAAA,IAC1B,UAAU,oBAAI,IAAY;AAAA,EAC5B;AACF;AAEA,SAAS,sBAAsB,aAA6B,OAAyB;AACnF,cAAY,OAAO,eAAe,MAAM;AACxC,cAAY,OAAO,gBAAgB,MAAM;AACzC,cAAY,OAAO,mBAAmB,MAAM;AAC5C,cAAY,OAAO,mBAAmB,MAAM;AAC5C,cAAY,OAAO,oBAAoB,MAAM;AAC7C,cAAY,OAAO,eAAe,MAAM;AACxC,cAAY,OAAO,UAAU,OAAO,YAAY,OAAO,SAAS,MAAM,WAAW,CAAC;AAElF,MAAI,MAAM,OAAO;AACf,gBAAY,SAAS,IAAI,MAAM,KAAK;AAAA,EACtC;AACF;AAEA,SAAS,UAAU,QAAqB,QAA2B;AACjE,SAAO,eAAe,OAAO;AAC7B,SAAO,gBAAgB,OAAO;AAC9B,SAAO,mBAAmB,OAAO;AACjC,SAAO,mBAAmB,OAAO;AACjC,SAAO,oBAAoB,OAAO;AAClC,SAAO,eAAe,OAAO;AAC7B,SAAO,UAAU,OAAO,OAAO,SAAS,OAAO,OAAO;AACxD;AAEA,SAAS,qBAAqB,MAAc,OAAuB;AACjE,QAAM,cAAsC;AAAA,IAC1C,IAAI;AAAA,IACJ,OAAO;AAAA,EACT;AAEA,QAAM,aAAa,YAAY,IAAI,KAAK,OAAO;AAC/C,QAAM,cAAc,YAAY,KAAK,KAAK,OAAO;AAEjD,MAAI,eAAe,aAAa;AAC9B,WAAO,aAAa;AAAA,EACtB;AAEA,SAAO,KAAK,cAAc,KAAK;AACjC;AAEO,SAAS,eACd,QACA,SACkB;AAClB,QAAM,YAAY,oBAAI,IAAyC;AAE/D,aAAW,SAAS,QAAQ;AAC1B,UAAM,YAAY,aAAa,MAAM,WAAW,QAAQ,aAAa,QAAQ,QAAQ;AACrF,UAAM,gBAAgB,UAAU,IAAI,SAAS,KAAK,oBAAI,IAA4B;AAClF,cAAU,IAAI,WAAW,aAAa;AAEtC,UAAM,iBAAiB,cAAc,IAAI,MAAM,MAAM,KAAK,qBAAqB;AAC/E,kBAAc,IAAI,MAAM,QAAQ,cAAc;AAE9C,0BAAsB,gBAAgB,KAAK;AAAA,EAC7C;AAEA,QAAM,mBAAmB,CAAC,GAAG,UAAU,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,UAAU,KAAK,cAAc,KAAK,CAAC;AAC9F,QAAM,OAAyB,CAAC;AAChC,QAAM,cAAc,kBAAkB;AACtC,QAAM,cAAc,oBAAI,IAAY;AAEpC,aAAW,aAAa,kBAAkB;AACxC,UAAM,YAAY,UAAU,IAAI,SAAS;AAEzC,QAAI,CAAC,WAAW;AACd;AAAA,IACF;AAEA,UAAM,uBAAuB,kBAAkB;AAC/C,UAAM,eAAe,oBAAI,IAAY;AAErC,UAAM,gBAAgB,CAAC,GAAG,UAAU,KAAK,CAAC,EAAE,KAAK,oBAAoB;AAErE,eAAW,UAAU,eAAe;AAClC,YAAM,cAAc,UAAU,IAAI,MAAM;AAExC,UAAI,CAAC,aAAa;AAChB;AAAA,MACF;AAEA,YAAM,YAA6B;AAAA,QACjC,SAAS;AAAA,QACT;AAAA,QACA;AAAA,QACA,QAAQ,mBAAmB,YAAY,QAAQ;AAAA,QAC/C,GAAG,YAAY;AAAA,MACjB;AAEA,WAAK,KAAK,SAAS;AAEnB,gBAAU,sBAAsB,YAAY,MAAM;AAClD,gBAAU,aAAa,YAAY,MAAM;AAEzC,iBAAW,SAAS,YAAY,UAAU;AACxC,qBAAa,IAAI,KAAK;AACtB,oBAAY,IAAI,KAAK;AAAA,MACvB;AAAA,IACF;AAEA,QAAI,cAAc,SAAS,GAAG;AAC5B,YAAM,cAAiC;AAAA,QACrC,SAAS;AAAA,QACT;AAAA,QACA,QAAQ;AAAA,QACR,QAAQ,mBAAmB,YAAY;AAAA,QACvC,GAAG;AAAA,MACL;AAEA,WAAK,KAAK,WAAW;AAAA,IACvB;AAAA,EACF;AAEA,QAAM,gBAA+B;AAAA,IACnC,SAAS;AAAA,IACT,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,QAAQ,mBAAmB,WAAW;AAAA,IACtC,GAAG;AAAA,EACL;AAEA,OAAK,KAAK,aAAa;AAEvB,SAAO;AACT;;;ACvKA,IAAM,cAAc;AAEpB,SAAS,uBAAuB,QAAgB,UAAsC;AACpF,MAAI,CAAC,YAAY,UAAU,GAAG;AAC5B,WAAO;AAAA,EACT;AAEA,SAAQ,SAAS,cAAe;AAClC;AAEO,SAAS,0BAA0B,OAAmB,SAA+B;AAC1F,QAAM,mBAAmB,QAAQ,oBAAoB;AAErD,QAAM,YAAY,uBAAuB,MAAM,aAAa,QAAQ,aAAa;AACjF,QAAM,aAAa,uBAAuB,MAAM,cAAc,QAAQ,cAAc;AACpF,QAAM,gBAAgB,uBAAuB,MAAM,iBAAiB,QAAQ,iBAAiB;AAC7F,QAAM,iBAAiB,uBAAuB,MAAM,kBAAkB,QAAQ,kBAAkB;AAEhG,QAAM,gBACJ,qBAAqB,aACjB,uBAAuB,MAAM,iBAAiB,QAAQ,iBAAiB,IACvE;AAEN,SAAO,YAAY,aAAa,gBAAgB,iBAAiB;AACnE;AAEO,SAAS,oBAAoB,OAAmB,eAA0C;AAC/F,MAAI,MAAM,aAAa,cAAc,MAAM,YAAY,QAAW;AAChE,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,MAAM,OAAO;AAChB,WAAO,EAAE,GAAG,OAAO,UAAU,YAAY;AAAA,EAC3C;AAEA,QAAM,UAAU,cAAc,WAAW,MAAM,KAAK;AAEpD,MAAI,CAAC,SAAS;AACZ,WAAO,EAAE,GAAG,OAAO,UAAU,YAAY;AAAA,EAC3C;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,SAAS,0BAA0B,OAAO,OAAO;AAAA,IACjD,UAAU;AAAA,EACZ;AACF;AAEO,SAAS,qBACd,QACA,eACc;AACd,SAAO,OAAO,IAAI,CAAC,UAAU,oBAAoB,OAAO,aAAa,CAAC;AACxE;;;ACxDA,SAAS,OAAO,UAAU,iBAAiB;AAC3C,OAAO,QAAQ;AACf,OAAO,UAAU;AAIjB,IAAMA,eAAc;AACpB,IAAM,uBAAuB,KAAK,KAAK,KAAK;AAC5C,IAAM,2BAA2B;AAE1B,IAAM,8BACX;AAkBF,SAAS,aAAa,OAAuB;AAC3C,SAAO,MAAM,KAAK,EAAE,YAAY;AAClC;AAEA,SAAS,SAAS,OAAqD;AACrE,MAAI,CAAC,SAAS,OAAO,UAAU,UAAU;AACvC,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,oBAAoB,OAAoC;AAC/D,MAAI,OAAO,UAAU,UAAU;AAC7B,QAAI,CAAC,OAAO,SAAS,KAAK,KAAK,QAAQ,GAAG;AACxC,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,UAAU,UAAU;AAC7B,QAAI,MAAM,KAAK,MAAM,IAAI;AACvB,aAAO;AAAA,IACT;AAEA,UAAM,cAAc,OAAO,KAAK;AAEhC,QAAI,CAAC,OAAO,SAAS,WAAW,KAAK,cAAc,GAAG;AACpD,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,sBAAsB,iBAAoE;AACjG,QAAM,gBACJ,oBAAoB,gBAAgB,oBAAoB,KACxD,oBAAoB,gBAAgB,6BAA6B;AACnE,QAAM,iBACJ,oBAAoB,gBAAgB,qBAAqB,KACzD,oBAAoB,gBAAgB,8BAA8B;AAEpE,MAAI,kBAAkB,UAAa,mBAAmB,QAAW;AAC/D,WAAO;AAAA,EACT;AAEA,QAAM,oBACJ,oBAAoB,gBAAgB,2BAA2B,KAC/D,oBAAoB,gBAAgB,oCAAoC;AAC1E,QAAM,qBAAqB,oBAAoB,gBAAgB,+BAA+B;AAC9F,QAAM,oBAAoB,oBAAoB,gBAAgB,+BAA+B;AAE7F,QAAM,eAA6B;AAAA,IACjC,eAAe,gBAAgBA;AAAA,IAC/B,gBAAgB,iBAAiBA;AAAA,EACnC;AAEA,MAAI,sBAAsB,QAAW;AACnC,iBAAa,oBAAoB,oBAAoBA;AAAA,EACvD;AAEA,MAAI,uBAAuB,QAAW;AACpC,iBAAa,qBAAqB,qBAAqBA;AAAA,EACzD;AAEA,MAAI,sBAAsB,QAAW;AACnC,iBAAa,oBAAoB,oBAAoBA;AACrD,iBAAa,mBAAmB;AAAA,EAClC;AAEA,SAAO;AACT;AAEA,SAAS,+BAA+B,SAA6C;AACnF,QAAM,gBAAgB,SAAS,OAAO;AAEtC,MAAI,CAAC,eAAe;AAClB,UAAM,IAAI,MAAM,+CAA+C;AAAA,EACjE;AAEA,QAAM,oBAAoB,oBAAI,IAA0B;AAExD,aAAW,CAAC,WAAW,eAAe,KAAK,OAAO,QAAQ,aAAa,GAAG;AACxE,UAAM,qBAAqB,SAAS,eAAe;AAEnD,QAAI,CAAC,oBAAoB;AACvB;AAAA,IACF;AAEA,UAAM,yBAAyB,sBAAsB,kBAAkB;AAEvE,QAAI,CAAC,wBAAwB;AAC3B;AAAA,IACF;AAEA,sBAAkB,IAAI,aAAa,SAAS,GAAG,sBAAsB;AAAA,EACvE;AAEA,MAAI,kBAAkB,SAAS,GAAG;AAChC,UAAM,IAAI,MAAM,0EAA0E;AAAA,EAC5F;AAEA,SAAO;AACT;AAEA,SAAS,kBAA0B;AACjC,QAAM,cAAc,QAAQ,IAAI;AAEhC,MAAI,aAAa;AACf,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,aAAa,SAAS;AAChC,UAAM,eAAe,QAAQ,IAAI;AAEjC,QAAI,cAAc;AAChB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO,KAAK,KAAK,GAAG,QAAQ,GAAG,QAAQ;AACzC;AAEO,SAAS,oCAA4C;AAC1D,SAAO,KAAK,KAAK,gBAAgB,GAAG,qBAAqB,4BAA4B;AACvF;AAEA,SAAS,oBAAoB,OAAuB;AAClD,QAAM,aAAa,MAAM,YAAY,GAAG;AAExC,MAAI,eAAe,IAAI;AACrB,WAAO;AAAA,EACT;AAEA,SAAO,MAAM,MAAM,aAAa,CAAC;AACnC;AAEA,SAAS,qBAAqB,OAAuB;AACnD,SAAO,MAAM,QAAQ,eAAe,EAAE;AACxC;AAEA,SAAS,oBAAoB,MAAc,OAAuB;AAChE,QAAM,aAAa,KAAK;AACxB,QAAM,cAAc,MAAM;AAE1B,QAAM,SAAS,MAAM,KAAK,EAAE,QAAQ,aAAa,EAAE,GAAG,CAAC,GAAG,aAAa;AACrE,WAAO,MAAM,KAAK,EAAE,QAAQ,cAAc,EAAE,GAAG,CAACC,IAAG,gBAAgB;AACjE,UAAI,aAAa,GAAG;AAClB,eAAO;AAAA,MACT;AAEA,UAAI,gBAAgB,GAAG;AACrB,eAAO;AAAA,MACT;AAEA,aAAO;AAAA,IACT,CAAC;AAAA,EACH,CAAC;AAED,WAAS,WAAW,GAAG,YAAY,YAAY,YAAY,GAAG;AAC5D,aAAS,cAAc,GAAG,eAAe,aAAa,eAAe,GAAG;AACtE,YAAM,mBAAmB,KAAK,WAAW,CAAC,MAAM,MAAM,cAAc,CAAC,IAAI,IAAI;AAE7E,aAAO,QAAQ,EAAE,WAAW,IAAI,KAAK;AAAA,QACnC,OAAO,WAAW,CAAC,EAAE,WAAW,IAAI;AAAA,QACpC,OAAO,QAAQ,EAAE,cAAc,CAAC,IAAI;AAAA,QACpC,OAAO,WAAW,CAAC,EAAE,cAAc,CAAC,IAAI;AAAA,MAC1C;AAAA,IACF;AAAA,EACF;AAEA,SAAO,OAAO,UAAU,EAAE,WAAW;AACvC;AAEO,IAAM,wBAAN,MAAqD;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAET,iBAAiB,oBAAI,IAA0B;AAAA,EAC/C,qBAAqB,oBAAI,IAAoB;AAAA,EAE9C,YAAY,UAAwC,CAAC,GAAG;AAC7D,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,gBAAgB,QAAQ,iBAAiB,kCAAkC;AAChF,SAAK,aAAa,QAAQ,cAAc;AACxC,SAAK,iBAAiB,QAAQ,kBAAkB;AAChD,SAAK,UAAU,QAAQ,WAAW;AAClC,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,MAAM,QAAQ,OAAO,KAAK;AAAA,EACjC;AAAA,EAEA,MAAa,OAAsB;AACjC,UAAM,cAAc,MAAM,KAAK,cAAc,EAAE,YAAY,MAAM,CAAC;AAElE,QAAI,aAAa;AACf;AAAA,IACF;AAEA,QAAI,KAAK,SAAS;AAChB,YAAM,mBAAmB,MAAM,KAAK,cAAc,EAAE,YAAY,KAAK,CAAC;AAEtE,UAAI,CAAC,kBAAkB;AACrB,cAAM,IAAI,MAAM,yEAAyE;AAAA,MAC3F;AAEA;AAAA,IACF;AAEA,QAAI;AACF,YAAM,KAAK,eAAe;AAAA,IAC5B,QAAQ;AACN,YAAM,mBAAmB,MAAM,KAAK,cAAc,EAAE,YAAY,KAAK,CAAC;AAEtE,UAAI,CAAC,kBAAkB;AACrB,cAAM,IAAI,MAAM,sDAAsD;AAAA,MACxE;AAAA,IACF;AAAA,EACF;AAAA,EAEO,kBAAkB,OAAuB;AAC9C,UAAM,kBAAkB,aAAa,KAAK;AAC1C,UAAM,cAAc,KAAK,mBAAmB,IAAI,eAAe;AAE/D,QAAI,aAAa;AACf,aAAO;AAAA,IACT;AAEA,UAAM,cAAc,KAAK,wBAAwB,eAAe;AAEhE,QAAI,aAAa;AACf,WAAK,mBAAmB,IAAI,iBAAiB,WAAW;AACxD,aAAO;AAAA,IACT;AAEA,UAAM,cAAc,KAAK,wBAAwB,eAAe;AAEhE,QAAI,aAAa;AACf,WAAK,mBAAmB,IAAI,iBAAiB,WAAW;AACxD,aAAO;AAAA,IACT;AAEA,UAAM,aAAa,KAAK,uBAAuB,eAAe;AAC9D,UAAM,gBAAgB,cAAc;AACpC,SAAK,mBAAmB,IAAI,iBAAiB,aAAa;AAE1D,WAAO;AAAA,EACT;AAAA,EAEO,WAAW,OAAyC;AACzD,UAAM,gBAAgB,KAAK,kBAAkB,KAAK;AAClD,WAAO,KAAK,eAAe,IAAI,aAAa;AAAA,EAC9C;AAAA,EAEQ,wBAAwB,iBAA6C;AAC3E,QAAI,KAAK,eAAe,IAAI,eAAe,GAAG;AAC5C,aAAO;AAAA,IACT;AAEA,UAAM,gBAAgB,oBAAoB,eAAe;AAEzD,QAAI,KAAK,eAAe,IAAI,aAAa,GAAG;AAC1C,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,wBAAwB,iBAA6C;AAC3E,UAAM,aAAa,CAAC,iBAAiB,oBAAoB,eAAe,CAAC;AAEzE,eAAW,aAAa,YAAY;AAClC,UAAI;AAEJ,iBAAW,aAAa,KAAK,eAAe,KAAK,GAAG;AAClD,YAAI,CAAC,UAAU,WAAW,SAAS,GAAG;AACpC;AAAA,QACF;AAEA,YACE,CAAC,aACD,UAAU,SAAS,UAAU,UAC5B,UAAU,WAAW,UAAU,UAAU,UAAU,cAAc,SAAS,IAAI,GAC/E;AACA,sBAAY;AAAA,QACd;AAAA,MACF;AAEA,UAAI,WAAW;AACb,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,uBAAuB,iBAA6C;AAC1E,UAAM,cAAc,qBAAqB,oBAAoB,eAAe,CAAC;AAE7E,QAAI,CAAC,aAAa;AAChB,aAAO;AAAA,IACT;AAEA,QAAI;AAEJ,eAAW,aAAa,KAAK,eAAe,KAAK,GAAG;AAClD,YAAM,iBAAiB,qBAAqB,SAAS;AAErD,UAAI,CAAC,gBAAgB;AACnB;AAAA,MACF;AAEA,YAAM,WAAW,oBAAoB,aAAa,cAAc;AAEhE,UAAI,CAAC,aAAa,WAAW,UAAU,UAAU;AAC/C,oBAAY,EAAE,WAAW,SAAS;AAAA,MACpC;AAAA,IACF;AAEA,QAAI,CAAC,WAAW;AACd,aAAO;AAAA,IACT;AAEA,UAAM,cAAc,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,SAAS,GAAG,CAAC;AAEpE,QAAI,UAAU,WAAW,aAAa;AACpC,aAAO;AAAA,IACT;AAEA,WAAO,UAAU;AAAA,EACnB;AAAA,EAEA,MAAc,iBAAgC;AAC5C,UAAM,WAAW,MAAM,KAAK,UAAU,KAAK,WAAW;AAAA,MACpD,QAAQ,YAAY,QAAQ,KAAK,cAAc;AAAA,IACjD,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,yCAAyC,SAAS,MAAM,EAAE;AAAA,IAC5E;AAEA,UAAM,UAAW,MAAM,SAAS,KAAK;AACrC,UAAM,oBAAoB,+BAA+B,OAAO;AAEhE,SAAK,iBAAiB;AACtB,SAAK,mBAAmB,MAAM;AAE9B,QAAI;AACF,YAAM,KAAK,WAAW;AAAA,IACxB,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEA,MAAc,cAAc,SAAoD;AAC9E,UAAM,mBAAmB,MAAM,KAAK,iBAAiB;AAErD,QAAI,CAAC,kBAAkB;AACrB,aAAO;AAAA,IACT;AAEA,QAAI,iBAAiB,cAAc,KAAK,WAAW;AACjD,aAAO;AAAA,IACT;AAEA,UAAM,UAAU,KAAK,IAAI,IAAI,iBAAiB,YAAY,KAAK;AAE/D,QAAI,WAAW,CAAC,QAAQ,YAAY;AAClC,aAAO;AAAA,IACT;AAEA,SAAK,iBAAiB,IAAI;AAAA,MACxB,OAAO,QAAQ,iBAAiB,cAAc,EAAE,IAAI,CAAC,CAAC,WAAW,OAAO,MAAM;AAAA,QAC5E,aAAa,SAAS;AAAA,QACtB;AAAA,MACF,CAAC;AAAA,IACH;AACA,SAAK,mBAAmB,MAAM;AAE9B,WAAO,KAAK,eAAe,OAAO;AAAA,EACpC;AAAA,EAEQ,uBAAuB,YAA+C;AAC5E,UAAM,gBAAgB,SAAS,UAAU;AAEzC,QAAI,CAAC,eAAe;AAClB,aAAO;AAAA,IACT;AAEA,UAAM,gBAAgB,oBAAoB,cAAc,aAAa;AACrE,UAAM,iBAAiB,oBAAoB,cAAc,cAAc;AAEvE,QAAI,kBAAkB,UAAa,mBAAmB,QAAW;AAC/D,aAAO;AAAA,IACT;AAEA,UAAM,eAA6B;AAAA,MACjC;AAAA,MACA;AAAA,IACF;AAEA,UAAM,oBAAoB,oBAAoB,cAAc,iBAAiB;AAE7E,QAAI,sBAAsB,QAAW;AACnC,mBAAa,oBAAoB;AAAA,IACnC;AAEA,UAAM,qBAAqB,oBAAoB,cAAc,kBAAkB;AAE/E,QAAI,uBAAuB,QAAW;AACpC,mBAAa,qBAAqB;AAAA,IACpC;AAEA,UAAM,oBAAoB,oBAAoB,cAAc,iBAAiB;AAE7E,QAAI,sBAAsB,QAAW;AACnC,mBAAa,oBAAoB;AACjC,mBAAa,mBAAmB;AAAA,IAClC;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,mBAA6D;AACzE,QAAI;AAEJ,QAAI;AACF,gBAAU,MAAM,SAAS,KAAK,eAAe,MAAM;AAAA,IACrD,QAAQ;AACN,aAAO;AAAA,IACT;AAEA,QAAI;AAEJ,QAAI;AACF,sBAAgB,KAAK,MAAM,OAAO;AAAA,IACpC,QAAQ;AACN,aAAO;AAAA,IACT;AAEA,UAAM,gBAAgB,SAAS,aAAa;AAE5C,QAAI,CAAC,eAAe;AAClB,aAAO;AAAA,IACT;AAEA,UAAM,YAAY,oBAAoB,cAAc,SAAS;AAC7D,UAAM,YACJ,OAAO,cAAc,cAAc,WAAW,cAAc,YAAY;AAC1E,UAAM,uBAAuB,SAAS,cAAc,cAAc;AAElE,QAAI,cAAc,UAAa,CAAC,aAAa,CAAC,sBAAsB;AAClE,aAAO;AAAA,IACT;AAEA,UAAM,iBAA+C,CAAC;AAEtD,eAAW,CAAC,WAAW,UAAU,KAAK,OAAO,QAAQ,oBAAoB,GAAG;AAC1E,YAAM,UAAU,KAAK,uBAAuB,UAAU;AAEtD,UAAI,CAAC,SAAS;AACZ;AAAA,MACF;AAEA,qBAAe,SAAS,IAAI;AAAA,IAC9B;AAEA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,aAA4B;AACxC,UAAM,gBAAgB,KAAK,QAAQ,KAAK,aAAa;AACrD,UAAM,MAAM,eAAe,EAAE,WAAW,KAAK,CAAC;AAE9C,UAAM,UAA+B;AAAA,MACnC,WAAW,KAAK,IAAI;AAAA,MACpB,WAAW,KAAK;AAAA,MAChB,gBAAgB,OAAO,YAAY,KAAK,eAAe,QAAQ,CAAC;AAAA,IAClE;AAEA,UAAM,UAAU,KAAK,eAAe,KAAK,UAAU,OAAO,GAAG,MAAM;AAAA,EACrE;AACF;;;ACpgBA,SAASC,cAAa,KAAqB;AACzC,SAAO,IAAI,KAAK,EAAE,YAAY;AAChC;AAEO,IAAM,sBAAN,MAAmD;AAAA,EACvC;AAAA,EACA;AAAA,EAEV,YAAY,SAAqC;AACtD,SAAK,iBAAiB,IAAI;AAAA,MACxB,OAAO,QAAQ,QAAQ,cAAc,EAAE,IAAI,CAAC,CAAC,OAAO,OAAO,MAAM;AAAA,QAC/DA,cAAa,KAAK;AAAA,QAClB;AAAA,MACF,CAAC;AAAA,IACH;AACA,SAAK,eAAe,IAAI;AAAA,MACtB,OAAO,QAAQ,QAAQ,gBAAgB,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,OAAO,MAAM,MAAM;AAAA,QAClEA,cAAa,KAAK;AAAA,QAClBA,cAAa,MAAM;AAAA,MACrB,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEO,kBAAkB,OAAuB;AAC9C,UAAM,kBAAkBA,cAAa,KAAK;AAC1C,WAAO,KAAK,aAAa,IAAI,eAAe,KAAK;AAAA,EACnD;AAAA,EAEO,WAAW,OAAyC;AACzD,UAAM,gBAAgB,KAAK,kBAAkB,KAAK;AAClD,WAAO,KAAK,eAAe,IAAI,aAAa;AAAA,EAC9C;AACF;AAEO,SAAS,mCAAwD;AACtE,SAAO,IAAI,oBAAoB;AAAA,IAC7B,gBAAgB;AAAA,MACd,eAAe;AAAA,QACb,eAAe;AAAA,QACf,gBAAgB;AAAA,QAChB,mBAAmB;AAAA,MACrB;AAAA,MACA,WAAW;AAAA,QACT,eAAe;AAAA,QACf,gBAAgB;AAAA,QAChB,mBAAmB;AAAA,MACrB;AAAA,IACF;AAAA,IACA,cAAc;AAAA,MACZ,iBAAiB;AAAA,MACjB,iBAAiB;AAAA,MACjB,iBAAiB;AAAA,IACnB;AAAA,EACF,CAAC;AACH;;;AC7DA,SAAS,qBAAqB;;;ACEvB,IAAM,oBAAoB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,mBAAmB,IAAI,KAAK,aAAa,OAAO;AACtD,IAAM,eAAe,IAAI,KAAK,aAAa,SAAS;AAAA,EAClD,OAAO;AAAA,EACP,UAAU;AAAA,EACV,uBAAuB;AAAA,EACvB,uBAAuB;AACzB,CAAC;AAED,SAAS,aAAa,KAA6B;AACjD,MAAI,IAAI,YAAY,eAAe;AACjC,WAAO;AAAA,EACT;AAEA,SAAO,IAAI;AACb;AAEA,SAAS,aAAa,QAA0B;AAC9C,SAAO,OAAO,SAAS,IAAI,OAAO,KAAK,IAAI,IAAI;AACjD;AAEA,SAAS,iBAAiB,OAAuB;AAC/C,SAAO,iBAAiB,OAAO,KAAK;AACtC;AAEA,SAAS,UAAU,OAAuB;AACxC,SAAO,aAAa,OAAO,KAAK;AAClC;AAEO,SAAS,kBAAkB,MAAoC;AACpE,SAAO,KAAK,IAAI,CAAC,QAAQ;AAAA,IACvB,IAAI;AAAA,IACJ,aAAa,GAAG;AAAA,IAChB,aAAa,IAAI,MAAM;AAAA,IACvB,iBAAiB,IAAI,WAAW;AAAA,IAChC,iBAAiB,IAAI,YAAY;AAAA,IACjC,iBAAiB,IAAI,eAAe;AAAA,IACpC,iBAAiB,IAAI,eAAe;AAAA,IACpC,iBAAiB,IAAI,gBAAgB;AAAA,IACrC,iBAAiB,IAAI,WAAW;AAAA,IAChC,UAAU,IAAI,OAAO;AAAA,EACvB,CAAC;AACH;;;ADnDA,IAAM,YAA2B,CAAC,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,GAAG;AAE3E,SAAS,oBAAoB,MAAgC;AAClE,QAAM,WAAW,kBAAkB,IAAI;AACvC,QAAM,YAAY,CAAC,MAAM,KAAK,iBAAiB,GAAG,GAAG,QAAQ;AAE7D,SAAO,cAAc,WAAW;AAAA,IAC9B,OAAO;AAAA,EACT,CAAC;AACH;;;AEdA,OAAO,QAAQ;AACf,SAAS,qBAAqB,aAAmC;AAKjE,IAAM,oBAAoB;AAM1B,SAAS,yBAAyB,OAAe,UAAkB,MAA2B;AAC5F,MAAI,UAAU,KAAK,UAAU,KAAK,UAAU,UAAU;AACpD,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,KAAK,QAAQ,CAAC;AAClC,QAAM,UAAU,KAAK,KAAK;AAE1B,QAAM,iBAAiB,YAAY,CAAC;AACpC,QAAM,aAAa,QAAQ,CAAC;AAC5B,QAAM,iBAAiB,YAAY,CAAC;AACpC,QAAM,aAAa,QAAQ,CAAC;AAE5B,SAAO,mBAAmB,cAAc,eAAe,WAAW,mBAAmB;AACvF;AAEA,SAAS,kBAAkB,MAAmC;AAC5D,SAAO;AAAA,IACL,QAAQ,oBAAoB,MAAM;AAAA,IAClC,oBAAoB,CAAC,OAAO,aAAa,yBAAyB,OAAO,UAAU,IAAI;AAAA,IACvF,eAAe;AAAA,MACb,aAAa;AAAA,MACb,cAAc;AAAA,MACd,mBAAmB;AAAA,IACrB;AAAA,IACA,SAAS;AAAA,MACP,GAAG,EAAE,WAAW,OAAO;AAAA,MACvB,GAAG,EAAE,WAAW,OAAO;AAAA,MACvB,CAAC,iBAAiB,GAAG;AAAA,QACnB,WAAW;AAAA,QACX,OAAO;AAAA,QACP,UAAU;AAAA,MACZ;AAAA,MACA,GAAG,EAAE,WAAW,QAAQ;AAAA,MACxB,GAAG,EAAE,WAAW,QAAQ;AAAA,MACxB,GAAG,EAAE,WAAW,QAAQ;AAAA,MACxB,GAAG,EAAE,WAAW,QAAQ;AAAA,MACxB,GAAG,EAAE,WAAW,QAAQ;AAAA,MACxB,GAAG,EAAE,WAAW,QAAQ;AAAA,MACxB,GAAG,EAAE,WAAW,QAAQ;AAAA,IAC1B;AAAA,EACF;AACF;AAEA,SAAS,0BAAmC;AAC1C,MAAI,QAAQ,IAAI,aAAa,QAAW;AACtC,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,IAAI,gBAAgB,UAAa,QAAQ,IAAI,gBAAgB,KAAK;AAC5E,WAAO;AAAA,EACT;AAEA,SAAO,QAAQ,OAAO;AACxB;AAEA,SAAS,YAAY,QAA0C;AAC7D,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO,GAAG;AAAA,IACZ,KAAK;AACH,aAAO,GAAG;AAAA,IACZ,KAAK;AACH,aAAO,GAAG;AAAA,IACZ,KAAK;AACH,aAAO,CAAC,SAAS,GAAG,KAAK,GAAG,MAAM,IAAI,CAAC;AAAA,IACzC;AACE,aAAO,CAAC,SAAS;AAAA,EACrB;AACF;AAEA,SAAS,iBACP,UACA,MACA,UACY;AACZ,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,SAAO,KAAK,IAAI,CAAC,KAAK,UAAU;AAC9B,UAAM,cAAc,CAAC,GAAI,SAAS,KAAK,KAAK,CAAC,CAAE;AAC/C,UAAM,eAAe,YAAY,YAAY,CAAC,KAAK,IAAI,MAAM;AAE7D,gBAAY,CAAC,IAAI,aAAa,YAAY,CAAC,KAAK,IAAI,MAAM;AAE1D,QAAI,IAAI,YAAY,eAAe;AACjC,aAAO,YAAY,IAAI,CAAC,SAAS,GAAG,KAAK,IAAI,CAAC;AAAA,IAChD;AAEA,QAAI,IAAI,YAAY,mBAAmB;AACrC,aAAO,YAAY,IAAI,CAAC,MAAM,cAAe,cAAc,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,CAAE;AAAA,IAC9F;AAEA,WAAO;AAAA,EACT,CAAC;AACH;AAEA,SAAS,eAAe,UAA6B;AACnD,QAAM,cAAc,MAAM,KAAK,iBAAiB;AAEhD,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,SAAO,YAAY,IAAI,CAAC,WAAW,GAAG,KAAK,GAAG,MAAM,MAAM,CAAC,CAAC;AAC9D;AAEO,SAAS,oBACd,MACA,UAAiC,CAAC,GAC1B;AACR,QAAM,WAAW,QAAQ,YAAY,wBAAwB;AAC7D,QAAM,WAAW,iBAAiB,kBAAkB,IAAI,GAAG,MAAM,QAAQ;AACzE,QAAM,cAAc,CAAC,eAAe,QAAQ,GAAG,GAAG,QAAQ;AAE1D,SAAO,MAAM,aAAa,kBAAkB,WAAW,CAAC;AAC1D;;;ACjIA,OAAOC,SAAQ;AACf,OAAOC,WAAU;;;AC8CjB,SAAS,YAAY,OAAe,WAA2B;AAC7D,QAAM,aAAa,MAAM,KAAK;AAE9B,MAAI,CAAC,YAAY;AACf,UAAM,IAAI,MAAM,cAAc,SAAS,6BAA6B;AAAA,EACtE;AAEA,SAAO;AACT;AAEA,SAAS,sBAAsB,OAA+C;AAC5E,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,MAAM,KAAK;AAC9B,SAAO,cAAc;AACvB;AAEA,SAAS,gBAAgB,UAAgC,SAAuC;AAC9F,MAAI,aAAa,cAAc,YAAY,QAAW;AACpD,UAAM,IAAI,MAAM,sDAAsD;AAAA,EACxE;AAEA,MAAI,UAAU;AACZ,WAAO;AAAA,EACT;AAEA,SAAO,YAAY,SAAY,cAAc;AAC/C;AAEO,SAAS,iBAAiB,OAAoC;AACnE,QAAM,cAAc,4BAA4B,MAAM,WAAW;AACjE,QAAM,eAAe,4BAA4B,MAAM,YAAY;AACnE,QAAM,kBAAkB,4BAA4B,MAAM,eAAe;AACzE,QAAM,kBAAkB,4BAA4B,MAAM,eAAe;AACzE,QAAM,mBAAmB,4BAA4B,MAAM,gBAAgB;AAC3E,QAAM,sBAAsB,4BAA4B,MAAM,WAAW;AACzE,QAAM,uBACJ,cAAc,eAAe,kBAAkB,kBAAkB;AACnE,QAAM,cAAc,sBAAsB,IAAI,sBAAsB;AAEpE,QAAM,UAAU,iBAAiB,MAAM,OAAO;AAC9C,QAAM,WAAW,gBAAgB,MAAM,UAAU,OAAO;AAExD,SAAO;AAAA,IACL,QAAQ,YAAY,MAAM,QAAQ,QAAQ;AAAA,IAC1C,WAAW,YAAY,MAAM,WAAW,WAAW;AAAA,IACnD,WAAW,mBAAmB,MAAM,SAAS;AAAA,IAC7C,UAAU,sBAAsB,MAAM,QAAQ;AAAA,IAC9C,OAAO,sBAAsB,MAAM,KAAK;AAAA,IACxC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AC3GA,SAAS,eAAe;AACxB,OAAOC,WAAU;AAEjB,eAAe,cAAc,SAAiB,KAA8B;AAC1E,QAAM,UAAU,MAAM,QAAQ,SAAS,EAAE,eAAe,KAAK,CAAC;AAC9D,UAAQ,KAAK,CAAC,MAAM,UAAU,KAAK,KAAK,cAAc,MAAM,IAAI,CAAC;AAEjE,aAAW,SAAS,SAAS;AAC3B,UAAM,YAAYA,MAAK,KAAK,SAAS,MAAM,IAAI;AAE/C,QAAI,MAAM,YAAY,GAAG;AACvB,YAAM,cAAc,WAAW,GAAG;AAClC;AAAA,IACF;AAEA,QAAI,MAAM,OAAO,KAAK,MAAM,KAAK,SAAS,QAAQ,GAAG;AACnD,UAAI,KAAK,SAAS;AAAA,IACpB;AAAA,EACF;AACF;AAEA,eAAsB,mBAAmB,SAAoC;AAC3E,QAAM,QAAkB,CAAC;AAEzB,MAAI;AACF,UAAM,cAAc,SAAS,KAAK;AAAA,EACpC,SAAS,OAAO;AACd,UAAM,YAAY;AAElB,QAAI,UAAU,SAAS,UAAU;AAC/B,aAAO,CAAC;AAAA,IACV;AAEA,UAAM;AAAA,EACR;AAEA,SAAO;AACT;;;ACrCA,SAAS,wBAAwB;AACjC,SAAS,uBAAuB;AAEhC,SAAS,SAAS,OAAqD;AACrE,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;AAC/D,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,gBAAuB,iBACrB,UAC0D;AAC1D,QAAM,SAAS,iBAAiB,UAAU;AAAA,IACxC,UAAU;AAAA,EACZ,CAAC;AACD,QAAM,aAAa,gBAAgB;AAAA,IACjC,OAAO;AAAA,IACP,WAAW;AAAA,EACb,CAAC;AAED,MAAI,cAAc;AAElB,MAAI;AACF,qBAAiB,WAAW,YAAY;AACtC,YAAM,iBAAiB,cAAc,QAAQ,QAAQ,YAAY,EAAE,IAAI;AACvE,oBAAc;AAEd,YAAM,WAAW,eAAe,KAAK;AAErC,UAAI,CAAC,UAAU;AACb;AAAA,MACF;AAEA,UAAI;AAEJ,UAAI;AACF,iBAAS,KAAK,MAAM,QAAQ;AAAA,MAC9B,QAAQ;AACN;AAAA,MACF;AAEA,YAAM,eAAe,SAAS,MAAM;AAEpC,UAAI,CAAC,cAAc;AACjB;AAAA,MACF;AAEA,YAAM;AAAA,IACR;AAAA,EACF,UAAE;AACA,eAAW,MAAM;AACjB,WAAO,QAAQ;AAAA,EACjB;AACF;;;AH5CA,IAAM,qBAAqBC,MAAK,KAAKC,IAAG,QAAQ,GAAG,UAAU,UAAU;AAEhE,IAAM,8BAA8B;AAqB3C,SAASC,UAAS,OAAqD;AACrE,MAAI,CAAC,SAAS,OAAO,UAAU,UAAU;AACvC,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,OAAO,OAAoC;AAClD,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,MAAM,KAAK;AAC9B,SAAO,cAAc;AACvB;AAEA,SAAS,QAAQ,OAAwC;AACvD,QAAM,QAAQA,UAAS,KAAK;AAE5B,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AAEA,QAAM,iBAAiB,4BAA4B,MAAM,YAA0B;AACnF,QAAM,kBAAkB,4BAA4B,MAAM,mBAAiC;AAC3F,QAAM,eAAe,4BAA4B,MAAM,aAA2B;AAElF,QAAM,cAAc,KAAK,IAAI,GAAG,iBAAiB,eAAe;AAEhE,SAAO;AAAA;AAAA;AAAA,IAGL;AAAA,IACA;AAAA,IACA;AAAA,IACA,iBAAiB,4BAA4B,MAAM,uBAAqC;AAAA;AAAA,IAExF,aAAa,cAAc,eAAe;AAAA,EAC5C;AACF;AAEA,SAAS,cAAc,SAAqB,UAAkC;AAC5E,SAAO;AAAA,IACL,aAAa,KAAK,IAAI,GAAG,QAAQ,cAAc,SAAS,WAAW;AAAA,IACnE,iBAAiB,KAAK,IAAI,GAAG,QAAQ,kBAAkB,SAAS,eAAe;AAAA,IAC/E,cAAc,KAAK,IAAI,GAAG,QAAQ,eAAe,SAAS,YAAY;AAAA,IACtE,iBAAiB,KAAK,IAAI,GAAG,QAAQ,kBAAkB,SAAS,eAAe;AAAA,IAC/E,aAAa,KAAK,IAAI,GAAG,QAAQ,cAAc,SAAS,WAAW;AAAA,EACrE;AACF;AAEA,SAAS,SAAS,MAAkB,OAA+B;AACjE,SAAO;AAAA,IACL,aAAa,KAAK,cAAc,MAAM;AAAA,IACtC,iBAAiB,KAAK,kBAAkB,MAAM;AAAA,IAC9C,cAAc,KAAK,eAAe,MAAM;AAAA,IACxC,iBAAiB,KAAK,kBAAkB,MAAM;AAAA,IAC9C,aAAa,KAAK,cAAc,MAAM;AAAA,EACxC;AACF;AAEA,SAAS,eAAe,OAA4B;AAClD,SACE,MAAM,cAAc,KACpB,MAAM,kBAAkB,KACxB,MAAM,eAAe,KACrB,MAAM,kBAAkB,KACxB,MAAM,cAAc;AAExB;AAEA,SAAS,iBACP,MACA,oBAC4D;AAC5D,QAAM,aAAa,QAAQ,KAAK,iBAAiB;AACjD,QAAM,YAAY,QAAQ,KAAK,gBAAgB;AAE/C,MAAI,WAAW;AACb,WAAO,EAAE,YAAY,WAAW,kBAAkB,WAAW;AAAA,EAC/D;AAEA,MAAI,CAAC,YAAY;AACf,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,aAAa,qBACf,cAAc,YAAY,kBAAkB,IAC5C;AAEJ,SAAO,EAAE,YAAY,kBAAkB,WAAW;AACpD;AAEA,SAAS,qBAAqB,UAA0B;AACtD,SAAOF,MAAK,SAAS,UAAU,QAAQ;AACzC;AAEO,IAAM,qBAAN,MAAkD;AAAA,EACvC,KAAK;AAAA,EAEJ;AAAA,EAEV,YAAY,UAAqC,CAAC,GAAG;AAC1D,SAAK,cAAc,QAAQ,eAAe;AAAA,EAC5C;AAAA,EAEA,MAAa,gBAAmC;AAC9C,WAAO,mBAAmB,KAAK,WAAW;AAAA,EAC5C;AAAA,EAEA,MAAa,UAAU,UAAyC;AAC9D,UAAM,SAAuB,CAAC;AAE9B,UAAM,QAA2B;AAAA,MAC/B,WAAW,qBAAqB,QAAQ;AAAA,MACxC,UAAU;AAAA,IACZ;AAEA,qBAAiB,QAAQ,iBAAiB,QAAQ,GAAG;AACnD,UAAI,KAAK,SAAS,gBAAgB;AAChC,cAAMG,WAAUD,UAAS,KAAK,OAAO;AACrC,cAAM,YAAY,OAAOC,UAAS,EAAE,KAAK,MAAM;AAC/C,cAAM,WAAW,OAAOA,UAAS,cAAc,KAAK,MAAM;AAC1D;AAAA,MACF;AAEA,UAAI,KAAK,SAAS,gBAAgB;AAChC,cAAMA,WAAUD,UAAS,KAAK,OAAO;AACrC,cAAM,QAAQ,OAAOC,UAAS,KAAK,KAAK,MAAM;AAC9C;AAAA,MACF;AAEA,UAAI,KAAK,SAAS,aAAa;AAC7B;AAAA,MACF;AAEA,YAAM,UAAUD,UAAS,KAAK,OAAO;AAErC,UAAI,SAAS,SAAS,eAAe;AACnC;AAAA,MACF;AAEA,YAAM,OAAOA,UAAS,QAAQ,IAAI;AAElC,UAAI,CAAC,MAAM;AACT;AAAA,MACF;AAEA,YAAM,EAAE,YAAY,iBAAiB,IAAI,iBAAiB,MAAM,MAAM,kBAAkB;AAExF,UAAI,CAAC,cAAc,CAAC,eAAe,UAAU,GAAG;AAC9C,cAAM,qBAAqB,oBAAoB,MAAM;AACrD;AAAA,MACF;AAEA,YAAM,YAAY,OAAO,KAAK,SAAS;AAEvC,UAAI,CAAC,WAAW;AACd,cAAM,qBAAqB,oBAAoB,MAAM;AACrD;AAAA,MACF;AAEA,YAAM,QAAQ,MAAM,SAAS;AAE7B,UAAI;AACF,eAAO;AAAA,UACL,iBAAiB;AAAA,YACf,QAAQ,KAAK;AAAA,YACb,WAAW,MAAM;AAAA,YACjB;AAAA,YACA,UAAU,MAAM;AAAA,YAChB;AAAA,YACA,aAAa,WAAW;AAAA,YACxB,cAAc,WAAW;AAAA,YACzB,iBAAiB,WAAW;AAAA,YAC5B,iBAAiB,WAAW;AAAA,YAC5B,kBAAkB;AAAA,YAClB,aAAa,WAAW;AAAA,YACxB,UAAU;AAAA,UACZ,CAAC;AAAA,QACH;AAAA,MACF,QAAQ;AAAA,MAER;AAEA,UAAI,kBAAkB;AACpB,cAAM,qBAAqB;AAAA,MAC7B,WAAW,MAAM,oBAAoB;AACnC,cAAM,qBAAqB,SAAS,MAAM,oBAAoB,UAAU;AAAA,MAC1E,OAAO;AACL,cAAM,qBAAqB;AAAA,MAC7B;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;;;AIvOA,OAAOE,SAAQ;AACf,OAAOC,WAAU;AASjB,IAAMC,sBAAqBC,MAAK,KAAKC,IAAG,QAAQ,GAAG,OAAO,SAAS,UAAU;AA0BtE,SAAS,iBAAiB,UAAuC;AACtE,SAAO,UAAU,YAAY,EAAE,SAAS,QAAQ,KAAK;AACvD;AAEA,SAASC,UAAS,OAAqD;AACrE,MAAI,CAAC,SAAS,OAAO,UAAU,UAAU;AACvC,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAASC,QAAO,OAAoC;AAClD,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,MAAM,KAAK;AAC9B,SAAO,cAAc;AACvB;AAEA,SAAS,aAAa,OAA4B;AAChD,MACE,UAAU,QACV,UAAU,UACV,OAAO,UAAU,YACjB,OAAO,UAAU,UACjB;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,iBACP,MACA,SACA,OACoB;AACpB,QAAM,aAAa,CAAC,KAAK,WAAW,SAAS,WAAW,MAAM,gBAAgB;AAE9E,aAAW,aAAa,YAAY;AAClC,QAAI,OAAO,cAAc,YAAY,OAAO,SAAS,SAAS,GAAG;AAC/D,YAAM,cAAc,KAAK,IAAI,SAAS,IAAI,OAAoB,YAAY,MAAO;AACjF,YAAM,OAAO,IAAI,KAAK,WAAW;AAEjC,UAAI,CAAC,OAAO,MAAM,KAAK,QAAQ,CAAC,GAAG;AACjC,eAAO,KAAK,YAAY;AAAA,MAC1B;AAEA;AAAA,IACF;AAEA,QAAI,OAAO,cAAc,YAAY,UAAU,KAAK,GAAG;AACrD,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,aAAa,MAA+B,SAA8C;AACjG,QAAM,QAAQD,UAAS,KAAK,KAAK,KAAKA,UAAS,SAAS,KAAK;AAE7D,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AAEA,QAAM,OAAOA,UAAS,MAAM,IAAI;AAEhC,QAAM,YAA4B;AAAA,IAChC,aAAa,aAAa,MAAM,KAAK;AAAA,IACrC,cAAc,aAAa,MAAM,MAAM;AAAA,IACvC,iBAAiB;AAAA,MACf,MAAM,aAAa,MAAM,mBAAmB,MAAM,mBAAmB,MAAM;AAAA,IAC7E;AAAA,IACA,iBAAiB,aAAa,MAAM,SAAS;AAAA,IAC7C,kBAAkB,aAAa,MAAM,UAAU;AAAA,IAC/C,aAAa,aAAa,MAAM,WAAW;AAAA,IAC3C,SAAS,aAAa,MAAM,KAAK;AAAA,EACnC;AAEA,QAAM,qBACJ,UAAU,gBAAgB,UAC1B,UAAU,iBAAiB,UAC3B,UAAU,oBAAoB,UAC9B,UAAU,oBAAoB,UAC9B,UAAU,qBAAqB,UAC/B,UAAU,gBAAgB,UAC1B,UAAU,YAAY;AAExB,SAAO,qBAAqB,YAAY;AAC1C;AAEA,SAASE,sBAAqB,UAA0B;AACtD,SAAOJ,MAAK,SAAS,UAAU,QAAQ;AACzC;AAEO,IAAM,kBAAN,MAA+C;AAAA,EACpC,KAAK;AAAA,EAEJ;AAAA,EACA;AAAA,EAEV,YAAY,UAAkC,CAAC,GAAG;AACvD,SAAK,cAAc,QAAQ,eAAeD;AAC1C,SAAK,iBAAiB,QAAQ,kBAAkB;AAAA,EAClD;AAAA,EAEA,MAAa,gBAAmC;AAC9C,WAAO,mBAAmB,KAAK,WAAW;AAAA,EAC5C;AAAA,EAEA,MAAa,UAAU,UAAyC;AAC9D,UAAM,SAAuB,CAAC;AAC9B,UAAM,QAAwB,EAAE,WAAWK,sBAAqB,QAAQ,EAAE;AAE1E,qBAAiB,QAAQ,iBAAiB,QAAQ,GAAG;AACnD,UAAI,KAAK,SAAS,WAAW;AAC3B,cAAM,YAAYD,QAAO,KAAK,EAAE,KAAK,MAAM;AAC3C,cAAM,mBAAmBA,QAAO,KAAK,SAAS,KAAK,MAAM;AACzD;AAAA,MACF;AAEA,UAAI,KAAK,SAAS,gBAAgB;AAChC,cAAM,WAAWA,QAAO,KAAK,QAAQ,KAAK,MAAM;AAChD,cAAM,QAAQA,QAAO,KAAK,OAAO,KAAKA,QAAO,KAAK,KAAK,KAAK,MAAM;AAClE;AAAA,MACF;AAEA,UAAI,KAAK,SAAS,WAAW;AAC3B;AAAA,MACF;AAEA,YAAM,UAAUD,UAAS,KAAK,OAAO;AACrC,YAAM,QAAQ,aAAa,MAAM,OAAO;AAExC,UAAI,CAAC,OAAO;AACV;AAAA,MACF;AAEA,YAAM,WAAWC,QAAO,KAAK,QAAQ,KAAKA,QAAO,SAAS,QAAQ,KAAK,MAAM;AAE7E,UAAI,CAAC,KAAK,eAAe,QAAQ,GAAG;AAClC;AAAA,MACF;AAEA,YAAM,YAAY,iBAAiB,MAAM,SAAS,KAAK;AAEvD,UAAI,CAAC,aAAa,CAAC,MAAM,WAAW;AAClC;AAAA,MACF;AAEA,YAAM,QACJA,QAAO,KAAK,KAAK,KAAKA,QAAO,KAAK,OAAO,KAAKA,QAAO,SAAS,KAAK,KAAK,MAAM;AAEhF,UAAI;AACF,eAAO;AAAA,UACL,iBAAiB;AAAA,YACf,QAAQ,KAAK;AAAA,YACb,WAAW,MAAM;AAAA,YACjB;AAAA,YACA;AAAA,YACA;AAAA,YACA,GAAG;AAAA,UACL,CAAC;AAAA,QACH;AAAA,MACF,QAAQ;AACN;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;;;ACrMA,IAAM,4BAA4B;AAgBlC,SAAS,kBAAkB,OAAe,UAAuC;AAC/E,MAAI,CAAC,uBAAuB,KAAK,KAAK,GAAG;AACvC,UAAM,IAAI,MAAM,GAAG,QAAQ,6BAA6B;AAAA,EAC1D;AAEA,QAAM,SAAS,oBAAI,KAAK,GAAG,KAAK,gBAAgB;AAEhD,MAAI,OAAO,MAAM,OAAO,QAAQ,CAAC,KAAK,OAAO,YAAY,EAAE,MAAM,GAAG,EAAE,MAAM,OAAO;AACjF,UAAM,IAAI,MAAM,GAAG,QAAQ,+BAA+B;AAAA,EAC5D;AACF;AAEA,SAAS,iBAAiB,UAAwB;AAChD,MAAI;AACF,QAAI,KAAK,eAAe,SAAS,EAAE,UAAU,SAAS,CAAC,EAAE,OAAO,oBAAI,KAAK,CAAC;AAAA,EAC5E,QAAQ;AACN,UAAM,IAAI,MAAM,qBAAqB,QAAQ,EAAE;AAAA,EACjD;AACF;AAEA,SAAS,wBAAwB,UAAkD;AACjF,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,SAAS,KAAK,EAAE,YAAY;AAC/C,SAAO,cAAc;AACvB;AAEA,SAAS,sBAAsB,QAAgE;AAC7F,MAAI,CAAC,UAAW,MAAM,QAAQ,MAAM,KAAK,OAAO,WAAW,GAAI;AAC7D,WAAO;AAAA,EACT;AAEA,QAAM,mBAAmB,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAM;AACjE,QAAM,oBAAoB,iBACvB,QAAQ,CAAC,cAAc,UAAU,MAAM,GAAG,CAAC,EAC3C,IAAI,CAAC,cAAc,UAAU,KAAK,EAAE,YAAY,CAAC,EACjD,OAAO,CAAC,cAAc,UAAU,SAAS,CAAC;AAE7C,MAAI,kBAAkB,WAAW,GAAG;AAClC,UAAM,IAAI,MAAM,wDAAwD;AAAA,EAC1E;AAEA,SAAO,IAAI,IAAI,iBAAiB;AAClC;AAEA,SAAS,2BACP,cACA,oBACM;AACN,MAAI,CAAC,cAAc;AACjB;AAAA,EACF;AAEA,QAAM,iBAAiB,CAAC,GAAG,YAAY,EAAE,OAAO,CAAC,WAAW,CAAC,mBAAmB,IAAI,MAAM,CAAC;AAE3F,MAAI,eAAe,WAAW,GAAG;AAC/B;AAAA,EACF;AAEA,QAAM,iBAAiB,CAAC,GAAG,kBAAkB,EAAE,KAAK,CAAC,MAAM,UAAU,KAAK,cAAc,KAAK,CAAC;AAE9F,QAAM,IAAI;AAAA,IACR,8BAA8B,eAAe,KAAK,IAAI,CAAC,qBAAqB,eAAe,KAAK,IAAI,CAAC;AAAA,EACvG;AACF;AAEA,SAAS,gBACP,UACA,gBACS;AACT,MAAI,CAAC,gBAAgB;AACnB,WAAO;AAAA,EACT;AAEA,SAAO,UAAU,YAAY,EAAE,SAAS,cAAc,KAAK;AAC7D;AAEA,eAAe,mBAAmB,SAA+C;AAC/E,QAAM,QAAQ,MAAM,QAAQ,cAAc;AAE1C,MAAI,MAAM,WAAW,GAAG;AACtB,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,eAA+B,MAAM,KAAK,EAAE,QAAQ,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC;AAClF,QAAM,cAAc,KAAK,IAAI,2BAA2B,MAAM,MAAM;AACpE,MAAI,gBAAgB;AAEpB,QAAM,UAAU,MAAM,KAAK,EAAE,QAAQ,YAAY,GAAG,YAAY;AAC9D,WAAO,gBAAgB,MAAM,QAAQ;AACnC,YAAM,YAAY;AAClB,uBAAiB;AAEjB,YAAM,WAAW,MAAM,SAAS;AAEhC,UAAI,CAAC,UAAU;AACb;AAAA,MACF;AAEA,mBAAa,SAAS,IAAI,MAAM,QAAQ,UAAU,QAAQ;AAAA,IAC5D;AAAA,EACF,CAAC;AAED,QAAM,QAAQ,IAAI,OAAO;AAEzB,SAAO,aAAa,KAAK;AAC3B;AAEA,SAAS,wBACP,QACA,UACA,OACA,OACc;AACd,SAAO,OAAO,OAAO,CAAC,UAAU;AAC9B,UAAM,YAAY,aAAa,MAAM,WAAW,SAAS,QAAQ;AAEjE,QAAI,SAAS,YAAY,OAAO;AAC9B,aAAO;AAAA,IACT;AAEA,QAAI,SAAS,YAAY,OAAO;AAC9B,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT,CAAC;AACH;AAEA,SAAS,mBAAmB,YAAsC;AAChE,MAAI,CAAC,YAAY;AACf;AAAA,EACF;AAEA,MAAI;AACF,UAAM,YAAY,IAAI,IAAI,UAAU;AAEpC,QAAI,CAAC,CAAC,SAAS,QAAQ,EAAE,SAAS,UAAU,QAAQ,GAAG;AACrD,YAAM,IAAI,MAAM,sBAAsB;AAAA,IACxC;AAAA,EACF,QAAQ;AACN,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC7D;AACF;AAEA,eAAe,qBAAqB,SAAuD;AACzF,QAAM,wBAAwB,iCAAiC;AAC/D,QAAM,wBAAwB,IAAI,sBAAsB;AAAA,IACtD,WAAW,QAAQ;AAAA,IACnB,SAAS,QAAQ;AAAA,EACnB,CAAC;AAED,MAAI;AACF,UAAM,sBAAsB,KAAK;AACjC,WAAO;AAAA,EACT,SAAS,OAAO;AACd,QAAI,QAAQ,gBAAgB;AAC1B,YAAM,IAAI,MAAM,gEAAgE;AAAA,IAClF;AAEA,QAAI,QAAQ,YAAY;AACtB,YAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACpE,YAAM,IAAI,MAAM,8CAA8C,MAAM,EAAE;AAAA,IACxE;AAEA,WAAO;AAAA,EACT;AACF;AAEA,SAAS,wBAAwB,OAA4B;AAC3D,MAAI,CAAC,MAAM,OAAO;AAChB,WAAO;AAAA,EACT;AAEA,SAAO,MAAM,aAAa,cAAc,MAAM,YAAY;AAC5D;AAEA,SAAS,wBAAwB,SAA+B,QAA+B;AAC7F,MAAI,QAAQ,cAAc,QAAQ,gBAAgB;AAChD,WAAO;AAAA,EACT;AAEA,SAAO,OAAO,KAAK,CAAC,UAAU,wBAAwB,KAAK,CAAC;AAC9D;AAEA,eAAsB,iBACpB,aACA,SACiB;AACjB,MAAI,QAAQ,YAAY,QAAQ,MAAM;AACpC,UAAM,IAAI,MAAM,8CAA8C;AAAA,EAChE;AAEA,MAAI,QAAQ,OAAO;AACjB,sBAAkB,QAAQ,OAAO,SAAS;AAAA,EAC5C;AAEA,MAAI,QAAQ,OAAO;AACjB,sBAAkB,QAAQ,OAAO,SAAS;AAAA,EAC5C;AAEA,MAAI,QAAQ,SAAS,QAAQ,SAAS,QAAQ,QAAQ,QAAQ,OAAO;AACnE,UAAM,IAAI,MAAM,+CAA+C;AAAA,EACjE;AAEA,qBAAmB,QAAQ,UAAU;AAErC,QAAM,WAAW,QAAQ,YAAY,KAAK,eAAe,EAAE,gBAAgB,EAAE;AAC7E,mBAAiB,QAAQ;AAEzB,QAAM,iBAAiB,wBAAwB,QAAQ,QAAQ;AAC/D,QAAM,eAAe,sBAAsB,QAAQ,MAAM;AACzD,QAAM,0BAA0B,kBAAkB;AAElD,QAAM,WAA4B;AAAA,IAChC,IAAI,gBAAgB;AAAA,MAClB,aAAa,QAAQ;AAAA,MACrB,gBAAgB,CAAC,aAAa,gBAAgB,UAAU,uBAAuB;AAAA,IACjF,CAAC;AAAA,IACD,IAAI,mBAAmB;AAAA,MACrB,aAAa,QAAQ;AAAA,IACvB,CAAC;AAAA,EACH;AAEA,QAAM,qBAAqB,IAAI,IAAI,SAAS,IAAI,CAAC,YAAY,QAAQ,GAAG,YAAY,CAAC,CAAC;AACtF,6BAA2B,cAAc,kBAAkB;AAE3D,QAAM,kBAAkB,eACpB,SAAS,OAAO,CAAC,YAAY,aAAa,IAAI,QAAQ,GAAG,YAAY,CAAC,CAAC,IACvE;AAEJ,QAAM,wBAAwB,MAAM,QAAQ;AAAA,IAC1C,gBAAgB,IAAI,CAAC,YAAY,mBAAmB,OAAO,CAAC;AAAA,EAC9D;AACA,QAAM,yBAAyB,sBAC5B,KAAK,EACL,OAAO,CAAC,UAAU,gBAAgB,MAAM,UAAU,uBAAuB,CAAC;AAE7E,QAAM,qBAAqB;AAAA,IACzB;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAEA,QAAM,eAAe,wBAAwB,SAAS,kBAAkB,IACpE,qBAAqB,oBAAoB,MAAM,qBAAqB,OAAO,CAAC,IAC5E;AAEJ,QAAM,OAAO,eAAe,cAAc;AAAA,IACxC;AAAA,IACA;AAAA,EACF,CAAC;AAED,MAAI,QAAQ,MAAM;AAChB,WAAO,KAAK,UAAU,MAAM,MAAM,CAAC;AAAA,EACrC;AAEA,MAAI,QAAQ,UAAU;AACpB,WAAO,oBAAoB,IAAI;AAAA,EACjC;AAEA,SAAO,oBAAoB,IAAI;AACjC;AAEA,eAAsB,eACpB,aACA,SACe;AACf,QAAM,SAAS,MAAM,iBAAiB,aAAa,OAAO;AAC1D,UAAQ,IAAI,MAAM;AACpB;;;Af1RA,IAAM,kBAAkB,KAAK,eAAe,EAAE,gBAAgB,EAAE,YAAY;AAE5E,SAAS,oBAAoB,OAAe,UAA8B;AACxE,SAAO,CAAC,GAAG,UAAU,GAAG,MAAM,MAAM,GAAG,CAAC;AAC1C;AAEA,SAAS,iBAAiB,SAA2B;AACnD,SAAO,QACJ,OAAO,mBAAmB,gCAAgC,EAC1D,OAAO,sBAAsB,mCAAmC,EAChE;AAAA,IACC;AAAA,IACA;AAAA,IACA;AAAA,IACA,CAAC;AAAA,EACH,EACC,OAAO,wBAAwB,6BAA6B,EAC5D,OAAO,wBAAwB,2BAA2B,EAC1D,OAAO,qBAAqB,0BAA0B,eAAe,EACrE,OAAO,qBAAqB,+CAA+C,EAC3E,OAAO,uBAAuB,qCAAqC,EACnE,OAAO,qBAAqB,oDAAoD,EAChF,OAAO,cAAc,iCAAiC,EACtD,OAAO,UAAU,uBAAuB;AAC7C;AAEA,SAAS,mBAAmB,aAAuC;AACjE,UAAQ,aAAa;AAAA,IACnB,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EACX;AACF;AAEA,SAAS,cAAc,aAAwC;AAC7D,QAAM,UAAU,IAAI,QAAQ,WAAW;AAEvC,mBAAiB,OAAO,EACrB,YAAY,mBAAmB,WAAW,CAAC,EAC3C,OAAO,OAAO,YAA2B;AACxC,UAAM,eAAe,aAAa,OAAO;AAAA,EAC3C,CAAC;AAEH,SAAO;AACT;AAEA,SAAS,kBAA0B;AACjC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAEO,SAAS,YAAqB;AACnC,QAAM,UAAU,IAAI,QAAQ;AAE5B,UACG,KAAK,WAAW,EAChB,YAAY,gBAAgB,CAAC,EAC7B,mBAAmB,EACnB,WAAW,cAAc,OAAO,CAAC,EACjC,WAAW,cAAc,QAAQ,CAAC,EAClC,WAAW,cAAc,SAAS,CAAC;AAEtC,SAAO;AACT;;;AgBzFA,IAAM,MAAM,UAAU;AAEtB,IAAI;AACF,QAAM,IAAI,WAAW,QAAQ,IAAI;AACnC,SAAS,OAAO;AACd,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,UAAQ,MAAM,OAAO;AACrB,UAAQ,WAAW;AACrB;","names":["ONE_MILLION","_","normalizeKey","os","path","path","path","os","asRecord","payload","os","path","defaultSessionsDir","path","os","asRecord","asText","getFallbackSessionId"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llm-usage-metrics",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "CLI for aggregating local LLM usage metrics from pi and codex sessions",
5
5
  "type": "module",
6
6
  "engines": {