@vibescore/tracker 0.0.8 → 0.0.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/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/package.json +1 -1
- package/src/commands/init.js +4 -0
- package/src/lib/rollout.js +85 -26
- package/src/lib/uploader.js +3 -3
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ _Real-time AI Analytics for Codex CLI_
|
|
|
13
13
|
|
|
14
14
|
[**English**](README.md) • [**中文说明**](README.zh-CN.md)
|
|
15
15
|
|
|
16
|
-
[**Documentation**](docs/) • [**Dashboard**](dashboard/) • [**Backend API**](BACKEND_API.md)
|
|
16
|
+
[**Documentation**](docs/) • [**Dashboard**](dashboard/) • [**Backend API**](BACKEND_API.md)
|
|
17
17
|
|
|
18
18
|
<br/>
|
|
19
19
|
|
package/README.zh-CN.md
CHANGED
|
@@ -13,7 +13,7 @@ _Codex CLI 实时 AI 分析工具_
|
|
|
13
13
|
|
|
14
14
|
[**English**](README.md) • [**中文说明**](README.zh-CN.md)
|
|
15
15
|
|
|
16
|
-
[**文档**](docs/) • [**控制台**](dashboard/) • [**后端接口**](BACKEND_API.md)
|
|
16
|
+
[**文档**](docs/) • [**控制台**](dashboard/) • [**后端接口**](BACKEND_API.md)
|
|
17
17
|
|
|
18
18
|
<br/>
|
|
19
19
|
|
package/package.json
CHANGED
package/src/commands/init.js
CHANGED
|
@@ -387,6 +387,10 @@ function spawnInitSync({ trackerBinPath, packageName }) {
|
|
|
387
387
|
stdio: 'ignore',
|
|
388
388
|
env: process.env
|
|
389
389
|
});
|
|
390
|
+
child.on('error', (err) => {
|
|
391
|
+
const msg = err && err.message ? err.message : 'unknown error';
|
|
392
|
+
process.stderr.write(`Initial sync spawn failed: ${msg}\n`);
|
|
393
|
+
});
|
|
390
394
|
child.unref();
|
|
391
395
|
}
|
|
392
396
|
|
package/src/lib/rollout.js
CHANGED
|
@@ -299,32 +299,62 @@ async function parseClaudeFile({ filePath, startOffset, hourlyState, touchedBuck
|
|
|
299
299
|
async function enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets }) {
|
|
300
300
|
if (!touchedBuckets || touchedBuckets.size === 0) return 0;
|
|
301
301
|
|
|
302
|
-
const
|
|
302
|
+
const touchedGroups = new Set();
|
|
303
303
|
for (const bucketStart of touchedBuckets) {
|
|
304
|
-
const
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
304
|
+
const parsed = parseBucketKey(bucketStart);
|
|
305
|
+
const hourStart = parsed.hourStart;
|
|
306
|
+
if (!hourStart) continue;
|
|
307
|
+
touchedGroups.add(groupBucketKey(parsed.source, hourStart));
|
|
308
|
+
}
|
|
309
|
+
if (touchedGroups.size === 0) return 0;
|
|
310
|
+
|
|
311
|
+
const grouped = new Map();
|
|
312
|
+
for (const [key, bucket] of Object.entries(hourlyState.buckets || {})) {
|
|
310
313
|
if (!bucket || !bucket.totals) continue;
|
|
311
|
-
const
|
|
312
|
-
|
|
314
|
+
const parsed = parseBucketKey(key);
|
|
315
|
+
const hourStart = parsed.hourStart;
|
|
316
|
+
if (!hourStart) continue;
|
|
317
|
+
const groupKey = groupBucketKey(parsed.source, hourStart);
|
|
318
|
+
if (!touchedGroups.has(groupKey)) continue;
|
|
319
|
+
|
|
320
|
+
let group = grouped.get(groupKey);
|
|
321
|
+
if (!group) {
|
|
322
|
+
group = {
|
|
323
|
+
source: normalizeSourceInput(parsed.source) || DEFAULT_SOURCE,
|
|
324
|
+
hourStart,
|
|
325
|
+
models: new Set(),
|
|
326
|
+
totals: initTotals()
|
|
327
|
+
};
|
|
328
|
+
grouped.set(groupKey, group);
|
|
329
|
+
}
|
|
330
|
+
group.models.add(parsed.model || DEFAULT_MODEL);
|
|
331
|
+
addTotals(group.totals, bucket.totals);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const toAppend = [];
|
|
335
|
+
const groupQueued = hourlyState.groupQueued && typeof hourlyState.groupQueued === 'object' ? hourlyState.groupQueued : {};
|
|
336
|
+
for (const group of grouped.values()) {
|
|
337
|
+
const model = group.models.size === 1 ? [...group.models][0] : DEFAULT_MODEL;
|
|
338
|
+
const key = totalsKey(group.totals);
|
|
339
|
+
const groupKey = groupBucketKey(group.source, group.hourStart);
|
|
340
|
+
if (groupQueued[groupKey] === key) continue;
|
|
313
341
|
toAppend.push(
|
|
314
342
|
JSON.stringify({
|
|
315
|
-
source,
|
|
343
|
+
source: group.source,
|
|
316
344
|
model,
|
|
317
|
-
hour_start: hourStart,
|
|
318
|
-
input_tokens:
|
|
319
|
-
cached_input_tokens:
|
|
320
|
-
output_tokens:
|
|
321
|
-
reasoning_output_tokens:
|
|
322
|
-
total_tokens:
|
|
345
|
+
hour_start: group.hourStart,
|
|
346
|
+
input_tokens: group.totals.input_tokens,
|
|
347
|
+
cached_input_tokens: group.totals.cached_input_tokens,
|
|
348
|
+
output_tokens: group.totals.output_tokens,
|
|
349
|
+
reasoning_output_tokens: group.totals.reasoning_output_tokens,
|
|
350
|
+
total_tokens: group.totals.total_tokens
|
|
323
351
|
})
|
|
324
352
|
);
|
|
325
|
-
|
|
353
|
+
groupQueued[groupKey] = key;
|
|
326
354
|
}
|
|
327
355
|
|
|
356
|
+
hourlyState.groupQueued = groupQueued;
|
|
357
|
+
|
|
328
358
|
if (toAppend.length > 0) {
|
|
329
359
|
await fs.appendFile(queuePath, toAppend.join('\n') + '\n', 'utf8');
|
|
330
360
|
}
|
|
@@ -335,15 +365,30 @@ async function enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets })
|
|
|
335
365
|
function normalizeHourlyState(raw) {
|
|
336
366
|
const state = raw && typeof raw === 'object' ? raw : {};
|
|
337
367
|
const version = Number(state.version || 1);
|
|
368
|
+
const rawBuckets = state.buckets && typeof state.buckets === 'object' ? state.buckets : {};
|
|
369
|
+
const buckets = {};
|
|
370
|
+
const groupQueued = {};
|
|
371
|
+
|
|
338
372
|
if (!Number.isFinite(version) || version < 2) {
|
|
373
|
+
for (const [key, value] of Object.entries(rawBuckets)) {
|
|
374
|
+
const parsed = parseBucketKey(key);
|
|
375
|
+
const hourStart = parsed.hourStart;
|
|
376
|
+
if (!hourStart) continue;
|
|
377
|
+
const source = normalizeSourceInput(parsed.source) || DEFAULT_SOURCE;
|
|
378
|
+
const normalizedKey = bucketKey(source, DEFAULT_MODEL, hourStart);
|
|
379
|
+
buckets[normalizedKey] = value;
|
|
380
|
+
if (value?.queuedKey) {
|
|
381
|
+
groupQueued[groupBucketKey(source, hourStart)] = value.queuedKey;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
339
384
|
return {
|
|
340
|
-
version:
|
|
341
|
-
buckets
|
|
342
|
-
|
|
385
|
+
version: 3,
|
|
386
|
+
buckets,
|
|
387
|
+
groupQueued,
|
|
388
|
+
updatedAt: typeof state.updatedAt === 'string' ? state.updatedAt : null
|
|
343
389
|
};
|
|
344
390
|
}
|
|
345
|
-
|
|
346
|
-
const buckets = {};
|
|
391
|
+
|
|
347
392
|
for (const [key, value] of Object.entries(rawBuckets)) {
|
|
348
393
|
const parsed = parseBucketKey(key);
|
|
349
394
|
const hourStart = parsed.hourStart;
|
|
@@ -351,9 +396,14 @@ function normalizeHourlyState(raw) {
|
|
|
351
396
|
const normalizedKey = bucketKey(parsed.source, parsed.model, hourStart);
|
|
352
397
|
buckets[normalizedKey] = value;
|
|
353
398
|
}
|
|
399
|
+
|
|
400
|
+
const existingGroupQueued =
|
|
401
|
+
state.groupQueued && typeof state.groupQueued === 'object' ? state.groupQueued : {};
|
|
402
|
+
|
|
354
403
|
return {
|
|
355
|
-
version:
|
|
404
|
+
version: 3,
|
|
356
405
|
buckets,
|
|
406
|
+
groupQueued: version >= 3 ? existingGroupQueued : {},
|
|
357
407
|
updatedAt: typeof state.updatedAt === 'string' ? state.updatedAt : null
|
|
358
408
|
};
|
|
359
409
|
}
|
|
@@ -434,6 +484,11 @@ function bucketKey(source, model, hourStart) {
|
|
|
434
484
|
return `${safeSource}${BUCKET_SEPARATOR}${safeModel}${BUCKET_SEPARATOR}${hourStart}`;
|
|
435
485
|
}
|
|
436
486
|
|
|
487
|
+
function groupBucketKey(source, hourStart) {
|
|
488
|
+
const safeSource = normalizeSourceInput(source) || DEFAULT_SOURCE;
|
|
489
|
+
return `${safeSource}${BUCKET_SEPARATOR}${hourStart}`;
|
|
490
|
+
}
|
|
491
|
+
|
|
437
492
|
function parseBucketKey(key) {
|
|
438
493
|
if (typeof key !== 'string') return { source: DEFAULT_SOURCE, model: DEFAULT_MODEL, hourStart: '' };
|
|
439
494
|
const first = key.indexOf(BUCKET_SEPARATOR);
|
|
@@ -523,12 +578,16 @@ function normalizeUsage(u) {
|
|
|
523
578
|
}
|
|
524
579
|
|
|
525
580
|
function normalizeClaudeUsage(u) {
|
|
581
|
+
const inputTokens = toNonNegativeInt(u?.input_tokens);
|
|
582
|
+
const outputTokens = toNonNegativeInt(u?.output_tokens);
|
|
583
|
+
const hasTotal = u && Object.prototype.hasOwnProperty.call(u, 'total_tokens');
|
|
584
|
+
const totalTokens = hasTotal ? toNonNegativeInt(u?.total_tokens) : inputTokens + outputTokens;
|
|
526
585
|
return {
|
|
527
|
-
input_tokens:
|
|
586
|
+
input_tokens: inputTokens,
|
|
528
587
|
cached_input_tokens: toNonNegativeInt(u?.cache_read_input_tokens),
|
|
529
|
-
output_tokens:
|
|
588
|
+
output_tokens: outputTokens,
|
|
530
589
|
reasoning_output_tokens: 0,
|
|
531
|
-
total_tokens:
|
|
590
|
+
total_tokens: totalTokens
|
|
532
591
|
};
|
|
533
592
|
}
|
|
534
593
|
|
package/src/lib/uploader.js
CHANGED
|
@@ -78,7 +78,7 @@ async function readBatch(queuePath, startOffset, maxBuckets) {
|
|
|
78
78
|
const model = normalizeModel(bucket?.model) || DEFAULT_MODEL;
|
|
79
79
|
bucket.source = source;
|
|
80
80
|
bucket.model = model;
|
|
81
|
-
bucketMap.set(bucketKey(source,
|
|
81
|
+
bucketMap.set(bucketKey(source, hourStart), bucket);
|
|
82
82
|
linesRead += 1;
|
|
83
83
|
if (linesRead >= maxBuckets) break;
|
|
84
84
|
}
|
|
@@ -97,8 +97,8 @@ async function safeFileSize(p) {
|
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
function bucketKey(source,
|
|
101
|
-
return `${source}${BUCKET_SEPARATOR}${
|
|
100
|
+
function bucketKey(source, hourStart) {
|
|
101
|
+
return `${source}${BUCKET_SEPARATOR}${hourStart}`;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
function normalizeSource(value) {
|