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.
Files changed (32) hide show
  1. package/dist/index.mjs +10 -4
  2. package/dist/public/_nuxt/{chy2QJx0.js → 7Dy4NLP8.js} +32 -32
  3. package/dist/public/_nuxt/B-VlGWDb.js +21 -0
  4. package/dist/public/_nuxt/{BeEwECnn.js → Be3rizqy.js} +1 -1
  5. package/dist/public/_nuxt/{15CW3D68.js → C0azgqnZ.js} +1 -1
  6. package/dist/public/_nuxt/{B6G-s9D-.js → CMNdiQCa.js} +1 -1
  7. package/dist/public/_nuxt/CPuXQJE_.js +1 -0
  8. package/dist/public/_nuxt/DenksPSi.js +119 -0
  9. package/dist/public/_nuxt/Dkya5WaL.js +9 -0
  10. package/dist/public/_nuxt/HN9OZyaQ.js +25 -0
  11. package/dist/public/_nuxt/{Bu4SpN_a.js → JtK-nXxy.js} +1 -1
  12. package/dist/public/_nuxt/builds/latest.json +1 -1
  13. package/dist/public/_nuxt/builds/meta/37e8bb21-a086-45bf-93dc-47eeeada7299.json +1 -0
  14. package/dist/public/_nuxt/{BeygfM9p.js → y3weNNd-.js} +2 -2
  15. package/dist/server/chunks/_/error-500.mjs +8 -4
  16. package/dist/server/chunks/_/shared.cjs.prod.mjs +1 -1
  17. package/dist/server/chunks/build/client.precomputed.mjs +1 -1
  18. package/dist/server/chunks/nitro/nitro.mjs +8218 -2844
  19. package/dist/server/chunks/routes/api/payload.json.mjs +11 -626
  20. package/dist/server/chunks/routes/api/projects/_project/modules.get.mjs +36 -0
  21. package/dist/server/chunks/routes/api/projects/catalog.get.mjs +26 -0
  22. package/dist/server/chunks/routes/renderer.mjs +9 -5
  23. package/dist/server/chunks/routes/ws.mjs +29 -971
  24. package/dist/server/index.mjs +9 -5
  25. package/package.json +7 -6
  26. package/dist/public/_nuxt/C6ydMk2z.js +0 -25
  27. package/dist/public/_nuxt/Dn8cXZx3.js +0 -9
  28. package/dist/public/_nuxt/DysUC14A.js +0 -119
  29. package/dist/public/_nuxt/KLhV325n.js +0 -1
  30. package/dist/public/_nuxt/builds/meta/ac4b25d6-d6eb-44bb-8c5b-b1d6f651c196.json +0 -1
  31. package/dist/public/_nuxt/pmnAmEjb.js +0 -21
  32. package/dist/server/chunks/_/index.min.mjs +0 -348
@@ -1,941 +1,36 @@
1
- import { c as getClaudeLookupCandidates, q as isOpenRouterFreeModel, B as getGeminiLookupCandidates, e as extractClaudeProjectFromPath, p as parseJsonlFile, g as getProjectName, d as decodeClaudeProjectPath, t as toIsoString, i as normalizeRepositoryUrl, k as extractModelName, l as normalizeRawUsage, v as parseJsonFile, w as getGeminiProjectRoot, x as getGeminiProjectKeyFromPath, y as getRepositoryNameFromProjectRoot, z as extractGeminiMessageText, n as normalizeNumber, s as subtractRawUsage, m as convertCodexRawUsage, o as isZeroUsage, r as roundCurrency, a as getDurationMinutes, A as convertGeminiTokenUsage, b as buildLoadUsageResult, F as getDateKey, G as getWeekLabel, H as getMonthKey, I as formatDuration, J as formatDateLabelFromDateKey, K as defineWebSocketHandler, E as useRuntimeConfig, D as resolveConfig } from '../nitro/nitro.mjs';
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
- const EMPTY_USAGE_ROOT = "/__usage-board-empty__";
21
- const PROJECT_USAGE_PLATFORMS = ["claudeCode", "codex", "gemini"];
22
- const DEFAULT_PROJECT_USAGE_DATA_MODULE = "meta";
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 loadProjectUsageCatalog(config));
31
+ sendData(peer, request, await runtime.getProjectCatalog());
937
32
  } else if (request.type === "project_data") {
938
- sendData(peer, request, await loadProjectUsageDataModule(config, request));
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, _e, _f;
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
- label: (_a = params.get("label")) != null ? _a : void 0,
977
- module: (_b = params.get("module")) != null ? _b : void 0,
978
- modules: params.getAll("modules").flatMap(splitValueList),
979
- path: params.getAll("path").flatMap(splitPathList),
980
- platform: (_c = params.get("platform")) != null ? _c : void 0,
981
- project: (_d = params.get("project")) != null ? _d : void 0,
982
- requestId: (_e = params.get("requestId")) != null ? _e : void 0,
983
- sessionId: (_f = params.get("sessionId")) != null ? _f : void 0,
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 = getString(record.type);
91
+ const type = (_a = normalizeStringValue(record.type)) != null ? _a : "";
996
92
  if (type === "project") {
997
93
  return {
998
- requestId: getString(record.requestId) || void 0,
94
+ requestId: normalizeStringValue(record.requestId),
999
95
  type
1000
96
  };
1001
97
  }
1002
98
  if (type === "project_data") {
1003
99
  return {
1004
- label: getString(record.label) || void 0,
1005
- module: normalizeProjectDataModule(record.module),
1006
- modules: normalizeProjectDataModules(record.modules),
1007
- path: normalizePathList(record.path),
1008
- platform: normalizePlatform(record.platform),
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));