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.
- package/dist/index.js +156 -11
- 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.
|
|
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:
|
|
6200
|
-
"openai-codex": { input: 2, output: 8 }
|
|
6210
|
+
"codex-mini": { input: 0.75, output: 3 }
|
|
6201
6211
|
};
|
|
6202
|
-
var DEFAULT_PRICING2 = { input:
|
|
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
|
|
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
|
|
6363
|
-
const pricing = CODEX_PRICING[
|
|
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:
|
|
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
|
-
|
|
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.
|
|
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();
|