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.
Files changed (2) hide show
  1. package/bundle/bin.js +98 -72
  2. 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. Fast mode
1627
- // (Claude Code's usage.speed) is published only for Opus 5.5, Opus 5 and Opus 4.8, at 2x every rate.
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 parseModels(models, readTiers2) {
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 [model, value] of Object.entries(models)) {
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.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.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",