codeam-cli 1.4.13 → 1.4.14

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 +73 -2
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -115,7 +115,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
115
115
  // package.json
116
116
  var package_default = {
117
117
  name: "codeam-cli",
118
- version: "1.4.13",
118
+ version: "1.4.14",
119
119
  description: "Remote control Claude Code from your mobile device",
120
120
  main: "dist/index.js",
121
121
  bin: {
@@ -1383,6 +1383,19 @@ function post(endpoint, body) {
1383
1383
  req.end();
1384
1384
  });
1385
1385
  }
1386
+ var MODEL_PRICING = {
1387
+ "claude-sonnet-4": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
1388
+ "claude-opus-4": { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75 },
1389
+ "claude-3-5-sonnet": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
1390
+ "claude-3-5-haiku": { input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1 },
1391
+ "claude-3-haiku": { input: 0.25, output: 1.25, cacheRead: 0.03, cacheWrite: 0.3 }
1392
+ };
1393
+ function getPricing(model) {
1394
+ for (const [prefix, pricing] of Object.entries(MODEL_PRICING)) {
1395
+ if (model.startsWith(prefix)) return pricing;
1396
+ }
1397
+ return MODEL_PRICING["claude-sonnet-4"];
1398
+ }
1386
1399
  var HistoryService = class {
1387
1400
  constructor(pluginId, cwd) {
1388
1401
  this.pluginId = pluginId;
@@ -1483,6 +1496,63 @@ var HistoryService = class {
1483
1496
  const percent = Math.min(100, Math.round(inputTokens / total * 100));
1484
1497
  return { used: inputTokens, total, percent, model: lastModel, outputTokens, cacheReadTokens: lastUsage["cache_read_input_tokens"] ?? 0 };
1485
1498
  }
1499
+ /**
1500
+ * Estimate the total API cost for the current month across all projects.
1501
+ * Scans all JSONL files modified this month under ~/.claude/projects/.
1502
+ */
1503
+ getMonthlyEstimatedCost() {
1504
+ const claudeDir = path4.join(os4.homedir(), ".claude", "projects");
1505
+ let projectDirs;
1506
+ try {
1507
+ projectDirs = fs4.readdirSync(claudeDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path4.join(claudeDir, e.name));
1508
+ } catch {
1509
+ return 0;
1510
+ }
1511
+ const now = /* @__PURE__ */ new Date();
1512
+ const monthStart = new Date(now.getFullYear(), now.getMonth(), 1).getTime();
1513
+ let totalCost = 0;
1514
+ for (const projectDir of projectDirs) {
1515
+ let files;
1516
+ try {
1517
+ files = fs4.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
1518
+ try {
1519
+ return fs4.statSync(path4.join(projectDir, f)).mtimeMs >= monthStart;
1520
+ } catch {
1521
+ return false;
1522
+ }
1523
+ });
1524
+ } catch {
1525
+ continue;
1526
+ }
1527
+ for (const file of files) {
1528
+ let raw;
1529
+ try {
1530
+ raw = fs4.readFileSync(path4.join(projectDir, file), "utf8");
1531
+ } catch {
1532
+ continue;
1533
+ }
1534
+ for (const line of raw.split("\n").filter(Boolean)) {
1535
+ try {
1536
+ const record = JSON.parse(line);
1537
+ if (record["type"] !== "assistant") continue;
1538
+ const msg = record["message"];
1539
+ if (!msg || msg["model"] === "<synthetic>") continue;
1540
+ const model = msg["model"] || "";
1541
+ const usage = msg["usage"];
1542
+ if (!usage) continue;
1543
+ const pricing = getPricing(model);
1544
+ const input = usage["input_tokens"] ?? 0;
1545
+ const output = usage["output_tokens"] ?? 0;
1546
+ const cacheRead = usage["cache_read_input_tokens"] ?? 0;
1547
+ const cacheWrite = usage["cache_creation_input_tokens"] ?? 0;
1548
+ totalCost += input / 1e6 * pricing.input + output / 1e6 * pricing.output + cacheRead / 1e6 * pricing.cacheRead + cacheWrite / 1e6 * pricing.cacheWrite;
1549
+ } catch {
1550
+ }
1551
+ }
1552
+ }
1553
+ }
1554
+ return Math.round(totalCost * 100) / 100;
1555
+ }
1486
1556
  /**
1487
1557
  * Read session list from disk and POST it to the API.
1488
1558
  * Called once ~2 s after Claude spawns (non-blocking).
@@ -1622,7 +1692,8 @@ async function start() {
1622
1692
  break;
1623
1693
  case "get_context": {
1624
1694
  const usage = historySvc.getCurrentUsage();
1625
- const result = usage ?? { used: 0, total: 2e5, percent: 0, model: null, outputTokens: 0, cacheReadTokens: 0, error: "No usage data found" };
1695
+ const monthlyCost = historySvc.getMonthlyEstimatedCost();
1696
+ const result = usage ? { ...usage, monthlyCost } : { used: 0, total: 2e5, percent: 0, model: null, outputTokens: 0, cacheReadTokens: 0, monthlyCost, error: "No usage data found" };
1626
1697
  await relay.sendResult(cmd.id, "completed", result);
1627
1698
  break;
1628
1699
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "1.4.13",
3
+ "version": "1.4.14",
4
4
  "description": "Remote control Claude Code from your mobile device",
5
5
  "main": "dist/index.js",
6
6
  "bin": {