Skip to content

Commit a721af3

Browse files
committed
feat(analytics): add usage analytics page with caching layer
- Add Analytics page with usage trends, model breakdown, sessions table - Add server-side caching layer for better-ccusage data (TTL-based) - Add request coalescing to prevent duplicate concurrent API calls - Add /api/usage/refresh endpoint to manually clear cache - Add date-range filter, summary cards, trend charts components - Fix API parameter mismatch (since/until in YYYYMMDD format) - Wire up Refresh button with loading state animation
1 parent 57032ee commit a721af3

File tree

17 files changed

+1961
-2
lines changed

17 files changed

+1961
-2
lines changed

bun.lock

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"": {
55
"name": "@kaitranntt/ccs",
66
"dependencies": {
7+
"better-ccusage": "^1.2.6",
78
"boxen": "^8.0.1",
89
"chalk": "^5.6.2",
910
"chokidar": "^5.0.0",
@@ -445,6 +446,8 @@
445446

446447
"before-after-hook": ["[email protected]", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="],
447448

449+
"better-ccusage": ["[email protected]", "", { "bin": { "better-ccusage": "dist/index.js" } }, "sha512-IZCYBX1kF0IfJ6ho9JMwLKn2o820WRiVGZ+2tVS2olODU5J7Np5mJ1j1i5HtazZPNo2S9wKU9C9iysc0f8Cjqw=="],
450+
448451
"body-parser": ["[email protected]", "", { "dependencies": { "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "~1.2.0", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", "qs": "~6.14.0", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" } }, "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA=="],
449452

450453
"bottleneck": ["[email protected]", "", {}, "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw=="],

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
"postinstall": "node scripts/postinstall.js"
8080
},
8181
"dependencies": {
82+
"better-ccusage": "^1.2.6",
8283
"boxen": "^8.0.1",
8384
"chalk": "^5.6.2",
8485
"chokidar": "^5.0.0",

src/types/external.d.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,81 @@
22
* Type shims for incomplete external dependencies
33
*/
44

5+
// better-ccusage types (package has JS exports but incomplete TS subpath support)
6+
declare module 'better-ccusage/data-loader' {
7+
export interface ModelBreakdown {
8+
modelName: string;
9+
inputTokens: number;
10+
outputTokens: number;
11+
cacheCreationTokens: number;
12+
cacheReadTokens: number;
13+
cost: number;
14+
}
15+
16+
export interface DailyUsage {
17+
date: string;
18+
source: string;
19+
inputTokens: number;
20+
outputTokens: number;
21+
cacheCreationTokens: number;
22+
cacheReadTokens: number;
23+
cost: number;
24+
totalCost: number;
25+
modelsUsed: string[];
26+
modelBreakdowns: ModelBreakdown[];
27+
}
28+
29+
export interface MonthlyUsage {
30+
month: string;
31+
source: string;
32+
inputTokens: number;
33+
outputTokens: number;
34+
cacheCreationTokens: number;
35+
cacheReadTokens: number;
36+
totalCost: number;
37+
modelsUsed: string[];
38+
modelBreakdowns: ModelBreakdown[];
39+
}
40+
41+
export interface SessionUsage {
42+
sessionId: string;
43+
projectPath: string;
44+
inputTokens: number;
45+
outputTokens: number;
46+
cacheCreationTokens: number;
47+
cacheReadTokens: number;
48+
cost: number;
49+
totalCost: number;
50+
lastActivity: string;
51+
versions: string[];
52+
modelsUsed: string[];
53+
modelBreakdowns: ModelBreakdown[];
54+
source: string;
55+
}
56+
57+
export interface DataLoaderOptions {
58+
mode?: 'calculate' | 'cached';
59+
claudePaths?: string[];
60+
}
61+
62+
export function loadDailyUsageData(options?: DataLoaderOptions): Promise<DailyUsage[]>;
63+
export function loadMonthlyUsageData(options?: DataLoaderOptions): Promise<MonthlyUsage[]>;
64+
export function loadSessionData(options?: DataLoaderOptions): Promise<SessionUsage[]>;
65+
}
66+
67+
declare module 'better-ccusage/calculate-cost' {
68+
export interface Totals {
69+
inputTokens: number;
70+
outputTokens: number;
71+
cacheCreationTokens: number;
72+
cacheReadTokens: number;
73+
costUSD: number;
74+
}
75+
76+
export function calculateTotals(entries: unknown[]): Totals;
77+
export function getTotalTokens(entries: unknown[]): number;
78+
}
79+
580
declare module 'cli-table3' {
681
interface TableOptions {
782
head?: string[];

src/web-server/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ export async function startServer(options: ServerOptions): Promise<ServerInstanc
4747
const { overviewRoutes } = await import('./overview-routes');
4848
app.use('/api/overview', overviewRoutes);
4949

50+
// Usage analytics routes
51+
const { usageRoutes } = await import('./usage-routes');
52+
app.use('/api/usage', usageRoutes);
53+
5054
// Dev mode: use Vite middleware for HMR
5155
if (options.dev) {
5256
const { createServer: createViteServer } = await import('vite');

0 commit comments

Comments
 (0)