tokenleak 0.5.0 → 1.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.
- package/package.json +1 -1
- package/tokenleak.js +86 -18
package/package.json
CHANGED
package/tokenleak.js
CHANGED
|
@@ -663,6 +663,26 @@ function computeTotalDays(daily) {
|
|
|
663
663
|
return Math.round((last - first) / ONE_DAY_MS) + 1;
|
|
664
664
|
}
|
|
665
665
|
// packages/core/dist/aggregation/merge.js
|
|
666
|
+
function mergeModelArrays(existing, incoming) {
|
|
667
|
+
const map = new Map;
|
|
668
|
+
for (const m of existing) {
|
|
669
|
+
map.set(m.model, { ...m });
|
|
670
|
+
}
|
|
671
|
+
for (const m of incoming) {
|
|
672
|
+
const prev = map.get(m.model);
|
|
673
|
+
if (prev) {
|
|
674
|
+
prev.inputTokens += m.inputTokens;
|
|
675
|
+
prev.outputTokens += m.outputTokens;
|
|
676
|
+
prev.cacheReadTokens += m.cacheReadTokens;
|
|
677
|
+
prev.cacheWriteTokens += m.cacheWriteTokens;
|
|
678
|
+
prev.totalTokens += m.totalTokens;
|
|
679
|
+
prev.cost += m.cost;
|
|
680
|
+
} else {
|
|
681
|
+
map.set(m.model, { ...m });
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
return [...map.values()];
|
|
685
|
+
}
|
|
666
686
|
function mergeProviderData(providers) {
|
|
667
687
|
const dateMap = new Map;
|
|
668
688
|
for (const provider of providers) {
|
|
@@ -675,7 +695,7 @@ function mergeProviderData(providers) {
|
|
|
675
695
|
existing.cacheWriteTokens += entry.cacheWriteTokens;
|
|
676
696
|
existing.totalTokens += entry.totalTokens;
|
|
677
697
|
existing.cost += entry.cost;
|
|
678
|
-
existing.models =
|
|
698
|
+
existing.models = mergeModelArrays(existing.models, entry.models);
|
|
679
699
|
} else {
|
|
680
700
|
dateMap.set(entry.date, {
|
|
681
701
|
date: entry.date,
|
|
@@ -736,7 +756,7 @@ function computePreviousPeriod(current) {
|
|
|
736
756
|
};
|
|
737
757
|
}
|
|
738
758
|
// packages/core/dist/index.js
|
|
739
|
-
var VERSION = "0.
|
|
759
|
+
var VERSION = "1.0.0";
|
|
740
760
|
|
|
741
761
|
// packages/registry/dist/models/normalizer.js
|
|
742
762
|
var DATE_SUFFIX_PATTERN = /-\d{8}$/;
|
|
@@ -1361,8 +1381,17 @@ function buildProviderData(records) {
|
|
|
1361
1381
|
};
|
|
1362
1382
|
}
|
|
1363
1383
|
function loadFromSqlite(dbPath, range) {
|
|
1364
|
-
|
|
1384
|
+
let db;
|
|
1385
|
+
try {
|
|
1386
|
+
db = new Database(dbPath, { readonly: true });
|
|
1387
|
+
} catch {
|
|
1388
|
+
return [];
|
|
1389
|
+
}
|
|
1365
1390
|
try {
|
|
1391
|
+
const tables = db.query("SELECT name FROM sqlite_master WHERE type='table' AND name='messages'").all();
|
|
1392
|
+
if (tables.length === 0) {
|
|
1393
|
+
return [];
|
|
1394
|
+
}
|
|
1366
1395
|
const rows = db.query("SELECT model, input_tokens, output_tokens, created_at FROM messages WHERE role = 'assistant'").all();
|
|
1367
1396
|
const records = [];
|
|
1368
1397
|
for (const row of rows) {
|
|
@@ -1377,6 +1406,8 @@ function loadFromSqlite(dbPath, range) {
|
|
|
1377
1406
|
}
|
|
1378
1407
|
}
|
|
1379
1408
|
return records;
|
|
1409
|
+
} catch {
|
|
1410
|
+
return [];
|
|
1380
1411
|
} finally {
|
|
1381
1412
|
db.close();
|
|
1382
1413
|
}
|
|
@@ -1385,24 +1416,28 @@ function loadFromJson(sessionsDir, range) {
|
|
|
1385
1416
|
const files = readdirSync3(sessionsDir).filter((f) => f.endsWith(".json"));
|
|
1386
1417
|
const records = [];
|
|
1387
1418
|
for (const file of files) {
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
}
|
|
1393
|
-
for (const msg of session.messages) {
|
|
1394
|
-
if (msg.role !== "assistant" || !msg.usage) {
|
|
1419
|
+
try {
|
|
1420
|
+
const content = readFileSync(join3(sessionsDir, file), "utf-8");
|
|
1421
|
+
const session = JSON.parse(content);
|
|
1422
|
+
if (!Array.isArray(session.messages)) {
|
|
1395
1423
|
continue;
|
|
1396
1424
|
}
|
|
1397
|
-
const
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1425
|
+
for (const msg of session.messages) {
|
|
1426
|
+
if (msg.role !== "assistant" || !msg.usage) {
|
|
1427
|
+
continue;
|
|
1428
|
+
}
|
|
1429
|
+
const date = extractDate2(msg.created_at);
|
|
1430
|
+
if (isInRange(date, range)) {
|
|
1431
|
+
records.push({
|
|
1432
|
+
date,
|
|
1433
|
+
model: msg.model,
|
|
1434
|
+
inputTokens: msg.usage.input_tokens,
|
|
1435
|
+
outputTokens: msg.usage.output_tokens
|
|
1436
|
+
});
|
|
1437
|
+
}
|
|
1405
1438
|
}
|
|
1439
|
+
} catch {
|
|
1440
|
+
continue;
|
|
1406
1441
|
}
|
|
1407
1442
|
}
|
|
1408
1443
|
return records;
|
|
@@ -3063,8 +3098,21 @@ function inferFormatFromPath(filePath) {
|
|
|
3063
3098
|
return null;
|
|
3064
3099
|
}
|
|
3065
3100
|
}
|
|
3101
|
+
var DATE_FORMAT = /^\d{4}-\d{2}-\d{2}$/;
|
|
3102
|
+
function isValidDate(dateStr) {
|
|
3103
|
+
if (!DATE_FORMAT.test(dateStr))
|
|
3104
|
+
return false;
|
|
3105
|
+
const d = new Date(dateStr + "T00:00:00Z");
|
|
3106
|
+
return !Number.isNaN(d.getTime()) && d.toISOString().slice(0, 10) === dateStr;
|
|
3107
|
+
}
|
|
3066
3108
|
function computeDateRange(args) {
|
|
3067
3109
|
const until = args.until ?? new Date().toISOString().slice(0, 10);
|
|
3110
|
+
if (args.until && !isValidDate(args.until)) {
|
|
3111
|
+
throw new TokenleakError(`Invalid --until date: "${args.until}". Use YYYY-MM-DD format.`);
|
|
3112
|
+
}
|
|
3113
|
+
if (args.since && !isValidDate(args.since)) {
|
|
3114
|
+
throw new TokenleakError(`Invalid --since date: "${args.since}". Use YYYY-MM-DD format.`);
|
|
3115
|
+
}
|
|
3068
3116
|
let since;
|
|
3069
3117
|
if (args.since) {
|
|
3070
3118
|
since = args.since;
|
|
@@ -3074,6 +3122,9 @@ function computeDateRange(args) {
|
|
|
3074
3122
|
d.setDate(d.getDate() - daysBack);
|
|
3075
3123
|
since = d.toISOString().slice(0, 10);
|
|
3076
3124
|
}
|
|
3125
|
+
if (since > until) {
|
|
3126
|
+
throw new TokenleakError(`--since (${since}) must not be after --until (${until}).`);
|
|
3127
|
+
}
|
|
3077
3128
|
return { since, until };
|
|
3078
3129
|
}
|
|
3079
3130
|
function resolveConfig(cliArgs) {
|
|
@@ -3230,6 +3281,10 @@ async function run(cliArgs) {
|
|
|
3230
3281
|
throw new TokenleakError("No provider data found");
|
|
3231
3282
|
}
|
|
3232
3283
|
if (config.compare) {
|
|
3284
|
+
if (config.format !== "json" && config.format !== "terminal") {
|
|
3285
|
+
process.stderr.write(`Warning: --compare only supports JSON output. Ignoring --format ${config.format}.
|
|
3286
|
+
`);
|
|
3287
|
+
}
|
|
3233
3288
|
const compareOutput = await runCompare(config.compare, dateRange, registry, available);
|
|
3234
3289
|
const rendered2 = JSON.stringify(compareOutput, null, 2);
|
|
3235
3290
|
if (config.output) {
|
|
@@ -3261,6 +3316,19 @@ async function run(cliArgs) {
|
|
|
3261
3316
|
aggregated: stats
|
|
3262
3317
|
};
|
|
3263
3318
|
if (config.liveServer) {
|
|
3319
|
+
const ignoredFlags = [];
|
|
3320
|
+
if (config.output)
|
|
3321
|
+
ignoredFlags.push("--output");
|
|
3322
|
+
if (config.clipboard)
|
|
3323
|
+
ignoredFlags.push("--clipboard");
|
|
3324
|
+
if (config.open)
|
|
3325
|
+
ignoredFlags.push("--open");
|
|
3326
|
+
if (config.upload)
|
|
3327
|
+
ignoredFlags.push("--upload");
|
|
3328
|
+
if (ignoredFlags.length > 0) {
|
|
3329
|
+
process.stderr.write(`Warning: ${ignoredFlags.join(", ")} ignored in --live-server mode.
|
|
3330
|
+
`);
|
|
3331
|
+
}
|
|
3264
3332
|
const renderOptions2 = {
|
|
3265
3333
|
format: config.format,
|
|
3266
3334
|
theme: config.theme,
|