tokenrace 0.1.7 → 0.1.9
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.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>tokenrace</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-rCnJ9m1Y.js"></script>
|
|
8
8
|
<link rel="stylesheet" crossorigin href="/assets/index-B0dy8dWP.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
package/package.json
CHANGED
package/src/store.js
CHANGED
|
@@ -28,23 +28,90 @@ export function setDataPathForTesting(newPath) {
|
|
|
28
28
|
// ─── Estado en memoria ───────────────────────────────────────────────────────
|
|
29
29
|
|
|
30
30
|
const state = {
|
|
31
|
-
timeseries:
|
|
32
|
-
sessions:
|
|
33
|
-
sessionMappings:
|
|
34
|
-
ignoredSessions:
|
|
35
|
-
events:
|
|
36
|
-
eventIndex:
|
|
37
|
-
projects:
|
|
38
|
-
tools:
|
|
39
|
-
agents:
|
|
40
|
-
models:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
31
|
+
timeseries: new Map(), // metricName → [{ ts, value, labels }]
|
|
32
|
+
sessions: new Map(), // sessionId → SessionData
|
|
33
|
+
sessionMappings: new Map(), // sessionId → projectName
|
|
34
|
+
ignoredSessions: new Set(), // sessionIds ignorados
|
|
35
|
+
events: [], // buffer circular, max 1000
|
|
36
|
+
eventIndex: 0,
|
|
37
|
+
projects: new Map(), // projectName → ProjectAggregates
|
|
38
|
+
tools: new Map(), // toolName → ToolStats
|
|
39
|
+
agents: new Map(), // agentId → AgentData
|
|
40
|
+
models: new Map(), // modelName → ModelStats
|
|
41
|
+
cumulativeValues: new Map(), // clave → último valor acumulativo (no persiste)
|
|
42
|
+
lastSeen: null,
|
|
43
|
+
startTime: Date.now(),
|
|
44
|
+
totalEvents: 0
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
// ─── Helpers internos ────────────────────────────────────────────────────────
|
|
47
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Convierte un valor acumulativo en un delta desde la última lectura.
|
|
51
|
+
* Se usa para métricas que Claude Code exporta con temporalidad cumulativa.
|
|
52
|
+
*/
|
|
53
|
+
function cumulativeDelta(key, newValue) {
|
|
54
|
+
const last = state.cumulativeValues.get(key) ?? 0
|
|
55
|
+
const delta = Math.max(0, newValue - last)
|
|
56
|
+
state.cumulativeValues.set(key, newValue)
|
|
57
|
+
return delta
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Normaliza los nombres de métricas nuevos de Claude Code a los nombres
|
|
62
|
+
* canónicos que usa el store, convirtiendo valores cumulativos a deltas.
|
|
63
|
+
* Devuelve { name, value } normalizado, o null para ignorar la métrica.
|
|
64
|
+
*/
|
|
65
|
+
function normalizeIncoming(name, value, labels) {
|
|
66
|
+
const sid = labels['session.id'] ?? ''
|
|
67
|
+
|
|
68
|
+
switch (name) {
|
|
69
|
+
case 'claude_code.token.usage': {
|
|
70
|
+
const type = labels.type
|
|
71
|
+
const key = `token:${sid}:${type}:${labels.model ?? ''}:${labels.query_source ?? ''}`
|
|
72
|
+
const delta = cumulativeDelta(key, value)
|
|
73
|
+
const nameMap = {
|
|
74
|
+
'input': 'claude_code.tokens.input',
|
|
75
|
+
'output': 'claude_code.tokens.output',
|
|
76
|
+
'cacheRead': 'claude_code.tokens.cache.read',
|
|
77
|
+
'cacheCreation': 'claude_code.tokens.cache.creation',
|
|
78
|
+
}
|
|
79
|
+
const canonical = nameMap[type]
|
|
80
|
+
return canonical ? { name: canonical, value: delta } : null
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
case 'claude_code.cost.usage': {
|
|
84
|
+
const key = `cost:${sid}:${labels.model ?? ''}:${labels.query_source ?? ''}:${labels.effort ?? ''}`
|
|
85
|
+
const delta = cumulativeDelta(key, value)
|
|
86
|
+
return { name: 'claude_code.cost', value: delta }
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
case 'claude_code.lines_of_code.count': {
|
|
90
|
+
const type = labels.type
|
|
91
|
+
const key = `lines:${sid}:${type}`
|
|
92
|
+
const delta = cumulativeDelta(key, value)
|
|
93
|
+
if (type === 'added') return { name: 'claude_code.lines_added', value: delta }
|
|
94
|
+
if (type === 'removed') return { name: 'claude_code.lines_removed', value: delta }
|
|
95
|
+
return null
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
case 'claude_code.commit.count': {
|
|
99
|
+
const key = `commit:${sid}`
|
|
100
|
+
const delta = cumulativeDelta(key, value)
|
|
101
|
+
return { name: 'claude_code.commits', value: delta }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Métricas que no necesitamos agregar (activo ya se calcula desde eventos)
|
|
105
|
+
case 'claude_code.session.count':
|
|
106
|
+
case 'claude_code.active_time.total':
|
|
107
|
+
case 'claude_code.code_edit_tool.decision':
|
|
108
|
+
return null
|
|
109
|
+
|
|
110
|
+
default:
|
|
111
|
+
return { name, value }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
48
115
|
/**
|
|
49
116
|
* Resuelve el proyecto de una sesión.
|
|
50
117
|
* Prioridad: mapping manual > resource project > null
|
|
@@ -137,7 +204,12 @@ function scheduleSave() {
|
|
|
137
204
|
* Procesa un punto de métrica normalizado.
|
|
138
205
|
* @param {{ name, value, timestamp, labels }} metric
|
|
139
206
|
*/
|
|
140
|
-
export function processMetric(
|
|
207
|
+
export function processMetric(raw) {
|
|
208
|
+
const normalized = normalizeIncoming(raw.name, raw.value, raw.labels)
|
|
209
|
+
if (!normalized) return
|
|
210
|
+
|
|
211
|
+
const { name, value } = normalized
|
|
212
|
+
const { timestamp, labels } = raw
|
|
141
213
|
const sessionId = labels['session.id']
|
|
142
214
|
|
|
143
215
|
// Saltar sesiones ignoradas
|
|
@@ -263,7 +335,44 @@ export function processEvent({ eventName, timestamp, severity, attributes }) {
|
|
|
263
335
|
}
|
|
264
336
|
state.eventIndex++
|
|
265
337
|
|
|
266
|
-
// ──
|
|
338
|
+
// ── Asegurar que la sesión existe (puede llegar un evento antes que una métrica) ──
|
|
339
|
+
if (sessionId && !state.sessions.has(sessionId)) {
|
|
340
|
+
state.sessions.set(sessionId, {
|
|
341
|
+
sessionId,
|
|
342
|
+
project,
|
|
343
|
+
feature: attributes.feature ?? null,
|
|
344
|
+
model,
|
|
345
|
+
startTime: timestamp,
|
|
346
|
+
lastSeen: timestamp,
|
|
347
|
+
durationActiveMs: 0,
|
|
348
|
+
tokensInput: 0,
|
|
349
|
+
tokensOutput: 0,
|
|
350
|
+
tokensCache: 0,
|
|
351
|
+
cost: 0,
|
|
352
|
+
apiRequests: 0,
|
|
353
|
+
toolCalls: 0
|
|
354
|
+
})
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (sessionId && state.sessions.has(sessionId)) {
|
|
358
|
+
const session = state.sessions.get(sessionId)
|
|
359
|
+
session.lastSeen = Math.max(session.lastSeen, timestamp)
|
|
360
|
+
if (model && !session.model) session.model = model
|
|
361
|
+
|
|
362
|
+
// ── Tiempo activo desde eventos api_request (duration_ms) ──
|
|
363
|
+
if (eventName === 'api_request') {
|
|
364
|
+
const dur = Number(attributes['duration_ms'] ?? 0)
|
|
365
|
+
if (dur > 0) session.durationActiveMs += dur
|
|
366
|
+
session.apiRequests++
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// ── Tool stats ──
|
|
370
|
+
if (eventName === 'tool_use') {
|
|
371
|
+
session.toolCalls++
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// ── Tool stats globales ──
|
|
267
376
|
if (eventName === 'tool_use') {
|
|
268
377
|
const toolName = attributes['tool.name'] ?? attributes.tool ?? 'unknown'
|
|
269
378
|
|
|
@@ -279,11 +388,6 @@ export function processEvent({ eventName, timestamp, severity, attributes }) {
|
|
|
279
388
|
if (attributes['tool.duration_ms'] !== undefined) {
|
|
280
389
|
tool.totalDurationMs += Number(attributes['tool.duration_ms'])
|
|
281
390
|
}
|
|
282
|
-
|
|
283
|
-
// Incrementar toolCalls en la sesión
|
|
284
|
-
if (sessionId && state.sessions.has(sessionId)) {
|
|
285
|
-
state.sessions.get(sessionId).toolCalls++
|
|
286
|
-
}
|
|
287
391
|
}
|
|
288
392
|
|
|
289
393
|
state.lastSeen = timestamp
|
|
@@ -368,6 +472,7 @@ export function reset() {
|
|
|
368
472
|
state.tools.clear()
|
|
369
473
|
state.agents.clear()
|
|
370
474
|
state.models.clear()
|
|
475
|
+
state.cumulativeValues.clear()
|
|
371
476
|
state.lastSeen = null
|
|
372
477
|
state.startTime = Date.now()
|
|
373
478
|
state.totalEvents = 0
|