@ipv9/tokentracker-cli 0.39.47 → 0.39.49
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 +11 -9
- package/dashboard/dist/assets/{Card-BMWFZ5k5.js → Card-CQ0G1GlR.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-HjWNRKq7.js → DashboardPage-B5aBShIc.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-BgLVBi0I.js → FadeIn-BG37Pb1J.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-CsS9rEbM.js → IpCheckPage-DD2nObWJ.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-BifpRDJ6.js → LimitsPage-L55ZcbUp.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-D7tdrheS.js → LocalOnlyNotice-3-SG-oKl.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-DjMB9uLV.js → PopoverPopup--Imnw4O7.js} +1 -1
- package/dashboard/dist/assets/{Select-DeCgT4YH.js → Select-BNQ73B60.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-DwZRVpPE.js → SelectItemText-Dlv4NEpN.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-B1l6jasL.js → SettingsPage-BDo6aFgs.js} +1 -1
- package/dashboard/dist/assets/{SkillsPage-DP5bdeUE.js → SkillsPage-CA2IaWI5.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-BVOQlozx.js → WidgetsPage-DTVOLZEy.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-ixZYG73C.js → WrappedPage-CYwBaHuQ.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-D74FAyJC.js → arrow-up-right-3x5AJIbb.js} +1 -1
- package/dashboard/dist/assets/{download-sKFNy5AQ.js → download-DSegD216.js} +1 -1
- package/dashboard/dist/assets/{format-Bl1W1y2T.js → format-yTFTff0D.js} +1 -1
- package/dashboard/dist/assets/{limitDisplay-C2rw2OOb.js → limitDisplay-DxvqWNgp.js} +1 -1
- package/dashboard/dist/assets/{main-NjvOmlYA.js → main-DBDTVhz8.js} +3 -3
- package/dashboard/dist/assets/{mock-data-DJ2XGAUP.js → mock-data-oSiCcgVh.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-C7BBDoNV.js → use-limits-display-prefs-C2F0IlsU.js} +1 -1
- package/dashboard/dist/assets/{use-native-settings-BYIme-b6.js → use-native-settings-DtQFof4g.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-CJzAqnSI.js → useCurrency-5LxVaA8B.js} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/package.json +1 -1
- package/src/cli.js +1 -1
- package/src/commands/init.js +5 -7
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +90 -17
package/src/lib/rollout.js
CHANGED
|
@@ -3294,12 +3294,6 @@ function readHermesSessions(dbPath, lastCompletedEpoch, unfinishedSessionIds = [
|
|
|
3294
3294
|
const forceIncludeSql = forceIds.length > 0
|
|
3295
3295
|
? ` OR id IN (${forceIds.map(sqliteStringLiteral).join(",")})`
|
|
3296
3296
|
: "";
|
|
3297
|
-
// Fetch sessions that started at/after the cursor, sessions that are still
|
|
3298
|
-
// in-progress (ended_at IS NULL), OR sessions that were previously observed
|
|
3299
|
-
// unfinished. Hermes updates token counts in real-time, including a final
|
|
3300
|
-
// delta when an active session later gets ended_at set.
|
|
3301
|
-
const sql = `SELECT id, model, started_at, ended_at, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, reasoning_tokens, message_count FROM sessions WHERE (started_at >= ${since} OR ended_at IS NULL${forceIncludeSql}) AND (input_tokens > 0 OR output_tokens > 0 OR cache_read_tokens > 0 OR reasoning_tokens > 0) ORDER BY started_at ASC`;
|
|
3302
|
-
|
|
3303
3297
|
let snapshot = null;
|
|
3304
3298
|
let effectiveDbPath = dbPath;
|
|
3305
3299
|
if (isUncPath(dbPath)) {
|
|
@@ -3311,6 +3305,65 @@ function readHermesSessions(dbPath, lastCompletedEpoch, unfinishedSessionIds = [
|
|
|
3311
3305
|
// the non-locked case (e.g. permissions, transient I/O).
|
|
3312
3306
|
}
|
|
3313
3307
|
}
|
|
3308
|
+
const eligible = `(started_at >= ${since} OR ended_at IS NULL${forceIncludeSql})`;
|
|
3309
|
+
const modelUsageColumns = new Set(
|
|
3310
|
+
readSqliteJsonRows(
|
|
3311
|
+
effectiveDbPath,
|
|
3312
|
+
"PRAGMA table_info(session_model_usage)",
|
|
3313
|
+
{ label: "Hermes", maxBuffer: 1024 * 1024, timeout: 5_000, ...sqliteOptions },
|
|
3314
|
+
).map((column) => column?.name).filter(Boolean),
|
|
3315
|
+
);
|
|
3316
|
+
const hasModelUsage = [
|
|
3317
|
+
"session_id",
|
|
3318
|
+
"model",
|
|
3319
|
+
"api_call_count",
|
|
3320
|
+
"input_tokens",
|
|
3321
|
+
"output_tokens",
|
|
3322
|
+
"cache_read_tokens",
|
|
3323
|
+
"cache_write_tokens",
|
|
3324
|
+
"reasoning_tokens",
|
|
3325
|
+
"first_seen",
|
|
3326
|
+
"last_seen",
|
|
3327
|
+
].every((column) => modelUsageColumns.has(column));
|
|
3328
|
+
const sql = hasModelUsage
|
|
3329
|
+
? `WITH eligible_sessions AS (
|
|
3330
|
+
SELECT * FROM sessions WHERE ${eligible}
|
|
3331
|
+
)
|
|
3332
|
+
SELECT s.id, TRIM(u.model) AS model, s.model AS session_model, s.started_at, s.ended_at,
|
|
3333
|
+
SUM(u.input_tokens) AS input_tokens,
|
|
3334
|
+
SUM(u.output_tokens) AS output_tokens,
|
|
3335
|
+
SUM(u.cache_read_tokens) AS cache_read_tokens,
|
|
3336
|
+
SUM(u.cache_write_tokens) AS cache_write_tokens,
|
|
3337
|
+
SUM(u.reasoning_tokens) AS reasoning_tokens,
|
|
3338
|
+
SUM(u.api_call_count) AS message_count,
|
|
3339
|
+
MIN(u.first_seen) AS first_seen,
|
|
3340
|
+
MAX(u.last_seen) AS last_seen,
|
|
3341
|
+
1 AS per_model
|
|
3342
|
+
FROM eligible_sessions s
|
|
3343
|
+
JOIN session_model_usage u ON u.session_id = s.id
|
|
3344
|
+
GROUP BY s.id, TRIM(u.model)
|
|
3345
|
+
HAVING SUM(u.input_tokens) > 0 OR SUM(u.output_tokens) > 0 OR SUM(u.cache_read_tokens) > 0 OR SUM(u.cache_write_tokens) > 0 OR SUM(u.reasoning_tokens) > 0
|
|
3346
|
+
UNION ALL
|
|
3347
|
+
SELECT s.id, s.model, s.model AS session_model, s.started_at, s.ended_at,
|
|
3348
|
+
s.input_tokens, s.output_tokens, s.cache_read_tokens,
|
|
3349
|
+
s.cache_write_tokens, s.reasoning_tokens, s.message_count,
|
|
3350
|
+
NULL AS first_seen, NULL AS last_seen, 0 AS per_model
|
|
3351
|
+
FROM eligible_sessions s
|
|
3352
|
+
WHERE NOT EXISTS (
|
|
3353
|
+
SELECT 1 FROM session_model_usage u
|
|
3354
|
+
WHERE u.session_id = s.id
|
|
3355
|
+
AND (u.input_tokens > 0 OR u.output_tokens > 0 OR u.cache_read_tokens > 0 OR u.cache_write_tokens > 0 OR u.reasoning_tokens > 0)
|
|
3356
|
+
)
|
|
3357
|
+
AND (s.input_tokens > 0 OR s.output_tokens > 0 OR s.cache_read_tokens > 0 OR s.cache_write_tokens > 0 OR s.reasoning_tokens > 0)
|
|
3358
|
+
ORDER BY 4 ASC`
|
|
3359
|
+
: `SELECT id, model, model AS session_model, started_at, ended_at,
|
|
3360
|
+
input_tokens, output_tokens, cache_read_tokens, cache_write_tokens,
|
|
3361
|
+
reasoning_tokens, message_count, NULL AS first_seen,
|
|
3362
|
+
NULL AS last_seen, 0 AS per_model
|
|
3363
|
+
FROM sessions
|
|
3364
|
+
WHERE ${eligible}
|
|
3365
|
+
AND (input_tokens > 0 OR output_tokens > 0 OR cache_read_tokens > 0 OR cache_write_tokens > 0 OR reasoning_tokens > 0)
|
|
3366
|
+
ORDER BY started_at ASC`;
|
|
3314
3367
|
|
|
3315
3368
|
try {
|
|
3316
3369
|
return readSqliteJsonRows(effectiveDbPath, sql, {
|
|
@@ -3368,6 +3421,11 @@ async function parseHermesIncremental({ hermesPath, dbPath, cursors, queuePath,
|
|
|
3368
3421
|
// Per-session snapshot from the previous sync: { [sessionId]: { in, out, cacheRead, cacheWrite, reasoning } }
|
|
3369
3422
|
const prevSnapshots = (dbState.snapshots && typeof dbState.snapshots === "object")
|
|
3370
3423
|
? dbState.snapshots : {};
|
|
3424
|
+
const hasPerModelRows = rows.some((row) => Number(row.per_model) === 1);
|
|
3425
|
+
const adoptingPerModelState =
|
|
3426
|
+
hasPerModelRows &&
|
|
3427
|
+
dbState.modelUsageVersion !== 1 &&
|
|
3428
|
+
Object.keys(prevSnapshots).length > 0;
|
|
3371
3429
|
|
|
3372
3430
|
// Only advance past sessions that have fully ended. Active sessions
|
|
3373
3431
|
// (ended_at IS NULL) must be re-read every sync because Hermes updates
|
|
@@ -3388,10 +3446,22 @@ async function parseHermesIncremental({ hermesPath, dbPath, cursors, queuePath,
|
|
|
3388
3446
|
const cacheWrite = toNonNegativeInt(row.cache_write_tokens);
|
|
3389
3447
|
const reasoning = toNonNegativeInt(row.reasoning_tokens);
|
|
3390
3448
|
const messageCount = toNonNegativeInt(row.message_count);
|
|
3391
|
-
if (inputTokens === 0 && outputTokens === 0 && cacheRead === 0 && reasoning === 0) continue;
|
|
3449
|
+
if (inputTokens === 0 && outputTokens === 0 && cacheRead === 0 && cacheWrite === 0 && reasoning === 0) continue;
|
|
3392
3450
|
|
|
3393
|
-
|
|
3394
|
-
|
|
3451
|
+
const perModel = Number(row.per_model) === 1;
|
|
3452
|
+
const model = normalizeModelInput(row.model) || "hermes-agent";
|
|
3453
|
+
const snapshotKey = perModel ? JSON.stringify([String(row.id || ""), model]) : row.id;
|
|
3454
|
+
const currentSnapshot = {
|
|
3455
|
+
in: inputTokens,
|
|
3456
|
+
out: outputTokens,
|
|
3457
|
+
cacheRead,
|
|
3458
|
+
cacheWrite,
|
|
3459
|
+
reasoning,
|
|
3460
|
+
message_count: messageCount,
|
|
3461
|
+
};
|
|
3462
|
+
// Save current snapshot for next sync. Mixed-model sessions are keyed by
|
|
3463
|
+
// both session and model so later growth is attributed independently.
|
|
3464
|
+
nextSnapshots[snapshotKey] = currentSnapshot;
|
|
3395
3465
|
|
|
3396
3466
|
const startedAt = Number(row.started_at);
|
|
3397
3467
|
const endedAt = row.ended_at == null ? null : Number(row.ended_at);
|
|
@@ -3407,7 +3477,10 @@ async function parseHermesIncremental({ hermesPath, dbPath, cursors, queuePath,
|
|
|
3407
3477
|
// Compute delta from previous snapshot (if any) so that we only count
|
|
3408
3478
|
// new usage since the last sync. First time we see a session the
|
|
3409
3479
|
// previous snapshot is absent, so the full amount is the delta.
|
|
3410
|
-
const
|
|
3480
|
+
const legacySessionSnapshot = prevSnapshots[row.id];
|
|
3481
|
+
const prev = adoptingPerModelState && perModel && legacySessionSnapshot
|
|
3482
|
+
? currentSnapshot
|
|
3483
|
+
: prevSnapshots[snapshotKey];
|
|
3411
3484
|
let dInput = inputTokens;
|
|
3412
3485
|
let dOutput = outputTokens;
|
|
3413
3486
|
let dCacheRead = cacheRead;
|
|
@@ -3425,18 +3498,17 @@ async function parseHermesIncremental({ hermesPath, dbPath, cursors, queuePath,
|
|
|
3425
3498
|
// Skip if delta is zero (session unchanged since last sync)
|
|
3426
3499
|
if (dInput === 0 && dOutput === 0 && dCacheRead === 0 && dCacheWrite === 0 && dReasoning === 0) continue;
|
|
3427
3500
|
|
|
3428
|
-
//
|
|
3429
|
-
//
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3501
|
+
// Per-model rows carry the authoritative last API-call timestamp. Older
|
|
3502
|
+
// Hermes schemas fall back to the session-level start/end policy.
|
|
3503
|
+
const lastSeen = row.last_seen == null ? null : Number(row.last_seen);
|
|
3504
|
+
const epochSec = perModel && Number.isFinite(lastSeen) && lastSeen > 0
|
|
3505
|
+
? lastSeen
|
|
3506
|
+
: endedAt ?? (prev ? Date.parse(updatedAt) / 1000 : startedAt);
|
|
3433
3507
|
if (!epochSec || !Number.isFinite(epochSec)) continue;
|
|
3434
3508
|
const tsIso = new Date(epochSec * 1000).toISOString();
|
|
3435
3509
|
const bucketStart = toUtcHalfHourStart(tsIso);
|
|
3436
3510
|
if (!bucketStart) continue;
|
|
3437
3511
|
|
|
3438
|
-
const model = normalizeModelInput(row.model) || "hermes-agent";
|
|
3439
|
-
|
|
3440
3512
|
const delta = {
|
|
3441
3513
|
input_tokens: dInput,
|
|
3442
3514
|
cached_input_tokens: dCacheRead,
|
|
@@ -3472,6 +3544,7 @@ async function parseHermesIncremental({ hermesPath, dbPath, cursors, queuePath,
|
|
|
3472
3544
|
lastCompletedStartedAt: nextLastCompletedStartedAt,
|
|
3473
3545
|
unfinishedSessionIds: Array.from(nextUnfinishedSessionIds),
|
|
3474
3546
|
snapshots: nextSnapshots,
|
|
3547
|
+
...(hasPerModelRows ? { modelUsageVersion: 1 } : {}),
|
|
3475
3548
|
updatedAt,
|
|
3476
3549
|
});
|
|
3477
3550
|
}
|