@diogonzafe/tokenwatch 0.1.6 → 0.1.8

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/dist/index.d.ts CHANGED
@@ -1,89 +1,5 @@
1
- interface ModelPrice {
2
- /** USD per 1 million input tokens */
3
- input: number;
4
- /** USD per 1 million output tokens */
5
- output: number;
6
- /** Maximum context window (input tokens) for this model */
7
- maxInputTokens?: number;
8
- }
9
- type PriceMap = Record<string, ModelPrice>;
10
- interface PricesFile {
11
- updated_at: string;
12
- source: string;
13
- models: PriceMap;
14
- }
15
- interface TrackerConfig {
16
- /** 'memory' (default) or 'sqlite' */
17
- storage?: 'memory' | 'sqlite';
18
- /** USD threshold — fires webhookUrl when totalCostUSD exceeds this */
19
- alertThreshold?: number;
20
- /** Discord / Slack / generic webhook URL */
21
- webhookUrl?: string;
22
- /** Fetch fresh prices from remote GitHub source (default: true) */
23
- syncPrices?: boolean;
24
- /** Per-model price overrides — highest priority */
25
- customPrices?: PriceMap;
26
- }
27
- interface UsageEntry {
28
- model: string;
29
- inputTokens: number;
30
- outputTokens: number;
31
- costUSD: number;
32
- sessionId?: string;
33
- userId?: string;
34
- timestamp: string;
35
- }
36
- interface ModelStats {
37
- costUSD: number;
38
- calls: number;
39
- tokens: {
40
- input: number;
41
- output: number;
42
- };
43
- }
44
- interface SessionStats {
45
- costUSD: number;
46
- calls: number;
47
- }
48
- interface UserStats {
49
- costUSD: number;
50
- calls: number;
51
- }
52
- interface Report {
53
- totalCostUSD: number;
54
- totalTokens: {
55
- input: number;
56
- output: number;
57
- };
58
- byModel: Record<string, ModelStats>;
59
- bySession: Record<string, SessionStats>;
60
- byUser: Record<string, UserStats>;
61
- period: {
62
- from: string;
63
- to: string;
64
- };
65
- }
66
- interface IStorage {
67
- record(entry: UsageEntry): void;
68
- getAll(): UsageEntry[];
69
- clearAll(): void;
70
- clearSession(sessionId: string): void;
71
- }
72
- interface Tracker {
73
- /** Accumulate a usage entry (called by providers) */
74
- track(entry: Omit<UsageEntry, 'costUSD' | 'timestamp'>): void;
75
- getReport(): Report;
76
- reset(): void;
77
- resetSession(sessionId: string): void;
78
- exportJSON(): string;
79
- exportCSV(): string;
80
- /** Returns price and context window info for a model, or null if unknown */
81
- getModelInfo(model: string): ModelPrice | null;
82
- }
83
- interface TrackingMeta {
84
- __sessionId?: string;
85
- __userId?: string;
86
- }
1
+ import { T as TrackerConfig, a as Tracker, b as TrackingMeta } from './index-Cy_sl3FI.js';
2
+ export { I as IStorage, M as ModelPrice, c as ModelStats, P as PriceMap, d as PricesFile, R as Report, S as SessionStats, U as UsageEntry, e as UserStats } from './index-Cy_sl3FI.js';
87
3
 
88
4
  declare function createTracker(config?: TrackerConfig): Tracker;
89
5
 
@@ -167,4 +83,4 @@ interface GenAILike {
167
83
  */
168
84
  declare function wrapGemini<T extends GenAILike>(client: T, tracker: Tracker): T;
169
85
 
170
- export { type IStorage, type ModelPrice, type ModelStats, type PriceMap, type PricesFile, type Report, type SessionStats, type Tracker, type TrackerConfig, type TrackingMeta, type UsageEntry, type UserStats, createTracker, wrapAnthropic, wrapOpenAI as wrapDeepSeek, wrapGemini, wrapOpenAI };
86
+ export { Tracker, TrackerConfig, TrackingMeta, createTracker, wrapAnthropic, wrapOpenAI as wrapDeepSeek, wrapGemini, wrapOpenAI };
package/dist/index.js CHANGED
@@ -1338,7 +1338,9 @@ var ModelPriceSchema = z.object({
1338
1338
  maxInputTokens: z.number().positive().optional()
1339
1339
  });
1340
1340
  var TrackerConfigSchema = z.object({
1341
- storage: z.enum(["memory", "sqlite"]).optional().default("memory"),
1341
+ storage: z.union([z.enum(["memory", "sqlite"]), z.custom((v) => {
1342
+ return v !== null && typeof v === "object" && typeof v.record === "function" && typeof v.getAll === "function" && typeof v.clearAll === "function" && typeof v.clearSession === "function";
1343
+ })]).optional().default("memory"),
1342
1344
  alertThreshold: z.number().positive().optional(),
1343
1345
  webhookUrl: z.string().url().optional(),
1344
1346
  syncPrices: z.boolean().optional().default(true),
@@ -1352,13 +1354,13 @@ function createTracker(config = {}) {
1352
1354
  ${issues}`);
1353
1355
  }
1354
1356
  const {
1355
- storage: storageType,
1357
+ storage: storageOption,
1356
1358
  alertThreshold,
1357
1359
  webhookUrl,
1358
1360
  syncPrices,
1359
1361
  customPrices
1360
1362
  } = parsed.data;
1361
- const storage = createStorage(storageType);
1363
+ const storage = typeof storageOption === "object" ? storageOption : createStorage(storageOption);
1362
1364
  let remotePrices;
1363
1365
  if (syncPrices) {
1364
1366
  getRemotePrices().then((result) => {
@@ -1388,9 +1390,13 @@ ${issues}`);
1388
1390
  }
1389
1391
  function maybeFireAlert() {
1390
1392
  if (!alertThreshold || !webhookUrl || alertFired) return;
1391
- const total = computeTotal(storage.getAll());
1392
- if (total >= alertThreshold) {
1393
- alertFired = true;
1393
+ alertFired = true;
1394
+ Promise.resolve(storage.getAll()).then((entries) => {
1395
+ const total = computeTotal(entries);
1396
+ if (total < alertThreshold) {
1397
+ alertFired = false;
1398
+ return;
1399
+ }
1394
1400
  const payload = {
1395
1401
  text: `[tokenwatch] Alert: total cost reached $${total.toFixed(4)} USD (threshold: $${alertThreshold})`
1396
1402
  };
@@ -1400,10 +1406,12 @@ ${issues}`);
1400
1406
  body: JSON.stringify(payload)
1401
1407
  }).catch(() => {
1402
1408
  });
1403
- }
1409
+ }).catch(() => {
1410
+ alertFired = false;
1411
+ });
1404
1412
  }
1405
- function getReport() {
1406
- const entries = storage.getAll();
1413
+ async function getReport() {
1414
+ const entries = await Promise.resolve(storage.getAll());
1407
1415
  const byModel = {};
1408
1416
  const bySession = {};
1409
1417
  const byUser = {};
@@ -1441,18 +1449,18 @@ ${issues}`);
1441
1449
  period: { from: startedAt, to: lastTimestamp }
1442
1450
  };
1443
1451
  }
1444
- function reset() {
1445
- storage.clearAll();
1452
+ async function reset() {
1453
+ await Promise.resolve(storage.clearAll());
1446
1454
  alertFired = false;
1447
1455
  }
1448
- function resetSession(sessionId) {
1449
- storage.clearSession(sessionId);
1456
+ async function resetSession(sessionId) {
1457
+ await Promise.resolve(storage.clearSession(sessionId));
1450
1458
  }
1451
- function exportJSON() {
1452
- return JSON.stringify(getReport(), null, 2);
1459
+ async function exportJSON() {
1460
+ return JSON.stringify(await getReport(), null, 2);
1453
1461
  }
1454
- function exportCSV() {
1455
- const entries = storage.getAll();
1462
+ async function exportCSV() {
1463
+ const entries = await Promise.resolve(storage.getAll());
1456
1464
  const header = "timestamp,model,inputTokens,outputTokens,costUSD,sessionId,userId";
1457
1465
  const rows = entries.map(
1458
1466
  (e) => [
@@ -1527,12 +1535,7 @@ function wrapOpenAI(client, tracker) {
1527
1535
  return async function(params) {
1528
1536
  const { cleaned, sessionId, userId } = extractMeta(params);
1529
1537
  const model = typeof cleaned["model"] === "string" ? cleaned["model"] : "unknown";
1530
- let result;
1531
- try {
1532
- result = await target.create(cleaned);
1533
- } catch (err) {
1534
- throw err;
1535
- }
1538
+ const result = await target.create(cleaned);
1536
1539
  if (result && typeof result === "object" && Symbol.asyncIterator in result) {
1537
1540
  return wrapStream(
1538
1541
  result,
@@ -1617,12 +1620,7 @@ function wrapAnthropic(client, tracker) {
1617
1620
  return async function(params) {
1618
1621
  const { cleaned, sessionId, userId } = extractMeta2(params);
1619
1622
  const model = typeof cleaned["model"] === "string" ? cleaned["model"] : "unknown";
1620
- let result;
1621
- try {
1622
- result = await target.create(cleaned);
1623
- } catch (err) {
1624
- throw err;
1625
- }
1623
+ const result = await target.create(cleaned);
1626
1624
  if (result && typeof result === "object" && Symbol.asyncIterator in result) {
1627
1625
  return wrapStream2(
1628
1626
  result,
@@ -1667,12 +1665,7 @@ function wrapGemini(client, tracker) {
1667
1665
  get(mTarget, mProp) {
1668
1666
  if (mProp === "generateContent") {
1669
1667
  return async function(params) {
1670
- let result;
1671
- try {
1672
- result = await mTarget.generateContent(params);
1673
- } catch (err) {
1674
- throw err;
1675
- }
1668
+ const result = await mTarget.generateContent(params);
1676
1669
  const meta = result.response.usageMetadata;
1677
1670
  tracker.track({
1678
1671
  model: modelId,
@@ -1684,12 +1677,7 @@ function wrapGemini(client, tracker) {
1684
1677
  }
1685
1678
  if (mProp === "generateContentStream") {
1686
1679
  return async function(params) {
1687
- let streamResult;
1688
- try {
1689
- streamResult = await mTarget.generateContentStream(params);
1690
- } catch (err) {
1691
- throw err;
1692
- }
1680
+ const streamResult = await mTarget.generateContentStream(params);
1693
1681
  streamResult.response.then((res) => {
1694
1682
  const meta = res.usageMetadata;
1695
1683
  tracker.track({