awarts 0.2.6 → 0.2.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.
Files changed (2) hide show
  1. package/dist/index.js +156 -11
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6181,11 +6181,20 @@ var CONFIG_DIRS = [
6181
6181
  var CODEX_PRICING = {
6182
6182
  "gpt-5.4": { input: 2.5, output: 15 },
6183
6183
  "gpt-5.4-pro": { input: 30, output: 180 },
6184
- "gpt-5.2": { input: 1.75, output: 14 },
6184
+ "gpt-5.3": { input: 1.75, output: 14 },
6185
+ "gpt-5.3-codex": { input: 1.75, output: 14 },
6186
+ "gpt-5.2": { input: 0.875, output: 7 },
6187
+ "gpt-5.2-codex": { input: 1.75, output: 14 },
6188
+ "gpt-5.2-pro": { input: 10.5, output: 84 },
6185
6189
  "gpt-5.1": { input: 1.25, output: 10 },
6190
+ "gpt-5.1-codex": { input: 1.25, output: 10 },
6191
+ "gpt-5.1-codex-mini": { input: 0.25, output: 2 },
6192
+ "gpt-5.1-codex-max": { input: 1.25, output: 10 },
6186
6193
  "gpt-5": { input: 1.25, output: 10 },
6194
+ "gpt-5-codex": { input: 1.25, output: 10 },
6187
6195
  "gpt-5-mini": { input: 0.25, output: 2 },
6188
6196
  "gpt-5-nano": { input: 0.05, output: 0.4 },
6197
+ "gpt-5-pro": { input: 15, output: 120 },
6189
6198
  "gpt-4.1": { input: 2, output: 8 },
6190
6199
  "gpt-4.1-mini": { input: 0.4, output: 1.6 },
6191
6200
  "gpt-4.1-nano": { input: 0.1, output: 0.4 },
@@ -6193,13 +6202,14 @@ var CODEX_PRICING = {
6193
6202
  "gpt-4o-mini": { input: 0.15, output: 0.6 },
6194
6203
  o3: { input: 2, output: 8 },
6195
6204
  "o3-pro": { input: 20, output: 80 },
6205
+ "o3-mini": { input: 0.55, output: 2.2 },
6196
6206
  "o4-mini": { input: 1.1, output: 4.4 },
6197
6207
  o1: { input: 15, output: 60 },
6208
+ "o1-mini": { input: 0.55, output: 2.2 },
6198
6209
  "o1-pro": { input: 150, output: 600 },
6199
- "codex-mini": { input: 1.5, output: 6 },
6200
- "openai-codex": { input: 2, output: 8 }
6210
+ "codex-mini": { input: 0.75, output: 3 }
6201
6211
  };
6202
- var DEFAULT_PRICING2 = { input: 2, output: 8 };
6212
+ var DEFAULT_PRICING2 = { input: 1.1, output: 4.4 };
6203
6213
  async function fetchOpenAICosts(apiKey) {
6204
6214
  const entries = [];
6205
6215
  const now = Math.floor(Date.now() / 1000);
@@ -6328,12 +6338,22 @@ async function findSqliteDb() {
6328
6338
  }
6329
6339
  return null;
6330
6340
  }
6341
+ async function readConfigModel() {
6342
+ const configPath = path7.join(HOME, ".codex", "config.toml");
6343
+ try {
6344
+ const content = await fs11.readFile(configPath, "utf-8");
6345
+ const match = content.match(/^\s*model\s*=\s*"([^"]+)"/m);
6346
+ return match ? match[1] : null;
6347
+ } catch {
6348
+ return null;
6349
+ }
6350
+ }
6331
6351
  async function readSqliteThreads() {
6332
6352
  const dbPath = await findSqliteDb();
6333
6353
  if (!dbPath)
6334
6354
  return [];
6335
6355
  try {
6336
- const query = `SELECT date(created_at/1000000000, 'unixepoch') as day, tokens_used, model_provider FROM threads WHERE tokens_used > 0 ORDER BY created_at;`;
6356
+ const query = `SELECT date(created_at, 'unixepoch') as day, tokens_used, model_provider FROM threads WHERE tokens_used > 0 ORDER BY created_at;`;
6337
6357
  const result = execFileSync2("sqlite3", [dbPath, query], {
6338
6358
  timeout: 5000,
6339
6359
  encoding: "utf-8",
@@ -6341,11 +6361,12 @@ async function readSqliteThreads() {
6341
6361
  }).trim();
6342
6362
  if (!result)
6343
6363
  return [];
6364
+ const configModel = await readConfigModel();
6344
6365
  const dayMap = new Map;
6345
6366
  for (const line of result.split(`
6346
6367
  `)) {
6347
6368
  const [day, tokensStr, model] = line.split("|");
6348
- if (!day)
6369
+ if (!day || day === "")
6349
6370
  continue;
6350
6371
  const tokens2 = Number(tokensStr) || 0;
6351
6372
  if (!dayMap.has(day))
@@ -6359,8 +6380,8 @@ async function readSqliteThreads() {
6359
6380
  for (const [date, { tokens: tokens2, models }] of dayMap) {
6360
6381
  const inputTokens = Math.round(tokens2 * 0.4);
6361
6382
  const outputTokens = Math.round(tokens2 * 0.6);
6362
- const modelName = [...models][0] ?? "openai-codex";
6363
- const pricing = CODEX_PRICING[modelName] || DEFAULT_PRICING2;
6383
+ const resolvedModel = configModel ?? [...models][0] ?? null;
6384
+ const pricing = resolvedModel && CODEX_PRICING[resolvedModel] || DEFAULT_PRICING2;
6364
6385
  const cost = (inputTokens * pricing.input + outputTokens * pricing.output) / 1e6;
6365
6386
  entries.push({
6366
6387
  date,
@@ -6368,7 +6389,7 @@ async function readSqliteThreads() {
6368
6389
  cost_usd: cost,
6369
6390
  input_tokens: inputTokens,
6370
6391
  output_tokens: outputTokens,
6371
- models: [...models].length > 0 ? [...models] : ["openai-codex"],
6392
+ models: resolvedModel ? [resolvedModel] : ["codex"],
6372
6393
  cost_source: "estimated"
6373
6394
  });
6374
6395
  }
@@ -6439,6 +6460,7 @@ var GEMINI_PRICING = {
6439
6460
  "gemini-3.1-pro": { input: 2, output: 12 },
6440
6461
  "gemini-3.1-flash-lite": { input: 0.25, output: 1.5 },
6441
6462
  "gemini-3-flash": { input: 0.5, output: 3 },
6463
+ "gemini-3-flash-preview": { input: 0.5, output: 3 },
6442
6464
  "gemini-2.0-flash": { input: 0.1, output: 0.4 },
6443
6465
  "gemini-2.0-flash-lite": { input: 0.075, output: 0.3 },
6444
6466
  "gemini-1.5-pro": { input: 1.25, output: 5 },
@@ -6522,6 +6544,73 @@ async function readLocalFiles2() {
6522
6544
  }
6523
6545
  return entries;
6524
6546
  }
6547
+ async function readSessionFiles() {
6548
+ const geminiDir = path8.join(HOME2, ".gemini", "tmp");
6549
+ if (!await dirExists3(geminiDir))
6550
+ return [];
6551
+ const entries = [];
6552
+ try {
6553
+ const userDirs = await fs12.readdir(geminiDir);
6554
+ for (const userDir of userDirs) {
6555
+ const chatsDir = path8.join(geminiDir, userDir, "chats");
6556
+ if (!await dirExists3(chatsDir))
6557
+ continue;
6558
+ let files;
6559
+ try {
6560
+ files = await fs12.readdir(chatsDir);
6561
+ } catch {
6562
+ continue;
6563
+ }
6564
+ const sessionFiles = files.filter((f) => f.startsWith("session-") && f.endsWith(".json"));
6565
+ const dayMap = new Map;
6566
+ for (const file of sessionFiles) {
6567
+ try {
6568
+ const raw = await fs12.readFile(path8.join(chatsDir, file), "utf-8");
6569
+ const session = JSON.parse(raw);
6570
+ let date = null;
6571
+ if (session.startTime) {
6572
+ date = session.startTime.split("T")[0];
6573
+ } else {
6574
+ const match = file.match(/session-(\d{4}-\d{2}-\d{2})/);
6575
+ if (match)
6576
+ date = match[1];
6577
+ }
6578
+ if (!date)
6579
+ continue;
6580
+ if (!dayMap.has(date))
6581
+ dayMap.set(date, { input: 0, output: 0, cached: 0, models: new Set });
6582
+ const day = dayMap.get(date);
6583
+ for (const msg of session.messages ?? []) {
6584
+ if (msg.type !== "gemini" || !msg.tokens)
6585
+ continue;
6586
+ day.input += msg.tokens.input ?? 0;
6587
+ day.output += msg.tokens.output ?? 0;
6588
+ day.cached += msg.tokens.cached ?? 0;
6589
+ if (msg.model)
6590
+ day.models.add(msg.model);
6591
+ }
6592
+ } catch {}
6593
+ }
6594
+ for (const [date, { input, output, cached, models }] of dayMap) {
6595
+ if (input === 0 && output === 0)
6596
+ continue;
6597
+ const modelName = [...models][0];
6598
+ const cost = calculateCost(input, output, modelName);
6599
+ entries.push({
6600
+ date,
6601
+ provider: "gemini",
6602
+ cost_usd: cost,
6603
+ input_tokens: input,
6604
+ output_tokens: output,
6605
+ cache_read_tokens: cached,
6606
+ models: [...models].length > 0 ? [...models] : ["gemini"],
6607
+ cost_source: "estimated"
6608
+ });
6609
+ }
6610
+ }
6611
+ } catch {}
6612
+ return entries;
6613
+ }
6525
6614
  var geminiAdapter = {
6526
6615
  name: "gemini",
6527
6616
  displayName: "Gemini",
@@ -6538,7 +6627,10 @@ var geminiAdapter = {
6538
6627
  return false;
6539
6628
  },
6540
6629
  async read() {
6541
- return readLocalFiles2();
6630
+ const localEntries = await readLocalFiles2();
6631
+ if (localEntries.length > 0)
6632
+ return localEntries;
6633
+ return readSessionFiles();
6542
6634
  }
6543
6635
  };
6544
6636
 
@@ -7118,6 +7210,48 @@ async function seedCommand(opts) {
7118
7210
  console.log();
7119
7211
  }
7120
7212
 
7213
+ // src/commands/cleanup.ts
7214
+ async function cleanupCommand(opts) {
7215
+ banner();
7216
+ const auth = await loadAuth();
7217
+ if (!auth) {
7218
+ error("Not logged in. Run awarts login first.");
7219
+ return;
7220
+ }
7221
+ const spin = spinner("Cleaning up invalid usage data...");
7222
+ spin.start();
7223
+ try {
7224
+ const body = {};
7225
+ if (opts.beforeDate)
7226
+ body.before_date = opts.beforeDate;
7227
+ if (opts.dates)
7228
+ body.dates = opts.dates;
7229
+ const res = await post("/api/usage/cleanup", body);
7230
+ if (!res.ok) {
7231
+ spin.fail("Cleanup failed.");
7232
+ if (res.status === 401) {
7233
+ error("Authentication expired. Run awarts login to re-authenticate.");
7234
+ } else {
7235
+ error(`Server returned status ${res.status}`);
7236
+ }
7237
+ return;
7238
+ }
7239
+ const { deleted, posts_deleted } = res.data;
7240
+ if (deleted === 0 && posts_deleted === 0) {
7241
+ spin.info("No invalid data found — nothing to clean up.");
7242
+ } else {
7243
+ spin.succeed(source_default.green("Cleanup complete!"));
7244
+ console.log();
7245
+ kv("Usage entries deleted", deleted);
7246
+ kv("Posts deleted", posts_deleted);
7247
+ }
7248
+ console.log();
7249
+ } catch (err) {
7250
+ spin.fail("Could not reach the AWARTS server.");
7251
+ error(err instanceof Error ? err.message : String(err));
7252
+ }
7253
+ }
7254
+
7121
7255
  // src/commands/daemon.ts
7122
7256
  async function daemonStartCommand(intervalMs) {
7123
7257
  banner();
@@ -7258,7 +7392,7 @@ async function checkForUpdates() {
7258
7392
 
7259
7393
  // src/index.ts
7260
7394
  var program2 = new Command;
7261
- program2.name("awarts").description("Track your AI coding spend across Claude, Codex, Gemini & Antigravity").version("0.2.5").hook("preAction", () => checkForUpdates());
7395
+ program2.name("awarts").description("Track your AI coding spend across Claude, Codex, Gemini & Antigravity").version("0.2.8").hook("preAction", () => checkForUpdates());
7262
7396
  program2.command("login").description("Authenticate with your AWARTS account via device auth").option("--force", "Re-authenticate even if already logged in").action(async (opts) => {
7263
7397
  try {
7264
7398
  if (opts.force) {
@@ -7299,6 +7433,17 @@ program2.command("seed").description("Generate sample usage data for providers w
7299
7433
  process.exit(1);
7300
7434
  }
7301
7435
  });
7436
+ program2.command("cleanup").description("Remove invalid/old usage data from AWARTS (e.g. entries with wrong dates)").option("--before <date>", "Delete entries before this date (default: 2020-01-01)", "2020-01-01").option("--date <dates...>", "Delete entries for specific dates (YYYY-MM-DD)").action(async (opts) => {
7437
+ try {
7438
+ await cleanupCommand({
7439
+ beforeDate: opts.before,
7440
+ dates: opts.date
7441
+ });
7442
+ } catch (err) {
7443
+ error(err instanceof Error ? err.message : String(err));
7444
+ process.exit(1);
7445
+ }
7446
+ });
7302
7447
  program2.command("status").description("Show auth status, configuration, and detected providers").action(async () => {
7303
7448
  try {
7304
7449
  await statusCommand();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "awarts",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "Track your AI coding across Claude, Codex, Gemini & Antigravity",
5
5
  "type": "module",
6
6
  "license": "MIT",