@hasna/economy 0.2.11 → 0.2.13

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,5 +1,6 @@
1
1
  import { SqliteAdapter as Database } from '@hasna/cloud';
2
2
  import type { EconomyRequest, EconomySession, EconomyProject, Budget, BudgetStatus, CostSummary, ModelBreakdown, ProjectBreakdown, Period, SessionFilter } from '../types/index.js';
3
+ export declare function getMachineId(): string;
3
4
  export declare function getDataDir(): string;
4
5
  export declare function getDbPath(): string;
5
6
  export declare function openDatabase(dbPath?: string, skipSeed?: boolean): Database;
@@ -8,7 +9,7 @@ export declare function upsertSession(db: Database, session: EconomySession): vo
8
9
  export declare function rollupSession(db: Database, sessionId: string): void;
9
10
  export declare function querySessions(db: Database, filter?: SessionFilter): EconomySession[];
10
11
  export declare function queryTopSessions(db: Database, n?: number, agent?: string): EconomySession[];
11
- export declare function querySummary(db: Database, period: Period): CostSummary;
12
+ export declare function querySummary(db: Database, period: Period, machine?: string): CostSummary;
12
13
  export declare function queryModelBreakdown(db: Database): ModelBreakdown[];
13
14
  export declare function queryProjectBreakdown(db: Database): ProjectBreakdown[];
14
15
  export declare function queryDailyBreakdown(db: Database, days?: number): Array<{
@@ -47,6 +48,14 @@ export declare function getGoalStatuses(db: Database): GoalStatus[];
47
48
  export declare function getIngestState(db: Database, source: string, key: string): string | null;
48
49
  export declare function setIngestState(db: Database, source: string, key: string, value: string): void;
49
50
  export declare function queryRequestsSince(db: Database, since: string): EconomyRequest[];
51
+ export interface MachineInfo {
52
+ machine_id: string;
53
+ sessions: number;
54
+ requests: number;
55
+ total_cost_usd: number;
56
+ last_active: string;
57
+ }
58
+ export declare function listMachines(db: Database): MachineInfo[];
50
59
  export interface DbModelPricing {
51
60
  model: string;
52
61
  input_per_1m: number;
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"pg-migrations.d.ts","sourceRoot":"","sources":["../../src/db/pg-migrations.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,aAAa,EAAE,MAAM,EAmGjC,CAAC"}
1
+ {"version":3,"file":"pg-migrations.d.ts","sourceRoot":"","sources":["../../src/db/pg-migrations.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,aAAa,EAAE,MAAM,EAuGjC,CAAC"}
package/dist/index.js CHANGED
@@ -107,8 +107,17 @@ var init_pricing = __esm(() => {
107
107
  // src/db/database.ts
108
108
  import { SqliteAdapter as Database } from "@hasna/cloud";
109
109
  import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from "fs";
110
+ import { hostname } from "os";
110
111
  import { homedir } from "os";
111
112
  import { join } from "path";
113
+ function getMachineId() {
114
+ if (process.env["ECONOMY_MACHINE_ID"])
115
+ return process.env["ECONOMY_MACHINE_ID"];
116
+ const h = hostname().toLowerCase();
117
+ if (h.startsWith("spark") || h.startsWith("apple"))
118
+ return h.split(".")[0];
119
+ return h.split(".")[0];
120
+ }
112
121
  function getDataDir() {
113
122
  const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir();
114
123
  const newDir = join(home, ".hasna", "economy");
@@ -141,6 +150,7 @@ function openDatabase(dbPath, skipSeed = false) {
141
150
  }
142
151
  const db = new Database(path);
143
152
  db.exec("PRAGMA journal_mode = WAL");
153
+ db.exec("PRAGMA busy_timeout = 5000");
144
154
  db.exec("PRAGMA foreign_keys = ON");
145
155
  initSchema(db);
146
156
  if (!skipSeed) {
@@ -162,7 +172,8 @@ function initSchema(db) {
162
172
  cost_usd REAL NOT NULL DEFAULT 0,
163
173
  duration_ms INTEGER DEFAULT 0,
164
174
  timestamp TEXT NOT NULL,
165
- source_request_id TEXT
175
+ source_request_id TEXT,
176
+ machine_id TEXT DEFAULT ''
166
177
  );
167
178
 
168
179
  CREATE TABLE IF NOT EXISTS sessions (
@@ -174,7 +185,8 @@ function initSchema(db) {
174
185
  ended_at TEXT,
175
186
  total_cost_usd REAL DEFAULT 0,
176
187
  total_tokens INTEGER DEFAULT 0,
177
- request_count INTEGER DEFAULT 0
188
+ request_count INTEGER DEFAULT 0,
189
+ machine_id TEXT DEFAULT ''
178
190
  );
179
191
 
180
192
  CREATE TABLE IF NOT EXISTS projects (
@@ -240,6 +252,15 @@ function initSchema(db) {
240
252
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
241
253
  );
242
254
  `);
255
+ const cols = db.prepare(`PRAGMA table_info(requests)`).all();
256
+ if (!cols.some((c) => c.name === "machine_id")) {
257
+ db.exec(`ALTER TABLE requests ADD COLUMN machine_id TEXT DEFAULT ''`);
258
+ db.exec(`ALTER TABLE sessions ADD COLUMN machine_id TEXT DEFAULT ''`);
259
+ }
260
+ db.exec(`
261
+ CREATE INDEX IF NOT EXISTS idx_requests_machine ON requests(machine_id);
262
+ CREATE INDEX IF NOT EXISTS idx_sessions_machine ON sessions(machine_id);
263
+ `);
243
264
  }
244
265
  function periodWhere(period) {
245
266
  switch (period) {
@@ -278,17 +299,17 @@ function upsertRequest(db, req) {
278
299
  INSERT OR REPLACE INTO requests
279
300
  (id, agent, session_id, model, input_tokens, output_tokens,
280
301
  cache_read_tokens, cache_create_tokens, cost_usd, duration_ms,
281
- timestamp, source_request_id)
282
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
283
- `).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);
302
+ timestamp, source_request_id, machine_id)
303
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
304
+ `).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 ?? "");
284
305
  }
285
306
  function upsertSession(db, session) {
286
307
  db.prepare(`
287
308
  INSERT OR REPLACE INTO sessions
288
309
  (id, agent, project_path, project_name, started_at, ended_at,
289
- total_cost_usd, total_tokens, request_count)
290
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
291
- `).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);
310
+ total_cost_usd, total_tokens, request_count, machine_id)
311
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
312
+ `).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 ?? "");
292
313
  }
293
314
  function rollupSession(db, sessionId) {
294
315
  db.prepare(`
@@ -318,6 +339,10 @@ function querySessions(db, filter = {}) {
318
339
  conditions.push("started_at >= ?");
319
340
  params.push(filter.since);
320
341
  }
342
+ if (filter.machine) {
343
+ conditions.push("machine_id = ?");
344
+ params.push(filter.machine);
345
+ }
321
346
  if (filter.search) {
322
347
  const q = `%${filter.search}%`;
323
348
  conditions.push("(project_name LIKE ? OR agent LIKE ? OR id LIKE ?)");
@@ -336,24 +361,25 @@ function queryTopSessions(db, n = 10, agent) {
336
361
  }
337
362
  return db.prepare(`SELECT * FROM sessions ORDER BY total_cost_usd DESC LIMIT ?`).all(n);
338
363
  }
339
- function querySummary(db, period) {
364
+ function querySummary(db, period, machine) {
340
365
  const rWhere = periodWhere(period);
341
366
  const sWhere = sessionPeriodWhere(period);
367
+ const machineClause = machine ? ` AND machine_id = '${machine.replace(/'/g, "''")}'` : "";
342
368
  const r = db.prepare(`
343
369
  SELECT COALESCE(SUM(cost_usd), 0) as total_usd,
344
370
  COUNT(*) as requests,
345
371
  COALESCE(SUM(input_tokens + output_tokens + cache_read_tokens + cache_create_tokens), 0) as tokens
346
- FROM requests WHERE ${rWhere}
372
+ FROM requests WHERE ${rWhere}${machineClause}
347
373
  `).get();
348
374
  const codexTotals = db.prepare(`
349
375
  SELECT COALESCE(SUM(total_cost_usd), 0) as cost_usd,
350
376
  COALESCE(SUM(total_tokens), 0) as tokens,
351
377
  COUNT(*) as sessions
352
378
  FROM sessions
353
- WHERE ${sWhere}
379
+ WHERE ${sWhere}${machineClause}
354
380
  AND id NOT IN (SELECT DISTINCT session_id FROM requests)
355
381
  `).get();
356
- const sessionCount = db.prepare(`SELECT COUNT(*) as sessions FROM sessions WHERE ${sWhere}`).get();
382
+ const sessionCount = db.prepare(`SELECT COUNT(*) as sessions FROM sessions WHERE ${sWhere}${machineClause}`).get();
357
383
  return {
358
384
  total_usd: r.total_usd + codexTotals.cost_usd,
359
385
  requests: r.requests,
@@ -507,6 +533,20 @@ function setIngestState(db, source, key, value) {
507
533
  function queryRequestsSince(db, since) {
508
534
  return db.prepare(`SELECT * FROM requests WHERE timestamp > ? ORDER BY timestamp ASC`).all(since);
509
535
  }
536
+ function listMachines(db) {
537
+ return db.prepare(`
538
+ SELECT
539
+ s.machine_id,
540
+ COUNT(DISTINCT s.id) as sessions,
541
+ COALESCE((SELECT COUNT(*) FROM requests r WHERE r.machine_id = s.machine_id), 0) as requests,
542
+ COALESCE(SUM(s.total_cost_usd), 0) as total_cost_usd,
543
+ MAX(s.started_at) as last_active
544
+ FROM sessions s
545
+ WHERE s.machine_id != ''
546
+ GROUP BY s.machine_id
547
+ ORDER BY total_cost_usd DESC
548
+ `).all();
549
+ }
510
550
  function upsertModelPricing(db, p) {
511
551
  db.prepare(`
512
552
  INSERT OR REPLACE INTO model_pricing
@@ -769,7 +809,8 @@ import { join as join3, basename } from "path";
769
809
  function autoDetectProject(cwd, projects) {
770
810
  return projects.find((p) => cwd === p.path || cwd.startsWith(p.path + "/"));
771
811
  }
772
- var PROJECTS_DIR = join3(homedir2(), ".claude", "projects");
812
+ var CLAUDE_PROJECTS_DIR = join3(homedir2(), ".claude", "projects");
813
+ var TAKUMI_PROJECTS_DIR = join3(homedir2(), ".takumi", "projects");
773
814
  function dirNameToPath(dirName) {
774
815
  return dirName.replace(/^-/, "/").replace(/-/g, "/").replace(/\/\//g, "/-");
775
816
  }
@@ -789,29 +830,36 @@ function collectJsonlFiles(projectDir) {
789
830
  return files;
790
831
  }
791
832
  async function ingestClaude(db, verbose = false, _telemetryDir) {
792
- if (!existsSync3(PROJECTS_DIR)) {
833
+ return ingestJsonlProjects(db, CLAUDE_PROJECTS_DIR, "claude", verbose);
834
+ }
835
+ async function ingestTakumi(db, verbose = false) {
836
+ return ingestJsonlProjects(db, TAKUMI_PROJECTS_DIR, "takumi", verbose);
837
+ }
838
+ async function ingestJsonlProjects(db, projectsDir, agentName, verbose = false) {
839
+ if (!existsSync3(projectsDir)) {
793
840
  if (verbose)
794
- console.log("Claude projects dir not found:", PROJECTS_DIR);
841
+ console.log(`${agentName} projects dir not found:`, projectsDir);
795
842
  return { files: 0, requests: 0, sessions: 0 };
796
843
  }
844
+ const machineId = getMachineId();
797
845
  let totalFiles = 0;
798
846
  let totalRequests = 0;
799
847
  const touchedSessions = new Set;
800
848
  const registeredProjects = db.prepare(`SELECT path, name FROM projects ORDER BY LENGTH(path) DESC`).all();
801
- const projectDirs = readdirSync2(PROJECTS_DIR, { withFileTypes: true }).filter((d) => d.isDirectory());
849
+ const projectDirs = readdirSync2(projectsDir, { withFileTypes: true }).filter((d) => d.isDirectory());
802
850
  for (const projectDirEntry of projectDirs) {
803
- const projectDirPath = join3(PROJECTS_DIR, projectDirEntry.name);
851
+ const projectDirPath = join3(projectsDir, projectDirEntry.name);
804
852
  const projectPath = dirNameToPath(projectDirEntry.name);
805
853
  const jsonlFiles = collectJsonlFiles(projectDirPath);
806
854
  for (const filePath of jsonlFiles) {
807
- const stateKey = filePath.replace(PROJECTS_DIR, "");
855
+ const stateKey = filePath.replace(projectsDir, "");
808
856
  let fileMtime = "0";
809
857
  try {
810
858
  fileMtime = statSync2(filePath).mtimeMs.toString();
811
859
  } catch {
812
860
  continue;
813
861
  }
814
- const processed = getIngestState(db, "claude", stateKey);
862
+ const processed = getIngestState(db, agentName, stateKey);
815
863
  if (processed === fileMtime)
816
864
  continue;
817
865
  let lines;
@@ -852,10 +900,10 @@ async function ingestClaude(db, verbose = false, _telemetryDir) {
852
900
  if (inputTokens + outputTokens + cacheWriteTokens === 0)
853
901
  continue;
854
902
  const costUsd = computeCostFromDb(db, model, inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens);
855
- const reqId = `claude-${sessionId}-${timestamp}`;
903
+ const reqId = `${agentName}-${sessionId}-${timestamp}`;
856
904
  upsertRequest(db, {
857
905
  id: reqId,
858
- agent: "claude",
906
+ agent: agentName,
859
907
  session_id: sessionId,
860
908
  model,
861
909
  input_tokens: inputTokens,
@@ -865,7 +913,8 @@ async function ingestClaude(db, verbose = false, _telemetryDir) {
865
913
  cost_usd: costUsd,
866
914
  duration_ms: 0,
867
915
  timestamp,
868
- source_request_id: reqId
916
+ source_request_id: reqId,
917
+ machine_id: machineId
869
918
  });
870
919
  if (!touchedSessions.has(sessionId)) {
871
920
  const existing = db.prepare(`SELECT id FROM sessions WHERE id = ?`).get(sessionId);
@@ -874,14 +923,15 @@ async function ingestClaude(db, verbose = false, _telemetryDir) {
874
923
  const detectedProject = autoDetectProject(effectiveCwd, registeredProjects);
875
924
  const session = {
876
925
  id: sessionId,
877
- agent: "claude",
926
+ agent: agentName,
878
927
  project_path: detectedProject ? detectedProject.path : effectiveCwd,
879
928
  project_name: detectedProject ? detectedProject.name : "",
880
929
  started_at: timestamp,
881
930
  ended_at: null,
882
931
  total_cost_usd: 0,
883
932
  total_tokens: 0,
884
- request_count: 0
933
+ request_count: 0,
934
+ machine_id: machineId
885
935
  };
886
936
  upsertSession(db, session);
887
937
  }
@@ -889,7 +939,7 @@ async function ingestClaude(db, verbose = false, _telemetryDir) {
889
939
  }
890
940
  totalRequests++;
891
941
  }
892
- setIngestState(db, "claude", stateKey, fileMtime);
942
+ setIngestState(db, agentName, stateKey, fileMtime);
893
943
  totalFiles++;
894
944
  }
895
945
  }
@@ -923,6 +973,7 @@ async function ingestCodex(db, verbose = false) {
923
973
  console.log("Codex DB not found:", CODEX_DB_PATH);
924
974
  return { sessions: 0 };
925
975
  }
976
+ const machineId = getMachineId();
926
977
  let codexDb = null;
927
978
  let ingested = 0;
928
979
  try {
@@ -947,7 +998,8 @@ async function ingestCodex(db, verbose = false) {
947
998
  ended_at: endedAt,
948
999
  total_cost_usd: costUsd,
949
1000
  total_tokens: thread.tokens_used,
950
- request_count: 1
1001
+ request_count: 1,
1002
+ machine_id: machineId
951
1003
  });
952
1004
  setIngestState(db, "codex", stateKey, "done");
953
1005
  ingested++;
@@ -982,14 +1034,17 @@ export {
982
1034
  normalizeModelName,
983
1035
  listProjects,
984
1036
  listModelPricing,
1037
+ listMachines,
985
1038
  listGoals,
986
1039
  listBudgets,
1040
+ ingestTakumi,
987
1041
  ingestCodex,
988
1042
  ingestClaude,
989
1043
  getProject,
990
1044
  getPricingFromDb,
991
1045
  getPricing,
992
1046
  getModelPricing,
1047
+ getMachineId,
993
1048
  getIngestState,
994
1049
  getGoalStatuses,
995
1050
  getDbPath,
@@ -4,4 +4,9 @@ export declare function ingestClaude(db: Database, verbose?: boolean, _telemetry
4
4
  requests: number;
5
5
  sessions: number;
6
6
  }>;
7
+ export declare function ingestTakumi(db: Database, verbose?: boolean): Promise<{
8
+ files: number;
9
+ requests: number;
10
+ sessions: number;
11
+ }>;
7
12
  //# sourceMappingURL=claude.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/ingest/claude.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AA2D7D,wBAAsB,YAAY,CAChC,EAAE,EAAE,QAAQ,EACZ,OAAO,UAAQ,EACf,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA2HhE"}
1
+ {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/ingest/claude.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AA4D7D,wBAAsB,YAAY,CAChC,EAAE,EAAE,QAAQ,EACZ,OAAO,UAAQ,EACf,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAEhE;AAED,wBAAsB,YAAY,CAChC,EAAE,EAAE,QAAQ,EACZ,OAAO,UAAQ,GACd,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAEhE"}
@@ -1 +1 @@
1
- {"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../src/ingest/codex.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AAkB7D,iBAAS,cAAc,IAAI,MAAM,CAShC;AAED,wBAAsB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAyD9F;AAED,OAAO,EAAE,cAAc,EAAE,CAAA"}
1
+ {"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../src/ingest/codex.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AAkB7D,iBAAS,cAAc,IAAI,MAAM,CAShC;AAED,wBAAsB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA2D9F;AAED,OAAO,EAAE,cAAc,EAAE,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/ingest/gemini.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AA0B7D,wBAAsB,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA2EjG"}
1
+ {"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/ingest/gemini.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AA0B7D,wBAAsB,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA6EjG"}