awarts 0.2.1 → 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/dist/index.js +83 -52
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5879,11 +5879,35 @@ import fs8 from "node:fs/promises";
5879
5879
  import path4 from "node:path";
5880
5880
  import os5 from "node:os";
5881
5881
  var CLAUDE_DIR = path4.join(os5.homedir(), ".claude");
5882
- var USAGE_DIR = path4.join(CLAUDE_DIR, "usage");
5883
- var ALT_USAGE_DIR = path4.join(CLAUDE_DIR, "projects");
5884
- function dateFromFilename(filename) {
5885
- const match = filename.match(/^(\d{4}-\d{2}-\d{2})\.json$/);
5886
- return match ? match[1] : null;
5882
+ var STATS_CACHE = path4.join(CLAUDE_DIR, "stats-cache.json");
5883
+ var MODEL_PRICING = {
5884
+ "claude-opus-4-6": { input: 15, output: 75, cacheRead: 1.875, cacheWrite: 18.75 },
5885
+ "claude-opus-4": { input: 15, output: 75, cacheRead: 1.875, cacheWrite: 18.75 },
5886
+ "claude-sonnet-4": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
5887
+ "claude-sonnet-3-5": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
5888
+ "claude-haiku-3-5": { input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1 }
5889
+ };
5890
+ var DEFAULT_PRICING = { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 };
5891
+ function getPricing(model) {
5892
+ if (MODEL_PRICING[model])
5893
+ return MODEL_PRICING[model];
5894
+ for (const [key, pricing] of Object.entries(MODEL_PRICING)) {
5895
+ if (model.startsWith(key))
5896
+ return pricing;
5897
+ }
5898
+ return DEFAULT_PRICING;
5899
+ }
5900
+ function estimateCost(model, inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens) {
5901
+ const p = getPricing(model);
5902
+ return inputTokens / 1e6 * p.input + outputTokens / 1e6 * p.output + cacheReadTokens / 1e6 * p.cacheRead + cacheCreationTokens / 1e6 * p.cacheWrite;
5903
+ }
5904
+ async function fileExists(filePath) {
5905
+ try {
5906
+ const stat = await fs8.stat(filePath);
5907
+ return stat.isFile();
5908
+ } catch {
5909
+ return false;
5910
+ }
5887
5911
  }
5888
5912
  async function dirExists(dir) {
5889
5913
  try {
@@ -5893,40 +5917,50 @@ async function dirExists(dir) {
5893
5917
  return false;
5894
5918
  }
5895
5919
  }
5896
- async function readUsageDir(dir) {
5897
- const entries = [];
5898
- let files;
5899
- try {
5900
- files = await fs8.readdir(dir);
5901
- } catch {
5902
- return entries;
5920
+ async function readStatsCache() {
5921
+ const raw = await fs8.readFile(STATS_CACHE, "utf-8");
5922
+ const cache = JSON.parse(raw);
5923
+ const dailyTokens = cache.dailyModelTokens;
5924
+ if (!dailyTokens || dailyTokens.length === 0)
5925
+ return [];
5926
+ let totalInputTokens = 0;
5927
+ let totalOutputTokens = 0;
5928
+ let totalCacheRead = 0;
5929
+ let totalCacheCreation = 0;
5930
+ let totalCost = 0;
5931
+ if (cache.modelUsage) {
5932
+ for (const [model, usage] of Object.entries(cache.modelUsage)) {
5933
+ totalInputTokens += usage.inputTokens || 0;
5934
+ totalOutputTokens += usage.outputTokens || 0;
5935
+ totalCacheRead += usage.cacheReadInputTokens || 0;
5936
+ totalCacheCreation += usage.cacheCreationInputTokens || 0;
5937
+ if (usage.costUSD > 0) {
5938
+ totalCost += usage.costUSD;
5939
+ } else {
5940
+ totalCost += estimateCost(model, usage.inputTokens || 0, usage.outputTokens || 0, usage.cacheReadInputTokens || 0, usage.cacheCreationInputTokens || 0);
5941
+ }
5942
+ }
5903
5943
  }
5904
- const jsonFiles = files.filter((f) => f.endsWith(".json"));
5905
- for (const file of jsonFiles) {
5906
- try {
5907
- const filePath = path4.join(dir, file);
5908
- const raw = await fs8.readFile(filePath, "utf-8");
5909
- const data = JSON.parse(raw);
5910
- const date = data.date ?? dateFromFilename(file);
5911
- if (!date)
5912
- continue;
5913
- const costUsd = data.cost_usd ?? data.totalCost ?? 0;
5914
- const inputTokens = data.input_tokens ?? data.totalInputTokens ?? 0;
5915
- const outputTokens = data.output_tokens ?? data.totalOutputTokens ?? 0;
5916
- const cacheCreation = data.cache_creation_tokens ?? data.cacheCreationTokens ?? 0;
5917
- const cacheRead = data.cache_read_tokens ?? data.cacheReadTokens ?? 0;
5918
- const models = data.models ? data.models : data.model ? [data.model] : [];
5919
- entries.push({
5920
- date,
5921
- provider: "claude",
5922
- cost_usd: Number(costUsd) || 0,
5923
- input_tokens: Number(inputTokens) || 0,
5924
- output_tokens: Number(outputTokens) || 0,
5925
- cache_creation_tokens: Number(cacheCreation) || 0,
5926
- cache_read_tokens: Number(cacheRead) || 0,
5927
- models
5928
- });
5929
- } catch {}
5944
+ const dailyOutputSum = dailyTokens.reduce((sum, day) => {
5945
+ return sum + Object.values(day.tokensByModel).reduce((s, t) => s + t, 0);
5946
+ }, 0);
5947
+ const entries = [];
5948
+ for (const day of dailyTokens) {
5949
+ const dayOutputTokens = Object.values(day.tokensByModel).reduce((s, t) => s + t, 0);
5950
+ if (dayOutputTokens === 0)
5951
+ continue;
5952
+ const share = dailyOutputSum > 0 ? dayOutputTokens / dailyOutputSum : 0;
5953
+ const models = Object.keys(day.tokensByModel).filter((m) => day.tokensByModel[m] > 0);
5954
+ entries.push({
5955
+ date: day.date,
5956
+ provider: "claude",
5957
+ output_tokens: dayOutputTokens,
5958
+ input_tokens: Math.round(totalInputTokens * share),
5959
+ cache_read_tokens: Math.round(totalCacheRead * share),
5960
+ cache_creation_tokens: Math.round(totalCacheCreation * share),
5961
+ cost_usd: Number((totalCost * share).toFixed(4)),
5962
+ models
5963
+ });
5930
5964
  }
5931
5965
  return entries;
5932
5966
  }
@@ -5934,21 +5968,18 @@ var claudeAdapter = {
5934
5968
  name: "claude",
5935
5969
  displayName: "Claude",
5936
5970
  async detect() {
5937
- if (await dirExists(USAGE_DIR))
5971
+ if (await fileExists(STATS_CACHE))
5938
5972
  return true;
5939
5973
  if (await dirExists(CLAUDE_DIR))
5940
5974
  return true;
5941
- if (await dirExists(ALT_USAGE_DIR))
5942
- return true;
5943
5975
  return false;
5944
5976
  },
5945
5977
  async read() {
5946
- const entries = [];
5947
- entries.push(...await readUsageDir(USAGE_DIR));
5948
- if (entries.length === 0) {
5949
- entries.push(...await readUsageDir(ALT_USAGE_DIR));
5978
+ try {
5979
+ return await readStatsCache();
5980
+ } catch {
5981
+ return [];
5950
5982
  }
5951
- return entries;
5952
5983
  }
5953
5984
  };
5954
5985
 
@@ -5962,7 +5993,7 @@ var CANDIDATE_DIRS = [
5962
5993
  path5.join(HOME, ".openai-codex", "usage"),
5963
5994
  path5.join(HOME, ".codex")
5964
5995
  ];
5965
- function dateFromFilename2(filename) {
5996
+ function dateFromFilename(filename) {
5966
5997
  const match = filename.match(/^(\d{4}-\d{2}-\d{2})\.json$/);
5967
5998
  return match ? match[1] : null;
5968
5999
  }
@@ -6004,7 +6035,7 @@ var codexAdapter = {
6004
6035
  const filePath = path5.join(dir, file);
6005
6036
  const raw = await fs9.readFile(filePath, "utf-8");
6006
6037
  const data = JSON.parse(raw);
6007
- const date = data.date ?? dateFromFilename2(file);
6038
+ const date = data.date ?? dateFromFilename(file);
6008
6039
  if (!date)
6009
6040
  continue;
6010
6041
  const costUsd = data.cost_usd ?? data.total_cost ?? 0;
@@ -6037,7 +6068,7 @@ var CANDIDATE_DIRS2 = [
6037
6068
  path6.join(HOME2, ".config", "gemini", "usage"),
6038
6069
  path6.join(HOME2, ".gemini")
6039
6070
  ];
6040
- function dateFromFilename3(filename) {
6071
+ function dateFromFilename2(filename) {
6041
6072
  const match = filename.match(/^(\d{4}-\d{2}-\d{2})\.json$/);
6042
6073
  return match ? match[1] : null;
6043
6074
  }
@@ -6079,7 +6110,7 @@ var geminiAdapter = {
6079
6110
  const filePath = path6.join(dir, file);
6080
6111
  const raw = await fs10.readFile(filePath, "utf-8");
6081
6112
  const data = JSON.parse(raw);
6082
- const date = data.date ?? dateFromFilename3(file);
6113
+ const date = data.date ?? dateFromFilename2(file);
6083
6114
  if (!date)
6084
6115
  continue;
6085
6116
  const costUsd = data.cost_usd ?? data.total_cost ?? 0;
@@ -6111,7 +6142,7 @@ var CANDIDATE_DIRS3 = [
6111
6142
  path7.join(HOME3, ".antigravity", "usage"),
6112
6143
  path7.join(HOME3, ".antigravity")
6113
6144
  ];
6114
- function dateFromFilename4(filename) {
6145
+ function dateFromFilename3(filename) {
6115
6146
  const match = filename.match(/^(\d{4}-\d{2}-\d{2})\.json$/);
6116
6147
  return match ? match[1] : null;
6117
6148
  }
@@ -6153,7 +6184,7 @@ var antigravityAdapter = {
6153
6184
  const filePath = path7.join(dir, file);
6154
6185
  const raw = await fs11.readFile(filePath, "utf-8");
6155
6186
  const data = JSON.parse(raw);
6156
- const date = data.date ?? dateFromFilename4(file);
6187
+ const date = data.date ?? dateFromFilename3(file);
6157
6188
  if (!date)
6158
6189
  continue;
6159
6190
  const costUsd = data.cost_usd ?? data.total_cost ?? 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "awarts",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Track your AI coding across Claude, Codex, Gemini & Antigravity",
5
5
  "type": "module",
6
6
  "bin": {