tokengraph 0.2.2 → 0.2.3
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/bundle/bin.js +98 -72
- package/package.json +2 -2
package/bundle/bin.js
CHANGED
|
@@ -1623,8 +1623,16 @@ var init_pricing = __esm({
|
|
|
1623
1623
|
GEMINI_LONG_CONTEXT = { ...OPENAI_LONG_CONTEXT, thresholdTokens: 2e5 };
|
|
1624
1624
|
XAI_LONG_CONTEXT = { input: 2, output: 2, thresholdTokens: 2e5 };
|
|
1625
1625
|
RATES = {
|
|
1626
|
-
// Anthropic, from platform.claude.com/docs/en/about-claude/pricing, checked 2026-10-12
|
|
1627
|
-
// (
|
|
1626
|
+
// Anthropic, from platform.claude.com/docs/en/about-claude/pricing, checked 2026-10-12, rechecked
|
|
1627
|
+
// 2026-11-09 (Fable 5.2 and Mythos 5.2 launched that day; no other row changed). Fast mode (Claude
|
|
1628
|
+
// Code's usage.speed) is published only for Fable 5.2, Mythos 5.2, Opus 5.5, Opus 5 and Opus 4.8,
|
|
1629
|
+
// at 2x every rate.
|
|
1630
|
+
"claude-fable-5-2": [
|
|
1631
|
+
{ effectiveFrom: "2000-01-01", inputPerMillion: 8, outputPerMillion: 40, cacheReadMultiplier: 0.025, fastMultiplier: 2 }
|
|
1632
|
+
],
|
|
1633
|
+
"claude-mythos-5-2": [
|
|
1634
|
+
{ effectiveFrom: "2000-01-01", inputPerMillion: 8, outputPerMillion: 40, cacheReadMultiplier: 0.025, fastMultiplier: 2 }
|
|
1635
|
+
],
|
|
1628
1636
|
"claude-fable-5-1": [
|
|
1629
1637
|
{ effectiveFrom: "2000-01-01", inputPerMillion: 10, outputPerMillion: 50, cacheReadMultiplier: 0.025 }
|
|
1630
1638
|
],
|
|
@@ -3860,6 +3868,69 @@ var init_fs_retry = __esm({
|
|
|
3860
3868
|
}
|
|
3861
3869
|
});
|
|
3862
3870
|
|
|
3871
|
+
// dist/adapters/model-ids.js
|
|
3872
|
+
function normalizeClaudeModel(raw) {
|
|
3873
|
+
const model = raw.trim().toLowerCase();
|
|
3874
|
+
if (!model.startsWith("claude-") || CANONICAL_CLAUDE.test(model) || LEGACY_CLAUDE.test(model)) {
|
|
3875
|
+
return model.startsWith("claude-") ? model : raw;
|
|
3876
|
+
}
|
|
3877
|
+
const parts3 = model.split("-");
|
|
3878
|
+
if (parts3.some((part) => /[^a-z0-9.]/.test(part)))
|
|
3879
|
+
return model;
|
|
3880
|
+
const family = parts3.find((part) => CLAUDE_FAMILIES.has(part));
|
|
3881
|
+
const version = parts3.filter((part) => /^\d{1,2}(\.\d{1,2})?$/.test(part)).flatMap((part) => part.split("."));
|
|
3882
|
+
if (!family || version.length === 0 || version.length > 2)
|
|
3883
|
+
return model;
|
|
3884
|
+
const major = Number(version[0]);
|
|
3885
|
+
return major < 4 ? `claude-${version.join("-")}-${family}` : `claude-${family}-${version.join("-")}`;
|
|
3886
|
+
}
|
|
3887
|
+
function migrateStoredModelId(model) {
|
|
3888
|
+
return model.startsWith("claude-") && model.split("-").includes("mythos") ? normalizeClaudeModel(model) : model;
|
|
3889
|
+
}
|
|
3890
|
+
function capModelName(base, suffix = "") {
|
|
3891
|
+
const appliedSuffix = suffix && !base.endsWith(suffix) ? suffix : "";
|
|
3892
|
+
const maxBaseLength = MAX_MODEL_NAME_LENGTH - appliedSuffix.length;
|
|
3893
|
+
return `${base.slice(0, maxBaseLength)}${appliedSuffix}`;
|
|
3894
|
+
}
|
|
3895
|
+
function looksLikePathOrArn(model) {
|
|
3896
|
+
return model.startsWith("/") || model.startsWith("~") || model.startsWith("arn:") || WINDOWS_DRIVE_PREFIX.test(model) || model.includes("/Users/") || model.includes("/home/");
|
|
3897
|
+
}
|
|
3898
|
+
function canonicalModelId(providerId, modelId) {
|
|
3899
|
+
let model = (modelId ?? "").trim();
|
|
3900
|
+
if (model.length === 0)
|
|
3901
|
+
return "unknown";
|
|
3902
|
+
if (looksLikePathOrArn(model))
|
|
3903
|
+
return "unknown";
|
|
3904
|
+
const provider = (providerId ?? "").trim().toLowerCase();
|
|
3905
|
+
if (BEDROCK_PROVIDERS.has(provider)) {
|
|
3906
|
+
model = model.replace(BEDROCK_REGION_PREFIX, "").replace(/^anthropic\./, "").replace(BEDROCK_VERSION_SUFFIX, "");
|
|
3907
|
+
} else if (VERTEX_PROVIDERS.has(provider)) {
|
|
3908
|
+
model = model.replace(VERTEX_VERSION_SUFFIX, "");
|
|
3909
|
+
}
|
|
3910
|
+
const slash = model.indexOf("/");
|
|
3911
|
+
if (slash > 0 && STRIPPED_VENDOR_PREFIXES.has(model.slice(0, slash).toLowerCase())) {
|
|
3912
|
+
model = model.slice(slash + 1);
|
|
3913
|
+
}
|
|
3914
|
+
return capModelName(normalizeClaudeModel(model));
|
|
3915
|
+
}
|
|
3916
|
+
var MAX_MODEL_NAME_LENGTH, CLAUDE_FAMILIES, CANONICAL_CLAUDE, LEGACY_CLAUDE, WINDOWS_DRIVE_PREFIX, STRIPPED_VENDOR_PREFIXES, BEDROCK_PROVIDERS, VERTEX_PROVIDERS, BEDROCK_REGION_PREFIX, BEDROCK_VERSION_SUFFIX, VERTEX_VERSION_SUFFIX;
|
|
3917
|
+
var init_model_ids = __esm({
|
|
3918
|
+
"dist/adapters/model-ids.js"() {
|
|
3919
|
+
"use strict";
|
|
3920
|
+
MAX_MODEL_NAME_LENGTH = 120;
|
|
3921
|
+
CLAUDE_FAMILIES = /* @__PURE__ */ new Set(["opus", "sonnet", "haiku", "fable", "mythos"]);
|
|
3922
|
+
CANONICAL_CLAUDE = /^claude-(opus|sonnet|haiku|fable|mythos)-\d+(-\d+)?(-\d{8})?$/;
|
|
3923
|
+
LEGACY_CLAUDE = /^claude-[1-3](-\d+)?-(opus|sonnet|haiku)(-\d{8})?$/;
|
|
3924
|
+
WINDOWS_DRIVE_PREFIX = /^[A-Za-z]:[\\/]/;
|
|
3925
|
+
STRIPPED_VENDOR_PREFIXES = /* @__PURE__ */ new Set(["anthropic", "openai", "google"]);
|
|
3926
|
+
BEDROCK_PROVIDERS = /* @__PURE__ */ new Set(["bedrock", "amazon-bedrock"]);
|
|
3927
|
+
VERTEX_PROVIDERS = /* @__PURE__ */ new Set(["vertex", "google-vertex", "google-vertex-anthropic"]);
|
|
3928
|
+
BEDROCK_REGION_PREFIX = /^(us|eu|apac|global|jp)\./;
|
|
3929
|
+
BEDROCK_VERSION_SUFFIX = /-v\d+(:\d+)?$/;
|
|
3930
|
+
VERTEX_VERSION_SUFFIX = /@[^@]+$/;
|
|
3931
|
+
}
|
|
3932
|
+
});
|
|
3933
|
+
|
|
3863
3934
|
// dist/ledger.js
|
|
3864
3935
|
import { readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
3865
3936
|
function emptyLedger() {
|
|
@@ -4074,7 +4145,7 @@ function parseAccounts(raw) {
|
|
|
4074
4145
|
const { coveredThrough, days } = value;
|
|
4075
4146
|
if (typeof coveredThrough !== "string" || Number.isNaN(Date.parse(coveredThrough)))
|
|
4076
4147
|
continue;
|
|
4077
|
-
cursor[key] = { coveredThrough: clampInstant(coveredThrough), days: parseDays(days, readTiers) ?? {} };
|
|
4148
|
+
cursor[key] = { coveredThrough: clampInstant(coveredThrough), days: parseDays(days, readTiers, modelKeysFor(CURSOR_SOURCE)) ?? {} };
|
|
4078
4149
|
}
|
|
4079
4150
|
return Object.keys(cursor).length > 0 ? { cursor } : void 0;
|
|
4080
4151
|
}
|
|
@@ -4089,7 +4160,7 @@ function parseProjects(raw) {
|
|
|
4089
4160
|
continue;
|
|
4090
4161
|
const folders = {};
|
|
4091
4162
|
for (const [folder, rawDays] of Object.entries(rawFolders)) {
|
|
4092
|
-
const days = isFolderKey(folder) ? parseDays(rawDays, readTiers) : null;
|
|
4163
|
+
const days = isFolderKey(folder) ? parseDays(rawDays, readTiers, modelKeysFor(source)) : null;
|
|
4093
4164
|
if (days)
|
|
4094
4165
|
folders[folder] = days;
|
|
4095
4166
|
}
|
|
@@ -4109,25 +4180,38 @@ function parseProjects(raw) {
|
|
|
4109
4180
|
function parseLedgerAccounts(raw) {
|
|
4110
4181
|
return parseAccounts(raw);
|
|
4111
4182
|
}
|
|
4112
|
-
function
|
|
4183
|
+
function modelKeysFor(source) {
|
|
4184
|
+
return RAW_MODEL_ID_SOURCES.has(source) ? (model) => model : migrateStoredModelId;
|
|
4185
|
+
}
|
|
4186
|
+
function parseModels(models, readTiers2, modelKey) {
|
|
4113
4187
|
if (typeof models !== "object" || models === null)
|
|
4114
4188
|
return null;
|
|
4115
4189
|
const parsed = {};
|
|
4116
|
-
for (const [
|
|
4190
|
+
for (const [stored, value] of Object.entries(models)) {
|
|
4117
4191
|
const tiers = readTiers2(value);
|
|
4118
|
-
if (tiers)
|
|
4192
|
+
if (!tiers)
|
|
4193
|
+
continue;
|
|
4194
|
+
const model = modelKey(stored);
|
|
4195
|
+
const existing = parsed[model];
|
|
4196
|
+
if (!existing) {
|
|
4119
4197
|
parsed[model] = tiers;
|
|
4198
|
+
continue;
|
|
4199
|
+
}
|
|
4200
|
+
for (const [tier, counts2] of Object.entries(tiers)) {
|
|
4201
|
+
const current = existing[tier];
|
|
4202
|
+
existing[tier] = current ? addSplitCounts(current, counts2) : counts2;
|
|
4203
|
+
}
|
|
4120
4204
|
}
|
|
4121
4205
|
return Object.keys(parsed).length > 0 ? parsed : null;
|
|
4122
4206
|
}
|
|
4123
|
-
function parseDays(rawDays, readTiers2) {
|
|
4207
|
+
function parseDays(rawDays, readTiers2, modelKey) {
|
|
4124
4208
|
if (typeof rawDays !== "object" || rawDays === null)
|
|
4125
4209
|
return null;
|
|
4126
4210
|
const days = {};
|
|
4127
4211
|
for (const [date, models] of Object.entries(rawDays)) {
|
|
4128
4212
|
if (!isDateKey(date))
|
|
4129
4213
|
continue;
|
|
4130
|
-
const parsed = parseModels(models, readTiers2);
|
|
4214
|
+
const parsed = parseModels(models, readTiers2, modelKey);
|
|
4131
4215
|
if (parsed)
|
|
4132
4216
|
days[date] = parsed;
|
|
4133
4217
|
}
|
|
@@ -4158,7 +4242,7 @@ function parseLedger(raw) {
|
|
|
4158
4242
|
const fields = raw;
|
|
4159
4243
|
const version = fields["version"];
|
|
4160
4244
|
if (version === 1 || version === 2) {
|
|
4161
|
-
const days = parseDays(fields["days"], legacyTiers(version === 1 ? readLegacyCounts : readSplitCounts));
|
|
4245
|
+
const days = parseDays(fields["days"], legacyTiers(version === 1 ? readLegacyCounts : readSplitCounts), modelKeysFor(CLAUDE_CODE_SOURCE));
|
|
4162
4246
|
return days ? { version: LEDGER_VERSION, sources: { [CLAUDE_CODE_SOURCE]: days } } : emptyLedger();
|
|
4163
4247
|
}
|
|
4164
4248
|
if (version !== LEDGER_VERSION)
|
|
@@ -4170,7 +4254,7 @@ function parseLedger(raw) {
|
|
|
4170
4254
|
for (const [source, rawDays] of Object.entries(rawSources)) {
|
|
4171
4255
|
if (!isKnownSource(source))
|
|
4172
4256
|
continue;
|
|
4173
|
-
const days = parseDays(rawDays, readTiers);
|
|
4257
|
+
const days = parseDays(rawDays, readTiers, modelKeysFor(source));
|
|
4174
4258
|
if (days)
|
|
4175
4259
|
sources[source] = days;
|
|
4176
4260
|
}
|
|
@@ -4215,18 +4299,20 @@ async function writeLedger(path, ledger) {
|
|
|
4215
4299
|
await writeFile2(temporary, JSON.stringify(ledger, null, 2), { encoding: "utf8", mode: 384 });
|
|
4216
4300
|
await renameWithRetry(temporary, path);
|
|
4217
4301
|
}
|
|
4218
|
-
var LEDGER_VERSION, ACCOUNT_KEY, MAX_FOLDER_LENGTH, ABSOLUTE_FOLDER, BASE_FIELDS, writeCounter;
|
|
4302
|
+
var LEDGER_VERSION, ACCOUNT_KEY, MAX_FOLDER_LENGTH, ABSOLUTE_FOLDER, BASE_FIELDS, RAW_MODEL_ID_SOURCES, writeCounter;
|
|
4219
4303
|
var init_ledger = __esm({
|
|
4220
4304
|
"dist/ledger.js"() {
|
|
4221
4305
|
"use strict";
|
|
4222
4306
|
init_clamp_instant();
|
|
4223
4307
|
init_fs_retry();
|
|
4308
|
+
init_model_ids();
|
|
4224
4309
|
init_dist();
|
|
4225
4310
|
LEDGER_VERSION = 3;
|
|
4226
4311
|
ACCOUNT_KEY = /^[0-9a-f]{16}$/;
|
|
4227
4312
|
MAX_FOLDER_LENGTH = 1024;
|
|
4228
4313
|
ABSOLUTE_FOLDER = /^(?:[a-z]:)?\//i;
|
|
4229
4314
|
BASE_FIELDS = ["inputTokens", "outputTokens", "cacheCreationTokens", "cacheReadTokens"];
|
|
4315
|
+
RAW_MODEL_ID_SOURCES = /* @__PURE__ */ new Set([CLAUDE_CODE_SOURCE, CODEX_CLI_SOURCE]);
|
|
4230
4316
|
writeCounter = 0;
|
|
4231
4317
|
}
|
|
4232
4318
|
});
|
|
@@ -4302,66 +4388,6 @@ var init_request_buckets = __esm({
|
|
|
4302
4388
|
}
|
|
4303
4389
|
});
|
|
4304
4390
|
|
|
4305
|
-
// dist/adapters/model-ids.js
|
|
4306
|
-
function normalizeClaudeModel(raw) {
|
|
4307
|
-
const model = raw.trim().toLowerCase();
|
|
4308
|
-
if (!model.startsWith("claude-") || CANONICAL_CLAUDE.test(model) || LEGACY_CLAUDE.test(model)) {
|
|
4309
|
-
return model.startsWith("claude-") ? model : raw;
|
|
4310
|
-
}
|
|
4311
|
-
const parts3 = model.split("-");
|
|
4312
|
-
if (parts3.some((part) => /[^a-z0-9.]/.test(part)))
|
|
4313
|
-
return model;
|
|
4314
|
-
const family = parts3.find((part) => CLAUDE_FAMILIES.has(part));
|
|
4315
|
-
const version = parts3.filter((part) => /^\d{1,2}(\.\d{1,2})?$/.test(part)).flatMap((part) => part.split("."));
|
|
4316
|
-
if (!family || version.length === 0 || version.length > 2)
|
|
4317
|
-
return model;
|
|
4318
|
-
const major = Number(version[0]);
|
|
4319
|
-
return major < 4 ? `claude-${version.join("-")}-${family}` : `claude-${family}-${version.join("-")}`;
|
|
4320
|
-
}
|
|
4321
|
-
function capModelName(base, suffix = "") {
|
|
4322
|
-
const appliedSuffix = suffix && !base.endsWith(suffix) ? suffix : "";
|
|
4323
|
-
const maxBaseLength = MAX_MODEL_NAME_LENGTH - appliedSuffix.length;
|
|
4324
|
-
return `${base.slice(0, maxBaseLength)}${appliedSuffix}`;
|
|
4325
|
-
}
|
|
4326
|
-
function looksLikePathOrArn(model) {
|
|
4327
|
-
return model.startsWith("/") || model.startsWith("~") || model.startsWith("arn:") || WINDOWS_DRIVE_PREFIX.test(model) || model.includes("/Users/") || model.includes("/home/");
|
|
4328
|
-
}
|
|
4329
|
-
function canonicalModelId(providerId, modelId) {
|
|
4330
|
-
let model = (modelId ?? "").trim();
|
|
4331
|
-
if (model.length === 0)
|
|
4332
|
-
return "unknown";
|
|
4333
|
-
if (looksLikePathOrArn(model))
|
|
4334
|
-
return "unknown";
|
|
4335
|
-
const provider = (providerId ?? "").trim().toLowerCase();
|
|
4336
|
-
if (BEDROCK_PROVIDERS.has(provider)) {
|
|
4337
|
-
model = model.replace(BEDROCK_REGION_PREFIX, "").replace(/^anthropic\./, "").replace(BEDROCK_VERSION_SUFFIX, "");
|
|
4338
|
-
} else if (VERTEX_PROVIDERS.has(provider)) {
|
|
4339
|
-
model = model.replace(VERTEX_VERSION_SUFFIX, "");
|
|
4340
|
-
}
|
|
4341
|
-
const slash = model.indexOf("/");
|
|
4342
|
-
if (slash > 0 && STRIPPED_VENDOR_PREFIXES.has(model.slice(0, slash).toLowerCase())) {
|
|
4343
|
-
model = model.slice(slash + 1);
|
|
4344
|
-
}
|
|
4345
|
-
return capModelName(normalizeClaudeModel(model));
|
|
4346
|
-
}
|
|
4347
|
-
var MAX_MODEL_NAME_LENGTH, CLAUDE_FAMILIES, CANONICAL_CLAUDE, LEGACY_CLAUDE, WINDOWS_DRIVE_PREFIX, STRIPPED_VENDOR_PREFIXES, BEDROCK_PROVIDERS, VERTEX_PROVIDERS, BEDROCK_REGION_PREFIX, BEDROCK_VERSION_SUFFIX, VERTEX_VERSION_SUFFIX;
|
|
4348
|
-
var init_model_ids = __esm({
|
|
4349
|
-
"dist/adapters/model-ids.js"() {
|
|
4350
|
-
"use strict";
|
|
4351
|
-
MAX_MODEL_NAME_LENGTH = 120;
|
|
4352
|
-
CLAUDE_FAMILIES = /* @__PURE__ */ new Set(["opus", "sonnet", "haiku", "fable"]);
|
|
4353
|
-
CANONICAL_CLAUDE = /^claude-(opus|sonnet|haiku|fable)-\d+(-\d+)?(-\d{8})?$/;
|
|
4354
|
-
LEGACY_CLAUDE = /^claude-[1-3](-\d+)?-(opus|sonnet|haiku)(-\d{8})?$/;
|
|
4355
|
-
WINDOWS_DRIVE_PREFIX = /^[A-Za-z]:[\\/]/;
|
|
4356
|
-
STRIPPED_VENDOR_PREFIXES = /* @__PURE__ */ new Set(["anthropic", "openai", "google"]);
|
|
4357
|
-
BEDROCK_PROVIDERS = /* @__PURE__ */ new Set(["bedrock", "amazon-bedrock"]);
|
|
4358
|
-
VERTEX_PROVIDERS = /* @__PURE__ */ new Set(["vertex", "google-vertex", "google-vertex-anthropic"]);
|
|
4359
|
-
BEDROCK_REGION_PREFIX = /^(us|eu|apac|global|jp)\./;
|
|
4360
|
-
BEDROCK_VERSION_SUFFIX = /-v\d+(:\d+)?$/;
|
|
4361
|
-
VERTEX_VERSION_SUFFIX = /@[^@]+$/;
|
|
4362
|
-
}
|
|
4363
|
-
});
|
|
4364
|
-
|
|
4365
4391
|
// dist/adapters/cursor/aggregator.js
|
|
4366
4392
|
function normalizeCursorModel(raw) {
|
|
4367
4393
|
const lower = raw.trim().toLowerCase();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tokengraph",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "A GitHub-style graph of the tokens your AI coding agents burn each day: Claude Code, Codex CLI, Cursor, Gemini CLI, Cline, OpenCode, Kilo CLI, Copilot CLI, pi, Qwen Code and Goose.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@resvg/resvg-wasm": "^2.6.2",
|
|
49
|
-
"@tokengraph/core": "0.2.
|
|
49
|
+
"@tokengraph/core": "0.2.3",
|
|
50
50
|
"@types/react": "^19.3.0",
|
|
51
51
|
"esbuild": "0.28.2",
|
|
52
52
|
"qrcode-generator": "^2.0.4",
|