mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-17 13:13:35 +08:00
Expose local Claude Code CLI transcript usage in Settings so users can inspect recent token consumption and daily activity without leaving the desktop app. The page uses server-side transcript aggregation for session, message, tool, model-token, and subagent token data. Daily token buckets use assistant message timestamps, and daily session counts use active parent sessions for the same date bucket so resumed sessions and cross-midnight work do not produce token-only days. Cache accounting is bumped to v5 to force recomputation under the corrected daily semantics. Constraint: Usage data must come from local Claude Code CLI transcripts rather than mock/demo data. Constraint: Desktop navigation keeps Token usage directly above Diagnostics. Rejected: Bucket all token usage by session start date | hides resumed-session and cross-midnight consumption from the actual day it was spent. Confidence: high Scope-risk: moderate Directive: Keep daily token and daily session counts on the same date-bucketing semantics. Tested: bun run check:desktop Tested: bun run check:server Tested: Browser verification for Token usage in English and Chinese locale date labels Not-tested: Full bun run verify quality gate
44 lines
1.1 KiB
TypeScript
44 lines
1.1 KiB
TypeScript
import {
|
|
aggregateClaudeCodeStatsForRange,
|
|
type StatsDateRange,
|
|
} from '../../utils/stats.js'
|
|
import { ApiError, errorResponse } from '../middleware/errorHandler.js'
|
|
|
|
const VALID_RANGES = new Set<StatsDateRange>(['7d', '30d', 'all'])
|
|
|
|
export async function handleActivityStatsApi(
|
|
req: Request,
|
|
_url: URL,
|
|
segments: string[],
|
|
): Promise<Response> {
|
|
try {
|
|
if (req.method !== 'GET') {
|
|
throw methodNotAllowed(req.method)
|
|
}
|
|
|
|
const requestedRange = segments[2]
|
|
const range: StatsDateRange = requestedRange === undefined ? 'all' : parseRange(requestedRange)
|
|
const stats = await aggregateClaudeCodeStatsForRange(range)
|
|
|
|
return Response.json({
|
|
stats,
|
|
range,
|
|
generatedAt: new Date().toISOString(),
|
|
})
|
|
} catch (error) {
|
|
return errorResponse(error)
|
|
}
|
|
}
|
|
|
|
function parseRange(range: string): StatsDateRange {
|
|
if (VALID_RANGES.has(range as StatsDateRange)) {
|
|
return range as StatsDateRange
|
|
}
|
|
|
|
throw ApiError.badRequest(`Unknown activity stats range: ${range}`)
|
|
}
|
|
|
|
function methodNotAllowed(method: string): ApiError {
|
|
return new ApiError(405, `Method ${method} not allowed`, 'METHOD_NOT_ALLOWED')
|
|
}
|