@kaitranntt/ccs 7.74.0-dev.11 → 7.74.0-dev.12

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.
Files changed (77) hide show
  1. package/dist/ui/assets/{accounts-ld6H4qVb.js → accounts-oSQYCZNY.js} +1 -1
  2. package/dist/ui/assets/{alert-dialog-BZ2goqtW.js → alert-dialog-RxXcYLJ-.js} +1 -1
  3. package/dist/ui/assets/{api-CthvA4sH.js → api-PDxwDKK6.js} +1 -1
  4. package/dist/ui/assets/{auth-section-o5qejfmB.js → auth-section-DGvyapUL.js} +1 -1
  5. package/dist/ui/assets/{backups-section-DH2I1yD9.js → backups-section-C_DMj0hU.js} +1 -1
  6. package/dist/ui/assets/{channels-PcQ1oO0o.js → channels-Bhrnp-Eo.js} +1 -1
  7. package/dist/ui/assets/{checkbox-DehBjrNH.js → checkbox-wRKw3eVf.js} +1 -1
  8. package/dist/ui/assets/{claude-extension-PyHYNmSG.js → claude-extension-BRPDX6oS.js} +1 -1
  9. package/dist/ui/assets/{cliproxy-lrpHXFMA.js → cliproxy-BsJfKNlA.js} +1 -1
  10. package/dist/ui/assets/{cliproxy-ai-providers-BEHZy9gR.js → cliproxy-ai-providers-CNZ8X477.js} +1 -1
  11. package/dist/ui/assets/{cliproxy-control-panel-34szpAk4.js → cliproxy-control-panel-BtjDR1Vg.js} +1 -1
  12. package/dist/ui/assets/{codex-UKB2QUN1.js → codex-BIPn4i-A.js} +1 -1
  13. package/dist/ui/assets/{confirm-dialog-64G67ESH.js → confirm-dialog-B8tZHrIu.js} +1 -1
  14. package/dist/ui/assets/{copilot-Bd-ViL4n.js → copilot-6YzLT_A3.js} +1 -1
  15. package/dist/ui/assets/{cursor-DH3z6BLJ.js → cursor-CrQ7YslH.js} +1 -1
  16. package/dist/ui/assets/{droid-Cng3BkQg.js → droid-Cpt_b7co.js} +1 -1
  17. package/dist/ui/assets/{globalenv-section-D3aT-fYK.js → globalenv-section-DwhnjKxb.js} +1 -1
  18. package/dist/ui/assets/{health-BRqYO0xG.js → health-MX_poTtR.js} +1 -1
  19. package/dist/ui/assets/{index-FPQVUJ_q.js → index-BNt4L6D7.js} +1 -1
  20. package/dist/ui/assets/{index-B2XY4MhT.js → index-CQlxBYgL.js} +2 -2
  21. package/dist/ui/assets/{index-CDoChLrM.js → index-DD888_1t.js} +1 -1
  22. package/dist/ui/assets/{index-DLIKuUBI.js → index-DFCrszOj.js} +1 -1
  23. package/dist/ui/assets/{index-DA5G610p.js → index-DfDJ6OTa.js} +1 -1
  24. package/dist/ui/assets/{index-1MP3pDe8.js → index-Dz2HaMuj.js} +1 -1
  25. package/dist/ui/assets/index-EEfi3_jA.js +1 -0
  26. package/dist/ui/assets/{logs-DNAtdY6A.js → logs-D5iryHgo.js} +1 -1
  27. package/dist/ui/assets/{masked-input-B4x8rBbs.js → masked-input-Bs2HnuUO.js} +1 -1
  28. package/dist/ui/assets/{proxy-status-widget-EuDHDJAP.js → proxy-status-widget-Bs4OIbHk.js} +1 -1
  29. package/dist/ui/assets/{raw-json-settings-editor-panel-DZRYrywv.js → raw-json-settings-editor-panel-wRXnigfA.js} +1 -1
  30. package/dist/ui/assets/{searchable-select-CwJpfFVb.js → searchable-select-B4APb47u.js} +1 -1
  31. package/dist/ui/assets/{separator-CD-HBQ24.js → separator-ny9jcCJ0.js} +1 -1
  32. package/dist/ui/assets/{shared-BlqdoNm4.js → shared-BHFt3N-w.js} +1 -1
  33. package/dist/ui/assets/{table-FmKRMU4a.js → table-BRAlw_wh.js} +1 -1
  34. package/dist/ui/assets/{updates-Bo-HBoN0.js → updates-wB9lmrfM.js} +1 -1
  35. package/dist/ui/index.html +1 -1
  36. package/dist/web-server/jsonl-parser.d.ts.map +1 -1
  37. package/dist/web-server/jsonl-parser.js +22 -4
  38. package/dist/web-server/jsonl-parser.js.map +1 -1
  39. package/dist/web-server/model-pricing.d.ts +3 -3
  40. package/dist/web-server/model-pricing.d.ts.map +1 -1
  41. package/dist/web-server/model-pricing.js +5 -12
  42. package/dist/web-server/model-pricing.js.map +1 -1
  43. package/dist/web-server/usage/aggregator.d.ts +1 -0
  44. package/dist/web-server/usage/aggregator.d.ts.map +1 -1
  45. package/dist/web-server/usage/aggregator.js +71 -4
  46. package/dist/web-server/usage/aggregator.js.map +1 -1
  47. package/dist/web-server/usage/cliproxy-usage-syncer.d.ts +0 -15
  48. package/dist/web-server/usage/cliproxy-usage-syncer.d.ts.map +1 -1
  49. package/dist/web-server/usage/cliproxy-usage-syncer.js +159 -55
  50. package/dist/web-server/usage/cliproxy-usage-syncer.js.map +1 -1
  51. package/dist/web-server/usage/cliproxy-usage-transformer.d.ts +22 -7
  52. package/dist/web-server/usage/cliproxy-usage-transformer.d.ts.map +1 -1
  53. package/dist/web-server/usage/cliproxy-usage-transformer.js +166 -20
  54. package/dist/web-server/usage/cliproxy-usage-transformer.js.map +1 -1
  55. package/dist/web-server/usage/codex-native-usage-collector.d.ts +11 -0
  56. package/dist/web-server/usage/codex-native-usage-collector.d.ts.map +1 -0
  57. package/dist/web-server/usage/codex-native-usage-collector.js +177 -0
  58. package/dist/web-server/usage/codex-native-usage-collector.js.map +1 -0
  59. package/dist/web-server/usage/data-aggregator.d.ts.map +1 -1
  60. package/dist/web-server/usage/data-aggregator.js +1 -0
  61. package/dist/web-server/usage/data-aggregator.js.map +1 -1
  62. package/dist/web-server/usage/droid-native-usage-collector.d.ts +14 -0
  63. package/dist/web-server/usage/droid-native-usage-collector.d.ts.map +1 -0
  64. package/dist/web-server/usage/droid-native-usage-collector.js +201 -0
  65. package/dist/web-server/usage/droid-native-usage-collector.js.map +1 -0
  66. package/dist/web-server/usage/handlers.d.ts +1 -0
  67. package/dist/web-server/usage/handlers.d.ts.map +1 -1
  68. package/dist/web-server/usage/handlers.js +147 -42
  69. package/dist/web-server/usage/handlers.js.map +1 -1
  70. package/dist/web-server/usage/sqlite-cli.d.ts +3 -0
  71. package/dist/web-server/usage/sqlite-cli.d.ts.map +1 -0
  72. package/dist/web-server/usage/sqlite-cli.js +62 -0
  73. package/dist/web-server/usage/sqlite-cli.js.map +1 -0
  74. package/dist/web-server/usage/types.d.ts +5 -0
  75. package/dist/web-server/usage/types.d.ts.map +1 -1
  76. package/package.json +1 -1
  77. package/dist/ui/assets/index-DJmpCPja.js +0 -1
@@ -0,0 +1,201 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.scanDroidNativeUsageEntries = void 0;
27
+ const fs = __importStar(require("fs"));
28
+ const path = __importStar(require("path"));
29
+ const droid_dashboard_service_1 = require("../services/droid-dashboard-service");
30
+ const ui_1 = require("../../utils/ui");
31
+ const sqlite_cli_1 = require("./sqlite-cli");
32
+ const DROID_COST_BATCH_SIZE = 1000;
33
+ function isObject(value) {
34
+ return typeof value === 'object' && value !== null;
35
+ }
36
+ function asString(value) {
37
+ return typeof value === 'string' && value.trim().length > 0 ? value.trim() : null;
38
+ }
39
+ function asNumber(value) {
40
+ const numeric = typeof value === 'number' ? value : Number(value);
41
+ return Number.isFinite(numeric) && numeric >= 0 ? numeric : null;
42
+ }
43
+ function isCcsManagedSelector(selector) {
44
+ return selector.startsWith('custom:CCS-') || selector.startsWith('custom:ccs-');
45
+ }
46
+ function buildSelectorAlias(displayName, index) {
47
+ return `${displayName.trim().replace(/\s+/g, '-')}-${index}`;
48
+ }
49
+ function normalizeCustomModels(settings) {
50
+ const raw = settings.customModels ?? settings.custom_models;
51
+ if (Array.isArray(raw)) {
52
+ return raw.filter((entry) => isObject(entry));
53
+ }
54
+ if (isObject(raw)) {
55
+ return Object.values(raw).filter((entry) => isObject(entry));
56
+ }
57
+ return [];
58
+ }
59
+ function buildCustomModelMap(settingsPath) {
60
+ const selectors = new Map();
61
+ if (!fs.existsSync(settingsPath))
62
+ return selectors;
63
+ try {
64
+ const parsed = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
65
+ if (!isObject(parsed))
66
+ return selectors;
67
+ for (const [index, entry] of normalizeCustomModels(parsed).entries()) {
68
+ const displayName = asString(entry.displayName ?? entry.model_display_name);
69
+ const model = asString(entry.model);
70
+ if (!displayName || !model)
71
+ continue;
72
+ selectors.set(`custom:${buildSelectorAlias(displayName, index)}`, model);
73
+ }
74
+ }
75
+ catch {
76
+ return selectors;
77
+ }
78
+ return selectors;
79
+ }
80
+ function collectSessionFiles(dir, suffix, results = []) {
81
+ if (!fs.existsSync(dir))
82
+ return results;
83
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
84
+ const entryPath = path.join(dir, entry.name);
85
+ if (entry.isDirectory()) {
86
+ collectSessionFiles(entryPath, suffix, results);
87
+ continue;
88
+ }
89
+ if (entry.isFile() && entry.name.endsWith(suffix)) {
90
+ results.push(entryPath);
91
+ }
92
+ }
93
+ return results;
94
+ }
95
+ function readSessionStartMetadata(filePath) {
96
+ try {
97
+ const firstLine = fs.readFileSync(filePath, 'utf8').split('\n')[0];
98
+ const parsed = JSON.parse(firstLine);
99
+ if (parsed?.type !== 'session_start')
100
+ return null;
101
+ return {
102
+ projectPath: asString(parsed.cwd) ?? '/',
103
+ version: typeof parsed.version === 'number'
104
+ ? String(parsed.version)
105
+ : (asString(parsed.version) ?? undefined),
106
+ };
107
+ }
108
+ catch {
109
+ return null;
110
+ }
111
+ }
112
+ function loadSessionMetadata(factoryDir) {
113
+ const metadata = new Map();
114
+ const selectorMap = buildCustomModelMap(path.join(factoryDir, 'settings.json'));
115
+ const sessionsDir = path.join(factoryDir, 'sessions');
116
+ for (const settingsPath of collectSessionFiles(sessionsDir, '.settings.json')) {
117
+ const sessionId = path.basename(settingsPath, '.settings.json');
118
+ const jsonlPath = settingsPath.replace(/\.settings\.json$/, '.jsonl');
119
+ const start = readSessionStartMetadata(jsonlPath);
120
+ if (!start)
121
+ continue;
122
+ try {
123
+ const parsed = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
124
+ const rawSelector = asString(parsed?.model) ?? 'unknown-droid-model';
125
+ metadata.set(sessionId, {
126
+ model: selectorMap.get(rawSelector) ?? rawSelector,
127
+ projectPath: start.projectPath,
128
+ rawSelector,
129
+ version: start.version,
130
+ });
131
+ }
132
+ catch {
133
+ continue;
134
+ }
135
+ }
136
+ return metadata;
137
+ }
138
+ async function scanDroidNativeUsageEntries(options = {}) {
139
+ const query = options.querySqliteJson ?? sqlite_cli_1.querySqliteJson;
140
+ const { settingsPath } = (0, droid_dashboard_service_1.resolveDroidConfigPaths)({
141
+ env: options.env,
142
+ homeDir: options.homeDir,
143
+ });
144
+ const factoryDir = path.dirname(settingsPath);
145
+ const costsDbPath = path.join(factoryDir, 'costs.db');
146
+ if (!fs.existsSync(costsDbPath))
147
+ return [];
148
+ const rows = [];
149
+ let offset = 0;
150
+ while (true) {
151
+ const batch = await query(costsDbPath, `SELECT session_id, timestamp, input_tokens, output_tokens FROM costs ` +
152
+ `ORDER BY timestamp ASC LIMIT ${DROID_COST_BATCH_SIZE} OFFSET ${offset};`);
153
+ if (!batch.length)
154
+ break;
155
+ rows.push(...batch);
156
+ if (batch.length < DROID_COST_BATCH_SIZE)
157
+ break;
158
+ offset += batch.length;
159
+ }
160
+ if (!rows.length)
161
+ return [];
162
+ const metadata = loadSessionMetadata(factoryDir);
163
+ const entries = [];
164
+ let skippedRowsWithoutMetadata = 0;
165
+ for (const row of rows) {
166
+ if (!isObject(row))
167
+ continue;
168
+ const sessionId = asString(row.session_id);
169
+ const timestamp = asString(row.timestamp);
170
+ const inputTokens = asNumber(row.input_tokens);
171
+ const outputTokens = asNumber(row.output_tokens);
172
+ if (!sessionId || !timestamp || inputTokens === null || outputTokens === null)
173
+ continue;
174
+ const session = metadata.get(sessionId);
175
+ if (!session) {
176
+ skippedRowsWithoutMetadata++;
177
+ continue;
178
+ }
179
+ if (!options.includeCcsManagedSessions && isCcsManagedSelector(session.rawSelector)) {
180
+ continue;
181
+ }
182
+ entries.push({
183
+ inputTokens,
184
+ outputTokens,
185
+ cacheCreationTokens: 0,
186
+ cacheReadTokens: 0,
187
+ model: session.model,
188
+ sessionId,
189
+ timestamp,
190
+ projectPath: session.projectPath,
191
+ version: session.version,
192
+ target: 'droid',
193
+ });
194
+ }
195
+ if (skippedRowsWithoutMetadata > 0) {
196
+ console.log((0, ui_1.warn)(`Skipped ${skippedRowsWithoutMetadata} Droid native cost row(s) without local session metadata`));
197
+ }
198
+ return entries;
199
+ }
200
+ exports.scanDroidNativeUsageEntries = scanDroidNativeUsageEntries;
201
+ //# sourceMappingURL=droid-native-usage-collector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"droid-native-usage-collector.js","sourceRoot":"","sources":["../../../src/web-server/usage/droid-native-usage-collector.ts"],"names":[],"mappings":"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"}
@@ -19,6 +19,7 @@ export interface UsageQuery {
19
19
  export declare function validateDate(dateString?: string): string | undefined;
20
20
  export declare function validateLimit(limit?: string): number;
21
21
  export declare function validateOffset(offset?: string): number;
22
+ export declare function validateDateRangeOrder(since?: string, until?: string): void;
22
23
  export declare function filterByDateRange<T extends {
23
24
  date?: string;
24
25
  month?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../../src/web-server/usage/handlers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../../src/web-server/usage/handlers.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@
6
6
  * Separated from routes for better testability and organization.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.handleInsights = exports.handleStatus = exports.handleRefresh = exports.handleMonthly = exports.handleSessions = exports.handleModels = exports.handleHourly = exports.handleDaily = exports.handleSummary = exports.summarizeAnomalies = exports.detectAnomalies = exports.fillHourlyGaps = exports.calculateTokenBreakdownCosts = exports.errorResponse = exports.filterByDateRange = exports.validateOffset = exports.validateLimit = exports.validateDate = void 0;
9
+ exports.handleInsights = exports.handleStatus = exports.handleRefresh = exports.handleMonthly = exports.handleSessions = exports.handleModels = exports.handleHourly = exports.handleDaily = exports.handleSummary = exports.summarizeAnomalies = exports.detectAnomalies = exports.fillHourlyGaps = exports.calculateTokenBreakdownCosts = exports.errorResponse = exports.filterByDateRange = exports.validateDateRangeOrder = exports.validateOffset = exports.validateLimit = exports.validateDate = void 0;
10
10
  const model_pricing_1 = require("../model-pricing");
11
11
  const aggregator_1 = require("./aggregator");
12
12
  // ============================================================================
@@ -65,6 +65,14 @@ function validateOffset(offset) {
65
65
  return num;
66
66
  }
67
67
  exports.validateOffset = validateOffset;
68
+ function validateDateRangeOrder(since, until) {
69
+ if (!since || !until)
70
+ return;
71
+ if (since > until) {
72
+ throw new Error('The "since" date must be earlier than or equal to "until"');
73
+ }
74
+ }
75
+ exports.validateDateRangeOrder = validateDateRangeOrder;
68
76
  function filterByDateRange(data, since, until) {
69
77
  if (!data || !Array.isArray(data))
70
78
  return [];
@@ -96,6 +104,45 @@ function errorResponse(res, error, defaultMessage) {
96
104
  });
97
105
  }
98
106
  exports.errorResponse = errorResponse;
107
+ function roundToCurrency(value) {
108
+ return Math.round((value + Number.EPSILON) * 100) / 100;
109
+ }
110
+ function calculateUsageTotalTokens(input, output, cacheCreation, cacheRead) {
111
+ return input + output + cacheCreation + cacheRead;
112
+ }
113
+ function parseDateKey(dateString) {
114
+ return new Date(Date.UTC(Number(dateString.slice(0, 4)), Number(dateString.slice(4, 6)) - 1, Number(dateString.slice(6, 8))));
115
+ }
116
+ function getInclusiveDayCount(start, end) {
117
+ const dayMs = 24 * 60 * 60 * 1000;
118
+ return Math.floor((end.getTime() - start.getTime()) / dayMs) + 1;
119
+ }
120
+ function countCalendarDays(data, since, until) {
121
+ const sortedDates = [...data]
122
+ .map((item) => item.date.replace(/-/g, ''))
123
+ .sort((a, b) => a.localeCompare(b));
124
+ const earliestDate = sortedDates[0];
125
+ const latestDate = sortedDates[sortedDates.length - 1];
126
+ if (since && until) {
127
+ return getInclusiveDayCount(parseDateKey(since), parseDateKey(until));
128
+ }
129
+ if (since) {
130
+ const end = until
131
+ ? parseDateKey(until)
132
+ : latestDate
133
+ ? parseDateKey(latestDate)
134
+ : parseDateKey(since);
135
+ return getInclusiveDayCount(parseDateKey(since), end);
136
+ }
137
+ if (until) {
138
+ const start = earliestDate ? parseDateKey(earliestDate) : parseDateKey(until);
139
+ return getInclusiveDayCount(start, parseDateKey(until));
140
+ }
141
+ if (data.length === 0) {
142
+ return 0;
143
+ }
144
+ return getInclusiveDayCount(parseDateKey(earliestDate), parseDateKey(latestDate));
145
+ }
99
146
  // ============================================================================
100
147
  // Cost Calculation Helpers
101
148
  // ============================================================================
@@ -117,10 +164,10 @@ function calculateTokenBreakdownCosts(dailyData) {
117
164
  }
118
165
  }
119
166
  return {
120
- input: { tokens: inputTokens, cost: Math.round(inputCost * 100) / 100 },
121
- output: { tokens: outputTokens, cost: Math.round(outputCost * 100) / 100 },
122
- cacheCreation: { tokens: cacheCreationTokens, cost: Math.round(cacheCreationCost * 100) / 100 },
123
- cacheRead: { tokens: cacheReadTokens, cost: Math.round(cacheReadCost * 100) / 100 },
167
+ input: { tokens: inputTokens, cost: roundToCurrency(inputCost) },
168
+ output: { tokens: outputTokens, cost: roundToCurrency(outputCost) },
169
+ cacheCreation: { tokens: cacheCreationTokens, cost: roundToCurrency(cacheCreationCost) },
170
+ cacheRead: { tokens: cacheReadTokens, cost: roundToCurrency(cacheReadCost) },
124
171
  };
125
172
  }
126
173
  exports.calculateTokenBreakdownCosts = calculateTokenBreakdownCosts;
@@ -271,6 +318,7 @@ async function handleSummary(req, res) {
271
318
  try {
272
319
  const since = validateDate(req.query.since);
273
320
  const until = validateDate(req.query.until);
321
+ validateDateRangeOrder(since, until);
274
322
  const dailyData = await (0, aggregator_1.getCachedDailyData)();
275
323
  const filtered = filterByDateRange(dailyData, since, until);
276
324
  let totalInputTokens = 0, totalOutputTokens = 0;
@@ -282,8 +330,10 @@ async function handleSummary(req, res) {
282
330
  totalCacheReadTokens += day.cacheReadTokens;
283
331
  totalCost += day.totalCost;
284
332
  }
285
- const totalTokens = totalInputTokens + totalOutputTokens;
333
+ const totalTokens = calculateUsageTotalTokens(totalInputTokens, totalOutputTokens, totalCacheCreationTokens, totalCacheReadTokens);
286
334
  const tokenBreakdown = calculateTokenBreakdownCosts(filtered);
335
+ const totalDays = countCalendarDays(filtered, since, until);
336
+ const activeDays = filtered.length;
287
337
  res.json({
288
338
  success: true,
289
339
  data: {
@@ -293,11 +343,14 @@ async function handleSummary(req, res) {
293
343
  totalCacheTokens: totalCacheCreationTokens + totalCacheReadTokens,
294
344
  totalCacheCreationTokens,
295
345
  totalCacheReadTokens,
296
- totalCost: Math.round(totalCost * 100) / 100,
346
+ totalCost: roundToCurrency(totalCost),
297
347
  tokenBreakdown,
298
- totalDays: filtered.length,
299
- averageTokensPerDay: filtered.length > 0 ? Math.round(totalTokens / filtered.length) : 0,
300
- averageCostPerDay: filtered.length > 0 ? Math.round((totalCost / filtered.length) * 100) / 100 : 0,
348
+ totalDays,
349
+ activeDays,
350
+ averageTokensPerDay: totalDays > 0 ? Math.round(totalTokens / totalDays) : 0,
351
+ averageTokensPerActiveDay: activeDays > 0 ? Math.round(totalTokens / activeDays) : 0,
352
+ averageCostPerDay: totalDays > 0 ? roundToCurrency(totalCost / totalDays) : 0,
353
+ averageCostPerActiveDay: activeDays > 0 ? roundToCurrency(totalCost / activeDays) : 0,
301
354
  },
302
355
  });
303
356
  }
@@ -310,15 +363,16 @@ async function handleDaily(req, res) {
310
363
  try {
311
364
  const since = validateDate(req.query.since);
312
365
  const until = validateDate(req.query.until);
366
+ validateDateRangeOrder(since, until);
313
367
  const dailyData = await (0, aggregator_1.getCachedDailyData)();
314
368
  const filtered = filterByDateRange(dailyData, since, until);
315
369
  const trends = filtered.map((day) => ({
316
370
  date: day.date,
317
- tokens: day.inputTokens + day.outputTokens,
371
+ tokens: calculateUsageTotalTokens(day.inputTokens, day.outputTokens, day.cacheCreationTokens, day.cacheReadTokens),
318
372
  inputTokens: day.inputTokens,
319
373
  outputTokens: day.outputTokens,
320
374
  cacheTokens: day.cacheCreationTokens + day.cacheReadTokens,
321
- cost: Math.round(day.totalCost * 100) / 100,
375
+ cost: roundToCurrency(day.totalCost),
322
376
  modelsUsed: day.modelsUsed.length,
323
377
  }));
324
378
  res.json({ success: true, data: trends });
@@ -332,6 +386,7 @@ async function handleHourly(req, res) {
332
386
  try {
333
387
  const since = validateDate(req.query.since);
334
388
  const until = validateDate(req.query.until);
389
+ validateDateRangeOrder(since, until);
335
390
  const hourlyData = await (0, aggregator_1.getCachedHourlyData)();
336
391
  const filtered = (hourlyData || []).filter((h) => {
337
392
  const hourDate = h.hour.slice(0, 10).replace(/-/g, '');
@@ -343,13 +398,13 @@ async function handleHourly(req, res) {
343
398
  });
344
399
  const trends = filtered.map((hour) => ({
345
400
  hour: hour.hour,
346
- tokens: hour.inputTokens + hour.outputTokens,
401
+ tokens: calculateUsageTotalTokens(hour.inputTokens, hour.outputTokens, hour.cacheCreationTokens, hour.cacheReadTokens),
347
402
  inputTokens: hour.inputTokens,
348
403
  outputTokens: hour.outputTokens,
349
404
  cacheTokens: hour.cacheCreationTokens + hour.cacheReadTokens,
350
- cost: Math.round(hour.totalCost * 100) / 100,
405
+ cost: roundToCurrency(hour.totalCost),
351
406
  modelsUsed: hour.modelsUsed.length,
352
- requests: hour.modelBreakdowns.length,
407
+ requests: hour.requestCount ?? hour.modelBreakdowns.length,
353
408
  }));
354
409
  const filledTrends = fillHourlyGaps(trends, since, until);
355
410
  res.json({ success: true, data: filledTrends });
@@ -363,6 +418,7 @@ async function handleModels(req, res) {
363
418
  try {
364
419
  const since = validateDate(req.query.since);
365
420
  const until = validateDate(req.query.until);
421
+ validateDateRangeOrder(since, until);
366
422
  const dailyData = await (0, aggregator_1.getCachedDailyData)();
367
423
  const filtered = filterByDateRange(dailyData, since, until);
368
424
  const modelMap = new Map();
@@ -385,7 +441,8 @@ async function handleModels(req, res) {
385
441
  }
386
442
  }
387
443
  const models = Array.from(modelMap.values());
388
- const totalTokens = models.reduce((sum, m) => sum + m.inputTokens + m.outputTokens, 0);
444
+ const totalTokens = models.reduce((sum, model) => sum +
445
+ calculateUsageTotalTokens(model.inputTokens, model.outputTokens, model.cacheCreationTokens, model.cacheReadTokens), 0);
389
446
  const result = models
390
447
  .map((m) => {
391
448
  const pricing = (0, model_pricing_1.getModelPricing)(m.model);
@@ -394,26 +451,25 @@ async function handleModels(req, res) {
394
451
  const cacheCreationCost = (m.cacheCreationTokens / 1000000) * pricing.cacheCreationPerMillion;
395
452
  const cacheReadCost = (m.cacheReadTokens / 1000000) * pricing.cacheReadPerMillion;
396
453
  const ioRatio = m.outputTokens > 0 ? m.inputTokens / m.outputTokens : 0;
454
+ const totalModelTokens = calculateUsageTotalTokens(m.inputTokens, m.outputTokens, m.cacheCreationTokens, m.cacheReadTokens);
397
455
  return {
398
456
  model: m.model,
399
- tokens: m.inputTokens + m.outputTokens,
457
+ tokens: totalModelTokens,
400
458
  inputTokens: m.inputTokens,
401
459
  outputTokens: m.outputTokens,
402
460
  cacheCreationTokens: m.cacheCreationTokens,
403
461
  cacheReadTokens: m.cacheReadTokens,
404
462
  cacheTokens: m.cacheCreationTokens + m.cacheReadTokens,
405
- cost: Math.round(m.cost * 100) / 100,
406
- percentage: totalTokens > 0
407
- ? Math.round(((m.inputTokens + m.outputTokens) / totalTokens) * 1000) / 10
408
- : 0,
463
+ cost: roundToCurrency(m.cost),
464
+ percentage: totalTokens > 0 ? Math.round((totalModelTokens / totalTokens) * 1000) / 10 : 0,
409
465
  costBreakdown: {
410
- input: { tokens: m.inputTokens, cost: Math.round(inputCost * 100) / 100 },
411
- output: { tokens: m.outputTokens, cost: Math.round(outputCost * 100) / 100 },
466
+ input: { tokens: m.inputTokens, cost: roundToCurrency(inputCost) },
467
+ output: { tokens: m.outputTokens, cost: roundToCurrency(outputCost) },
412
468
  cacheCreation: {
413
469
  tokens: m.cacheCreationTokens,
414
- cost: Math.round(cacheCreationCost * 100) / 100,
470
+ cost: roundToCurrency(cacheCreationCost),
415
471
  },
416
- cacheRead: { tokens: m.cacheReadTokens, cost: Math.round(cacheReadCost * 100) / 100 },
472
+ cacheRead: { tokens: m.cacheReadTokens, cost: roundToCurrency(cacheReadCost) },
417
473
  },
418
474
  ioRatio: Math.round(ioRatio * 10) / 10,
419
475
  };
@@ -430,6 +486,7 @@ async function handleSessions(req, res) {
430
486
  try {
431
487
  const since = validateDate(req.query.since);
432
488
  const until = validateDate(req.query.until);
489
+ validateDateRangeOrder(since, until);
433
490
  const limit = validateLimit(req.query.limit);
434
491
  const offset = validateOffset(req.query.offset);
435
492
  const sessionData = await (0, aggregator_1.getCachedSessionData)();
@@ -439,10 +496,10 @@ async function handleSessions(req, res) {
439
496
  const sessions = paginated.map((s) => ({
440
497
  sessionId: s.sessionId,
441
498
  projectPath: s.projectPath,
442
- tokens: s.inputTokens + s.outputTokens,
499
+ tokens: calculateUsageTotalTokens(s.inputTokens, s.outputTokens, s.cacheCreationTokens, s.cacheReadTokens),
443
500
  inputTokens: s.inputTokens,
444
501
  outputTokens: s.outputTokens,
445
- cost: Math.round(s.totalCost * 100) / 100,
502
+ cost: roundToCurrency(s.totalCost),
446
503
  lastActivity: s.lastActivity,
447
504
  modelsUsed: s.modelsUsed,
448
505
  target: s.target || 'claude',
@@ -467,24 +524,72 @@ async function handleMonthly(req, res) {
467
524
  try {
468
525
  const since = validateDate(req.query.since);
469
526
  const until = validateDate(req.query.until);
470
- const monthlyData = await (0, aggregator_1.getCachedMonthlyData)();
471
- const filtered = since || until
472
- ? monthlyData.filter((m) => {
473
- const monthDate = m.month.replace('-', '') + '01';
474
- if (since && monthDate < since)
475
- return false;
476
- if (until && monthDate > until)
477
- return false;
478
- return true;
479
- })
480
- : monthlyData;
527
+ validateDateRangeOrder(since, until);
528
+ let filtered;
529
+ if (since || until) {
530
+ const dailyData = filterByDateRange(await (0, aggregator_1.getCachedDailyData)(), since, until);
531
+ const monthMap = new Map();
532
+ for (const day of dailyData) {
533
+ const month = day.date.slice(0, 7);
534
+ const existing = monthMap.get(month) ?? {
535
+ month,
536
+ inputTokens: 0,
537
+ outputTokens: 0,
538
+ cacheCreationTokens: 0,
539
+ cacheReadTokens: 0,
540
+ totalCost: 0,
541
+ modelsUsed: new Set(),
542
+ modelBreakdowns: new Map(),
543
+ };
544
+ existing.inputTokens += day.inputTokens;
545
+ existing.outputTokens += day.outputTokens;
546
+ existing.cacheCreationTokens += day.cacheCreationTokens;
547
+ existing.cacheReadTokens += day.cacheReadTokens;
548
+ existing.totalCost += day.totalCost;
549
+ for (const model of day.modelsUsed) {
550
+ existing.modelsUsed.add(model);
551
+ }
552
+ for (const breakdown of day.modelBreakdowns) {
553
+ const existingBreakdown = existing.modelBreakdowns.get(breakdown.modelName) ?? {
554
+ modelName: breakdown.modelName,
555
+ inputTokens: 0,
556
+ outputTokens: 0,
557
+ cacheCreationTokens: 0,
558
+ cacheReadTokens: 0,
559
+ cost: 0,
560
+ };
561
+ existingBreakdown.inputTokens += breakdown.inputTokens;
562
+ existingBreakdown.outputTokens += breakdown.outputTokens;
563
+ existingBreakdown.cacheCreationTokens += breakdown.cacheCreationTokens;
564
+ existingBreakdown.cacheReadTokens += breakdown.cacheReadTokens;
565
+ existingBreakdown.cost += breakdown.cost;
566
+ existing.modelBreakdowns.set(breakdown.modelName, existingBreakdown);
567
+ }
568
+ monthMap.set(month, existing);
569
+ }
570
+ filtered = Array.from(monthMap.values())
571
+ .map((month) => ({
572
+ month: month.month,
573
+ inputTokens: month.inputTokens,
574
+ outputTokens: month.outputTokens,
575
+ cacheCreationTokens: month.cacheCreationTokens,
576
+ cacheReadTokens: month.cacheReadTokens,
577
+ totalCost: month.totalCost,
578
+ modelsUsed: Array.from(month.modelsUsed),
579
+ modelBreakdowns: Array.from(month.modelBreakdowns.values()),
580
+ }))
581
+ .sort((a, b) => a.month.localeCompare(b.month));
582
+ }
583
+ else {
584
+ filtered = await (0, aggregator_1.getCachedMonthlyData)();
585
+ }
481
586
  const result = filtered.map((m) => ({
482
587
  month: m.month,
483
- tokens: m.inputTokens + m.outputTokens,
588
+ tokens: calculateUsageTotalTokens(m.inputTokens, m.outputTokens, m.cacheCreationTokens, m.cacheReadTokens),
484
589
  inputTokens: m.inputTokens,
485
590
  outputTokens: m.outputTokens,
486
591
  cacheTokens: m.cacheCreationTokens + m.cacheReadTokens,
487
- cost: Math.round(m.totalCost * 100) / 100,
592
+ cost: roundToCurrency(m.totalCost),
488
593
  modelsUsed: m.modelsUsed.length,
489
594
  }));
490
595
  res.json({ success: true, data: result.sort((a, b) => a.month.localeCompare(b.month)) });
@@ -505,10 +610,9 @@ async function handleRefresh(_req, res) {
505
610
  }
506
611
  exports.handleRefresh = handleRefresh;
507
612
  function handleStatus(_req, res) {
508
- const cache = new Map(); // Note: this is a placeholder, actual cache is in aggregator
509
613
  res.json({
510
614
  success: true,
511
- data: { lastFetch: (0, aggregator_1.getLastFetchTimestamp)(), cacheSize: cache.size },
615
+ data: { lastFetch: (0, aggregator_1.getLastFetchTimestamp)(), cacheSize: (0, aggregator_1.getUsageCacheSize)() },
512
616
  });
513
617
  }
514
618
  exports.handleStatus = handleStatus;
@@ -516,6 +620,7 @@ async function handleInsights(req, res) {
516
620
  try {
517
621
  const since = validateDate(req.query.since);
518
622
  const until = validateDate(req.query.until);
623
+ validateDateRangeOrder(since, until);
519
624
  const dailyData = await (0, aggregator_1.getCachedDailyData)();
520
625
  const filtered = filterByDateRange(dailyData, since, until);
521
626
  const anomalies = detectAnomalies(filtered);