tokenrace 0.2.0 → 0.2.1

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/store.js +36 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tokenrace",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Monitor en tiempo real para Claude Code",
5
5
  "bin": {
6
6
  "tokenrace": "bin/cli.js"
package/src/store.js CHANGED
@@ -221,6 +221,30 @@ function rebuildProjectAggregates() {
221
221
  }
222
222
  }
223
223
 
224
+ /**
225
+ * Reconstruye state.models desde las timeseries (labels.model).
226
+ * Se usa al cargar datos guardados por versiones que no persistían modelos.
227
+ * `requests` no es reconstruible desde timeseries y queda en 0.
228
+ */
229
+ function rebuildModelAggregates() {
230
+ const metricField = {
231
+ 'claude_code.tokens.input': 'tokensInput',
232
+ 'claude_code.tokens.output': 'tokensOutput',
233
+ 'claude_code.cost': 'cost'
234
+ }
235
+
236
+ for (const [metricName, field] of Object.entries(metricField)) {
237
+ for (const point of state.timeseries.get(metricName) ?? []) {
238
+ const model = point.labels?.model
239
+ if (!model) continue
240
+ if (!state.models.has(model)) {
241
+ state.models.set(model, { tokensInput: 0, tokensOutput: 0, cost: 0, requests: 0 })
242
+ }
243
+ state.models.get(model)[field] += point.value
244
+ }
245
+ }
246
+ }
247
+
224
248
  /** Debounce: guarda a disco en el siguiente tick */
225
249
  function scheduleSave() {
226
250
  setTimeout(() => saveSync(), 0)
@@ -946,7 +970,8 @@ export function saveSync() {
946
970
  eventIndex: state.eventIndex,
947
971
  totalEvents: state.totalEvents,
948
972
  startTime: state.startTime,
949
- tools: Array.from(state.tools.entries())
973
+ tools: Array.from(state.tools.entries()),
974
+ models: Array.from(state.models.entries())
950
975
  }
951
976
 
952
977
  fs.writeFileSync(dataFile, JSON.stringify(data), { mode: 0o600 })
@@ -1024,6 +1049,16 @@ export function loadFromDisk() {
1024
1049
  }
1025
1050
  }
1026
1051
 
1052
+ // Restaurar stats de modelos; si el archivo es de una versión que no los
1053
+ // persistía, reconstruirlos desde las timeseries
1054
+ if (Array.isArray(data.models) && data.models.length > 0) {
1055
+ for (const [model, stats] of data.models) {
1056
+ state.models.set(model, stats)
1057
+ }
1058
+ } else {
1059
+ rebuildModelAggregates()
1060
+ }
1061
+
1027
1062
  // Re-aplicar mappings a sesiones (timeseries no necesitan propagación:
1028
1063
  // resolveProject() consulta sessionMappings primero en todos los lectores)
1029
1064
  for (const [sessionId, project] of state.sessionMappings.entries()) {