@hasna/economy 0.2.10 → 0.2.12

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.
@@ -1,2 +1,3 @@
1
+ #!/usr/bin/env bun
1
2
  export {};
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env bun
1
2
  // @bun
2
3
  var __defProp = Object.defineProperty;
3
4
  var __returnValue = (v) => v;
@@ -108,8 +109,17 @@ var init_pricing = __esm(() => {
108
109
  // src/db/database.ts
109
110
  import { SqliteAdapter as Database } from "@hasna/cloud";
110
111
  import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from "fs";
112
+ import { hostname } from "os";
111
113
  import { homedir } from "os";
112
114
  import { join } from "path";
115
+ function getMachineId() {
116
+ if (process.env["ECONOMY_MACHINE_ID"])
117
+ return process.env["ECONOMY_MACHINE_ID"];
118
+ const h = hostname().toLowerCase();
119
+ if (h.startsWith("spark") || h.startsWith("apple"))
120
+ return h.split(".")[0];
121
+ return h.split(".")[0];
122
+ }
113
123
  function getDataDir() {
114
124
  const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir();
115
125
  const newDir = join(home, ".hasna", "economy");
@@ -142,6 +152,7 @@ function openDatabase(dbPath, skipSeed = false) {
142
152
  }
143
153
  const db = new Database(path);
144
154
  db.exec("PRAGMA journal_mode = WAL");
155
+ db.exec("PRAGMA busy_timeout = 5000");
145
156
  db.exec("PRAGMA foreign_keys = ON");
146
157
  initSchema(db);
147
158
  if (!skipSeed) {
@@ -163,7 +174,8 @@ function initSchema(db) {
163
174
  cost_usd REAL NOT NULL DEFAULT 0,
164
175
  duration_ms INTEGER DEFAULT 0,
165
176
  timestamp TEXT NOT NULL,
166
- source_request_id TEXT
177
+ source_request_id TEXT,
178
+ machine_id TEXT DEFAULT ''
167
179
  );
168
180
 
169
181
  CREATE TABLE IF NOT EXISTS sessions (
@@ -175,7 +187,8 @@ function initSchema(db) {
175
187
  ended_at TEXT,
176
188
  total_cost_usd REAL DEFAULT 0,
177
189
  total_tokens INTEGER DEFAULT 0,
178
- request_count INTEGER DEFAULT 0
190
+ request_count INTEGER DEFAULT 0,
191
+ machine_id TEXT DEFAULT ''
179
192
  );
180
193
 
181
194
  CREATE TABLE IF NOT EXISTS projects (
@@ -241,6 +254,15 @@ function initSchema(db) {
241
254
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
242
255
  );
243
256
  `);
257
+ const cols = db.prepare(`PRAGMA table_info(requests)`).all();
258
+ if (!cols.some((c) => c.name === "machine_id")) {
259
+ db.exec(`ALTER TABLE requests ADD COLUMN machine_id TEXT DEFAULT ''`);
260
+ db.exec(`ALTER TABLE sessions ADD COLUMN machine_id TEXT DEFAULT ''`);
261
+ }
262
+ db.exec(`
263
+ CREATE INDEX IF NOT EXISTS idx_requests_machine ON requests(machine_id);
264
+ CREATE INDEX IF NOT EXISTS idx_sessions_machine ON sessions(machine_id);
265
+ `);
244
266
  }
245
267
  function periodWhere(period) {
246
268
  switch (period) {
@@ -279,17 +301,17 @@ function upsertRequest(db, req) {
279
301
  INSERT OR REPLACE INTO requests
280
302
  (id, agent, session_id, model, input_tokens, output_tokens,
281
303
  cache_read_tokens, cache_create_tokens, cost_usd, duration_ms,
282
- timestamp, source_request_id)
283
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
284
- `).run(req.id, req.agent, req.session_id, req.model, req.input_tokens, req.output_tokens, req.cache_read_tokens, req.cache_create_tokens, req.cost_usd, req.duration_ms, req.timestamp, req.source_request_id);
304
+ timestamp, source_request_id, machine_id)
305
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
306
+ `).run(req.id, req.agent, req.session_id, req.model, req.input_tokens, req.output_tokens, req.cache_read_tokens, req.cache_create_tokens, req.cost_usd, req.duration_ms, req.timestamp, req.source_request_id, req.machine_id ?? "");
285
307
  }
286
308
  function upsertSession(db, session) {
287
309
  db.prepare(`
288
310
  INSERT OR REPLACE INTO sessions
289
311
  (id, agent, project_path, project_name, started_at, ended_at,
290
- total_cost_usd, total_tokens, request_count)
291
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
292
- `).run(session.id, session.agent, session.project_path, session.project_name, session.started_at, session.ended_at ?? null, session.total_cost_usd, session.total_tokens, session.request_count);
312
+ total_cost_usd, total_tokens, request_count, machine_id)
313
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
314
+ `).run(session.id, session.agent, session.project_path, session.project_name, session.started_at, session.ended_at ?? null, session.total_cost_usd, session.total_tokens, session.request_count, session.machine_id ?? "");
293
315
  }
294
316
  function rollupSession(db, sessionId) {
295
317
  db.prepare(`
@@ -319,6 +341,10 @@ function querySessions(db, filter = {}) {
319
341
  conditions.push("started_at >= ?");
320
342
  params.push(filter.since);
321
343
  }
344
+ if (filter.machine) {
345
+ conditions.push("machine_id = ?");
346
+ params.push(filter.machine);
347
+ }
322
348
  if (filter.search) {
323
349
  const q = `%${filter.search}%`;
324
350
  conditions.push("(project_name LIKE ? OR agent LIKE ? OR id LIKE ?)");
@@ -337,24 +363,25 @@ function queryTopSessions(db, n = 10, agent) {
337
363
  }
338
364
  return db.prepare(`SELECT * FROM sessions ORDER BY total_cost_usd DESC LIMIT ?`).all(n);
339
365
  }
340
- function querySummary(db, period) {
366
+ function querySummary(db, period, machine) {
341
367
  const rWhere = periodWhere(period);
342
368
  const sWhere = sessionPeriodWhere(period);
369
+ const machineClause = machine ? ` AND machine_id = '${machine.replace(/'/g, "''")}'` : "";
343
370
  const r = db.prepare(`
344
371
  SELECT COALESCE(SUM(cost_usd), 0) as total_usd,
345
372
  COUNT(*) as requests,
346
373
  COALESCE(SUM(input_tokens + output_tokens + cache_read_tokens + cache_create_tokens), 0) as tokens
347
- FROM requests WHERE ${rWhere}
374
+ FROM requests WHERE ${rWhere}${machineClause}
348
375
  `).get();
349
376
  const codexTotals = db.prepare(`
350
377
  SELECT COALESCE(SUM(total_cost_usd), 0) as cost_usd,
351
378
  COALESCE(SUM(total_tokens), 0) as tokens,
352
379
  COUNT(*) as sessions
353
380
  FROM sessions
354
- WHERE ${sWhere}
381
+ WHERE ${sWhere}${machineClause}
355
382
  AND id NOT IN (SELECT DISTINCT session_id FROM requests)
356
383
  `).get();
357
- const sessionCount = db.prepare(`SELECT COUNT(*) as sessions FROM sessions WHERE ${sWhere}`).get();
384
+ const sessionCount = db.prepare(`SELECT COUNT(*) as sessions FROM sessions WHERE ${sWhere}${machineClause}`).get();
358
385
  return {
359
386
  total_usd: r.total_usd + codexTotals.cost_usd,
360
387
  requests: r.requests,
@@ -499,6 +526,20 @@ function getIngestState(db, source, key) {
499
526
  function setIngestState(db, source, key, value) {
500
527
  db.prepare(`INSERT OR REPLACE INTO ingest_state (source, key, value) VALUES (?, ?, ?)`).run(source, key, value);
501
528
  }
529
+ function listMachines(db) {
530
+ return db.prepare(`
531
+ SELECT
532
+ s.machine_id,
533
+ COUNT(DISTINCT s.id) as sessions,
534
+ COALESCE((SELECT COUNT(*) FROM requests r WHERE r.machine_id = s.machine_id), 0) as requests,
535
+ COALESCE(SUM(s.total_cost_usd), 0) as total_cost_usd,
536
+ MAX(s.started_at) as last_active
537
+ FROM sessions s
538
+ WHERE s.machine_id != ''
539
+ GROUP BY s.machine_id
540
+ ORDER BY total_cost_usd DESC
541
+ `).all();
542
+ }
502
543
  function upsertModelPricing(db, p) {
503
544
  db.prepare(`
504
545
  INSERT OR REPLACE INTO model_pricing
@@ -570,6 +611,7 @@ async function ingestClaude(db, verbose = false, _telemetryDir) {
570
611
  console.log("Claude projects dir not found:", PROJECTS_DIR);
571
612
  return { files: 0, requests: 0, sessions: 0 };
572
613
  }
614
+ const machineId = getMachineId();
573
615
  let totalFiles = 0;
574
616
  let totalRequests = 0;
575
617
  const touchedSessions = new Set;
@@ -641,7 +683,8 @@ async function ingestClaude(db, verbose = false, _telemetryDir) {
641
683
  cost_usd: costUsd,
642
684
  duration_ms: 0,
643
685
  timestamp,
644
- source_request_id: reqId
686
+ source_request_id: reqId,
687
+ machine_id: machineId
645
688
  });
646
689
  if (!touchedSessions.has(sessionId)) {
647
690
  const existing = db.prepare(`SELECT id FROM sessions WHERE id = ?`).get(sessionId);
@@ -657,7 +700,8 @@ async function ingestClaude(db, verbose = false, _telemetryDir) {
657
700
  ended_at: null,
658
701
  total_cost_usd: 0,
659
702
  total_tokens: 0,
660
- request_count: 0
703
+ request_count: 0,
704
+ machine_id: machineId
661
705
  };
662
706
  upsertSession(db, session);
663
707
  }
@@ -680,7 +724,7 @@ init_database();
680
724
  import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
681
725
  import { homedir as homedir3 } from "os";
682
726
  import { join as join3, basename as basename2 } from "path";
683
- import { Database as Database2 } from "bun:sqlite";
727
+ import { Database as BunDatabase } from "bun:sqlite";
684
728
  var CODEX_DB_PATH = join3(homedir3(), ".codex", "state_5.sqlite");
685
729
  var CODEX_CONFIG_PATH = join3(homedir3(), ".codex", "config.toml");
686
730
  async function ingestCodex(db, verbose = false) {
@@ -689,10 +733,11 @@ async function ingestCodex(db, verbose = false) {
689
733
  console.log("Codex DB not found:", CODEX_DB_PATH);
690
734
  return { sessions: 0 };
691
735
  }
736
+ const machineId = getMachineId();
692
737
  let codexDb = null;
693
738
  let ingested = 0;
694
739
  try {
695
- codexDb = new Database2(CODEX_DB_PATH, { readonly: true });
740
+ codexDb = new BunDatabase(CODEX_DB_PATH, { readonly: true });
696
741
  const threads = codexDb.prepare(`SELECT id, cwd, created_at, updated_at, tokens_used, title FROM threads WHERE tokens_used > 0`).all();
697
742
  for (const thread of threads) {
698
743
  const stateKey = thread.id;
@@ -713,7 +758,8 @@ async function ingestCodex(db, verbose = false) {
713
758
  ended_at: endedAt,
714
759
  total_cost_usd: costUsd,
715
760
  total_tokens: thread.tokens_used,
716
- request_count: 1
761
+ request_count: 1,
762
+ machine_id: machineId
717
763
  });
718
764
  setIngestState(db, "codex", stateKey, "done");
719
765
  ingested++;
@@ -726,6 +772,85 @@ async function ingestCodex(db, verbose = false) {
726
772
  return { sessions: ingested };
727
773
  }
728
774
 
775
+ // src/ingest/gemini.ts
776
+ init_database();
777
+ import { readdirSync as readdirSync3, readFileSync as readFileSync3, existsSync as existsSync4, statSync as statSync3 } from "fs";
778
+ import { homedir as homedir4 } from "os";
779
+ import { join as join4 } from "path";
780
+ var GEMINI_TMP_DIR = join4(homedir4(), ".gemini", "tmp");
781
+ async function ingestGemini(db, verbose) {
782
+ if (!existsSync4(GEMINI_TMP_DIR)) {
783
+ if (verbose)
784
+ console.log("Gemini tmp dir not found:", GEMINI_TMP_DIR);
785
+ return { sessions: 0 };
786
+ }
787
+ const machineId = getMachineId();
788
+ let totalSessions = 0;
789
+ const touchedSessions = new Set;
790
+ let projectHashDirs = [];
791
+ try {
792
+ projectHashDirs = readdirSync3(GEMINI_TMP_DIR, { withFileTypes: true }).filter((d) => d.isDirectory() && /^[0-9a-f]{64}$/.test(d.name)).map((d) => join4(GEMINI_TMP_DIR, d.name));
793
+ } catch {
794
+ return { sessions: 0 };
795
+ }
796
+ for (const projectDir of projectHashDirs) {
797
+ const chatsDir = join4(projectDir, "chats");
798
+ if (!existsSync4(chatsDir))
799
+ continue;
800
+ let chatFiles = [];
801
+ try {
802
+ chatFiles = readdirSync3(chatsDir).filter((f) => f.endsWith(".json")).map((f) => join4(chatsDir, f));
803
+ } catch {
804
+ continue;
805
+ }
806
+ for (const filePath of chatFiles) {
807
+ const stateKey = filePath.replace(homedir4(), "~");
808
+ let fileMtime = "0";
809
+ try {
810
+ fileMtime = statSync3(filePath).mtimeMs.toString();
811
+ } catch {
812
+ continue;
813
+ }
814
+ const processed = getIngestState(db, "gemini", stateKey);
815
+ if (processed === fileMtime)
816
+ continue;
817
+ let chatData;
818
+ try {
819
+ chatData = JSON.parse(readFileSync3(filePath, "utf-8"));
820
+ } catch {
821
+ continue;
822
+ }
823
+ const sessionId = chatData.sessionId;
824
+ if (!sessionId)
825
+ continue;
826
+ const startTime = chatData.startTime ?? new Date().toISOString();
827
+ const existing = db.prepare(`SELECT id FROM sessions WHERE id = ?`).get(sessionId);
828
+ if (!existing) {
829
+ const session = {
830
+ id: sessionId,
831
+ agent: "gemini",
832
+ project_path: "",
833
+ project_name: "",
834
+ started_at: startTime,
835
+ ended_at: chatData.lastUpdated ?? null,
836
+ total_cost_usd: 0,
837
+ total_tokens: 0,
838
+ request_count: 0,
839
+ machine_id: machineId
840
+ };
841
+ upsertSession(db, session);
842
+ touchedSessions.add(sessionId);
843
+ totalSessions++;
844
+ }
845
+ setIngestState(db, "gemini", stateKey, fileMtime);
846
+ }
847
+ }
848
+ for (const sessionId of touchedSessions) {
849
+ rollupSession(db, sessionId);
850
+ }
851
+ return { sessions: totalSessions };
852
+ }
853
+
729
854
  // src/server/serve.ts
730
855
  init_pricing();
731
856
  import { randomUUID } from "crypto";
@@ -746,6 +871,20 @@ function ok(data, meta) {
746
871
  function err(message, status = 400) {
747
872
  return json({ error: message }, status);
748
873
  }
874
+ function normalizeBudgetPeriod(value) {
875
+ switch (value) {
876
+ case "day":
877
+ case "daily":
878
+ return "daily";
879
+ case "week":
880
+ case "weekly":
881
+ return "weekly";
882
+ case "month":
883
+ case "monthly":
884
+ default:
885
+ return "monthly";
886
+ }
887
+ }
749
888
  function applyFields(obj, fields) {
750
889
  if (!fields || fields.length === 0)
751
890
  return obj;
@@ -762,7 +901,11 @@ function createHandler(db) {
762
901
  return ok({ status: "ok", ts: new Date().toISOString() });
763
902
  if (path === "/api/summary" && method === "GET") {
764
903
  const period = url.searchParams.get("period") ?? "today";
765
- return ok(querySummary(db, period));
904
+ const machine = url.searchParams.get("machine") ?? undefined;
905
+ return ok(querySummary(db, period, machine));
906
+ }
907
+ if (path === "/api/machines" && method === "GET") {
908
+ return ok(listMachines(db), { current_machine: getMachineId() });
766
909
  }
767
910
  if (path === "/api/daily" && method === "GET") {
768
911
  const days = Number(url.searchParams.get("days") ?? 30);
@@ -771,12 +914,22 @@ function createHandler(db) {
771
914
  if (path === "/api/sessions" && method === "GET") {
772
915
  const agent = url.searchParams.get("agent");
773
916
  const project = url.searchParams.get("project") ?? undefined;
917
+ const search = url.searchParams.get("search") ?? undefined;
918
+ const machine = url.searchParams.get("machine") ?? undefined;
774
919
  const limit = Number(url.searchParams.get("limit") ?? 50);
775
920
  const offset = Number(url.searchParams.get("offset") ?? 0);
776
921
  const since = url.searchParams.get("since") ?? undefined;
777
922
  const fieldsParam = url.searchParams.get("fields");
778
923
  const fields = fieldsParam ? fieldsParam.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
779
- const sessions = querySessions(db, { agent: agent ?? undefined, project, limit, offset, since });
924
+ const sessions = querySessions(db, {
925
+ agent: agent ?? undefined,
926
+ project,
927
+ search,
928
+ machine,
929
+ limit,
930
+ offset,
931
+ since
932
+ });
780
933
  return ok(fields ? sessions.map((s) => applyFields(s, fields)) : sessions, { limit, offset });
781
934
  }
782
935
  if (path === "/api/top" && method === "GET") {
@@ -804,7 +957,7 @@ function createHandler(db) {
804
957
  id: randomUUID(),
805
958
  project_path: body["project_path"] ?? null,
806
959
  agent: body["agent"] ?? null,
807
- period: body["period"] ?? "monthly",
960
+ period: normalizeBudgetPeriod(body["period"]),
808
961
  limit_usd: Number(body["limit_usd"]),
809
962
  alert_at_percent: Number(body["alert_at_percent"] ?? 80),
810
963
  created_at: now,
@@ -867,6 +1020,8 @@ function createHandler(db) {
867
1020
  results["claude"] = await ingestClaude(db);
868
1021
  if (sources === "all" || sources === "codex")
869
1022
  results["codex"] = await ingestCodex(db);
1023
+ if (sources === "all" || sources === "gemini")
1024
+ results["gemini"] = await ingestGemini(db);
870
1025
  return ok(results);
871
1026
  }
872
1027
  const sessionRequestsMatch = path.match(/^\/api\/sessions\/([^/]+)\/requests$/);
@@ -916,15 +1071,15 @@ function startServer(port = 3456) {
916
1071
  return apiHandler(req);
917
1072
  }
918
1073
  try {
919
- const { existsSync: existsSync4 } = await import("fs");
920
- if (existsSync4(dashboardDir)) {
1074
+ const { existsSync: existsSync5 } = await import("fs");
1075
+ if (existsSync5(dashboardDir)) {
921
1076
  let filePath = url.pathname === "/" ? "/index.html" : url.pathname;
922
1077
  const fullPath = dashboardDir + filePath;
923
- if (existsSync4(fullPath)) {
1078
+ if (existsSync5(fullPath)) {
924
1079
  return new Response(Bun.file(fullPath));
925
1080
  }
926
1081
  const indexPath = dashboardDir + "/index.html";
927
- if (existsSync4(indexPath)) {
1082
+ if (existsSync5(indexPath)) {
928
1083
  return new Response(Bun.file(indexPath));
929
1084
  }
930
1085
  }
@@ -935,6 +1090,62 @@ function startServer(port = 3456) {
935
1090
  console.log(`economy-serve listening on http://localhost:${port}`);
936
1091
  }
937
1092
 
1093
+ // src/lib/package-metadata.ts
1094
+ import { readFileSync as readFileSync4 } from "fs";
1095
+ var cachedMetadata = null;
1096
+ function getPackageMetadata() {
1097
+ if (cachedMetadata)
1098
+ return cachedMetadata;
1099
+ const raw = readFileSync4(new URL("../../package.json", import.meta.url), "utf8");
1100
+ const parsed = JSON.parse(raw);
1101
+ cachedMetadata = {
1102
+ name: parsed.name ?? "@hasna/economy",
1103
+ version: parsed.version ?? "0.0.0"
1104
+ };
1105
+ return cachedMetadata;
1106
+ }
1107
+ var packageMetadata = getPackageMetadata();
1108
+
938
1109
  // src/server/index.ts
939
- var port = Number(process.env["ECONOMY_PORT"] ?? 3456);
940
- startServer(port);
1110
+ function printHelp() {
1111
+ console.log(`Usage: economy-serve [options]
1112
+
1113
+ REST API server for ${packageMetadata.name}
1114
+
1115
+ Options:
1116
+ -p, --port <port> Port to bind (default: ECONOMY_PORT or 3456)
1117
+ -V, --version output the version number
1118
+ -h, --help display help for command`);
1119
+ }
1120
+ function resolvePort(argv) {
1121
+ for (let i = 0;i < argv.length; i++) {
1122
+ const arg = argv[i];
1123
+ if ((arg === "--port" || arg === "-p") && argv[i + 1]) {
1124
+ const value2 = Number(argv[i + 1]);
1125
+ if (!Number.isFinite(value2) || value2 <= 0) {
1126
+ throw new Error(`Invalid port: ${argv[i + 1]}`);
1127
+ }
1128
+ return value2;
1129
+ }
1130
+ }
1131
+ const value = Number(process.env["ECONOMY_PORT"] ?? 3456);
1132
+ if (!Number.isFinite(value) || value <= 0) {
1133
+ throw new Error(`Invalid ECONOMY_PORT: ${process.env["ECONOMY_PORT"]}`);
1134
+ }
1135
+ return value;
1136
+ }
1137
+ var args = process.argv.slice(2);
1138
+ if (args.includes("--help") || args.includes("-h")) {
1139
+ printHelp();
1140
+ process.exit(0);
1141
+ }
1142
+ if (args.includes("--version") || args.includes("-V")) {
1143
+ console.log(packageMetadata.version);
1144
+ process.exit(0);
1145
+ }
1146
+ try {
1147
+ startServer(resolvePort(args));
1148
+ } catch (error) {
1149
+ console.error(error instanceof Error ? error.message : String(error));
1150
+ process.exit(1);
1151
+ }
@@ -1,4 +1,4 @@
1
- import type { Database } from 'bun:sqlite';
1
+ import type { SqliteAdapter as Database } from '@hasna/cloud';
2
2
  export declare function createHandler(db: Database): (req: Request) => Promise<Response>;
3
3
  export declare function startServer(port?: number): void;
4
4
  //# sourceMappingURL=serve.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/server/serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AA2C1C,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,IACV,KAAK,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CA+K/D;AAED,wBAAgB,WAAW,CAAC,IAAI,SAAO,GAAG,IAAI,CAuC7C"}
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/server/serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AA4D7D,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,IACV,KAAK,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CAgM/D;AAED,wBAAgB,WAAW,CAAC,IAAI,SAAO,GAAG,IAAI,CAuC7C"}
@@ -13,6 +13,7 @@ export interface EconomyRequest {
13
13
  duration_ms: number;
14
14
  timestamp: string;
15
15
  source_request_id: string;
16
+ machine_id?: string;
16
17
  }
17
18
  export interface EconomySession {
18
19
  id: string;
@@ -24,6 +25,7 @@ export interface EconomySession {
24
25
  total_cost_usd: number;
25
26
  total_tokens: number;
26
27
  request_count: number;
28
+ machine_id?: string;
27
29
  }
28
30
  export interface EconomyProject {
29
31
  id: string;
@@ -97,5 +99,6 @@ export interface SessionFilter {
97
99
  offset?: number;
98
100
  since?: string;
99
101
  search?: string;
102
+ machine?: string;
100
103
  }
101
104
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hasna/economy",
3
- "version": "0.2.10",
4
- "description": "AI coding cost tracker \u2014 CLI + MCP server + REST API + web dashboard for Claude Code, Codex, and Gemini",
3
+ "version": "0.2.12",
4
+ "description": "AI coding cost tracker CLI + MCP server + REST API + web dashboard for Claude Code, Codex, and Gemini",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -53,14 +53,15 @@
53
53
  "access": "public"
54
54
  },
55
55
  "dependencies": {
56
- "@hasna/cloud": "^0.1.0",
56
+ "@hasna/cloud": "^0.1.24",
57
57
  "@modelcontextprotocol/sdk": "^1.12.1",
58
58
  "chalk": "^5.4.1",
59
- "commander": "^13.1.0"
59
+ "commander": "^13.1.0",
60
+ "zod": "^3.24.2"
60
61
  },
61
62
  "devDependencies": {
62
63
  "@types/bun": "latest",
63
64
  "bun-types": "latest",
64
65
  "typescript": "^5.7.2"
65
66
  }
66
- }
67
+ }