vibestats 1.3.3 → 1.3.4

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/dist/index.js +285 -212
  2. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -24,6 +24,13 @@ var MODEL_PRICING = {
24
24
  cacheWrite: 6.25,
25
25
  cacheRead: 0.5
26
26
  },
27
+ // Sonnet 4.6 (same pricing as Sonnet 4.5)
28
+ "claude-sonnet-4-6-20260101": {
29
+ input: 3,
30
+ output: 15,
31
+ cacheWrite: 3.75,
32
+ cacheRead: 0.3
33
+ },
27
34
  // Sonnet 4.5
28
35
  "claude-sonnet-4-5-20250929": {
29
36
  input: 3,
@@ -79,6 +86,9 @@ function getModelPricing(modelName) {
79
86
  if (modelName.includes("opus-4-1") || modelName.includes("opus-4.1") || modelName.includes("opus-4")) {
80
87
  return MODEL_PRICING["claude-opus-4-1-20250805"];
81
88
  }
89
+ if (modelName.includes("sonnet-4-6") || modelName.includes("sonnet-4.6")) {
90
+ return MODEL_PRICING["claude-sonnet-4-6-20260101"];
91
+ }
82
92
  if (modelName.includes("sonnet-4-5") || modelName.includes("sonnet-4.5")) {
83
93
  return MODEL_PRICING["claude-sonnet-4-5-20250929"];
84
94
  }
@@ -98,6 +108,7 @@ function getModelDisplayName(modelName) {
98
108
  if (modelName.includes("opus-4-5") || modelName.includes("opus-4.5")) return "Opus 4.5";
99
109
  if (modelName.includes("opus-4-1") || modelName.includes("opus-4.1")) return "Opus 4.1";
100
110
  if (modelName.includes("opus")) return "Opus";
111
+ if (modelName.includes("sonnet-4-6") || modelName.includes("sonnet-4.6")) return "Sonnet 4.6";
101
112
  if (modelName.includes("sonnet-4-5") || modelName.includes("sonnet-4.5")) return "Sonnet 4.5";
102
113
  if (modelName.includes("sonnet-3-5") || modelName.includes("sonnet-3.5")) return "Sonnet 3.5";
103
114
  if (modelName.includes("sonnet")) return "Sonnet";
@@ -108,172 +119,272 @@ function getModelDisplayName(modelName) {
108
119
  }
109
120
 
110
121
  // src/codex-pricing.ts
111
- var CODEX_MODEL_PRICING = {
112
- // GPT-5.3 (latest flagship)
113
- "gpt-5.3": {
114
- input: 1.75,
115
- output: 14,
116
- cachedInput: 0.175
122
+ var GPT_54_PRICING = { input: 2.5, output: 15, cachedInput: 0.25 };
123
+ var GPT_53_PRICING = { input: 1.75, output: 14, cachedInput: 0.175 };
124
+ var GPT_51_PRICING = { input: 1.25, output: 10, cachedInput: 0.125 };
125
+ var GPT_5_MINI_PRICING = { input: 0.25, output: 2, cachedInput: 0.025 };
126
+ var GPT_5_NANO_PRICING = { input: 0.05, output: 0.4, cachedInput: 5e-3 };
127
+ var GPT_4O_PRICING = { input: 2.5, output: 10, cachedInput: 1.25 };
128
+ var GPT_4O_MINI_PRICING = { input: 0.15, output: 0.6, cachedInput: 0.075 };
129
+ var CODEX_MODEL_DEFINITIONS = [
130
+ {
131
+ key: "gpt-5.4",
132
+ displayName: "GPT-5.4",
133
+ token: "g54",
134
+ sortPriority: 77,
135
+ pricing: GPT_54_PRICING,
136
+ aliases: ["GPT-5.4"],
137
+ exactNames: ["gpt-5.4"]
117
138
  },
118
- "gpt-5.3-codex-spark": {
119
- input: 1.75,
120
- output: 14,
121
- cachedInput: 0.175
139
+ {
140
+ key: "gpt-5.3-codex-spark",
141
+ displayName: "GPT-5.3 Codex Spark",
142
+ token: "g53s",
143
+ sortPriority: 76,
144
+ pricing: GPT_53_PRICING,
145
+ aliases: ["GPT-5.3 Codex Spark"],
146
+ exactNames: ["gpt-5.3-codex-spark"],
147
+ includePatterns: [["5.3", "spark"]]
122
148
  },
123
- "gpt-5.3-codex": {
124
- input: 1.75,
125
- output: 14,
126
- cachedInput: 0.175
149
+ {
150
+ key: "gpt-5.3-codex",
151
+ displayName: "GPT-5.3 Codex",
152
+ token: "g53c",
153
+ sortPriority: 75,
154
+ pricing: GPT_53_PRICING,
155
+ aliases: ["GPT-5.3 Codex"],
156
+ exactNames: ["gpt-5.3-codex"],
157
+ includePatterns: [["5.3", "codex"]]
127
158
  },
128
- // GPT-5.2
129
- // Cached input is ~10% of input price (prompt caching discount)
130
- "gpt-5.2": {
131
- input: 1.75,
132
- output: 14,
133
- cachedInput: 0.175
159
+ {
160
+ key: "gpt-5.3",
161
+ displayName: "GPT-5.3",
162
+ token: "g53",
163
+ sortPriority: 74,
164
+ pricing: GPT_53_PRICING,
165
+ aliases: ["GPT-5.3"],
166
+ exactNames: ["gpt-5.3"],
167
+ includePatterns: [["5.3"]]
134
168
  },
135
- "gpt-5.2-codex": {
136
- input: 1.75,
137
- output: 14,
138
- cachedInput: 0.175
169
+ {
170
+ key: "gpt-5.2-codex",
171
+ displayName: "GPT-5.2 Codex",
172
+ token: "g52c",
173
+ sortPriority: 73,
174
+ pricing: GPT_53_PRICING,
175
+ aliases: ["GPT-5.2 Codex"],
176
+ exactNames: ["gpt-5.2-codex"],
177
+ includePatterns: [["5.2", "codex"]]
139
178
  },
140
- // GPT-5.1 models
141
- "gpt-5.1": {
142
- input: 1.25,
143
- output: 10,
144
- cachedInput: 0.125
179
+ {
180
+ key: "gpt-5.2",
181
+ displayName: "GPT-5.2",
182
+ token: "g52",
183
+ sortPriority: 72,
184
+ pricing: GPT_53_PRICING,
185
+ aliases: ["GPT-5.2"],
186
+ exactNames: ["gpt-5.2"],
187
+ includePatterns: [["5.2"]]
145
188
  },
146
- "gpt-5.1-codex": {
147
- input: 1.25,
148
- output: 10,
149
- cachedInput: 0.125
189
+ {
190
+ key: "gpt-5.1-codex-max",
191
+ displayName: "GPT-5.1 Codex Max",
192
+ token: "g51cm",
193
+ sortPriority: 71,
194
+ pricing: GPT_51_PRICING,
195
+ aliases: ["GPT-5.1 Codex Max"],
196
+ exactNames: ["gpt-5.1-codex-max"],
197
+ includePatterns: [["5.1", "codex", "max"]]
150
198
  },
151
- "gpt-5.1-codex-max": {
152
- input: 1.25,
153
- output: 10,
154
- cachedInput: 0.125
199
+ {
200
+ key: "legacy-gpt-5.1-max",
201
+ displayName: "GPT-5.1 Max",
202
+ token: "g51m",
203
+ sortPriority: 70,
204
+ pricing: GPT_51_PRICING,
205
+ aliases: ["GPT-5.1 Max"]
155
206
  },
156
- // GPT-5.1 Mini
157
- "gpt-5.1-codex-mini": {
158
- input: 0.25,
159
- output: 2,
160
- cachedInput: 0.025
207
+ {
208
+ key: "gpt-5.1-codex-mini",
209
+ displayName: "GPT-5.1 Codex Mini",
210
+ token: "g51cn",
211
+ sortPriority: 69,
212
+ pricing: GPT_5_MINI_PRICING,
213
+ aliases: ["GPT-5.1 Codex Mini"],
214
+ exactNames: ["gpt-5.1-codex-mini"],
215
+ includePatterns: [["5.1", "codex", "mini"]]
161
216
  },
162
- // GPT-5 base models
163
- "gpt-5": {
164
- input: 1.25,
165
- output: 10,
166
- cachedInput: 0.125
217
+ {
218
+ key: "legacy-gpt-5.1-mini",
219
+ displayName: "GPT-5.1 Mini",
220
+ token: "g51n",
221
+ sortPriority: 68,
222
+ pricing: GPT_5_MINI_PRICING,
223
+ aliases: ["GPT-5.1 Mini"]
167
224
  },
168
- "gpt-5-codex": {
169
- input: 1.25,
170
- output: 10,
171
- cachedInput: 0.125
225
+ {
226
+ key: "gpt-5.1-codex",
227
+ displayName: "GPT-5.1 Codex",
228
+ token: "g51c",
229
+ sortPriority: 67,
230
+ pricing: GPT_51_PRICING,
231
+ aliases: ["GPT-5.1 Codex"],
232
+ exactNames: ["gpt-5.1-codex"],
233
+ includePatterns: [["5.1", "codex"]]
172
234
  },
173
- // GPT-5 Mini variants
174
- "gpt-5-mini": {
175
- input: 0.25,
176
- output: 2,
177
- cachedInput: 0.025
235
+ {
236
+ key: "gpt-5.1",
237
+ displayName: "GPT-5.1",
238
+ token: "g51",
239
+ sortPriority: 66,
240
+ pricing: GPT_51_PRICING,
241
+ aliases: ["GPT-5.1"],
242
+ exactNames: ["gpt-5.1"],
243
+ includePatterns: [["5.1"]]
178
244
  },
179
- "gpt-5-codex-mini": {
180
- input: 0.25,
181
- output: 2,
182
- cachedInput: 0.025
245
+ {
246
+ key: "gpt-5",
247
+ displayName: "GPT-5",
248
+ token: "g5",
249
+ sortPriority: 64,
250
+ pricing: GPT_51_PRICING,
251
+ aliases: ["GPT-5"],
252
+ exactNames: ["gpt-5"]
183
253
  },
184
- // GPT-5 Nano
185
- "gpt-5-nano": {
186
- input: 0.05,
187
- output: 0.4,
188
- cachedInput: 5e-3
254
+ {
255
+ key: "gpt-5-codex",
256
+ displayName: "GPT-5 Codex",
257
+ sortPriority: 64,
258
+ pricing: GPT_51_PRICING,
259
+ aliases: ["GPT-5 Codex"],
260
+ exactNames: ["gpt-5-codex"]
189
261
  },
190
- // GPT-4o (fallback for older sessions)
191
- "gpt-4o": {
192
- input: 2.5,
193
- output: 10,
194
- cachedInput: 1.25
262
+ {
263
+ key: "gpt-5-mini",
264
+ displayName: "GPT-5 Mini",
265
+ token: "g5n",
266
+ sortPriority: 63,
267
+ pricing: GPT_5_MINI_PRICING,
268
+ aliases: ["GPT-5 Mini"],
269
+ exactNames: ["gpt-5-mini"]
195
270
  },
196
- "gpt-4o-mini": {
197
- input: 0.15,
198
- output: 0.6,
199
- cachedInput: 0.075
271
+ {
272
+ key: "gpt-5-codex-mini",
273
+ displayName: "GPT-5 Mini",
274
+ token: "g5n",
275
+ sortPriority: 63,
276
+ pricing: GPT_5_MINI_PRICING,
277
+ aliases: ["GPT-5 Codex Mini"],
278
+ exactNames: ["gpt-5-codex-mini"]
279
+ },
280
+ {
281
+ key: "gpt-5-nano",
282
+ displayName: "GPT-5 Nano",
283
+ sortPriority: 62,
284
+ pricing: GPT_5_NANO_PRICING,
285
+ aliases: ["GPT-5 Nano"],
286
+ exactNames: ["gpt-5-nano"]
287
+ },
288
+ {
289
+ key: "gpt-4o",
290
+ displayName: "GPT-4o",
291
+ token: "g4o",
292
+ sortPriority: 60,
293
+ pricing: GPT_4O_PRICING,
294
+ aliases: ["GPT-4o"],
295
+ exactNames: ["gpt-4o"]
296
+ },
297
+ {
298
+ key: "gpt-4o-mini",
299
+ displayName: "GPT-4o Mini",
300
+ token: "g4om",
301
+ sortPriority: 59,
302
+ pricing: GPT_4O_MINI_PRICING,
303
+ aliases: ["GPT-4o Mini"],
304
+ exactNames: ["gpt-4o-mini"]
305
+ }
306
+ ];
307
+ function normalizeName(value) {
308
+ return value.trim().toLowerCase();
309
+ }
310
+ function findCodexModelDefinition(modelName) {
311
+ const normalized = normalizeName(modelName);
312
+ const exactMatch = CODEX_MODEL_DEFINITIONS.find((definition) => {
313
+ if (normalizeName(definition.displayName) === normalized) {
314
+ return true;
315
+ }
316
+ if (definition.aliases?.some((alias) => normalizeName(alias) === normalized)) {
317
+ return true;
318
+ }
319
+ if (definition.exactNames?.includes(normalized)) {
320
+ return true;
321
+ }
322
+ return false;
323
+ });
324
+ if (exactMatch) {
325
+ return exactMatch;
200
326
  }
201
- };
327
+ return CODEX_MODEL_DEFINITIONS.find(
328
+ (definition) => definition.includePatterns?.some(
329
+ (pattern) => pattern.every((part) => normalized.includes(part))
330
+ ) ?? false
331
+ );
332
+ }
333
+ function formatUnknownCodexDisplayName(modelName) {
334
+ const normalized = normalizeName(modelName);
335
+ if (normalized.includes("4o-mini")) return "GPT-4o Mini";
336
+ if (normalized.includes("4o")) return "GPT-4o";
337
+ const gpt5Match = normalized.match(/gpt[- ]?5(?:\.(\d+))?/);
338
+ if (!gpt5Match) {
339
+ return modelName;
340
+ }
341
+ const version = gpt5Match[1] ? `.${gpt5Match[1]}` : "";
342
+ if (normalized.includes("mini")) return `GPT-5${version} Mini`;
343
+ if (normalized.includes("nano")) return `GPT-5${version} Nano`;
344
+ if (normalized.includes("codex")) return `GPT-5${version} Codex`;
345
+ return `GPT-5${version}`;
346
+ }
347
+ function encodeRawModelToken(displayName) {
348
+ return `raw_${encodeURIComponent(displayName)}`;
349
+ }
350
+ var CODEX_MODEL_PRICING = CODEX_MODEL_DEFINITIONS.reduce(
351
+ (acc, definition) => {
352
+ if (definition.pricing && definition.exactNames?.length) {
353
+ acc[definition.exactNames[0]] = definition.pricing;
354
+ }
355
+ return acc;
356
+ },
357
+ {}
358
+ );
202
359
  function getCodexModelPricing(modelName) {
203
- if (CODEX_MODEL_PRICING[modelName]) {
204
- return CODEX_MODEL_PRICING[modelName];
205
- }
206
- const normalized = modelName.toLowerCase();
207
- if (normalized.includes("5.3") && normalized.includes("spark")) {
208
- return CODEX_MODEL_PRICING["gpt-5.3-codex-spark"];
209
- }
210
- if (normalized.includes("5.3") && normalized.includes("codex")) {
211
- return CODEX_MODEL_PRICING["gpt-5.3-codex"];
212
- }
213
- if (normalized.includes("5.3")) {
214
- return CODEX_MODEL_PRICING["gpt-5.3"];
215
- }
216
- if (normalized.includes("5.2") && normalized.includes("codex")) {
217
- return CODEX_MODEL_PRICING["gpt-5.2-codex"];
218
- }
219
- if (normalized.includes("5.2")) {
220
- return CODEX_MODEL_PRICING["gpt-5.2"];
221
- }
222
- if (normalized.includes("5.1") && normalized.includes("codex") && normalized.includes("mini")) {
223
- return CODEX_MODEL_PRICING["gpt-5.1-codex-mini"];
224
- }
225
- if (normalized.includes("5.1") && normalized.includes("codex") && normalized.includes("max")) {
226
- return CODEX_MODEL_PRICING["gpt-5.1-codex-max"];
227
- }
228
- if (normalized.includes("5.1") && normalized.includes("codex")) {
229
- return CODEX_MODEL_PRICING["gpt-5.1-codex"];
230
- }
231
- if (normalized.includes("5.1") && normalized.includes("max")) {
232
- return CODEX_MODEL_PRICING["gpt-5.1"];
233
- }
234
- if (normalized.includes("5.1") && normalized.includes("mini")) {
235
- return CODEX_MODEL_PRICING["gpt-5.1-codex-mini"];
236
- }
237
- if (normalized.includes("5.1")) {
238
- return CODEX_MODEL_PRICING["gpt-5.1"];
239
- }
240
- if (normalized.includes("nano")) {
241
- return CODEX_MODEL_PRICING["gpt-5-nano"];
242
- }
243
- if (normalized.includes("5") && normalized.includes("mini")) {
244
- return CODEX_MODEL_PRICING["gpt-5-mini"];
245
- }
246
- if (normalized.includes("gpt-5") || normalized.includes("gpt5")) {
247
- return CODEX_MODEL_PRICING["gpt-5"];
248
- }
249
- if (normalized.includes("4o-mini")) {
250
- return CODEX_MODEL_PRICING["gpt-4o-mini"];
251
- }
252
- if (normalized.includes("4o")) {
253
- return CODEX_MODEL_PRICING["gpt-4o"];
360
+ return findCodexModelDefinition(modelName)?.pricing ?? GPT_51_PRICING;
361
+ }
362
+ function getCodexModelAbbreviation(modelName) {
363
+ const definition = findCodexModelDefinition(modelName);
364
+ if (definition?.token) {
365
+ return definition.token;
254
366
  }
255
- return CODEX_MODEL_PRICING["gpt-5"];
367
+ return encodeRawModelToken(getCodexModelDisplayName(modelName));
256
368
  }
257
369
  function getCodexModelDisplayName(modelName) {
258
- const normalized = modelName.toLowerCase();
259
- if (normalized.includes("5.3") && normalized.includes("spark")) return "GPT-5.3 Codex Spark";
260
- if (normalized.includes("5.3") && normalized.includes("codex")) return "GPT-5.3 Codex";
261
- if (normalized.includes("5.3")) return "GPT-5.3";
262
- if (normalized.includes("5.2") && normalized.includes("codex")) return "GPT-5.2 Codex";
263
- if (normalized.includes("5.2")) return "GPT-5.2";
264
- if (normalized.includes("5.1") && normalized.includes("codex") && normalized.includes("max")) return "GPT-5.1 Codex Max";
265
- if (normalized.includes("5.1") && normalized.includes("codex") && normalized.includes("mini")) return "GPT-5.1 Codex Mini";
266
- if (normalized.includes("5.1") && normalized.includes("max")) return "GPT-5.1 Max";
267
- if (normalized.includes("5.1") && normalized.includes("mini")) return "GPT-5.1 Mini";
268
- if (normalized.includes("5.1") && normalized.includes("codex")) return "GPT-5.1 Codex";
269
- if (normalized.includes("5.1")) return "GPT-5.1";
270
- if (normalized.includes("nano")) return "GPT-5 Nano";
271
- if (normalized.includes("5") && normalized.includes("mini")) return "GPT-5 Mini";
272
- if (normalized.includes("5") && normalized.includes("codex")) return "GPT-5 Codex";
273
- if (normalized.includes("gpt-5")) return "GPT-5";
274
- if (normalized.includes("4o-mini")) return "GPT-4o Mini";
275
- if (normalized.includes("4o")) return "GPT-4o";
276
- return modelName;
370
+ return findCodexModelDefinition(modelName)?.displayName ?? formatUnknownCodexDisplayName(modelName);
371
+ }
372
+ function getCodexModelSortPriority(modelName) {
373
+ const definition = findCodexModelDefinition(modelName);
374
+ if (definition) {
375
+ return definition.sortPriority;
376
+ }
377
+ const displayName = getCodexModelDisplayName(modelName);
378
+ const gpt5VersionMatch = displayName.match(/^GPT-5\.(\d+)/);
379
+ if (gpt5VersionMatch) {
380
+ return 64 + parseInt(gpt5VersionMatch[1], 10);
381
+ }
382
+ if (displayName === "GPT-5 Mini") return 63;
383
+ if (displayName === "GPT-5 Nano") return 62;
384
+ if (displayName === "GPT-5") return 64;
385
+ if (displayName === "GPT-4o Mini") return 59;
386
+ if (displayName === "GPT-4o") return 60;
387
+ return 0;
277
388
  }
278
389
  function calculateCodexCost(modelName, inputTokens, outputTokens, cachedInputTokens) {
279
390
  const pricing = getCodexModelPricing(modelName);
@@ -479,36 +590,22 @@ function filterByDateRange(entries, since, until) {
479
590
  });
480
591
  }
481
592
  function sortModelsByTier(models) {
482
- const tierPriority = {
593
+ const claudeTierPriority = {
483
594
  "Opus 4.6": 101,
484
595
  "Opus 4.5": 100,
485
596
  "Opus 4.1": 99,
486
597
  "Opus": 98,
598
+ "Sonnet 4.6": 91,
487
599
  "Sonnet 4.5": 90,
488
600
  "Sonnet 3.5": 89,
489
601
  "Sonnet": 88,
490
602
  "Haiku 4.5": 80,
491
603
  "Haiku 3.5": 79,
492
- "Haiku": 78,
493
- "GPT-5.3 Codex Spark": 76,
494
- "GPT-5.3 Codex": 75,
495
- "GPT-5.3": 74,
496
- "GPT-5.2 Codex": 73,
497
- "GPT-5.2": 72,
498
- "GPT-5.1 Codex Max": 71,
499
- "GPT-5.1 Max": 70,
500
- "GPT-5.1 Codex Mini": 69,
501
- "GPT-5.1 Mini": 68,
502
- "GPT-5.1 Codex": 67,
503
- "GPT-5.1": 66,
504
- "GPT-5": 64,
505
- "GPT-5 Mini": 63,
506
- "GPT-4o": 60,
507
- "GPT-4o Mini": 59
604
+ "Haiku": 78
508
605
  };
509
606
  return models.sort((a, b) => {
510
- const priorityA = tierPriority[a] ?? 0;
511
- const priorityB = tierPriority[b] ?? 0;
607
+ const priorityA = claudeTierPriority[a] ?? getCodexModelSortPriority(a);
608
+ const priorityB = claudeTierPriority[b] ?? getCodexModelSortPriority(b);
512
609
  return priorityB - priorityA;
513
610
  });
514
611
  }
@@ -1633,9 +1730,9 @@ function encodeStatsToUrl(stats, baseUrl = "https://vibestats.wolfai.dev") {
1633
1730
  params.set("cs", stats.currentStreak.toString());
1634
1731
  params.set("ph", stats.peakHour.toString());
1635
1732
  params.set("pd", (dayToNumber[stats.peakDay] ?? 0).toString());
1636
- params.set("fm", getModelAbbrevFromDisplayName(stats.favoriteModel));
1733
+ params.set("fm", getModelTokenFromDisplayName(stats.favoriteModel));
1637
1734
  const mbParts = stats.modelBreakdown.slice(0, 3).map((m) => {
1638
- const abbr = getModelAbbrevFromDisplayName(m.model);
1735
+ const abbr = getModelTokenFromDisplayName(m.model);
1639
1736
  return `${abbr}:${m.percentage}`;
1640
1737
  });
1641
1738
  params.set("mb", mbParts.join(","));
@@ -1665,60 +1762,36 @@ function encodeStatsToUrl(stats, baseUrl = "https://vibestats.wolfai.dev") {
1665
1762
  }
1666
1763
  return `${baseUrl}/wrapped/?${params.toString()}`;
1667
1764
  }
1668
- function getModelAbbrevFromDisplayName(displayName) {
1669
- const map = {
1670
- // Claude models
1671
- "Opus 4.5": "o45",
1672
- "Opus 4.1": "o41",
1673
- Opus: "opus",
1674
- "Sonnet 4.5": "s45",
1675
- "Sonnet 3.5": "s35",
1676
- Sonnet: "sonnet",
1677
- "Haiku 4.5": "h45",
1678
- "Haiku 3.5": "h35",
1679
- Haiku: "haiku",
1680
- // Codex/OpenAI models
1681
- "GPT-5.3 Codex Spark": "g53s",
1682
- "GPT-5.3 Codex": "g53c",
1683
- "GPT-5.3": "g53",
1684
- "GPT-5.2 Codex": "g52c",
1685
- "GPT-5.2": "g52",
1686
- "GPT-5.1 Codex Max": "g51cm",
1687
- "GPT-5.1 Max": "g51m",
1688
- "GPT-5.1 Codex Mini": "g51cn",
1689
- "GPT-5.1 Mini": "g51n",
1690
- "GPT-5.1 Codex": "g51c",
1691
- "GPT-5.1": "g51",
1692
- "GPT-5": "g5",
1693
- "GPT-5 Mini": "g5n",
1694
- "GPT-4o": "g4o",
1695
- "GPT-4o Mini": "g4om"
1696
- };
1697
- if (map[displayName]) return map[displayName];
1765
+ var CLAUDE_DISPLAY_TO_TOKEN = {
1766
+ "Opus 4.6": "o46",
1767
+ "Opus 4.5": "o45",
1768
+ "Opus 4.1": "o41",
1769
+ Opus: "opus",
1770
+ "Sonnet 4.6": "s46",
1771
+ "Sonnet 4.5": "s45",
1772
+ "Sonnet 3.5": "s35",
1773
+ Sonnet: "sonnet",
1774
+ "Haiku 4.5": "h45",
1775
+ "Haiku 3.5": "h35",
1776
+ Haiku: "haiku"
1777
+ };
1778
+ function getModelTokenFromDisplayName(displayName) {
1779
+ if (CLAUDE_DISPLAY_TO_TOKEN[displayName]) {
1780
+ return CLAUDE_DISPLAY_TO_TOKEN[displayName];
1781
+ }
1698
1782
  const normalized = displayName.toLowerCase();
1699
- if (normalized.includes("5.3") && normalized.includes("spark")) return "g53s";
1700
- if (normalized.includes("5.3") && normalized.includes("codex")) return "g53c";
1701
- if (normalized.includes("5.2")) return "g52";
1702
- if (normalized.includes("5.1") && normalized.includes("codex") && normalized.includes("max")) return "g51cm";
1703
- if (normalized.includes("5.1") && normalized.includes("max")) return "g51m";
1704
- if (normalized.includes("5.1") && normalized.includes("codex") && normalized.includes("mini")) return "g51cn";
1705
- if (normalized.includes("5.1") && normalized.includes("codex")) return "g51c";
1706
- if (normalized.includes("5.1") && normalized.includes("mini")) return "g51n";
1707
- if (normalized.includes("5.1")) return "g51";
1708
- if (normalized.includes("5") && normalized.includes("mini")) return "g5n";
1709
- if (normalized.includes("gpt-5") || normalized.includes("gpt5")) return "g5";
1710
- if (normalized.includes("4o") && normalized.includes("mini")) return "g4om";
1711
- if (normalized.includes("4o")) return "g4o";
1783
+ if (normalized.includes("opus") && normalized.includes("4.6")) return "o46";
1712
1784
  if (normalized.includes("opus") && normalized.includes("4.5")) return "o45";
1713
1785
  if (normalized.includes("opus") && normalized.includes("4.1")) return "o41";
1714
1786
  if (normalized.includes("opus")) return "opus";
1787
+ if (normalized.includes("sonnet") && normalized.includes("4.6")) return "s46";
1715
1788
  if (normalized.includes("sonnet") && normalized.includes("4.5")) return "s45";
1716
1789
  if (normalized.includes("sonnet") && normalized.includes("3.5")) return "s35";
1717
1790
  if (normalized.includes("sonnet")) return "sonnet";
1718
1791
  if (normalized.includes("haiku") && normalized.includes("4.5")) return "h45";
1719
1792
  if (normalized.includes("haiku") && normalized.includes("3.5")) return "h35";
1720
1793
  if (normalized.includes("haiku")) return "haiku";
1721
- return displayName.slice(0, 5).toLowerCase();
1794
+ return getCodexModelAbbreviation(displayName);
1722
1795
  }
1723
1796
  function aggregateRowsToMonthly(rows) {
1724
1797
  const monthMap = /* @__PURE__ */ new Map();
@@ -1787,7 +1860,7 @@ function encodeUsageToUrl(stats, baseUrl = "https://vibestats.wolfai.dev") {
1787
1860
  ].join(":"));
1788
1861
  if (stats.modelBreakdown.length > 0) {
1789
1862
  const mb = stats.modelBreakdown.slice(0, 5).map((m) => {
1790
- const abbr = getModelAbbrevFromDisplayName(m.model);
1863
+ const abbr = getModelTokenFromDisplayName(m.model);
1791
1864
  return `${abbr}:${m.percentage}:${m.cost.toFixed(2)}`;
1792
1865
  });
1793
1866
  params.set("mb", mb.join(","));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibestats",
3
- "version": "1.3.3",
3
+ "version": "1.3.4",
4
4
  "description": "AI coding stats - usage tracking and annual wrapped for Claude Code & Codex",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -48,6 +48,7 @@
48
48
  },
49
49
  "scripts": {
50
50
  "dev": "tsx src/index.ts",
51
- "build": "tsup src/index.ts --format esm --dts --clean --shims"
51
+ "build": "tsup src/index.ts --format esm --dts --clean --shims",
52
+ "test": "tsx --test src/**/*.test.ts"
52
53
  }
53
54
  }