llm-usage-metrics 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,6 +9,8 @@ Reports are available for daily, weekly (Monday-start), and monthly periods.
9
9
 
10
10
  Project documentation is available in [`docs/`](./docs/README.md).
11
11
 
12
+ Built-in adapters currently support `.pi` and `.codex`. The codebase is structured to add more sources (for example Claude/Gemini exports) through the `SourceAdapter` pattern. See [`CONTRIBUTING.md`](./CONTRIBUTING.md).
13
+
12
14
  ## Install
13
15
 
14
16
  ```bash
@@ -26,49 +28,70 @@ npx llm-usage-metrics daily
26
28
  ### Daily report (default terminal table)
27
29
 
28
30
  ```bash
29
- usage daily
31
+ llm-usage daily
30
32
  ```
31
33
 
32
34
  ### Weekly report with custom timezone
33
35
 
34
36
  ```bash
35
- usage weekly --timezone Europe/Paris
37
+ llm-usage weekly --timezone Europe/Paris
36
38
  ```
37
39
 
38
40
  ### Monthly report with date range
39
41
 
40
42
  ```bash
41
- usage monthly --since 2026-01-01 --until 2026-01-31
43
+ llm-usage monthly --since 2026-01-01 --until 2026-01-31
42
44
  ```
43
45
 
44
46
  ### Markdown output
45
47
 
46
48
  ```bash
47
- usage daily --markdown
49
+ llm-usage daily --markdown
48
50
  ```
49
51
 
50
52
  ### JSON output
51
53
 
52
54
  ```bash
53
- usage daily --json
55
+ llm-usage daily --json
54
56
  ```
55
57
 
56
58
  ### Offline pricing (use cached LiteLLM pricing only)
57
59
 
58
60
  ```bash
59
- usage monthly --pricing-offline
61
+ llm-usage monthly --pricing-offline
60
62
  ```
61
63
 
62
64
  ### Override pricing URL
63
65
 
64
66
  ```bash
65
- usage monthly --pricing-url https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json
67
+ llm-usage monthly --pricing-url https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json
66
68
  ```
67
69
 
68
70
  ### Custom session directories
69
71
 
70
72
  ```bash
71
- usage daily --pi-dir /path/to/pi/sessions --codex-dir /path/to/codex/sessions
73
+ llm-usage daily --pi-dir /path/to/pi/sessions --codex-dir /path/to/codex/sessions
74
+ ```
75
+
76
+ ### Filter by source
77
+
78
+ Only codex rows:
79
+
80
+ ```bash
81
+ llm-usage monthly --source codex
82
+ ```
83
+
84
+ Only pi rows:
85
+
86
+ ```bash
87
+ llm-usage monthly --source pi
88
+ ```
89
+
90
+ Multiple sources (repeat or comma-separated):
91
+
92
+ ```bash
93
+ llm-usage monthly --source pi --source codex
94
+ llm-usage monthly --source pi,codex
72
95
  ```
73
96
 
74
97
  ## Output semantics
package/dist/index.js CHANGED
@@ -1262,12 +1262,29 @@ function normalizeProviderFilter(provider) {
1262
1262
  const normalized = provider.trim().toLowerCase();
1263
1263
  return normalized || void 0;
1264
1264
  }
1265
+ function normalizeSourceFilter(source) {
1266
+ if (!source || Array.isArray(source) && source.length === 0) {
1267
+ return void 0;
1268
+ }
1269
+ const sourceCandidates = Array.isArray(source) ? source : [source];
1270
+ const normalizedSources = sourceCandidates.flatMap((candidate) => candidate.split(",")).map((candidate) => candidate.trim().toLowerCase()).filter((candidate) => candidate.length > 0);
1271
+ if (normalizedSources.length === 0) {
1272
+ throw new Error("--source must contain at least one non-empty source id");
1273
+ }
1274
+ return new Set(normalizedSources);
1275
+ }
1265
1276
  function matchesProvider(provider, providerFilter) {
1266
1277
  if (!providerFilter) {
1267
1278
  return true;
1268
1279
  }
1269
1280
  return provider?.toLowerCase().includes(providerFilter) ?? false;
1270
1281
  }
1282
+ function matchesSource(source, sourceFilter) {
1283
+ if (!sourceFilter) {
1284
+ return true;
1285
+ }
1286
+ return sourceFilter.has(source.toLowerCase());
1287
+ }
1271
1288
  async function parseAdapterEvents(adapter) {
1272
1289
  const files = await adapter.discoverFiles();
1273
1290
  const parsedByFile = await Promise.all(files.map((filePath) => adapter.parseFile(filePath)));
@@ -1335,6 +1352,7 @@ async function buildUsageReport(granularity, options) {
1335
1352
  const timezone = options.timezone ?? Intl.DateTimeFormat().resolvedOptions().timeZone;
1336
1353
  validateTimezone(timezone);
1337
1354
  const providerFilter = normalizeProviderFilter(options.provider);
1355
+ const sourceFilter = normalizeSourceFilter(options.source);
1338
1356
  const effectiveProviderFilter = providerFilter ?? "openai";
1339
1357
  const pricingSource = await resolvePricingSource(options);
1340
1358
  const piAdapter = new PiSourceAdapter({
@@ -1348,11 +1366,11 @@ async function buildUsageReport(granularity, options) {
1348
1366
  parseAdapterEvents(piAdapter),
1349
1367
  parseAdapterEvents(codexAdapter)
1350
1368
  ]);
1351
- const providerFilteredEvents = [...piEvents, ...codexEvents].filter(
1352
- (event) => matchesProvider(event.provider, effectiveProviderFilter)
1369
+ const providerAndSourceFilteredEvents = [...piEvents, ...codexEvents].filter(
1370
+ (event) => matchesProvider(event.provider, effectiveProviderFilter) && matchesSource(event.source, sourceFilter)
1353
1371
  );
1354
1372
  const dateFilteredEvents = filterEventsByDateRange(
1355
- providerFilteredEvents,
1373
+ providerAndSourceFilteredEvents,
1356
1374
  timezone,
1357
1375
  options.since,
1358
1376
  options.until
@@ -1377,8 +1395,16 @@ async function runUsageReport(granularity, options) {
1377
1395
 
1378
1396
  // src/cli/create-cli.ts
1379
1397
  var defaultTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
1398
+ function collectSourceOption(value, previous) {
1399
+ return [...previous, ...value.split(",")];
1400
+ }
1380
1401
  function addSharedOptions(command) {
1381
- return command.option("--pi-dir <path>", "Path to .pi sessions directory").option("--codex-dir <path>", "Path to .codex sessions directory").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");
1402
+ return command.option("--pi-dir <path>", "Path to .pi sessions directory").option("--codex-dir <path>", "Path to .codex sessions directory").option(
1403
+ "--source <name>",
1404
+ "Filter by source id (repeatable or comma-separated, e.g. --source codex or --source pi,codex)",
1405
+ collectSourceOption,
1406
+ []
1407
+ ).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");
1382
1408
  }
1383
1409
  function commandDescription(granularity) {
1384
1410
  switch (granularity) {
@@ -1399,7 +1425,7 @@ function createCommand(granularity) {
1399
1425
  }
1400
1426
  function createCli() {
1401
1427
  const program = new Command();
1402
- program.name("usage").description("Aggregate local LLM usage metrics from pi and codex sessions").showHelpAfterError().addCommand(createCommand("daily")).addCommand(createCommand("weekly")).addCommand(createCommand("monthly"));
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"));
1403
1429
  return program;
1404
1430
  }
1405
1431
 
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 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 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('--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('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 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 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 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 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 providerFilteredEvents = [...piEvents, ...codexEvents].filter((event) =>\n matchesProvider(event.provider, effectiveProviderFilter),\n );\n\n const dateFilteredEvents = filterEventsByDateRange(\n providerFilteredEvents,\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/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":";;;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,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,WAAW,MAAM,WAAW;AAE/C,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,WAAW,OAAO;AAC3B;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;;;ACjKA,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;AACzE,UAAM,aAAa,CAAC,GAAG,KAAK,eAAe,KAAK,CAAC;AAEjD,eAAW,aAAa,YAAY;AAClC,YAAM,gBAAgB,WAAW,OAAO,CAAC,cAAc;AACrD,eAAO,UAAU,WAAW,SAAS;AAAA,MACvC,CAAC;AAED,UAAI,cAAc,SAAS,GAAG;AAC5B,eAAO,cAAc;AAAA,UACnB,CAAC,MAAM,UAAU,MAAM,SAAS,KAAK,UAAU,KAAK,cAAc,KAAK;AAAA,QACzE,EAAE,CAAC;AAAA,MACL;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;;;AC3fA,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,SAAS,YAAAC,iBAAgB;AACzB,OAAOC,SAAQ;AACf,OAAOC,WAAU;;;AC6CjB,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;;;AF1BA,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,UAAU,MAAMG,UAAS,UAAU,MAAM;AAC/C,UAAM,SAAuB,CAAC;AAE9B,UAAM,QAA2B;AAAA,MAC/B,WAAW,qBAAqB,QAAQ;AAAA,MACxC,UAAU;AAAA,IACZ;AAEA,eAAW,WAAW,QAAQ,MAAM,QAAQ,GAAG;AAC7C,YAAM,WAAW,QAAQ,KAAK;AAE9B,UAAI,CAAC,UAAU;AACb;AAAA,MACF;AAEA,UAAI;AAEJ,UAAI;AACF,qBAAa,KAAK,MAAM,QAAQ;AAAA,MAClC,QAAQ;AACN;AAAA,MACF;AAEA,YAAM,OAAOD,UAAS,UAAU;AAEhC,UAAI,CAAC,MAAM;AACT;AAAA,MACF;AAEA,UAAI,KAAK,SAAS,gBAAgB;AAChC,cAAME,WAAUF,UAAS,KAAK,OAAO;AACrC,cAAM,YAAY,OAAOE,UAAS,EAAE,KAAK,MAAM;AAC/C,cAAM,WAAW,OAAOA,UAAS,cAAc,KAAK,MAAM;AAC1D;AAAA,MACF;AAEA,UAAI,KAAK,SAAS,gBAAgB;AAChC,cAAMA,WAAUF,UAAS,KAAK,OAAO;AACrC,cAAM,QAAQ,OAAOE,UAAS,KAAK,KAAK,MAAM;AAC9C;AAAA,MACF;AAEA,UAAI,KAAK,SAAS,aAAa;AAC7B;AAAA,MACF;AAEA,YAAM,UAAUF,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;;;AG5PA,SAAS,YAAAG,iBAAgB;AACzB,OAAOC,SAAQ;AACf,OAAOC,WAAU;AAQjB,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,aAAO,IAAI,KAAK,SAAS,EAAE,YAAY;AAAA,IACzC;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,UAAU,MAAMM,UAAS,UAAU,MAAM;AAC/C,UAAM,SAAuB,CAAC;AAC9B,UAAM,QAAwB,EAAE,WAAWD,sBAAqB,QAAQ,EAAE;AAE1E,eAAW,WAAW,QAAQ,MAAM,QAAQ,GAAG;AAC7C,YAAM,WAAW,QAAQ,KAAK;AAE9B,UAAI,CAAC,UAAU;AACb;AAAA,MACF;AAEA,UAAI;AAEJ,UAAI;AACF,qBAAa,KAAK,MAAM,QAAQ;AAAA,MAClC,QAAQ;AACN;AAAA,MACF;AAEA,YAAM,OAAOF,UAAS,UAAU;AAEhC,UAAI,CAAC,MAAM;AACT;AAAA,MACF;AAEA,UAAI,KAAK,SAAS,WAAW;AAC3B,cAAM,YAAYC,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,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,gBACP,UACA,gBACS;AACT,MAAI,CAAC,gBAAgB;AACnB,WAAO;AAAA,EACT;AAEA,SAAO,UAAU,YAAY,EAAE,SAAS,cAAc,KAAK;AAC7D;AAEA,SAAS,cAAc,QAAgB,cAAgD;AACrF,MAAI,CAAC,cAAc;AACjB,WAAO;AAAA,EACT;AAEA,SAAO,aAAa,IAAI,OAAO,YAAY,CAAC;AAC9C;AAEA,eAAe,mBAAmB,SAA+C;AAC/E,QAAM,QAAQ,MAAM,QAAQ,cAAc;AAC1C,QAAM,eAAe,MAAM,QAAQ,IAAI,MAAM,IAAI,CAAC,aAAa,QAAQ,UAAU,QAAQ,CAAC,CAAC;AAE3F,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,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;AAClD,QAAM,gBAAgB,MAAM,qBAAqB,OAAO;AAExD,QAAM,YAAY,IAAI,gBAAgB;AAAA,IACpC,aAAa,QAAQ;AAAA,IACrB,gBAAgB,CAAC,aAAa,gBAAgB,UAAU,uBAAuB;AAAA,EACjF,CAAC;AACD,QAAM,eAAe,IAAI,mBAAmB;AAAA,IAC1C,aAAa,QAAQ;AAAA,EACvB,CAAC;AAED,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,QAAQ,IAAI;AAAA,IAChD,mBAAmB,SAAS;AAAA,IAC5B,mBAAmB,YAAY;AAAA,EACjC,CAAC;AAED,QAAM,kCAAkC,CAAC,GAAG,UAAU,GAAG,WAAW,EAAE;AAAA,IACpE,CAAC,UACC,gBAAgB,MAAM,UAAU,uBAAuB,KACvD,cAAc,MAAM,QAAQ,YAAY;AAAA,EAC5C;AAEA,QAAM,qBAAqB;AAAA,IACzB;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAEA,QAAM,eAAe,qBAAqB,oBAAoB,aAAa;AAC3E,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;;;Ad5NA,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;AAEO,SAAS,YAAqB;AACnC,QAAM,UAAU,IAAI,QAAQ;AAE5B,UACG,KAAK,WAAW,EAChB,YAAY,8DAA8D,EAC1E,mBAAmB,EACnB,WAAW,cAAc,OAAO,CAAC,EACjC,WAAW,cAAc,QAAQ,CAAC,EAClC,WAAW,cAAc,SAAS,CAAC;AAEtC,SAAO;AACT;;;Ae7EA,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","readFile","os","path","path","path","os","asRecord","readFile","payload","readFile","os","path","defaultSessionsDir","path","os","asRecord","asText","getFallbackSessionId","readFile"]}
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "llm-usage-metrics",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "CLI for aggregating local LLM usage metrics from pi and codex sessions",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": ">=20"
8
8
  },
9
9
  "bin": {
10
- "usage": "dist/index.js"
10
+ "llm-usage": "dist/index.js"
11
11
  },
12
12
  "files": [
13
13
  "dist",
@@ -19,10 +19,13 @@
19
19
  "cli": "bun run src/cli/index.ts",
20
20
  "lint": "eslint .",
21
21
  "typecheck": "tsc --noEmit",
22
- "test": "vitest run",
22
+ "test": "vitest run --coverage",
23
23
  "format:check": "prettier --check .",
24
24
  "format:write": "prettier --write .",
25
- "pack:check": "npm pack"
25
+ "pack:check": "npm pack",
26
+ "release": "release-it",
27
+ "release:dry": "release-it --dry-run",
28
+ "release:ci": "release-it --ci"
26
29
  },
27
30
  "keywords": [
28
31
  "llm",
@@ -32,6 +35,14 @@
32
35
  "codex"
33
36
  ],
34
37
  "license": "MIT",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "https://github.com/ayagmar/llm-usage-metrics"
41
+ },
42
+ "bugs": {
43
+ "url": "https://github.com/ayagmar/llm-usage-metrics/issues"
44
+ },
45
+ "homepage": "https://github.com/ayagmar/llm-usage-metrics",
35
46
  "dependencies": {
36
47
  "commander": "^13.1.0",
37
48
  "markdown-table": "^3.0.4",
@@ -40,10 +51,13 @@
40
51
  },
41
52
  "devDependencies": {
42
53
  "@eslint/js": "^9.20.0",
54
+ "@release-it/conventional-changelog": "^10.0.5",
43
55
  "@types/node": "^22.13.10",
56
+ "@vitest/coverage-v8": "3.2.4",
44
57
  "eslint": "^9.20.0",
45
58
  "eslint-config-prettier": "^10.0.1",
46
59
  "prettier": "^3.5.2",
60
+ "release-it": "^19.2.4",
47
61
  "tsup": "^8.3.6",
48
62
  "typescript": "^5.7.3",
49
63
  "typescript-eslint": "^8.24.1",