usage-board 2.1.2 → 3.0.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/dist/index.mjs +10 -4
- package/dist/public/_nuxt/{chy2QJx0.js → 7Dy4NLP8.js} +32 -32
- package/dist/public/_nuxt/B-VlGWDb.js +21 -0
- package/dist/public/_nuxt/{BeEwECnn.js → Be3rizqy.js} +1 -1
- package/dist/public/_nuxt/{15CW3D68.js → C0azgqnZ.js} +1 -1
- package/dist/public/_nuxt/{B6G-s9D-.js → CMNdiQCa.js} +1 -1
- package/dist/public/_nuxt/CPuXQJE_.js +1 -0
- package/dist/public/_nuxt/DenksPSi.js +119 -0
- package/dist/public/_nuxt/Dkya5WaL.js +9 -0
- package/dist/public/_nuxt/HN9OZyaQ.js +25 -0
- package/dist/public/_nuxt/{Bu4SpN_a.js → JtK-nXxy.js} +1 -1
- package/dist/public/_nuxt/builds/latest.json +1 -1
- package/dist/public/_nuxt/builds/meta/37e8bb21-a086-45bf-93dc-47eeeada7299.json +1 -0
- package/dist/public/_nuxt/{BeygfM9p.js → y3weNNd-.js} +2 -2
- package/dist/server/chunks/_/error-500.mjs +8 -4
- package/dist/server/chunks/_/shared.cjs.prod.mjs +1 -1
- package/dist/server/chunks/build/client.precomputed.mjs +1 -1
- package/dist/server/chunks/nitro/nitro.mjs +8218 -2844
- package/dist/server/chunks/routes/api/payload.json.mjs +11 -626
- package/dist/server/chunks/routes/api/projects/_project/modules.get.mjs +36 -0
- package/dist/server/chunks/routes/api/projects/catalog.get.mjs +26 -0
- package/dist/server/chunks/routes/renderer.mjs +9 -5
- package/dist/server/chunks/routes/ws.mjs +29 -971
- package/dist/server/index.mjs +9 -5
- package/package.json +7 -6
- package/dist/public/_nuxt/C6ydMk2z.js +0 -25
- package/dist/public/_nuxt/Dn8cXZx3.js +0 -9
- package/dist/public/_nuxt/DysUC14A.js +0 -119
- package/dist/public/_nuxt/KLhV325n.js +0 -1
- package/dist/public/_nuxt/builds/meta/ac4b25d6-d6eb-44bb-8c5b-b1d6f651c196.json +0 -1
- package/dist/public/_nuxt/pmnAmEjb.js +0 -21
- package/dist/server/chunks/_/index.min.mjs +0 -348
|
@@ -1,941 +1,36 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { existsSync } from 'node:fs';
|
|
3
|
-
import { homedir } from 'node:os';
|
|
4
|
-
import { join, sep, basename, resolve } from 'node:path';
|
|
5
|
-
import { c as createLiteLLMPricingResolver, C as CLAUDE_MODEL_ALIASES, b as CLAUDE_FALLBACK_MODEL, e as CODEX_MODEL_ALIASES, d as CODEX_FALLBACK_MODEL, f as GEMINI_MODEL_ALIASES, g as GEMINI_FALLBACK_PRICING_TABLE, G as GEMINI_FALLBACK_MODEL, Z as Ze, a as calculateUsageCostUSD } from '../_/index.min.mjs';
|
|
6
|
-
import 'node:fs/promises';
|
|
1
|
+
import { e as defineWebSocketHandler, u as useRuntimeConfig, r as resolveConfig, g as getUsageDataRuntime, c as normalizeStringList, n as normalizeStringValue } from '../nitro/nitro.mjs';
|
|
7
2
|
import 'node:http';
|
|
8
3
|
import 'node:https';
|
|
9
4
|
import 'node:events';
|
|
10
5
|
import 'node:buffer';
|
|
6
|
+
import 'node:fs';
|
|
7
|
+
import 'node:path';
|
|
11
8
|
import 'node:crypto';
|
|
9
|
+
import 'node:sqlite';
|
|
12
10
|
import 'node:url';
|
|
13
|
-
import 'node:util';
|
|
14
|
-
import 'node:process';
|
|
15
|
-
import 'node:tty';
|
|
16
11
|
import 'fs';
|
|
12
|
+
import 'node:fs/promises';
|
|
17
13
|
import 'node:stream';
|
|
18
14
|
import 'node:string_decoder';
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const PROJECT_USAGE_DATA_MODULES = [
|
|
24
|
-
"daily_trend",
|
|
25
|
-
"meta",
|
|
26
|
-
"model_usage",
|
|
27
|
-
"overview_cards",
|
|
28
|
-
"session_interactions",
|
|
29
|
-
"session_list",
|
|
30
|
-
"token_usage"
|
|
31
|
-
];
|
|
32
|
-
async function loadProjectsUsage(config) {
|
|
33
|
-
const [
|
|
34
|
-
claudeResolvePricing,
|
|
35
|
-
codexResolvePricing,
|
|
36
|
-
geminiResolvePricing
|
|
37
|
-
] = await Promise.all([
|
|
38
|
-
createLiteLLMPricingResolver({
|
|
39
|
-
aliases: CLAUDE_MODEL_ALIASES,
|
|
40
|
-
fallbackModel: CLAUDE_FALLBACK_MODEL,
|
|
41
|
-
getLookupCandidates: getClaudeLookupCandidates
|
|
42
|
-
}),
|
|
43
|
-
createLiteLLMPricingResolver({
|
|
44
|
-
aliases: CODEX_MODEL_ALIASES,
|
|
45
|
-
fallbackModel: CODEX_FALLBACK_MODEL,
|
|
46
|
-
isZeroCostModel: isOpenRouterFreeModel
|
|
47
|
-
}),
|
|
48
|
-
createLiteLLMPricingResolver({
|
|
49
|
-
aliases: GEMINI_MODEL_ALIASES,
|
|
50
|
-
fallbackModel: GEMINI_FALLBACK_MODEL,
|
|
51
|
-
fallbackPricingTable: GEMINI_FALLBACK_PRICING_TABLE,
|
|
52
|
-
getLookupCandidates: getGeminiLookupCandidates
|
|
53
|
-
})
|
|
54
|
-
]);
|
|
55
|
-
const [
|
|
56
|
-
claudeDetails,
|
|
57
|
-
codexDetails,
|
|
58
|
-
geminiDetails
|
|
59
|
-
] = await Promise.all([
|
|
60
|
-
loadClaudeSessionDetails(config, claudeResolvePricing),
|
|
61
|
-
loadCodexSessionDetails(config, codexResolvePricing),
|
|
62
|
-
loadGeminiSessionDetails(config, geminiResolvePricing)
|
|
63
|
-
]);
|
|
64
|
-
const platformProjects = {
|
|
65
|
-
claudeCode: getPlatformProjectNames(claudeDetails),
|
|
66
|
-
codex: getPlatformProjectNames(codexDetails),
|
|
67
|
-
gemini: getPlatformProjectNames(geminiDetails)
|
|
68
|
-
};
|
|
69
|
-
const projectNames = uniqueItems([
|
|
70
|
-
...platformProjects.claudeCode,
|
|
71
|
-
...platformProjects.codex,
|
|
72
|
-
...platformProjects.gemini
|
|
73
|
-
]).sort((a, b) => a.localeCompare(b));
|
|
74
|
-
return projectNames.map((projectName) => {
|
|
75
|
-
const analyzing = {
|
|
76
|
-
claudeCode: buildPlatformProjectUsage(claudeDetails, projectName, "claudeCode"),
|
|
77
|
-
codex: buildPlatformProjectUsage(codexDetails, projectName, "codex"),
|
|
78
|
-
gemini: buildPlatformProjectUsage(geminiDetails, projectName, "gemini")
|
|
79
|
-
};
|
|
80
|
-
const sessions = [
|
|
81
|
-
...analyzing.claudeCode.sessions,
|
|
82
|
-
...analyzing.codex.sessions,
|
|
83
|
-
...analyzing.gemini.sessions
|
|
84
|
-
];
|
|
85
|
-
return {
|
|
86
|
-
[projectName]: {
|
|
87
|
-
label: projectName,
|
|
88
|
-
models: collectSessionModels(sessions),
|
|
89
|
-
createTime: getEarliestStartedAt(sessions),
|
|
90
|
-
sessionCound: sessions.length,
|
|
91
|
-
analyzing
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
async function loadProjectUsageCatalog(config) {
|
|
97
|
-
const projects = await loadProjectsUsage(config);
|
|
98
|
-
return projects.map((project) => {
|
|
99
|
-
var _a;
|
|
100
|
-
const [label, detail] = (_a = Object.entries(project)[0]) != null ? _a : [];
|
|
101
|
-
if (!label || !detail) {
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
104
|
-
const platforms = getProjectDetailPlatforms(detail);
|
|
105
|
-
return {
|
|
106
|
-
label,
|
|
107
|
-
path: uniqueItems(platforms.flatMap((platform) => getPlatformUsageRoots(config, platform))).map(toHomeRelativePath),
|
|
108
|
-
type: getProjectCatalogType(platforms)
|
|
109
|
-
};
|
|
110
|
-
}).filter((item) => item !== null);
|
|
111
|
-
}
|
|
112
|
-
async function loadProjectUsageData(config, options) {
|
|
113
|
-
var _a, _b, _c;
|
|
114
|
-
const projectName = (options.project || options.label || "").trim();
|
|
115
|
-
if (!projectName) {
|
|
116
|
-
throw new Error("Missing project name for project_data request.");
|
|
117
|
-
}
|
|
118
|
-
const scopedConfig = ((_a = options.path) == null ? void 0 : _a.length) ? createConfigForUsagePaths(config, options.path) : config;
|
|
119
|
-
const projects = await loadProjectsUsage(scopedConfig);
|
|
120
|
-
return (_c = (_b = projects.find((project) => project[projectName])) == null ? void 0 : _b[projectName]) != null ? _c : null;
|
|
121
|
-
}
|
|
122
|
-
async function loadProjectUsageDataModule(config, options) {
|
|
123
|
-
var _a, _b;
|
|
124
|
-
const detail = await loadProjectUsageData(config, options);
|
|
125
|
-
if (!detail) {
|
|
126
|
-
return null;
|
|
127
|
-
}
|
|
128
|
-
const modules = uniqueItems(((_a = options.modules) == null ? void 0 : _a.length) ? options.modules : [(_b = options.module) != null ? _b : DEFAULT_PROJECT_USAGE_DATA_MODULE]);
|
|
129
|
-
for (const module of modules) {
|
|
130
|
-
assertProjectUsageDataModule(module);
|
|
131
|
-
}
|
|
132
|
-
if (options.platform) {
|
|
133
|
-
assertProjectUsagePlatformScope(options.platform);
|
|
134
|
-
}
|
|
135
|
-
if (modules.length === 1) {
|
|
136
|
-
const module = modules[0];
|
|
137
|
-
const data = buildProjectUsageDataModule(detail, module, options);
|
|
138
|
-
return {
|
|
139
|
-
data,
|
|
140
|
-
label: detail.label,
|
|
141
|
-
module
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
return {
|
|
145
|
-
label: detail.label,
|
|
146
|
-
modules: Object.fromEntries(modules.map((module) => [
|
|
147
|
-
module,
|
|
148
|
-
buildProjectUsageDataModule(detail, module, options)
|
|
149
|
-
]))
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
function buildProjectUsageDataModule(detail, module, options) {
|
|
153
|
-
var _a;
|
|
154
|
-
if (module === "meta") {
|
|
155
|
-
return buildProjectMetaModule(detail);
|
|
156
|
-
}
|
|
157
|
-
if (module === "session_interactions") {
|
|
158
|
-
return buildProjectSessionInteractionsModule(detail, options);
|
|
159
|
-
}
|
|
160
|
-
return buildProjectPlatformModule(detail, module, (_a = options.platform) != null ? _a : "all");
|
|
161
|
-
}
|
|
162
|
-
function buildProjectMetaModule(detail) {
|
|
163
|
-
return {
|
|
164
|
-
createTime: detail.createTime,
|
|
165
|
-
label: detail.label,
|
|
166
|
-
models: detail.models,
|
|
167
|
-
platforms: getProjectDetailPlatforms(detail),
|
|
168
|
-
sessionCound: detail.sessionCound
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
function buildProjectPlatformModule(detail, module, platform) {
|
|
172
|
-
if (platform !== "all") {
|
|
173
|
-
return buildPlatformModulePayload(detail.analyzing[platform], module);
|
|
174
|
-
}
|
|
175
|
-
if (module === "session_list") {
|
|
176
|
-
const sessions = getProjectDetailSessions(detail);
|
|
177
|
-
const allUsage2 = buildProjectLoadUsageResult(sessions);
|
|
178
|
-
return {
|
|
179
|
-
all: {
|
|
180
|
-
sessionRows: allUsage2.sessionRows,
|
|
181
|
-
sessionUsage: sessions.map(toProjectSessionListItem),
|
|
182
|
-
sessions: sessions.map(toProjectSessionListItem)
|
|
183
|
-
},
|
|
184
|
-
claudeCode: buildPlatformModulePayload(detail.analyzing.claudeCode, module),
|
|
185
|
-
codex: buildPlatformModulePayload(detail.analyzing.codex, module),
|
|
186
|
-
gemini: buildPlatformModulePayload(detail.analyzing.gemini, module)
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
const allUsage = buildProjectLoadUsageResult(getProjectDetailSessions(detail));
|
|
190
|
-
return {
|
|
191
|
-
all: buildLoadUsageModulePayload(allUsage, module),
|
|
192
|
-
claudeCode: buildPlatformModulePayload(detail.analyzing.claudeCode, module),
|
|
193
|
-
codex: buildPlatformModulePayload(detail.analyzing.codex, module),
|
|
194
|
-
gemini: buildPlatformModulePayload(detail.analyzing.gemini, module)
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
function buildPlatformModulePayload(usage, module) {
|
|
198
|
-
if (module === "session_list") {
|
|
199
|
-
return {
|
|
200
|
-
sessionRows: usage.sessionRows,
|
|
201
|
-
sessionUsage: usage.sessions.map(toProjectSessionListItem),
|
|
202
|
-
sessions: usage.sessions.map(toProjectSessionListItem)
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
return buildLoadUsageModulePayload(usage, module);
|
|
206
|
-
}
|
|
207
|
-
function buildLoadUsageModulePayload(usage, module) {
|
|
208
|
-
if (module === "overview_cards") {
|
|
209
|
-
return {
|
|
210
|
-
overviewCards: usage.overviewCards,
|
|
211
|
-
todayTopModel: usage.todayTopModel,
|
|
212
|
-
todayTopProject: usage.todayTopProject,
|
|
213
|
-
todayTotalCost: usage.todayTotalCost,
|
|
214
|
-
todayTotalTokens: usage.todayTotalTokens
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
if (module === "daily_trend") {
|
|
218
|
-
return {
|
|
219
|
-
dailyRows: usage.dailyRows,
|
|
220
|
-
dailyTokenUsage: usage.dailyTokenUsage
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
if (module === "model_usage") {
|
|
224
|
-
return {
|
|
225
|
-
dailyTokenUsage: usage.dailyTokenUsage,
|
|
226
|
-
monthlyModelUsage: usage.monthlyModelUsage
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
return {
|
|
230
|
-
dailyRows: usage.dailyRows,
|
|
231
|
-
monthlyRows: usage.monthlyRows,
|
|
232
|
-
sessionRows: usage.sessionRows,
|
|
233
|
-
weeklyRows: usage.weeklyRows
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
function buildProjectSessionInteractionsModule(detail, options) {
|
|
237
|
-
var _a, _b;
|
|
238
|
-
const sessionId = (_a = options.sessionId) == null ? void 0 : _a.trim();
|
|
239
|
-
if (!sessionId) {
|
|
240
|
-
throw new Error("Missing sessionId for session_interactions module.");
|
|
241
|
-
}
|
|
242
|
-
const sessions = getProjectDetailSessions(detail, (_b = options.platform) != null ? _b : "all");
|
|
243
|
-
const session = sessions.find((session2) => session2.sessionId === sessionId);
|
|
244
|
-
if (!session) {
|
|
245
|
-
return null;
|
|
246
|
-
}
|
|
247
|
-
return {
|
|
248
|
-
...toProjectSessionListItem(session),
|
|
249
|
-
interactions: session.interactions.map(({ raw: _raw, ...interaction }) => interaction)
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
function getProjectDetailSessions(detail, platform = "all") {
|
|
253
|
-
if (platform !== "all") {
|
|
254
|
-
return detail.analyzing[platform].sessions;
|
|
255
|
-
}
|
|
256
|
-
return [
|
|
257
|
-
...detail.analyzing.claudeCode.sessions,
|
|
258
|
-
...detail.analyzing.codex.sessions,
|
|
259
|
-
...detail.analyzing.gemini.sessions
|
|
260
|
-
].sort((a, b) => Date.parse(b.startedAt) - Date.parse(a.startedAt));
|
|
261
|
-
}
|
|
262
|
-
function toProjectSessionListItem(session) {
|
|
263
|
-
const { interactions: _interactions, ...item } = session;
|
|
264
|
-
return item;
|
|
265
|
-
}
|
|
266
|
-
function assertProjectUsageDataModule(module) {
|
|
267
|
-
if (!PROJECT_USAGE_DATA_MODULES.includes(module)) {
|
|
268
|
-
throw new Error(`Unsupported project data module: ${module}.`);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
function assertProjectUsagePlatformScope(platform) {
|
|
272
|
-
if (platform !== "all" && !PROJECT_USAGE_PLATFORMS.includes(platform)) {
|
|
273
|
-
throw new Error(`Unsupported project data platform: ${platform}.`);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
function getProjectDetailPlatforms(detail) {
|
|
277
|
-
return PROJECT_USAGE_PLATFORMS.filter((platform) => detail.analyzing[platform].sessions.length > 0);
|
|
278
|
-
}
|
|
279
|
-
function getProjectCatalogType(platforms) {
|
|
280
|
-
return platforms.length === 1 ? platforms[0] : "mixed";
|
|
281
|
-
}
|
|
282
|
-
function getPlatformUsageRoots(config, platform) {
|
|
283
|
-
if (platform === "claudeCode") {
|
|
284
|
-
return config.claudeCodePaths.map((path) => join(path, "projects"));
|
|
285
|
-
}
|
|
286
|
-
if (platform === "codex") {
|
|
287
|
-
return [join(config.codexPath, "sessions")];
|
|
288
|
-
}
|
|
289
|
-
return [join(config.geminiPath, "tmp")];
|
|
290
|
-
}
|
|
291
|
-
function createConfigForUsagePaths(config, paths) {
|
|
292
|
-
var _a, _b, _c;
|
|
293
|
-
const platformRoots = getScopedPlatformRoots(config, paths);
|
|
294
|
-
return {
|
|
295
|
-
...config,
|
|
296
|
-
claudeCodePath: (_a = platformRoots.claudeCode[0]) != null ? _a : EMPTY_USAGE_ROOT,
|
|
297
|
-
claudeCodePaths: platformRoots.claudeCode,
|
|
298
|
-
codexPath: (_b = platformRoots.codex[0]) != null ? _b : EMPTY_USAGE_ROOT,
|
|
299
|
-
geminiPath: (_c = platformRoots.gemini[0]) != null ? _c : EMPTY_USAGE_ROOT
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
function getScopedPlatformRoots(config, paths) {
|
|
303
|
-
const roots = {
|
|
304
|
-
claudeCode: [],
|
|
305
|
-
codex: [],
|
|
306
|
-
gemini: []
|
|
307
|
-
};
|
|
308
|
-
for (const inputPath of paths) {
|
|
309
|
-
const normalizedPath = normalizeUsagePath(inputPath);
|
|
310
|
-
const platform = detectUsagePlatform(config, normalizedPath);
|
|
311
|
-
if (platform === "claudeCode") {
|
|
312
|
-
roots.claudeCode.push(getClaudeRootFromUsagePath(normalizedPath));
|
|
313
|
-
} else if (platform === "codex") {
|
|
314
|
-
roots.codex.push(getCodexRootFromUsagePath(normalizedPath));
|
|
315
|
-
} else if (platform === "gemini") {
|
|
316
|
-
roots.gemini.push(getGeminiRootFromUsagePath(normalizedPath));
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
return {
|
|
320
|
-
claudeCode: uniqueItems(roots.claudeCode),
|
|
321
|
-
codex: uniqueItems(roots.codex),
|
|
322
|
-
gemini: uniqueItems(roots.gemini)
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
function detectUsagePlatform(config, path) {
|
|
326
|
-
if (isSameOrChildPath(path, config.codexPath)) {
|
|
327
|
-
return "codex";
|
|
328
|
-
}
|
|
329
|
-
if (isSameOrChildPath(path, config.geminiPath)) {
|
|
330
|
-
return "gemini";
|
|
331
|
-
}
|
|
332
|
-
if (config.claudeCodePaths.some((claudePath) => isSameOrChildPath(path, claudePath))) {
|
|
333
|
-
return "claudeCode";
|
|
334
|
-
}
|
|
335
|
-
if (path.includes(`${sep}.codex${sep}`) || path.endsWith(`${sep}.codex`)) {
|
|
336
|
-
return "codex";
|
|
337
|
-
}
|
|
338
|
-
if (path.includes(`${sep}.gemini${sep}`) || path.endsWith(`${sep}.gemini`)) {
|
|
339
|
-
return "gemini";
|
|
340
|
-
}
|
|
341
|
-
if (path.includes(`${sep}.claude${sep}`) || path.endsWith(`${sep}.claude`) || path.includes(`${sep}claude${sep}`)) {
|
|
342
|
-
return "claudeCode";
|
|
343
|
-
}
|
|
344
|
-
return null;
|
|
345
|
-
}
|
|
346
|
-
function getClaudeRootFromUsagePath(path) {
|
|
347
|
-
const parts = path.split(sep);
|
|
348
|
-
const projectsIndex = parts.lastIndexOf("projects");
|
|
349
|
-
if (projectsIndex > 0) {
|
|
350
|
-
return parts.slice(0, projectsIndex).join(sep) || sep;
|
|
351
|
-
}
|
|
352
|
-
return path;
|
|
353
|
-
}
|
|
354
|
-
function getCodexRootFromUsagePath(path) {
|
|
355
|
-
return basename(path) === "sessions" ? resolve(path, "..") : path;
|
|
356
|
-
}
|
|
357
|
-
function getGeminiRootFromUsagePath(path) {
|
|
358
|
-
const parts = path.split(sep);
|
|
359
|
-
const tmpIndex = parts.lastIndexOf("tmp");
|
|
360
|
-
if (tmpIndex > 0) {
|
|
361
|
-
return parts.slice(0, tmpIndex).join(sep) || sep;
|
|
362
|
-
}
|
|
363
|
-
return path;
|
|
364
|
-
}
|
|
365
|
-
function normalizeUsagePath(path) {
|
|
366
|
-
const trimmedPath = path.trim();
|
|
367
|
-
if (trimmedPath === "~") {
|
|
368
|
-
return homedir();
|
|
369
|
-
}
|
|
370
|
-
if (trimmedPath.startsWith(`~${sep}`)) {
|
|
371
|
-
return resolve(homedir(), trimmedPath.slice(2));
|
|
372
|
-
}
|
|
373
|
-
return resolve(trimmedPath);
|
|
374
|
-
}
|
|
375
|
-
function toHomeRelativePath(path) {
|
|
376
|
-
const home = homedir();
|
|
377
|
-
if (path === home) {
|
|
378
|
-
return "~";
|
|
379
|
-
}
|
|
380
|
-
if (path.startsWith(`${home}${sep}`)) {
|
|
381
|
-
return `~${sep}${path.slice(home.length + 1)}`;
|
|
382
|
-
}
|
|
383
|
-
return path;
|
|
384
|
-
}
|
|
385
|
-
function isSameOrChildPath(path, parent) {
|
|
386
|
-
const normalizedPath = resolve(path);
|
|
387
|
-
const normalizedParent = resolve(parent);
|
|
388
|
-
return normalizedPath === normalizedParent || normalizedPath.startsWith(`${normalizedParent}${sep}`);
|
|
389
|
-
}
|
|
390
|
-
function getPlatformProjectNames(detailsBySession) {
|
|
391
|
-
return uniqueItems(Array.from(detailsBySession.values()).map((detail) => detail.project)).sort((a, b) => a.localeCompare(b));
|
|
392
|
-
}
|
|
393
|
-
function buildPlatformProjectUsage(detailsBySession, projectName, platform) {
|
|
394
|
-
const sessions = getProjectSessions(detailsBySession, projectName);
|
|
395
|
-
return {
|
|
396
|
-
...buildProjectLoadUsageResult(sessions, platform),
|
|
397
|
-
sessions
|
|
398
|
-
};
|
|
399
|
-
}
|
|
400
|
-
function getProjectSessions(detailsBySession, projectName) {
|
|
401
|
-
return Array.from(detailsBySession.values()).filter((detail) => detail.project === projectName).map((detail) => createSessionFromDetail(detail)).sort((a, b) => Date.parse(b.startedAt) - Date.parse(a.startedAt));
|
|
402
|
-
}
|
|
403
|
-
function buildProjectLoadUsageResult(sessions, platform = "all") {
|
|
404
|
-
const usage = buildLoadUsageResult(getProjectAggregateEvents(sessions), sessions, {
|
|
405
|
-
aggregateOptions: {
|
|
406
|
-
includeModel: (event) => platform !== "claudeCode" || event.model !== "<synthetic>"
|
|
407
|
-
}
|
|
408
|
-
});
|
|
409
|
-
return {
|
|
410
|
-
...usage,
|
|
411
|
-
sessionUsage: sessions
|
|
412
|
-
};
|
|
413
|
-
}
|
|
414
|
-
function getProjectAggregateEvents(sessions) {
|
|
415
|
-
return sessions.flatMap((session) => session.interactions.filter((interaction) => interaction.usage && interaction.timestamp && hasBillableUsage(interaction.usage)).map((interaction) => {
|
|
416
|
-
var _a, _b;
|
|
417
|
-
return {
|
|
418
|
-
cachedInputTokens: interaction.usage.cachedInputTokens,
|
|
419
|
-
costUSD: interaction.usage.costUSD,
|
|
420
|
-
inputTokens: interaction.usage.inputTokens,
|
|
421
|
-
isFallbackModel: (_a = interaction.usage.isFallbackModel) != null ? _a : false,
|
|
422
|
-
model: (_b = interaction.model) != null ? _b : session.model,
|
|
423
|
-
outputTokens: interaction.usage.outputTokens,
|
|
424
|
-
project: session.project,
|
|
425
|
-
reasoningOutputTokens: interaction.usage.reasoningOutputTokens,
|
|
426
|
-
repository: session.repository,
|
|
427
|
-
sessionId: session.sessionId,
|
|
428
|
-
timestamp: interaction.timestamp,
|
|
429
|
-
totalTokens: interaction.usage.totalTokens
|
|
430
|
-
};
|
|
431
|
-
})).sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
|
|
432
|
-
}
|
|
433
|
-
function hasBillableUsage(usage) {
|
|
434
|
-
return usage.totalTokens > 0 || usage.costUSD > 0;
|
|
435
|
-
}
|
|
436
|
-
async function loadClaudeSessionDetails(config, resolvePricing) {
|
|
437
|
-
var _a, _b, _c;
|
|
438
|
-
const details = /* @__PURE__ */ new Map();
|
|
439
|
-
const files = await globClaudeUsageFiles(config);
|
|
440
|
-
const processedHashes = /* @__PURE__ */ new Set();
|
|
441
|
-
for (const filePath of files.sort((a, b) => a.localeCompare(b))) {
|
|
442
|
-
const projectPath = extractClaudeProjectFromPath(filePath);
|
|
443
|
-
const fallbackSessionId = basename(filePath, ".jsonl");
|
|
444
|
-
const lines = parseJsonlFile(filePath);
|
|
445
|
-
for (let index = 0; index < lines.length; index += 1) {
|
|
446
|
-
const line = lines[index];
|
|
447
|
-
const uniqueHash = getClaudeUniqueHash(line);
|
|
448
|
-
if (uniqueHash && processedHashes.has(uniqueHash)) {
|
|
449
|
-
continue;
|
|
450
|
-
}
|
|
451
|
-
if (uniqueHash) {
|
|
452
|
-
processedHashes.add(uniqueHash);
|
|
453
|
-
}
|
|
454
|
-
const sessionId = getString$1(line.sessionId) || fallbackSessionId;
|
|
455
|
-
const cwd = getString$1(line.cwd);
|
|
456
|
-
const project = getProjectName(cwd, "") || decodeClaudeProjectPath(projectPath);
|
|
457
|
-
const timestamp = (_a = toIsoString(line.timestamp)) != null ? _a : null;
|
|
458
|
-
const message = getRecord(line.message);
|
|
459
|
-
const usageRecord = getRecord(message == null ? void 0 : message.usage);
|
|
460
|
-
const model = getClaudeDisplayModel(line);
|
|
461
|
-
const usage = usageRecord ? getClaudeInteractionUsage(usageRecord, model, resolvePricing, line) : null;
|
|
462
|
-
const interaction = {
|
|
463
|
-
content: extractClaudeMessageText(message == null ? void 0 : message.content),
|
|
464
|
-
costUSD: (_b = usage == null ? void 0 : usage.costUSD) != null ? _b : 0,
|
|
465
|
-
index,
|
|
466
|
-
model: model != null ? model : null,
|
|
467
|
-
raw: line,
|
|
468
|
-
role: getInteractionRole(line, message),
|
|
469
|
-
timestamp,
|
|
470
|
-
type: getString$1(line.type) || getString$1(message == null ? void 0 : message.type) || "message",
|
|
471
|
-
usage
|
|
472
|
-
};
|
|
473
|
-
const key = getSessionLookupKey(project, sessionId);
|
|
474
|
-
const detail = (_c = details.get(key)) != null ? _c : createSessionDetail({
|
|
475
|
-
project,
|
|
476
|
-
repository: `local/${project}`,
|
|
477
|
-
sessionId,
|
|
478
|
-
startedAt: timestamp,
|
|
479
|
-
threadName: `Session for ${project}`
|
|
480
|
-
});
|
|
481
|
-
addInteraction(detail, interaction);
|
|
482
|
-
details.set(key, detail);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
return finalizeSessionDetails(details);
|
|
486
|
-
}
|
|
487
|
-
async function loadCodexSessionDetails(config, resolvePricing) {
|
|
488
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
|
|
489
|
-
const details = /* @__PURE__ */ new Map();
|
|
490
|
-
const sessionsDir = join(config.codexPath, "sessions");
|
|
491
|
-
if (!existsSync(sessionsDir)) {
|
|
492
|
-
return details;
|
|
493
|
-
}
|
|
494
|
-
const files = await Ze("**/*.jsonl", {
|
|
495
|
-
absolute: true,
|
|
496
|
-
cwd: sessionsDir
|
|
497
|
-
});
|
|
498
|
-
for (const filePath of files.sort((a, b) => a.localeCompare(b))) {
|
|
499
|
-
const lines = parseJsonlFile(filePath);
|
|
500
|
-
const sessionMeta = (_a = lines.find((line) => line.type === "session_meta")) == null ? void 0 : _a.payload;
|
|
501
|
-
const sessionId = getSessionId(filePath, getString$1(sessionMeta == null ? void 0 : sessionMeta.id));
|
|
502
|
-
const startedAt = (_c = toIsoString(sessionMeta == null ? void 0 : sessionMeta.timestamp)) != null ? _c : toIsoString((_b = lines[0]) == null ? void 0 : _b.timestamp);
|
|
503
|
-
const project = getProjectName(getString$1(sessionMeta == null ? void 0 : sessionMeta.cwd));
|
|
504
|
-
const repository = normalizeRepositoryUrl((_d = sessionMeta == null ? void 0 : sessionMeta.git) == null ? void 0 : _d.repository_url) || `local/${project}`;
|
|
505
|
-
const detail = createSessionDetail({
|
|
506
|
-
project,
|
|
507
|
-
repository,
|
|
508
|
-
sessionId,
|
|
509
|
-
startedAt,
|
|
510
|
-
threadName: `Session for ${project}`
|
|
511
|
-
});
|
|
512
|
-
let previousTotals = null;
|
|
513
|
-
let currentModel;
|
|
514
|
-
let currentModelIsFallback = false;
|
|
515
|
-
for (let index = 0; index < lines.length; index += 1) {
|
|
516
|
-
const line = lines[index];
|
|
517
|
-
if (line.type === "turn_context") {
|
|
518
|
-
const contextModel = extractModelName(line.payload);
|
|
519
|
-
if (contextModel) {
|
|
520
|
-
currentModel = contextModel;
|
|
521
|
-
currentModelIsFallback = false;
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
const timestamp = (_f = toIsoString(line.timestamp)) != null ? _f : toIsoString((_e = line.payload) == null ? void 0 : _e.timestamp);
|
|
525
|
-
const extractedModel = extractModelName(line.payload);
|
|
526
|
-
if (extractedModel) {
|
|
527
|
-
currentModel = extractedModel;
|
|
528
|
-
currentModelIsFallback = false;
|
|
529
|
-
}
|
|
530
|
-
const rawUsage = getCodexRawUsage(line, previousTotals);
|
|
531
|
-
const totalUsage = normalizeRawUsage((_h = (_g = line.payload) == null ? void 0 : _g.info) == null ? void 0 : _h.total_token_usage);
|
|
532
|
-
if (totalUsage) {
|
|
533
|
-
previousTotals = totalUsage;
|
|
534
|
-
}
|
|
535
|
-
let model = extractedModel != null ? extractedModel : currentModel;
|
|
536
|
-
let isFallbackModel = false;
|
|
537
|
-
if (!model && rawUsage) {
|
|
538
|
-
model = CODEX_FALLBACK_MODEL;
|
|
539
|
-
isFallbackModel = true;
|
|
540
|
-
currentModel = model;
|
|
541
|
-
currentModelIsFallback = true;
|
|
542
|
-
} else if (!extractedModel && currentModelIsFallback) {
|
|
543
|
-
isFallbackModel = true;
|
|
544
|
-
}
|
|
545
|
-
const usage = rawUsage ? getCodexInteractionUsage(rawUsage, model != null ? model : CODEX_FALLBACK_MODEL, resolvePricing) : null;
|
|
546
|
-
addInteraction(detail, {
|
|
547
|
-
content: extractCodexContent(line),
|
|
548
|
-
costUSD: (_i = usage == null ? void 0 : usage.costUSD) != null ? _i : 0,
|
|
549
|
-
index,
|
|
550
|
-
model: model != null ? model : null,
|
|
551
|
-
raw: line,
|
|
552
|
-
role: getCodexRole(line),
|
|
553
|
-
timestamp,
|
|
554
|
-
type: (_l = (_k = (_j = line.payload) == null ? void 0 : _j.type) != null ? _k : line.type) != null ? _l : "event",
|
|
555
|
-
usage: usage ? { ...usage, isFallbackModel } : null
|
|
556
|
-
});
|
|
557
|
-
}
|
|
558
|
-
const finalized = finalizeSessionDetail(detail);
|
|
559
|
-
if (hasBillableSessionDetail(finalized)) {
|
|
560
|
-
details.set(getSessionLookupKey(project, sessionId), finalized);
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
return details;
|
|
564
|
-
}
|
|
565
|
-
async function loadGeminiSessionDetails(config, resolvePricing) {
|
|
566
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
567
|
-
const details = /* @__PURE__ */ new Map();
|
|
568
|
-
const tmpDir = `${config.geminiPath}/tmp`;
|
|
569
|
-
if (!existsSync(tmpDir)) {
|
|
570
|
-
return details;
|
|
571
|
-
}
|
|
572
|
-
const fileGroups = await Promise.all([
|
|
573
|
-
Ze(`${tmpDir}/*/chats/session-*.json`, { absolute: true }),
|
|
574
|
-
Ze(`${tmpDir}/*/chats/sessions-*.json`, { absolute: true })
|
|
575
|
-
]);
|
|
576
|
-
const files = uniqueItems(fileGroups.flat()).sort((a, b) => a.localeCompare(b));
|
|
577
|
-
for (const filePath of files) {
|
|
578
|
-
const data = parseJsonFile(filePath);
|
|
579
|
-
if (!isGeminiSessionFile(data)) {
|
|
580
|
-
continue;
|
|
581
|
-
}
|
|
582
|
-
const startedAt = (_b = (_a = toIsoString(data.startTime)) != null ? _a : data.messages.map((message) => toIsoString(message.timestamp)).find(Boolean)) != null ? _b : null;
|
|
583
|
-
const lastTimestamp = (_d = (_c = toIsoString(data.lastUpdated)) != null ? _c : [...data.messages].reverse().map((message) => toIsoString(message.timestamp)).find(Boolean)) != null ? _d : null;
|
|
584
|
-
const projectRoot = getGeminiProjectRoot(filePath);
|
|
585
|
-
const project = getProjectName(projectRoot, "") || getGeminiProjectKeyFromPath(filePath);
|
|
586
|
-
const repository = getRepositoryNameFromProjectRoot(projectRoot) || `local/${project}`;
|
|
587
|
-
const sessionId = ((_e = data.sessionId) == null ? void 0 : _e.trim()) || basename(filePath, ".json");
|
|
588
|
-
const detail = createSessionDetail({
|
|
589
|
-
project,
|
|
590
|
-
repository,
|
|
591
|
-
sessionId,
|
|
592
|
-
startedAt,
|
|
593
|
-
threadName: getGeminiThreadName(data, project)
|
|
594
|
-
});
|
|
595
|
-
detail.durationEndAt = lastTimestamp != null ? lastTimestamp : "";
|
|
596
|
-
for (let index = 0; index < data.messages.length; index += 1) {
|
|
597
|
-
const message = data.messages[index];
|
|
598
|
-
const timestamp = toIsoString(message.timestamp);
|
|
599
|
-
const model = ((_f = message.model) == null ? void 0 : _f.trim()) || (message.tokens ? GEMINI_FALLBACK_MODEL : null);
|
|
600
|
-
const usage = message.tokens && model ? getGeminiInteractionUsage(message.tokens, model, resolvePricing) : null;
|
|
601
|
-
addInteraction(detail, {
|
|
602
|
-
content: extractGeminiMessageText(message.content),
|
|
603
|
-
costUSD: (_g = usage == null ? void 0 : usage.costUSD) != null ? _g : 0,
|
|
604
|
-
index,
|
|
605
|
-
model,
|
|
606
|
-
raw: message,
|
|
607
|
-
role: getGeminiRole(message),
|
|
608
|
-
timestamp,
|
|
609
|
-
type: (_h = message.type) != null ? _h : "message",
|
|
610
|
-
usage
|
|
611
|
-
});
|
|
612
|
-
}
|
|
613
|
-
const finalized = finalizeSessionDetail(detail);
|
|
614
|
-
if (hasBillableSessionDetail(finalized)) {
|
|
615
|
-
details.set(getSessionLookupKey(project, sessionId), finalized);
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
return details;
|
|
619
|
-
}
|
|
620
|
-
function getClaudeInteractionUsage(usage, model, resolvePricing, line) {
|
|
621
|
-
var _a;
|
|
622
|
-
const cacheCreationTokens = normalizeNumber(usage.cache_creation_input_tokens);
|
|
623
|
-
const cacheReadTokens = normalizeNumber(usage.cache_read_input_tokens);
|
|
624
|
-
const inputTokens = normalizeNumber(usage.input_tokens);
|
|
625
|
-
const outputTokens = normalizeNumber(usage.output_tokens);
|
|
626
|
-
const costUSD = (_a = normalizeOptionalNumber(line.costUSD)) != null ? _a : model ? calculateUsageCostUSD({
|
|
627
|
-
cacheCreationTokens,
|
|
628
|
-
cachedInputTokens: cacheReadTokens,
|
|
629
|
-
inputTokens,
|
|
630
|
-
outputTokens
|
|
631
|
-
}, resolvePricing(model), {
|
|
632
|
-
speed: usage.speed === "fast" ? "fast" : void 0
|
|
633
|
-
}) : 0;
|
|
634
|
-
return {
|
|
635
|
-
cacheCreationTokens,
|
|
636
|
-
cacheReadTokens,
|
|
637
|
-
cachedInputTokens: cacheCreationTokens + cacheReadTokens,
|
|
638
|
-
costUSD,
|
|
639
|
-
inputTokens,
|
|
640
|
-
outputTokens,
|
|
641
|
-
reasoningOutputTokens: 0,
|
|
642
|
-
totalTokens: inputTokens + outputTokens + cacheCreationTokens + cacheReadTokens
|
|
643
|
-
};
|
|
644
|
-
}
|
|
645
|
-
function getCodexRawUsage(line, previousTotals) {
|
|
646
|
-
var _a;
|
|
647
|
-
if (line.type !== "event_msg" || ((_a = line.payload) == null ? void 0 : _a.type) !== "token_count") {
|
|
648
|
-
return null;
|
|
649
|
-
}
|
|
650
|
-
const info = line.payload.info;
|
|
651
|
-
const lastUsage = normalizeRawUsage(info == null ? void 0 : info.last_token_usage);
|
|
652
|
-
const totalUsage = normalizeRawUsage(info == null ? void 0 : info.total_token_usage);
|
|
653
|
-
return lastUsage != null ? lastUsage : totalUsage ? subtractRawUsage(totalUsage, previousTotals) : null;
|
|
654
|
-
}
|
|
655
|
-
function getCodexInteractionUsage(rawUsage, model, resolvePricing) {
|
|
656
|
-
const usage = convertCodexRawUsage(rawUsage);
|
|
657
|
-
if (isZeroUsage(usage)) {
|
|
658
|
-
return null;
|
|
659
|
-
}
|
|
660
|
-
return {
|
|
661
|
-
...usage,
|
|
662
|
-
costUSD: calculateUsageCostUSD(usage, resolvePricing(model))
|
|
663
|
-
};
|
|
664
|
-
}
|
|
665
|
-
function getGeminiInteractionUsage(tokens, model, resolvePricing) {
|
|
666
|
-
const usage = convertGeminiTokenUsage(tokens);
|
|
667
|
-
if (isZeroUsage(usage)) {
|
|
668
|
-
return null;
|
|
669
|
-
}
|
|
670
|
-
const toolTokens = normalizeNumber(tokens.tool);
|
|
671
|
-
const costUSD = calculateUsageCostUSD({
|
|
672
|
-
cachedInputTokens: usage.cachedInputTokens,
|
|
673
|
-
inputTokens: usage.inputTokens,
|
|
674
|
-
outputTokens: usage.outputTokens + usage.reasoningOutputTokens + toolTokens
|
|
675
|
-
}, resolvePricing(model));
|
|
676
|
-
return {
|
|
677
|
-
...usage,
|
|
678
|
-
costUSD,
|
|
679
|
-
toolTokens
|
|
680
|
-
};
|
|
681
|
-
}
|
|
682
|
-
function createSessionDetail(options) {
|
|
683
|
-
var _a, _b;
|
|
684
|
-
return {
|
|
685
|
-
cachedInputTokens: 0,
|
|
686
|
-
costUSD: 0,
|
|
687
|
-
durationEndAt: "",
|
|
688
|
-
durationMinutes: 0,
|
|
689
|
-
inputTokens: 0,
|
|
690
|
-
interactions: [],
|
|
691
|
-
lastActivity: (_a = options.startedAt) != null ? _a : "",
|
|
692
|
-
modelTotals: /* @__PURE__ */ new Map(),
|
|
693
|
-
models: [],
|
|
694
|
-
outputTokens: 0,
|
|
695
|
-
project: options.project,
|
|
696
|
-
reasoningOutputTokens: 0,
|
|
697
|
-
repository: options.repository,
|
|
698
|
-
sessionId: options.sessionId,
|
|
699
|
-
startedAt: (_b = options.startedAt) != null ? _b : "",
|
|
700
|
-
topModel: "unknown",
|
|
701
|
-
threadName: options.threadName,
|
|
702
|
-
tokenTotal: 0
|
|
703
|
-
};
|
|
704
|
-
}
|
|
705
|
-
function addInteraction(detail, interaction) {
|
|
706
|
-
var _a;
|
|
707
|
-
detail.interactions.push(interaction);
|
|
708
|
-
if (interaction.timestamp) {
|
|
709
|
-
if (!detail.startedAt || Date.parse(interaction.timestamp) < Date.parse(detail.startedAt)) {
|
|
710
|
-
detail.startedAt = interaction.timestamp;
|
|
711
|
-
}
|
|
712
|
-
if (!detail.lastActivity || Date.parse(interaction.timestamp) > Date.parse(detail.lastActivity)) {
|
|
713
|
-
detail.lastActivity = interaction.timestamp;
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
if (!interaction.usage) {
|
|
717
|
-
return;
|
|
718
|
-
}
|
|
719
|
-
detail.inputTokens += interaction.usage.inputTokens;
|
|
720
|
-
detail.cachedInputTokens += interaction.usage.cachedInputTokens;
|
|
721
|
-
detail.outputTokens += interaction.usage.outputTokens;
|
|
722
|
-
detail.reasoningOutputTokens += interaction.usage.reasoningOutputTokens;
|
|
723
|
-
detail.tokenTotal += interaction.usage.totalTokens;
|
|
724
|
-
detail.costUSD += interaction.usage.costUSD;
|
|
725
|
-
if (interaction.model) {
|
|
726
|
-
detail.models = uniqueItems([...detail.models, interaction.model]);
|
|
727
|
-
detail.modelTotals.set(
|
|
728
|
-
interaction.model,
|
|
729
|
-
((_a = detail.modelTotals.get(interaction.model)) != null ? _a : 0) + interaction.usage.totalTokens
|
|
730
|
-
);
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
function finalizeSessionDetails(details) {
|
|
734
|
-
for (const [key, detail] of details) {
|
|
735
|
-
const finalized = finalizeSessionDetail(detail);
|
|
736
|
-
if (hasBillableSessionDetail(finalized)) {
|
|
737
|
-
details.set(key, finalized);
|
|
738
|
-
continue;
|
|
739
|
-
}
|
|
740
|
-
details.delete(key);
|
|
741
|
-
}
|
|
742
|
-
return details;
|
|
743
|
-
}
|
|
744
|
-
function finalizeSessionDetail(detail) {
|
|
745
|
-
var _a, _b;
|
|
746
|
-
detail.costUSD = roundCurrency(detail.costUSD);
|
|
747
|
-
detail.durationMinutes = getDurationMinutes(detail.startedAt, detail.durationEndAt || detail.lastActivity);
|
|
748
|
-
detail.interactions = detail.interactions.sort((a, b) => {
|
|
749
|
-
if (a.timestamp && b.timestamp) {
|
|
750
|
-
return Date.parse(a.timestamp) - Date.parse(b.timestamp) || a.index - b.index;
|
|
751
|
-
}
|
|
752
|
-
return a.index - b.index;
|
|
753
|
-
});
|
|
754
|
-
detail.topModel = (_b = (_a = Array.from(detail.modelTotals.entries()).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))[0]) == null ? void 0 : _a[0]) != null ? _b : "unknown";
|
|
755
|
-
detail.models = detail.models.sort((a, b) => a.localeCompare(b));
|
|
756
|
-
return detail;
|
|
757
|
-
}
|
|
758
|
-
function hasBillableSessionDetail(detail) {
|
|
759
|
-
return detail.tokenTotal > 0 || detail.costUSD > 0;
|
|
760
|
-
}
|
|
761
|
-
function createSessionFromDetail(detail) {
|
|
762
|
-
var _a, _b, _c;
|
|
763
|
-
const startedAt = (_b = (_a = getValidTimestamp(detail.startedAt)) != null ? _a : getValidTimestamp(detail.lastActivity)) != null ? _b : (/* @__PURE__ */ new Date(0)).toISOString();
|
|
764
|
-
const lastActivity = (_c = getValidTimestamp(detail.lastActivity)) != null ? _c : startedAt;
|
|
765
|
-
const startedAtDate = new Date(startedAt);
|
|
766
|
-
const dateKey = Number.isFinite(startedAtDate.getTime()) ? getDateKey(startedAtDate) : "";
|
|
767
|
-
return {
|
|
768
|
-
cachedInputTokens: detail.cachedInputTokens,
|
|
769
|
-
costUSD: detail.costUSD,
|
|
770
|
-
date: dateKey ? formatDateLabelFromDateKey(dateKey) : "",
|
|
771
|
-
duration: formatDuration(detail.durationMinutes),
|
|
772
|
-
durationMinutes: detail.durationMinutes,
|
|
773
|
-
id: detail.sessionId,
|
|
774
|
-
inputTokens: detail.inputTokens,
|
|
775
|
-
interactions: detail.interactions,
|
|
776
|
-
lastActivity,
|
|
777
|
-
model: detail.topModel,
|
|
778
|
-
models: detail.models,
|
|
779
|
-
month: Number.isFinite(startedAtDate.getTime()) ? getMonthKey(startedAtDate) : "",
|
|
780
|
-
outputTokens: detail.outputTokens,
|
|
781
|
-
project: detail.project,
|
|
782
|
-
reasoningOutputTokens: detail.reasoningOutputTokens,
|
|
783
|
-
repository: detail.repository,
|
|
784
|
-
sessionId: detail.sessionId,
|
|
785
|
-
startedAt,
|
|
786
|
-
topModel: detail.topModel,
|
|
787
|
-
threadName: detail.threadName,
|
|
788
|
-
tokenTotal: detail.tokenTotal,
|
|
789
|
-
week: Number.isFinite(startedAtDate.getTime()) ? getWeekLabel(startedAtDate) : ""
|
|
790
|
-
};
|
|
791
|
-
}
|
|
792
|
-
function getValidTimestamp(value) {
|
|
793
|
-
return Number.isFinite(Date.parse(value)) ? value : null;
|
|
794
|
-
}
|
|
795
|
-
async function globClaudeUsageFiles(config) {
|
|
796
|
-
var _a;
|
|
797
|
-
const claudePaths = ((_a = config.claudeCodePaths) == null ? void 0 : _a.length) ? config.claudeCodePaths : [config.claudeCodePath];
|
|
798
|
-
const fileGroups = await Promise.all(claudePaths.map(async (claudePath) => {
|
|
799
|
-
const projectsDir = `${claudePath}/projects`;
|
|
800
|
-
if (!existsSync(projectsDir)) {
|
|
801
|
-
return [];
|
|
802
|
-
}
|
|
803
|
-
return Ze(`${projectsDir}/**/*.jsonl`, {
|
|
804
|
-
absolute: true
|
|
805
|
-
}).catch(() => []);
|
|
806
|
-
}));
|
|
807
|
-
return fileGroups.flat();
|
|
808
|
-
}
|
|
809
|
-
function getClaudeDisplayModel(line) {
|
|
810
|
-
const message = getRecord(line.message);
|
|
811
|
-
const model = getString$1(message == null ? void 0 : message.model);
|
|
812
|
-
const usage = getRecord(message == null ? void 0 : message.usage);
|
|
813
|
-
if (!model) {
|
|
814
|
-
return void 0;
|
|
815
|
-
}
|
|
816
|
-
return (usage == null ? void 0 : usage.speed) === "fast" ? `${model}-fast` : model;
|
|
817
|
-
}
|
|
818
|
-
function getClaudeUniqueHash(line) {
|
|
819
|
-
const message = getRecord(line.message);
|
|
820
|
-
const messageId = getString$1(message == null ? void 0 : message.id);
|
|
821
|
-
const requestId = getString$1(line.requestId);
|
|
822
|
-
return messageId && requestId ? `${messageId}:${requestId}` : null;
|
|
823
|
-
}
|
|
824
|
-
function extractClaudeMessageText(content) {
|
|
825
|
-
if (typeof content === "string") {
|
|
826
|
-
return content;
|
|
827
|
-
}
|
|
828
|
-
if (!Array.isArray(content)) {
|
|
829
|
-
return "";
|
|
830
|
-
}
|
|
831
|
-
return content.map((item) => typeof item === "object" && item ? getString$1(item.text) : "").filter(Boolean).join("\n");
|
|
832
|
-
}
|
|
833
|
-
function extractCodexContent(line) {
|
|
834
|
-
const payload = line.payload;
|
|
835
|
-
if (!payload) {
|
|
836
|
-
return "";
|
|
837
|
-
}
|
|
838
|
-
const message = payload.message;
|
|
839
|
-
if (typeof message === "string") {
|
|
840
|
-
return message;
|
|
841
|
-
}
|
|
842
|
-
const text = getString$1(payload.text) || getString$1(payload.output) || getString$1(payload.content);
|
|
843
|
-
return text;
|
|
844
|
-
}
|
|
845
|
-
function getInteractionRole(line, message) {
|
|
846
|
-
const role = getString$1(line.type) || getString$1(message == null ? void 0 : message.role) || getString$1(message == null ? void 0 : message.type);
|
|
847
|
-
return normalizeRole(role);
|
|
848
|
-
}
|
|
849
|
-
function getCodexRole(line) {
|
|
850
|
-
var _a, _b, _c;
|
|
851
|
-
const type = (_c = (_b = (_a = line.payload) == null ? void 0 : _a.type) != null ? _b : line.type) != null ? _c : "";
|
|
852
|
-
if (type === "token_count") {
|
|
853
|
-
return "usage";
|
|
854
|
-
}
|
|
855
|
-
return normalizeRole(type);
|
|
856
|
-
}
|
|
857
|
-
function getGeminiRole(message) {
|
|
858
|
-
var _a;
|
|
859
|
-
if (message.type === "gemini") {
|
|
860
|
-
return "assistant";
|
|
861
|
-
}
|
|
862
|
-
return normalizeRole((_a = message.type) != null ? _a : "");
|
|
863
|
-
}
|
|
864
|
-
function normalizeRole(value) {
|
|
865
|
-
const normalized = value.toLowerCase();
|
|
866
|
-
if (normalized.includes("user")) {
|
|
867
|
-
return "user";
|
|
868
|
-
}
|
|
869
|
-
if (normalized.includes("assistant") || normalized.includes("agent") || normalized.includes("gemini")) {
|
|
870
|
-
return "assistant";
|
|
871
|
-
}
|
|
872
|
-
if (normalized.includes("system")) {
|
|
873
|
-
return "system";
|
|
874
|
-
}
|
|
875
|
-
if (normalized.includes("tool")) {
|
|
876
|
-
return "tool";
|
|
877
|
-
}
|
|
878
|
-
if (normalized.includes("token") || normalized.includes("usage")) {
|
|
879
|
-
return "usage";
|
|
880
|
-
}
|
|
881
|
-
return "unknown";
|
|
882
|
-
}
|
|
883
|
-
function getSessionId(filePath, sessionMetaId) {
|
|
884
|
-
return (sessionMetaId == null ? void 0 : sessionMetaId.trim()) || basename(filePath, ".jsonl");
|
|
885
|
-
}
|
|
886
|
-
function isGeminiSessionFile(value) {
|
|
887
|
-
if (!value || typeof value !== "object") {
|
|
888
|
-
return false;
|
|
889
|
-
}
|
|
890
|
-
return Array.isArray(value.messages);
|
|
891
|
-
}
|
|
892
|
-
function getGeminiThreadName(data, project) {
|
|
893
|
-
var _a;
|
|
894
|
-
const firstUserMessage = data.messages.filter((message) => message.type === "user").map((message) => extractGeminiMessageText(message.content)).find(Boolean);
|
|
895
|
-
const summary = (_a = data.summary) == null ? void 0 : _a.trim();
|
|
896
|
-
const name = firstUserMessage || summary;
|
|
897
|
-
if (!name) {
|
|
898
|
-
return `Session for ${project}`;
|
|
899
|
-
}
|
|
900
|
-
return name.length > 96 ? `${name.slice(0, 93)}...` : name;
|
|
901
|
-
}
|
|
902
|
-
function collectSessionModels(sessions) {
|
|
903
|
-
return uniqueItems(sessions.flatMap((session) => session.models)).sort((a, b) => a.localeCompare(b));
|
|
904
|
-
}
|
|
905
|
-
function getEarliestStartedAt(sessions) {
|
|
906
|
-
var _a;
|
|
907
|
-
return (_a = sessions.map((session) => session.startedAt).filter((timestamp) => Number.isFinite(Date.parse(timestamp))).sort((a, b) => Date.parse(a) - Date.parse(b))[0]) != null ? _a : null;
|
|
908
|
-
}
|
|
909
|
-
function getSessionLookupKey(project, sessionId) {
|
|
910
|
-
return `${project}:${sessionId}`;
|
|
911
|
-
}
|
|
912
|
-
function getRecord(value) {
|
|
913
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
914
|
-
}
|
|
915
|
-
function getString$1(value) {
|
|
916
|
-
return typeof value === "string" ? value.trim() : "";
|
|
917
|
-
}
|
|
918
|
-
function normalizeOptionalNumber(value) {
|
|
919
|
-
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
920
|
-
}
|
|
921
|
-
function uniqueItems(items) {
|
|
922
|
-
return Array.from(new Set(items));
|
|
923
|
-
}
|
|
15
|
+
import 'node:os';
|
|
16
|
+
import 'node:util';
|
|
17
|
+
import 'node:process';
|
|
18
|
+
import 'node:tty';
|
|
924
19
|
|
|
925
20
|
const ws = defineWebSocketHandler({
|
|
926
21
|
open(peer) {
|
|
927
22
|
console.log(`[ws] opened: ${peer.id}`);
|
|
928
|
-
peer.send("Welcome use WebSocket server\uFF01");
|
|
929
23
|
},
|
|
930
24
|
async message(peer, message) {
|
|
931
25
|
try {
|
|
932
26
|
const request = parseProjectRequest(message);
|
|
933
27
|
const runtimeConfig = useRuntimeConfig();
|
|
934
28
|
const config = resolveConfig(runtimeConfig.public);
|
|
29
|
+
const runtime = getUsageDataRuntime(config);
|
|
935
30
|
if (request.type === "project") {
|
|
936
|
-
sendData(peer, request, await
|
|
31
|
+
sendData(peer, request, await runtime.getProjectCatalog());
|
|
937
32
|
} else if (request.type === "project_data") {
|
|
938
|
-
sendData(peer, request, await
|
|
33
|
+
sendData(peer, request, await runtime.getProjectDataModules(request));
|
|
939
34
|
}
|
|
940
35
|
} catch (error) {
|
|
941
36
|
sendError(peer, error instanceof Error ? error.message : "Failed to handle websocket request.");
|
|
@@ -963,7 +58,7 @@ function sendError(peer, message) {
|
|
|
963
58
|
}));
|
|
964
59
|
}
|
|
965
60
|
function parseTextRequest(text) {
|
|
966
|
-
var _a, _b, _c, _d
|
|
61
|
+
var _a, _b, _c, _d;
|
|
967
62
|
if (text === "project" || text === "project_data") {
|
|
968
63
|
return { type: text };
|
|
969
64
|
}
|
|
@@ -973,18 +68,19 @@ function parseTextRequest(text) {
|
|
|
973
68
|
return null;
|
|
974
69
|
}
|
|
975
70
|
return {
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
71
|
+
module: (_a = params.get("module")) != null ? _a : void 0,
|
|
72
|
+
modules: params.getAll("modules").flatMap((item) => {
|
|
73
|
+
var _a2;
|
|
74
|
+
return (_a2 = normalizeStringList(item)) != null ? _a2 : [];
|
|
75
|
+
}),
|
|
76
|
+
platform: (_b = params.get("platform")) != null ? _b : void 0,
|
|
77
|
+
project: (_c = params.get("project")) != null ? _c : void 0,
|
|
78
|
+
requestId: (_d = params.get("requestId")) != null ? _d : void 0,
|
|
984
79
|
type
|
|
985
80
|
};
|
|
986
81
|
}
|
|
987
82
|
function normalizeProjectRequest(value) {
|
|
83
|
+
var _a;
|
|
988
84
|
if (typeof value === "string") {
|
|
989
85
|
return normalizeProjectRequest(parseTextRequest(value.trim()));
|
|
990
86
|
}
|
|
@@ -992,63 +88,25 @@ function normalizeProjectRequest(value) {
|
|
|
992
88
|
throw new Error("Websocket message must include a supported type.");
|
|
993
89
|
}
|
|
994
90
|
const record = value;
|
|
995
|
-
const type =
|
|
91
|
+
const type = (_a = normalizeStringValue(record.type)) != null ? _a : "";
|
|
996
92
|
if (type === "project") {
|
|
997
93
|
return {
|
|
998
|
-
requestId:
|
|
94
|
+
requestId: normalizeStringValue(record.requestId),
|
|
999
95
|
type
|
|
1000
96
|
};
|
|
1001
97
|
}
|
|
1002
98
|
if (type === "project_data") {
|
|
1003
99
|
return {
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
project: getString(record.project) || void 0,
|
|
1010
|
-
requestId: getString(record.requestId) || void 0,
|
|
1011
|
-
sessionId: getString(record.sessionId) || void 0,
|
|
100
|
+
module: normalizeStringValue(record.module),
|
|
101
|
+
modules: normalizeStringList(record.modules),
|
|
102
|
+
platform: normalizeStringValue(record.platform),
|
|
103
|
+
project: normalizeStringValue(record.project),
|
|
104
|
+
requestId: normalizeStringValue(record.requestId),
|
|
1012
105
|
type
|
|
1013
106
|
};
|
|
1014
107
|
}
|
|
1015
108
|
throw new Error(`Unsupported websocket request type: ${type || "unknown"}.`);
|
|
1016
109
|
}
|
|
1017
|
-
function normalizePathList(value) {
|
|
1018
|
-
if (Array.isArray(value)) {
|
|
1019
|
-
return value.flatMap((item) => typeof item === "string" ? splitPathList(item) : []);
|
|
1020
|
-
}
|
|
1021
|
-
if (typeof value === "string") {
|
|
1022
|
-
return splitPathList(value);
|
|
1023
|
-
}
|
|
1024
|
-
return void 0;
|
|
1025
|
-
}
|
|
1026
|
-
function normalizeProjectDataModule(value) {
|
|
1027
|
-
const module = getString(value);
|
|
1028
|
-
return module ? module : void 0;
|
|
1029
|
-
}
|
|
1030
|
-
function normalizeProjectDataModules(value) {
|
|
1031
|
-
if (Array.isArray(value)) {
|
|
1032
|
-
return value.flatMap((item) => typeof item === "string" ? splitValueList(item) : []);
|
|
1033
|
-
}
|
|
1034
|
-
if (typeof value === "string") {
|
|
1035
|
-
return splitValueList(value);
|
|
1036
|
-
}
|
|
1037
|
-
return void 0;
|
|
1038
|
-
}
|
|
1039
|
-
function normalizePlatform(value) {
|
|
1040
|
-
const platform = getString(value);
|
|
1041
|
-
return platform ? platform : void 0;
|
|
1042
|
-
}
|
|
1043
|
-
function splitPathList(value) {
|
|
1044
|
-
return splitValueList(value);
|
|
1045
|
-
}
|
|
1046
|
-
function splitValueList(value) {
|
|
1047
|
-
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
1048
|
-
}
|
|
1049
|
-
function getString(value) {
|
|
1050
|
-
return typeof value === "string" ? value.trim() : "";
|
|
1051
|
-
}
|
|
1052
110
|
function sendData(peer, request, data) {
|
|
1053
111
|
if (!request.requestId) {
|
|
1054
112
|
peer.send(JSON.stringify(data));
|