agent-usage-report 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md ADDED
@@ -0,0 +1,78 @@
1
+ # AGENTS.md - agent-usage-report
2
+
3
+ ## Scope
4
+ - This is a standalone TypeScript CLI package intended for `pnpm`, `bun`, `node`, and eventual `npx` usage.
5
+
6
+ ## Purpose
7
+ - Generate a self-contained local agent usage HTML report and JSON payload
8
+ - Maintain feature parity with the Python implementation in the sibling local project
9
+ - Support current local providers:
10
+ - Codex
11
+ - Claude Code
12
+ - OpenCode
13
+ - Pi Coding Agent
14
+ - Preserve provider-aware reporting:
15
+ - per-provider views
16
+ - combined view
17
+ - daily totals
18
+ - monthly rollups
19
+ - Codex and Claude monthly spend-vs-plan comparison
20
+ - Claude `history.jsonl` activity-only fallback days
21
+
22
+ ## Primary Entry Points
23
+ - CLI entry: `src/cli.ts`
24
+ - Core logic: `src/generator.ts`
25
+ - HTML template: `src/template.html`
26
+ - Tests: `test/generator.test.ts`
27
+
28
+ ## Build And Test
29
+ - Install: `pnpm install`
30
+ - Typecheck: `pnpm run typecheck`
31
+ - Test: `pnpm run test`
32
+ - Build: `pnpm run build`
33
+ - Smoke run:
34
+ - `node dist/cli.js --help`
35
+ - `bun run src/cli.ts --help`
36
+
37
+ ## Packaging Expectations
38
+ - Package name: `agent-usage-report`
39
+ - Intended runner UX:
40
+ - `npx agent-usage-report@latest`
41
+ - `bunx agent-usage-report@latest`
42
+ - Do not introduce native dependencies unless strictly required for distribution.
43
+ - Prefer Node built-ins over native modules when possible so package-runner adoption stays easy.
44
+
45
+ ## Current CLI Surface
46
+ - `--codex-home`
47
+ - `--claude-config-dir`
48
+ - `--opencode-dir`
49
+ - `--pi-agent-dir`
50
+ - `--timezone`
51
+ - `--output-html`
52
+ - `--output-json`
53
+ - `--skip-archived`
54
+
55
+ ## Implementation Notes
56
+ - Keep the JSON payload shape aligned with the Python project:
57
+ - `schemaVersion`
58
+ - `providerOrder`
59
+ - `providers`
60
+ - `combined`
61
+ - top-level default-provider mirrors
62
+ - Claude `history.jsonl` days are activity-only:
63
+ - they should render in the heatmap
64
+ - they must not contribute to token totals or spend
65
+ - OpenCode should prefer `opencode.db` when available and fall back to `storage/message/**/*.json`
66
+
67
+ ## Editing Guidance
68
+ - Prefer targeted edits in `src/generator.ts` until the port stabilizes
69
+ - If changing the UI, edit `src/template.html` or the template-loading flow carefully
70
+ - After behavior changes:
71
+ - rerun `pnpm run typecheck`
72
+ - rerun `pnpm run test`
73
+ - rerun `pnpm run build`
74
+
75
+ ## Non-Goals
76
+ - Do not turn this into a monorepo unless clearly needed
77
+ - Do not add a backend here
78
+ - Do not reduce current provider/report parity with the Python version
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ignacio Alonso
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # agent-usage-report
2
+
3
+ `agent-usage-report` scans local agent usage data on your machine and generates a self-contained HTML report you can open directly in the browser.
4
+
5
+ Current providers:
6
+ - Codex
7
+ - Claude Code
8
+ - OpenCode
9
+ - Pi Coding Agent
10
+
11
+ Features:
12
+ - GitHub-style daily heatmap
13
+ - Per-provider and combined views
14
+ - Estimated token-cost reporting
15
+ - Daily cost tables
16
+ - Monthly spend-vs-plan comparison for Codex and Claude
17
+ - Claude legacy activity fallback from `history.jsonl`
18
+
19
+ ## Quick Start
20
+
21
+ Run without installing globally:
22
+
23
+ ```bash
24
+ npx agent-usage-report@latest
25
+ ```
26
+
27
+ With Bun:
28
+
29
+ ```bash
30
+ bunx agent-usage-report@latest
31
+ ```
32
+
33
+ Local development:
34
+
35
+ ```bash
36
+ pnpm install
37
+ pnpm run build
38
+ node dist/cli.js
39
+ ```
40
+
41
+ ## Usage
42
+
43
+ ```bash
44
+ agent-usage-report \
45
+ --codex-home ~/.codex \
46
+ --claude-config-dir ~/.config/claude,~/.claude \
47
+ --opencode-dir ~/.local/share/opencode \
48
+ --pi-agent-dir ~/.pi/agent \
49
+ --timezone America/Mexico_City \
50
+ --output-html agent-usage-report.html \
51
+ --output-json agent-usage-data.json
52
+ ```
53
+
54
+ Flags:
55
+ - `--codex-home`: Codex home directory
56
+ - `--claude-config-dir`: Claude config directory or comma-separated directories
57
+ - `--opencode-dir`: OpenCode data directory
58
+ - `--pi-agent-dir`: Pi Coding Agent directory or sessions directory
59
+ - `--timezone`: IANA timezone for day bucketing
60
+ - `--output-html`: HTML output path
61
+ - `--output-json`: JSON output path
62
+ - `--skip-archived`: skip `~/.codex/archived_sessions`
63
+
64
+ ## Privacy
65
+
66
+ The CLI reads local usage data and generates local files.
67
+
68
+ The generated HTML and JSON reports stay on your machine unless you choose to share them manually.
69
+
70
+ ## Development
71
+
72
+ ```bash
73
+ pnpm install
74
+ pnpm run typecheck
75
+ pnpm run test
76
+ pnpm run build
77
+ ```
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { main } from "./generator.js";
3
+ main().catch((error) => {
4
+ console.error(error instanceof Error ? error.message : String(error));
5
+ process.exit(1);
6
+ });
7
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAEtC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACtE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,112 @@
1
+ declare const FREE_MODEL_PRICING: {
2
+ inputCostPerMToken: number;
3
+ cachedInputCostPerMToken: number;
4
+ outputCostPerMToken: number;
5
+ };
6
+ declare const SCAN_OUTPUT_KEYS: readonly ["filesScanned", "jsonlFilesScanned", "jsonFilesScanned", "parseErrors", "nullInfoEventsSkipped", "duplicateEventsSkipped", "syntheticEventsSkipped", "zeroTotalEventsSkipped", "tokenEventsCounted", "unsupportedLegacyFiles", "activityOnlyDays"];
7
+ type Pricing = typeof FREE_MODEL_PRICING;
8
+ type ScanKey = (typeof SCAN_OUTPUT_KEYS)[number];
9
+ export interface CliArgs {
10
+ codexHome: string;
11
+ claudeConfigDir: string | null;
12
+ opencodeDir: string | null;
13
+ piAgentDir: string | null;
14
+ timezone: string;
15
+ outputHtml: string;
16
+ outputJson: string;
17
+ skipArchived: boolean;
18
+ }
19
+ interface UsageTotals {
20
+ input_tokens: number;
21
+ cached_input_tokens: number;
22
+ output_tokens: number;
23
+ reasoning_output_tokens: number;
24
+ total_tokens: number;
25
+ }
26
+ interface CostBreakdown {
27
+ inputUSD: number;
28
+ cachedInputUSD: number;
29
+ outputUSD: number;
30
+ totalUSD: number;
31
+ }
32
+ interface ModelUsageAccumulator extends UsageTotals {
33
+ events: number;
34
+ is_fallback_model: boolean;
35
+ }
36
+ interface DailyUsageAccumulator extends UsageTotals {
37
+ events: number;
38
+ model_usage: Record<string, ModelUsageAccumulator>;
39
+ }
40
+ export interface DailyReportRow {
41
+ date: string;
42
+ inputTokens: number;
43
+ cachedInputTokens: number;
44
+ outputTokens: number;
45
+ reasoningTokens: number;
46
+ totalTokens: number;
47
+ events: number;
48
+ costBreakdownUSD: CostBreakdown;
49
+ costUSD: number;
50
+ modelTotals: Record<string, number>;
51
+ modelBreakdown: Array<Record<string, unknown>>;
52
+ displayValue: number;
53
+ }
54
+ export interface MonthlyReportRow {
55
+ month: string;
56
+ inputTokens: number;
57
+ cachedInputTokens: number;
58
+ outputTokens: number;
59
+ reasoningTokens: number;
60
+ totalTokens: number;
61
+ events: number;
62
+ activeDays: number;
63
+ costBreakdownUSD: CostBreakdown;
64
+ costUSD: number;
65
+ modelTotals: Record<string, number>;
66
+ modelBreakdown: Array<Record<string, unknown>>;
67
+ }
68
+ interface ProviderReport {
69
+ providerId: string;
70
+ providerLabel: string;
71
+ providerShortLabel: string;
72
+ sourceHome: string | null;
73
+ days: DailyReportRow[];
74
+ monthly: MonthlyReportRow[];
75
+ pricing: {
76
+ source: string;
77
+ sourceLabel: string;
78
+ url: string;
79
+ missingModels: string[];
80
+ };
81
+ costTotalsUSD: CostBreakdown;
82
+ scan: Record<ScanKey, number>;
83
+ }
84
+ interface ProviderScanResult {
85
+ providerId: string;
86
+ providerLabel: string;
87
+ providerShortLabel: string;
88
+ sourceHome: string | null;
89
+ dailyUsage: Map<string, DailyUsageAccumulator>;
90
+ displayValuesByDay: Map<string, number>;
91
+ stats: Record<ScanKey, number>;
92
+ }
93
+ interface ResolvedPricing {
94
+ requestedModel: string;
95
+ resolvedModel: string | null;
96
+ pricing: Pricing;
97
+ isMissing: boolean;
98
+ isAlias: boolean;
99
+ source: string;
100
+ }
101
+ export declare function fetchLiteLLMPricingDataset(): Promise<Record<string, Record<string, unknown>>>;
102
+ export declare function resolveModelPricing(model: string, dataset: Record<string, Record<string, unknown>> | null): ResolvedPricing;
103
+ export declare function calculateCostBreakdown(usage: UsageTotals, pricing: Pricing): CostBreakdown;
104
+ export declare function providerScanHasUsage(scan: ProviderScanResult): boolean;
105
+ export declare function buildMonthlyRollups(reportDays: DailyReportRow[]): MonthlyReportRow[];
106
+ export declare function buildCombinedReport(providerReports: ProviderReport[]): ProviderReport;
107
+ export declare function buildReportPayload(providerScans: ProviderScanResult[], timeZone: string): Promise<Record<string, unknown>>;
108
+ export declare function writeOutput(report: Record<string, unknown>, outputHtml: string, outputJson: string): Promise<void>;
109
+ export declare function parseCliArgs(argv: string[]): CliArgs;
110
+ export declare function generateReport(args: CliArgs): Promise<Record<string, unknown>>;
111
+ export declare function main(argv?: string[]): Promise<void>;
112
+ export {};