@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.
- package/dist/ui/assets/{accounts-ld6H4qVb.js → accounts-oSQYCZNY.js} +1 -1
- package/dist/ui/assets/{alert-dialog-BZ2goqtW.js → alert-dialog-RxXcYLJ-.js} +1 -1
- package/dist/ui/assets/{api-CthvA4sH.js → api-PDxwDKK6.js} +1 -1
- package/dist/ui/assets/{auth-section-o5qejfmB.js → auth-section-DGvyapUL.js} +1 -1
- package/dist/ui/assets/{backups-section-DH2I1yD9.js → backups-section-C_DMj0hU.js} +1 -1
- package/dist/ui/assets/{channels-PcQ1oO0o.js → channels-Bhrnp-Eo.js} +1 -1
- package/dist/ui/assets/{checkbox-DehBjrNH.js → checkbox-wRKw3eVf.js} +1 -1
- package/dist/ui/assets/{claude-extension-PyHYNmSG.js → claude-extension-BRPDX6oS.js} +1 -1
- package/dist/ui/assets/{cliproxy-lrpHXFMA.js → cliproxy-BsJfKNlA.js} +1 -1
- package/dist/ui/assets/{cliproxy-ai-providers-BEHZy9gR.js → cliproxy-ai-providers-CNZ8X477.js} +1 -1
- package/dist/ui/assets/{cliproxy-control-panel-34szpAk4.js → cliproxy-control-panel-BtjDR1Vg.js} +1 -1
- package/dist/ui/assets/{codex-UKB2QUN1.js → codex-BIPn4i-A.js} +1 -1
- package/dist/ui/assets/{confirm-dialog-64G67ESH.js → confirm-dialog-B8tZHrIu.js} +1 -1
- package/dist/ui/assets/{copilot-Bd-ViL4n.js → copilot-6YzLT_A3.js} +1 -1
- package/dist/ui/assets/{cursor-DH3z6BLJ.js → cursor-CrQ7YslH.js} +1 -1
- package/dist/ui/assets/{droid-Cng3BkQg.js → droid-Cpt_b7co.js} +1 -1
- package/dist/ui/assets/{globalenv-section-D3aT-fYK.js → globalenv-section-DwhnjKxb.js} +1 -1
- package/dist/ui/assets/{health-BRqYO0xG.js → health-MX_poTtR.js} +1 -1
- package/dist/ui/assets/{index-FPQVUJ_q.js → index-BNt4L6D7.js} +1 -1
- package/dist/ui/assets/{index-B2XY4MhT.js → index-CQlxBYgL.js} +2 -2
- package/dist/ui/assets/{index-CDoChLrM.js → index-DD888_1t.js} +1 -1
- package/dist/ui/assets/{index-DLIKuUBI.js → index-DFCrszOj.js} +1 -1
- package/dist/ui/assets/{index-DA5G610p.js → index-DfDJ6OTa.js} +1 -1
- package/dist/ui/assets/{index-1MP3pDe8.js → index-Dz2HaMuj.js} +1 -1
- package/dist/ui/assets/index-EEfi3_jA.js +1 -0
- package/dist/ui/assets/{logs-DNAtdY6A.js → logs-D5iryHgo.js} +1 -1
- package/dist/ui/assets/{masked-input-B4x8rBbs.js → masked-input-Bs2HnuUO.js} +1 -1
- package/dist/ui/assets/{proxy-status-widget-EuDHDJAP.js → proxy-status-widget-Bs4OIbHk.js} +1 -1
- package/dist/ui/assets/{raw-json-settings-editor-panel-DZRYrywv.js → raw-json-settings-editor-panel-wRXnigfA.js} +1 -1
- package/dist/ui/assets/{searchable-select-CwJpfFVb.js → searchable-select-B4APb47u.js} +1 -1
- package/dist/ui/assets/{separator-CD-HBQ24.js → separator-ny9jcCJ0.js} +1 -1
- package/dist/ui/assets/{shared-BlqdoNm4.js → shared-BHFt3N-w.js} +1 -1
- package/dist/ui/assets/{table-FmKRMU4a.js → table-BRAlw_wh.js} +1 -1
- package/dist/ui/assets/{updates-Bo-HBoN0.js → updates-wB9lmrfM.js} +1 -1
- package/dist/ui/index.html +1 -1
- package/dist/web-server/jsonl-parser.d.ts.map +1 -1
- package/dist/web-server/jsonl-parser.js +22 -4
- package/dist/web-server/jsonl-parser.js.map +1 -1
- package/dist/web-server/model-pricing.d.ts +3 -3
- package/dist/web-server/model-pricing.d.ts.map +1 -1
- package/dist/web-server/model-pricing.js +5 -12
- package/dist/web-server/model-pricing.js.map +1 -1
- package/dist/web-server/usage/aggregator.d.ts +1 -0
- package/dist/web-server/usage/aggregator.d.ts.map +1 -1
- package/dist/web-server/usage/aggregator.js +71 -4
- package/dist/web-server/usage/aggregator.js.map +1 -1
- package/dist/web-server/usage/cliproxy-usage-syncer.d.ts +0 -15
- package/dist/web-server/usage/cliproxy-usage-syncer.d.ts.map +1 -1
- package/dist/web-server/usage/cliproxy-usage-syncer.js +159 -55
- package/dist/web-server/usage/cliproxy-usage-syncer.js.map +1 -1
- package/dist/web-server/usage/cliproxy-usage-transformer.d.ts +22 -7
- package/dist/web-server/usage/cliproxy-usage-transformer.d.ts.map +1 -1
- package/dist/web-server/usage/cliproxy-usage-transformer.js +166 -20
- package/dist/web-server/usage/cliproxy-usage-transformer.js.map +1 -1
- package/dist/web-server/usage/codex-native-usage-collector.d.ts +11 -0
- package/dist/web-server/usage/codex-native-usage-collector.d.ts.map +1 -0
- package/dist/web-server/usage/codex-native-usage-collector.js +177 -0
- package/dist/web-server/usage/codex-native-usage-collector.js.map +1 -0
- package/dist/web-server/usage/data-aggregator.d.ts.map +1 -1
- package/dist/web-server/usage/data-aggregator.js +1 -0
- package/dist/web-server/usage/data-aggregator.js.map +1 -1
- package/dist/web-server/usage/droid-native-usage-collector.d.ts +14 -0
- package/dist/web-server/usage/droid-native-usage-collector.d.ts.map +1 -0
- package/dist/web-server/usage/droid-native-usage-collector.js +201 -0
- package/dist/web-server/usage/droid-native-usage-collector.js.map +1 -0
- package/dist/web-server/usage/handlers.d.ts +1 -0
- package/dist/web-server/usage/handlers.d.ts.map +1 -1
- package/dist/web-server/usage/handlers.js +147 -42
- package/dist/web-server/usage/handlers.js.map +1 -1
- package/dist/web-server/usage/sqlite-cli.d.ts +3 -0
- package/dist/web-server/usage/sqlite-cli.d.ts.map +1 -0
- package/dist/web-server/usage/sqlite-cli.js +62 -0
- package/dist/web-server/usage/sqlite-cli.js.map +1 -0
- package/dist/web-server/usage/types.d.ts +5 -0
- package/dist/web-server/usage/types.d.ts.map +1 -1
- package/package.json +1 -1
- 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":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;
|
|
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:
|
|
121
|
-
output: { tokens: outputTokens, cost:
|
|
122
|
-
cacheCreation: { tokens: cacheCreationTokens, cost:
|
|
123
|
-
cacheRead: { tokens: cacheReadTokens, cost:
|
|
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
|
|
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:
|
|
346
|
+
totalCost: roundToCurrency(totalCost),
|
|
297
347
|
tokenBreakdown,
|
|
298
|
-
totalDays
|
|
299
|
-
|
|
300
|
-
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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,
|
|
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:
|
|
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:
|
|
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:
|
|
411
|
-
output: { tokens: m.outputTokens, cost:
|
|
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:
|
|
470
|
+
cost: roundToCurrency(cacheCreationCost),
|
|
415
471
|
},
|
|
416
|
-
cacheRead: { tokens: m.cacheReadTokens, cost:
|
|
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
|
|
499
|
+
tokens: calculateUsageTotalTokens(s.inputTokens, s.outputTokens, s.cacheCreationTokens, s.cacheReadTokens),
|
|
443
500
|
inputTokens: s.inputTokens,
|
|
444
501
|
outputTokens: s.outputTokens,
|
|
445
|
-
cost:
|
|
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
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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);
|