@hasna/economy 0.3.6 → 0.3.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 (92) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +73 -60
  3. package/dashboard/README.md +17 -64
  4. package/dist/cli/commands/extras.d.ts.map +1 -1
  5. package/dist/cli/commands/todos.d.ts +3 -0
  6. package/dist/cli/commands/todos.d.ts.map +1 -1
  7. package/dist/cli/index.js +914 -199
  8. package/dist/db/cloud.d.ts +3 -4
  9. package/dist/db/cloud.d.ts.map +1 -1
  10. package/dist/db/database.d.ts +10 -3
  11. package/dist/db/database.d.ts.map +1 -1
  12. package/dist/db/dialect.d.ts +13 -0
  13. package/dist/db/dialect.d.ts.map +1 -0
  14. package/dist/db/pg-migrate.d.ts +30 -0
  15. package/dist/db/pg-migrate.d.ts.map +1 -0
  16. package/dist/db/pg-migrations.d.ts.map +1 -1
  17. package/dist/db/sqlite-adapter.d.ts +36 -0
  18. package/dist/db/sqlite-adapter.d.ts.map +1 -0
  19. package/dist/db/sync-pg.d.ts +1 -1
  20. package/dist/db/sync-pg.d.ts.map +1 -1
  21. package/dist/index.js +309 -59
  22. package/dist/ingest/billing.d.ts +1 -1
  23. package/dist/ingest/billing.d.ts.map +1 -1
  24. package/dist/ingest/claude-quota.d.ts +1 -1
  25. package/dist/ingest/claude-quota.d.ts.map +1 -1
  26. package/dist/ingest/claude.d.ts +1 -1
  27. package/dist/ingest/claude.d.ts.map +1 -1
  28. package/dist/ingest/codex-quota.d.ts +1 -1
  29. package/dist/ingest/codex-quota.d.ts.map +1 -1
  30. package/dist/ingest/codex.d.ts +1 -1
  31. package/dist/ingest/codex.d.ts.map +1 -1
  32. package/dist/ingest/cursor.d.ts +1 -1
  33. package/dist/ingest/cursor.d.ts.map +1 -1
  34. package/dist/ingest/gemini.d.ts +1 -1
  35. package/dist/ingest/gemini.d.ts.map +1 -1
  36. package/dist/ingest/hermes.d.ts +1 -1
  37. package/dist/ingest/hermes.d.ts.map +1 -1
  38. package/dist/ingest/loops.d.ts +6 -0
  39. package/dist/ingest/loops.d.ts.map +1 -0
  40. package/dist/ingest/opencode.d.ts +1 -1
  41. package/dist/ingest/opencode.d.ts.map +1 -1
  42. package/dist/ingest/otel.d.ts +16 -3
  43. package/dist/ingest/otel.d.ts.map +1 -1
  44. package/dist/ingest/pi.d.ts +1 -1
  45. package/dist/ingest/pi.d.ts.map +1 -1
  46. package/dist/ingest/plugin.d.ts +1 -1
  47. package/dist/ingest/plugin.d.ts.map +1 -1
  48. package/dist/lib/accounts.d.ts.map +1 -1
  49. package/dist/lib/analytics.d.ts +1 -1
  50. package/dist/lib/analytics.d.ts.map +1 -1
  51. package/dist/lib/billing-diff.d.ts +1 -1
  52. package/dist/lib/billing-diff.d.ts.map +1 -1
  53. package/dist/lib/brief.d.ts +1 -1
  54. package/dist/lib/brief.d.ts.map +1 -1
  55. package/dist/lib/open-projects.d.ts +10 -2
  56. package/dist/lib/open-projects.d.ts.map +1 -1
  57. package/dist/lib/pricing.d.ts +1 -1
  58. package/dist/lib/pricing.d.ts.map +1 -1
  59. package/dist/lib/savings.d.ts +1 -1
  60. package/dist/lib/savings.d.ts.map +1 -1
  61. package/dist/lib/spikes.d.ts +1 -1
  62. package/dist/lib/spikes.d.ts.map +1 -1
  63. package/dist/lib/store/index.d.ts +9 -1
  64. package/dist/lib/store/index.d.ts.map +1 -1
  65. package/dist/lib/sync-all.d.ts +3 -1
  66. package/dist/lib/sync-all.d.ts.map +1 -1
  67. package/dist/lib/webhooks.d.ts +1 -1
  68. package/dist/lib/webhooks.d.ts.map +1 -1
  69. package/dist/mcp/index.js +757 -166
  70. package/dist/mcp/server.d.ts.map +1 -1
  71. package/dist/openapi.d.ts.map +1 -1
  72. package/dist/otel/index.js +262 -27
  73. package/dist/server/index.js +683 -135
  74. package/dist/server/serve.d.ts +1 -1
  75. package/dist/server/serve.d.ts.map +1 -1
  76. package/dist/types/index.d.ts +37 -4
  77. package/dist/types/index.d.ts.map +1 -1
  78. package/docs/README.md +12 -0
  79. package/docs/cli.md +88 -0
  80. package/docs/configuration.md +86 -0
  81. package/docs/ingestion.md +47 -0
  82. package/docs/mcp.md +65 -0
  83. package/docs/otel.md +53 -0
  84. package/docs/rest-api.md +89 -0
  85. package/package.json +4 -5
  86. package/dist/lib/cloud-sync.d.ts +0 -35
  87. package/dist/lib/cloud-sync.d.ts.map +0 -1
  88. package/dist/lib/fleet-sync.d.ts +0 -61
  89. package/dist/lib/fleet-sync.d.ts.map +0 -1
  90. package/dist/lib/peer-sync.d.ts +0 -21
  91. package/dist/lib/peer-sync.d.ts.map +0 -1
  92. package/dist/storage.js +0 -2746
package/dist/index.js CHANGED
@@ -15,6 +15,62 @@ var __export = (target, all) => {
15
15
  };
16
16
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
17
 
18
+ // src/db/sqlite-adapter.ts
19
+ import { Database } from "bun:sqlite";
20
+
21
+ class SqliteAdapter {
22
+ db;
23
+ constructor(path) {
24
+ this.db = new Database(path, { create: true });
25
+ this.db.exec("PRAGMA journal_mode=WAL");
26
+ this.db.exec("PRAGMA foreign_keys=ON");
27
+ }
28
+ run(sql, ...params) {
29
+ const result = this.db.prepare(sql).run(...params);
30
+ return { changes: result.changes, lastInsertRowid: result.lastInsertRowid };
31
+ }
32
+ get(sql, ...params) {
33
+ return this.db.prepare(sql).get(...params);
34
+ }
35
+ all(sql, ...params) {
36
+ return this.db.prepare(sql).all(...params);
37
+ }
38
+ exec(sql) {
39
+ this.db.exec(sql);
40
+ }
41
+ query(sql) {
42
+ return this.db.query(sql);
43
+ }
44
+ prepare(sql) {
45
+ const stmt = this.db.prepare(sql);
46
+ return {
47
+ run(...params) {
48
+ const r = stmt.run(...params);
49
+ return { changes: r.changes, lastInsertRowid: r.lastInsertRowid };
50
+ },
51
+ get(...params) {
52
+ return stmt.get(...params);
53
+ },
54
+ all(...params) {
55
+ return stmt.all(...params);
56
+ },
57
+ finalize() {
58
+ stmt.finalize();
59
+ }
60
+ };
61
+ }
62
+ close() {
63
+ this.db.close();
64
+ }
65
+ transaction(fn) {
66
+ return this.db.transaction(fn)();
67
+ }
68
+ get raw() {
69
+ return this.db;
70
+ }
71
+ }
72
+ var init_sqlite_adapter = () => {};
73
+
18
74
  // src/lib/pricing.ts
19
75
  var exports_pricing = {};
20
76
  __export(exports_pricing, {
@@ -533,7 +589,6 @@ var init_pricing = __esm(() => {
533
589
  });
534
590
 
535
591
  // src/db/database.ts
536
- import { SqliteAdapter as Database } from "@hasna/cloud";
537
592
  import { execFileSync } from "child_process";
538
593
  import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from "fs";
539
594
  import { hostname, platform } from "os";
@@ -626,7 +681,7 @@ function openDatabase(dbPath, skipSeed = false) {
626
681
  mkdirSync(dir, { recursive: true });
627
682
  }
628
683
  const db = withSqliteBusyRetry(() => {
629
- const opened = new Database(path);
684
+ const opened = new SqliteAdapter(path);
630
685
  try {
631
686
  opened.exec("PRAGMA busy_timeout = 10000");
632
687
  opened.exec("PRAGMA journal_mode = WAL");
@@ -667,6 +722,16 @@ function addColumnIfMissing(db, table, column, definition) {
667
722
  }
668
723
  function initSchema(db) {
669
724
  db.exec(`
725
+ CREATE TABLE IF NOT EXISTS cost_centers (
726
+ id TEXT PRIMARY KEY,
727
+ kind TEXT NOT NULL,
728
+ name TEXT NOT NULL,
729
+ repo_path TEXT,
730
+ labels_json TEXT DEFAULT '{}',
731
+ created_at TEXT NOT NULL,
732
+ updated_at TEXT NOT NULL
733
+ );
734
+
670
735
  CREATE TABLE IF NOT EXISTS requests (
671
736
  id TEXT PRIMARY KEY,
672
737
  agent TEXT NOT NULL,
@@ -683,6 +748,7 @@ function initSchema(db) {
683
748
  timestamp TEXT NOT NULL,
684
749
  source_request_id TEXT,
685
750
  machine_id TEXT DEFAULT '',
751
+ cost_center_id TEXT,
686
752
  account_key TEXT DEFAULT '',
687
753
  account_tool TEXT DEFAULT '',
688
754
  account_name TEXT DEFAULT '',
@@ -701,6 +767,7 @@ function initSchema(db) {
701
767
  total_tokens INTEGER DEFAULT 0,
702
768
  request_count INTEGER DEFAULT 0,
703
769
  machine_id TEXT DEFAULT '',
770
+ cost_center_id TEXT,
704
771
  account_key TEXT DEFAULT '',
705
772
  account_tool TEXT DEFAULT '',
706
773
  account_name TEXT DEFAULT '',
@@ -721,6 +788,7 @@ function initSchema(db) {
721
788
  id TEXT PRIMARY KEY,
722
789
  project_path TEXT,
723
790
  agent TEXT,
791
+ cost_center_id TEXT,
724
792
  period TEXT NOT NULL,
725
793
  limit_usd REAL NOT NULL,
726
794
  alert_at_percent INTEGER DEFAULT 80,
@@ -751,6 +819,7 @@ function initSchema(db) {
751
819
  CREATE INDEX IF NOT EXISTS idx_sessions_agent ON sessions(agent);
752
820
  CREATE INDEX IF NOT EXISTS idx_sessions_project ON sessions(project_path);
753
821
  CREATE INDEX IF NOT EXISTS idx_sessions_started ON sessions(started_at);
822
+ CREATE INDEX IF NOT EXISTS idx_cost_centers_kind ON cost_centers(kind);
754
823
 
755
824
  CREATE TABLE IF NOT EXISTS model_pricing (
756
825
  model TEXT PRIMARY KEY,
@@ -837,6 +906,12 @@ function initSchema(db) {
837
906
  `);
838
907
  addColumnIfMissing(db, "requests", "machine_id", `TEXT DEFAULT ''`);
839
908
  addColumnIfMissing(db, "sessions", "machine_id", `TEXT DEFAULT ''`);
909
+ if (addColumnIfMissing(db, "cost_centers", "updated_at", `TEXT DEFAULT ''`)) {
910
+ db.exec(`UPDATE cost_centers SET updated_at = created_at WHERE updated_at = '' OR updated_at IS NULL`);
911
+ }
912
+ addColumnIfMissing(db, "requests", "cost_center_id", `TEXT`);
913
+ addColumnIfMissing(db, "sessions", "cost_center_id", `TEXT`);
914
+ addColumnIfMissing(db, "budgets", "cost_center_id", `TEXT`);
840
915
  if (addColumnIfMissing(db, "requests", "cache_create_5m_tokens", "INTEGER DEFAULT 0")) {
841
916
  db.exec(`UPDATE requests SET cache_create_5m_tokens = cache_create_tokens WHERE cache_create_5m_tokens = 0`);
842
917
  }
@@ -865,6 +940,10 @@ function initSchema(db) {
865
940
  CREATE INDEX IF NOT EXISTS idx_sessions_machine ON sessions(machine_id);
866
941
  CREATE INDEX IF NOT EXISTS idx_requests_account ON requests(account_key);
867
942
  CREATE INDEX IF NOT EXISTS idx_sessions_account ON sessions(account_key);
943
+ CREATE INDEX IF NOT EXISTS idx_requests_cost_center ON requests(cost_center_id);
944
+ CREATE INDEX IF NOT EXISTS idx_sessions_cost_center ON sessions(cost_center_id);
945
+ CREATE INDEX IF NOT EXISTS idx_budgets_cost_center ON budgets(cost_center_id);
946
+ CREATE INDEX IF NOT EXISTS idx_cost_centers_kind ON cost_centers(kind);
868
947
  `);
869
948
  }
870
949
  function periodWhere(period) {
@@ -1129,9 +1208,12 @@ function labelForPath(projectPath, projectName) {
1129
1208
  function groupKeyForPath(projectPath, projectName) {
1130
1209
  return labelForPath(projectPath, projectName).trim().toLowerCase();
1131
1210
  }
1132
- function queryProjectBreakdown(db, period = "all", machine) {
1133
- const requestWhere = requestPeriodWhere(period);
1134
- const sessionWhere = sessionPeriodWhere(period);
1211
+ function laterTimestamp(current, candidate) {
1212
+ if (!candidate)
1213
+ return current;
1214
+ return candidate > current ? candidate : current;
1215
+ }
1216
+ function queryProjectBreakdownScoped(db, requestWhere, sessionWhere, requestScopeParams = [], sessionScopeParams = [], machine) {
1135
1217
  const sessionMachineClause = machine ? " AND (machine_id = ? OR id IN (SELECT DISTINCT session_id FROM requests WHERE machine_id = ?))" : "";
1136
1218
  const requestMachineClause = machine ? " AND machine_id = ?" : "";
1137
1219
  const sessionMachineParams = machine ? [machine, machine] : [];
@@ -1139,67 +1221,89 @@ function queryProjectBreakdown(db, period = "all", machine) {
1139
1221
  const sessionOnlyMachineClause = machine ? " AND machine_id = ?" : "";
1140
1222
  const sessionOnlyMachineParams = machine ? [machine] : [];
1141
1223
  const sessions = db.prepare(`
1142
- SELECT id, project_path, project_name, total_cost_usd, started_at
1224
+ SELECT id, project_path, project_name
1143
1225
  FROM sessions
1144
1226
  WHERE (project_path != '' OR project_name != '')${sessionMachineClause}
1145
1227
  `).all(...sessionMachineParams);
1146
1228
  const groups = new Map;
1229
+ const groupKeyBySession = new Map;
1147
1230
  for (const s of sessions) {
1148
1231
  const label = labelForPath(s.project_path, s.project_name);
1149
1232
  if (!label)
1150
1233
  continue;
1151
1234
  const key = groupKeyForPath(s.project_path, s.project_name);
1152
- const g = groups.get(key) ?? { label, sessionIds: [], samplePath: s.project_path };
1153
- g.sessionIds.push(s.id);
1235
+ const g = groups.get(key) ?? { label, samplePath: s.project_path, sessions: 0, requests: 0, total_tokens: 0, cost_usd: 0, last_active: "" };
1154
1236
  if (!g.samplePath)
1155
1237
  g.samplePath = s.project_path;
1156
1238
  groups.set(key, g);
1239
+ groupKeyBySession.set(s.id, key);
1240
+ }
1241
+ const groupFor = (sessionId) => {
1242
+ const key = groupKeyBySession.get(sessionId);
1243
+ return key === undefined ? undefined : groups.get(key);
1244
+ };
1245
+ const requestRows = db.prepare(`
1246
+ SELECT session_id,
1247
+ COUNT(*) as requests,
1248
+ COALESCE(SUM(cost_usd), 0) as cost_usd,
1249
+ COALESCE(SUM(input_tokens + output_tokens + cache_read_tokens + cache_create_tokens), 0) as total_tokens,
1250
+ MAX(timestamp) as last_active
1251
+ FROM requests
1252
+ WHERE ${requestWhere}${requestMachineClause}
1253
+ GROUP BY session_id
1254
+ `).all(...requestScopeParams, ...requestMachineParams);
1255
+ for (const row of requestRows) {
1256
+ const g = groupFor(row.session_id);
1257
+ if (!g)
1258
+ continue;
1259
+ g.sessions += 1;
1260
+ g.requests += row.requests;
1261
+ g.cost_usd += row.cost_usd;
1262
+ g.total_tokens += row.total_tokens;
1263
+ g.last_active = laterTimestamp(g.last_active, row.last_active);
1264
+ }
1265
+ const sessionsWithRequests = new Set(db.prepare(`SELECT DISTINCT session_id FROM requests`).all().map((row) => row.session_id));
1266
+ const sessionOnlyRows = db.prepare(`
1267
+ SELECT id,
1268
+ COALESCE(request_count, 0) as request_count,
1269
+ COALESCE(total_tokens, 0) as total_tokens,
1270
+ COALESCE(total_cost_usd, 0) as total_cost_usd,
1271
+ started_at
1272
+ FROM sessions
1273
+ WHERE ${sessionWhere}${sessionOnlyMachineClause}
1274
+ `).all(...sessionScopeParams, ...sessionOnlyMachineParams);
1275
+ for (const row of sessionOnlyRows) {
1276
+ if (sessionsWithRequests.has(row.id))
1277
+ continue;
1278
+ const g = groupFor(row.id);
1279
+ if (!g)
1280
+ continue;
1281
+ g.sessions += 1;
1282
+ g.requests += row.request_count;
1283
+ g.total_tokens += row.total_tokens;
1284
+ g.cost_usd += row.total_cost_usd;
1285
+ g.last_active = laterTimestamp(g.last_active, row.started_at);
1157
1286
  }
1158
1287
  const result = [];
1159
1288
  for (const g of groups.values()) {
1160
- const placeholders = g.sessionIds.map(() => "?").join(",");
1161
- const reqStats = placeholders.length ? db.prepare(`
1162
- SELECT
1163
- COUNT(DISTINCT session_id) as sessions,
1164
- COUNT(*) as requests,
1165
- COALESCE(SUM(cost_usd), 0) as cost_usd,
1166
- COALESCE(SUM(input_tokens + output_tokens + cache_read_tokens + cache_create_tokens), 0) as total_tokens,
1167
- MAX(timestamp) as last_active
1168
- FROM requests
1169
- WHERE session_id IN (${placeholders})
1170
- AND ${requestWhere}
1171
- ${requestMachineClause}
1172
- `).get(...g.sessionIds, ...requestMachineParams) : { sessions: 0, requests: 0, cost_usd: 0, total_tokens: 0, last_active: null };
1173
- const sessionOnlyStats = placeholders.length ? db.prepare(`
1174
- SELECT
1175
- COUNT(*) as sessions,
1176
- COALESCE(SUM(request_count), 0) as requests,
1177
- COALESCE(SUM(total_tokens), 0) as total_tokens,
1178
- COALESCE(SUM(total_cost_usd), 0) as cost_usd,
1179
- MAX(started_at) as last_active
1180
- FROM sessions
1181
- WHERE id IN (${placeholders})
1182
- AND ${sessionWhere}
1183
- ${sessionOnlyMachineClause}
1184
- AND id NOT IN (SELECT DISTINCT session_id FROM requests)
1185
- `).get(...g.sessionIds, ...sessionOnlyMachineParams) : { sessions: 0, requests: 0, total_tokens: 0, cost_usd: 0, last_active: null };
1186
- const totalSessions = reqStats.sessions + sessionOnlyStats.sessions;
1187
- if (totalSessions === 0)
1289
+ if (g.sessions === 0)
1188
1290
  continue;
1189
- const lastActive = [reqStats.last_active, sessionOnlyStats.last_active].filter(Boolean).sort().at(-1) ?? "";
1190
1291
  result.push({
1191
1292
  project_path: g.samplePath,
1192
1293
  project_name: g.label,
1193
- sessions: totalSessions,
1194
- requests: reqStats.requests + sessionOnlyStats.requests,
1195
- total_tokens: reqStats.total_tokens + sessionOnlyStats.total_tokens,
1196
- cost_usd: reqStats.cost_usd + sessionOnlyStats.cost_usd,
1197
- last_active: lastActive
1294
+ sessions: g.sessions,
1295
+ requests: g.requests,
1296
+ total_tokens: g.total_tokens,
1297
+ cost_usd: g.cost_usd,
1298
+ last_active: g.last_active
1198
1299
  });
1199
1300
  }
1200
1301
  result.sort((a, b) => b.cost_usd - a.cost_usd);
1201
1302
  return result;
1202
1303
  }
1304
+ function queryProjectBreakdown(db, period = "all", machine) {
1305
+ return queryProjectBreakdownScoped(db, requestPeriodWhere(period), sessionPeriodWhere(period), [], [], machine);
1306
+ }
1203
1307
  function normalizeAccountEmail(email) {
1204
1308
  return (email ?? "").trim().toLowerCase();
1205
1309
  }
@@ -1340,17 +1444,141 @@ function queryAccountBreakdown(db, period = "all", machine) {
1340
1444
  result.sort((a, b) => b.cost_usd - a.cost_usd);
1341
1445
  return result;
1342
1446
  }
1343
- function queryProjectBreakdownSince(db, since) {
1344
- return db.prepare(`
1345
- SELECT project_path, project_name,
1346
- COUNT(*) as sessions,
1347
- COALESCE(SUM(total_tokens), 0) as total_tokens,
1348
- COALESCE(SUM(request_count), 0) as requests,
1349
- COALESCE(SUM(total_cost_usd), 0) as cost_usd,
1350
- MAX(started_at) as last_active
1351
- FROM sessions WHERE started_at >= ?
1352
- GROUP BY project_path ORDER BY cost_usd DESC
1353
- `).all(since);
1447
+ function kindFromCostCenterId(id) {
1448
+ const prefix = id.split(":", 1)[0] ?? "";
1449
+ return ["loop", "app", "repo", "service", "team"].includes(prefix) ? prefix : "";
1450
+ }
1451
+ function addCostCenterBreakdownRow(groups, row, sessionOnly) {
1452
+ if (!row.cost_center_id)
1453
+ return;
1454
+ const derivedKind = row.kind || kindFromCostCenterId(row.cost_center_id) || "service";
1455
+ const group = groups.get(row.cost_center_id) ?? {
1456
+ cost_center_id: row.cost_center_id,
1457
+ kind: derivedKind,
1458
+ name: row.name || row.cost_center_id,
1459
+ repo_path: row.repo_path ?? null,
1460
+ labels_json: row.labels_json || "{}",
1461
+ sessionIds: new Set,
1462
+ requests: 0,
1463
+ total_tokens: 0,
1464
+ api_equivalent_usd: 0,
1465
+ metered_api_usd: 0,
1466
+ subscription_included_usd: 0,
1467
+ estimated_usd: 0,
1468
+ unknown_usd: 0,
1469
+ last_active: ""
1470
+ };
1471
+ group.kind = derivedKind;
1472
+ if (row.name)
1473
+ group.name = row.name;
1474
+ if (row.repo_path && !group.repo_path)
1475
+ group.repo_path = row.repo_path;
1476
+ if (row.labels_json && row.labels_json !== "{}")
1477
+ group.labels_json = row.labels_json;
1478
+ if (row.session_id)
1479
+ group.sessionIds.add(row.session_id);
1480
+ group.requests += row.requests;
1481
+ group.total_tokens += row.total_tokens;
1482
+ group.api_equivalent_usd += row.cost_usd;
1483
+ if (sessionOnly) {
1484
+ group.estimated_usd += row.cost_usd;
1485
+ } else if (row.cost_basis === "metered_api") {
1486
+ group.metered_api_usd += row.cost_usd;
1487
+ } else if (row.cost_basis === "subscription_included") {
1488
+ group.subscription_included_usd += row.cost_usd;
1489
+ } else if (row.cost_basis === "unknown") {
1490
+ group.unknown_usd += row.cost_usd;
1491
+ } else {
1492
+ group.estimated_usd += row.cost_usd;
1493
+ }
1494
+ if (!group.last_active || row.last_active > group.last_active)
1495
+ group.last_active = row.last_active;
1496
+ groups.set(row.cost_center_id, group);
1497
+ }
1498
+ function queryCostCenterBreakdown(db, period = "all", filter = {}) {
1499
+ const requestWhere = filter.since ? "r.timestamp >= ?" : requestPeriodWhere(period);
1500
+ const sessionWhere = filter.since ? "s.started_at >= ?" : sessionPeriodWhere(period);
1501
+ const requestMachineClause = filter.machine ? " AND r.machine_id = ?" : "";
1502
+ const sessionMachineClause = filter.machine ? " AND s.machine_id = ?" : "";
1503
+ const requestParams = [];
1504
+ const sessionParams = [];
1505
+ if (filter.since) {
1506
+ requestParams.push(filter.since);
1507
+ sessionParams.push(filter.since);
1508
+ }
1509
+ if (filter.machine) {
1510
+ requestParams.push(filter.machine);
1511
+ sessionParams.push(filter.machine);
1512
+ }
1513
+ const groups = new Map;
1514
+ const requestRows = db.prepare(`
1515
+ SELECT
1516
+ COALESCE(NULLIF(r.cost_center_id, ''), NULLIF(s.cost_center_id, '')) as cost_center_id,
1517
+ COALESCE(cc.kind, '') as kind,
1518
+ COALESCE(cc.name, COALESCE(NULLIF(r.cost_center_id, ''), NULLIF(s.cost_center_id, ''))) as name,
1519
+ cc.repo_path as repo_path,
1520
+ COALESCE(cc.labels_json, '{}') as labels_json,
1521
+ r.session_id as session_id,
1522
+ 1 as requests,
1523
+ COALESCE(r.input_tokens + r.output_tokens + r.cache_read_tokens + r.cache_create_tokens, 0) as total_tokens,
1524
+ COALESCE(r.cost_usd, 0) as cost_usd,
1525
+ COALESCE(NULLIF(r.cost_basis, ''), 'estimated') as cost_basis,
1526
+ r.timestamp as last_active
1527
+ FROM requests r
1528
+ LEFT JOIN sessions s ON s.id = r.session_id
1529
+ LEFT JOIN cost_centers cc ON cc.id = COALESCE(NULLIF(r.cost_center_id, ''), NULLIF(s.cost_center_id, ''))
1530
+ WHERE ${requestWhere}${requestMachineClause}
1531
+ AND COALESCE(NULLIF(r.cost_center_id, ''), NULLIF(s.cost_center_id, '')) IS NOT NULL
1532
+ AND COALESCE(NULLIF(r.cost_center_id, ''), NULLIF(s.cost_center_id, '')) != ''
1533
+ `).all(...requestParams);
1534
+ for (const row of requestRows)
1535
+ addCostCenterBreakdownRow(groups, row, false);
1536
+ const sessionOnlyRows = db.prepare(`
1537
+ SELECT
1538
+ s.cost_center_id as cost_center_id,
1539
+ COALESCE(cc.kind, '') as kind,
1540
+ COALESCE(cc.name, s.cost_center_id) as name,
1541
+ cc.repo_path as repo_path,
1542
+ COALESCE(cc.labels_json, '{}') as labels_json,
1543
+ s.id as session_id,
1544
+ COALESCE(s.request_count, 0) as requests,
1545
+ COALESCE(s.total_tokens, 0) as total_tokens,
1546
+ COALESCE(s.total_cost_usd, 0) as cost_usd,
1547
+ 'estimated' as cost_basis,
1548
+ s.started_at as last_active
1549
+ FROM sessions s
1550
+ LEFT JOIN cost_centers cc ON cc.id = s.cost_center_id
1551
+ WHERE ${sessionWhere}${sessionMachineClause}
1552
+ AND s.id NOT IN (SELECT DISTINCT session_id FROM requests)
1553
+ AND s.cost_center_id IS NOT NULL
1554
+ AND s.cost_center_id != ''
1555
+ `).all(...sessionParams);
1556
+ for (const row of sessionOnlyRows)
1557
+ addCostCenterBreakdownRow(groups, row, true);
1558
+ const result = [...groups.values()].map((group) => ({
1559
+ cost_center_id: group.cost_center_id,
1560
+ kind: group.kind,
1561
+ name: group.name,
1562
+ repo_path: group.repo_path,
1563
+ labels_json: group.labels_json,
1564
+ sessions: group.sessionIds.size,
1565
+ requests: group.requests,
1566
+ total_tokens: group.total_tokens,
1567
+ api_equivalent_usd: group.api_equivalent_usd,
1568
+ billable_usd: group.metered_api_usd,
1569
+ metered_api_usd: group.metered_api_usd,
1570
+ subscription_included_usd: group.subscription_included_usd,
1571
+ estimated_usd: group.estimated_usd,
1572
+ unknown_usd: group.unknown_usd,
1573
+ cost_usd: group.api_equivalent_usd,
1574
+ last_active: group.last_active
1575
+ }));
1576
+ const filtered = filter.kind ? result.filter((row) => row.kind === filter.kind) : result;
1577
+ filtered.sort((a, b) => b.cost_usd - a.cost_usd);
1578
+ return filtered;
1579
+ }
1580
+ function queryProjectBreakdownSince(db, since, machine) {
1581
+ return queryProjectBreakdownScoped(db, "timestamp >= ?", "started_at >= ?", [since], [since], machine);
1354
1582
  }
1355
1583
  function queryAgentBreakdownSince(db, since) {
1356
1584
  return db.prepare(`
@@ -1511,9 +1739,9 @@ function deleteProject(db, path) {
1511
1739
  function upsertBudget(db, budget) {
1512
1740
  db.prepare(`
1513
1741
  INSERT OR REPLACE INTO budgets
1514
- (id, project_path, agent, period, limit_usd, alert_at_percent, created_at, updated_at)
1515
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
1516
- `).run(budget.id, budget.project_path ?? null, budget.agent ?? null, budget.period, budget.limit_usd, budget.alert_at_percent, budget.created_at, budget.updated_at);
1742
+ (id, project_path, agent, cost_center_id, period, limit_usd, alert_at_percent, created_at, updated_at)
1743
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
1744
+ `).run(budget.id, budget.project_path ?? null, budget.agent ?? null, budget.cost_center_id ?? null, budget.period, budget.limit_usd, budget.alert_at_percent, budget.created_at, budget.updated_at);
1517
1745
  }
1518
1746
  function listBudgets(db) {
1519
1747
  return db.prepare(`SELECT * FROM budgets ORDER BY created_at DESC`).all();
@@ -1535,6 +1763,10 @@ function getBudgetStatuses(db) {
1535
1763
  spendQuery += ` AND agent = ?`;
1536
1764
  params.push(b.agent);
1537
1765
  }
1766
+ if (b.cost_center_id) {
1767
+ spendQuery += ` AND COALESCE(NULLIF(cost_center_id, ''), (SELECT NULLIF(s.cost_center_id, '') FROM sessions s WHERE s.id = requests.session_id)) = ?`;
1768
+ params.push(b.cost_center_id);
1769
+ }
1538
1770
  const row = db.prepare(spendQuery).get(...params);
1539
1771
  const spend = row.spend;
1540
1772
  const percent = b.limit_usd > 0 ? spend / b.limit_usd * 100 : 0;
@@ -1724,7 +1956,9 @@ function queryUsageSnapshots(db, opts = {}) {
1724
1956
  function listMachineRegistry(db) {
1725
1957
  return db.prepare(`SELECT * FROM machines ORDER BY last_seen_at DESC`).all();
1726
1958
  }
1727
- var init_database = () => {};
1959
+ var init_database = __esm(() => {
1960
+ init_sqlite_adapter();
1961
+ });
1728
1962
 
1729
1963
  // src/lib/agents.ts
1730
1964
  var AGENTS = [
@@ -3202,11 +3436,18 @@ class LocalStore {
3202
3436
  return query.since ? queryAgentBreakdownSince(this.db(), query.since) : queryAgentBreakdown(this.db(), query.period ?? "all", query.machine);
3203
3437
  }
3204
3438
  async projectBreakdown(query = {}) {
3205
- return query.since ? queryProjectBreakdownSince(this.db(), query.since) : queryProjectBreakdown(this.db(), query.period ?? "all", query.machine);
3439
+ return query.since ? queryProjectBreakdownSince(this.db(), query.since, query.machine) : queryProjectBreakdown(this.db(), query.period ?? "all", query.machine);
3206
3440
  }
3207
3441
  async accountBreakdown(query = {}) {
3208
3442
  return query.since ? queryAccountBreakdownSince(this.db(), query.since) : queryAccountBreakdown(this.db(), query.period ?? "all", query.machine);
3209
3443
  }
3444
+ async costCenterBreakdown(query = {}) {
3445
+ return queryCostCenterBreakdown(this.db(), query.period ?? "all", {
3446
+ kind: query.kind,
3447
+ machine: query.machine,
3448
+ since: query.since
3449
+ });
3450
+ }
3210
3451
  async accounts(period) {
3211
3452
  return queryAccountBreakdown(this.db(), period);
3212
3453
  }
@@ -3391,11 +3632,19 @@ class ApiStore {
3391
3632
  return cloudListItems(this.cloud, "breakdown", q({ by: "agent", period: query.period, since: query.since }));
3392
3633
  }
3393
3634
  async projectBreakdown(query = {}) {
3394
- return cloudListItems(this.cloud, "breakdown", q({ by: "project", period: query.period, since: query.since }));
3635
+ return cloudListItems(this.cloud, "breakdown", q({ by: "project", period: query.period, since: query.since, machine: query.machine }));
3395
3636
  }
3396
3637
  async accountBreakdown(query = {}) {
3397
3638
  return cloudListItems(this.cloud, "breakdown", q({ by: "account", period: query.period, since: query.since }));
3398
3639
  }
3640
+ async costCenterBreakdown(query = {}) {
3641
+ const rows = await cloudListItems(this.cloud, "breakdown", q({
3642
+ by: query.kind ?? "cost-center",
3643
+ period: query.period,
3644
+ since: query.since
3645
+ }));
3646
+ return rows.filter((row) => Boolean(row?.cost_center_id));
3647
+ }
3399
3648
  async accounts(period) {
3400
3649
  return cloudListItems(this.cloud, "accounts", q({ period }));
3401
3650
  }
@@ -3469,6 +3718,7 @@ class ApiStore {
3469
3718
  const created = await this.cloud.client.create("budgets", {
3470
3719
  project_path: input.project_path,
3471
3720
  ...input.agent ? { agent: input.agent } : {},
3721
+ ...input.cost_center_id ? { cost_center_id: input.cost_center_id } : {},
3472
3722
  period: input.period,
3473
3723
  limit_usd: input.limit_usd,
3474
3724
  alert_at_percent: input.alert_at_percent
@@ -1,4 +1,4 @@
1
- import type { SqliteAdapter as Database } from '@hasna/cloud';
1
+ import type { SqliteAdapter as Database } from '../db/sqlite-adapter.js';
2
2
  export declare function syncAnthropicBilling(db: Database, opts?: {
3
3
  days?: number;
4
4
  fromDate?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"billing.d.ts","sourceRoot":"","sources":["../../src/ingest/billing.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AA+I7D,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,QAAQ,EACZ,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/D,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAuD7C;AAeD,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,QAAQ,EACZ,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/D,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAqD7C;AAED,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,QAAQ,EACZ,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/D,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAwC/D"}
1
+ {"version":3,"file":"billing.d.ts","sourceRoot":"","sources":["../../src/ingest/billing.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AA+IxE,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,QAAQ,EACZ,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/D,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAuD7C;AAeD,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,QAAQ,EACZ,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/D,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAqD7C;AAED,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,QAAQ,EACZ,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/D,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAwC/D"}
@@ -1,4 +1,4 @@
1
- import type { SqliteAdapter as Database } from '@hasna/cloud';
1
+ import type { SqliteAdapter as Database } from '../db/sqlite-adapter.js';
2
2
  export declare function ingestClaudeQuota(db: Database, verbose?: boolean): Promise<{
3
3
  snapshots: number;
4
4
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"claude-quota.d.ts","sourceRoot":"","sources":["../../src/ingest/claude-quota.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AAqE7D,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CA6FrG"}
1
+ {"version":3,"file":"claude-quota.d.ts","sourceRoot":"","sources":["../../src/ingest/claude-quota.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAqExE,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CA6FrG"}
@@ -1,4 +1,4 @@
1
- import type { SqliteAdapter as Database } from '@hasna/cloud';
1
+ import type { SqliteAdapter as Database } from '../db/sqlite-adapter.js';
2
2
  import type { Agent } from '../types/index.js';
3
3
  export declare function ingestClaude(db: Database, verbose?: boolean, projectsDir?: string): Promise<{
4
4
  files: number;
@@ -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;AAY7D,OAAO,KAAK,EAAkB,KAAK,EAAE,MAAM,mBAAmB,CAAA;AA+D9D,wBAAsB,YAAY,CAChC,EAAE,EAAE,QAAQ,EACZ,OAAO,UAAQ,EACf,WAAW,SAAsB,GAChC,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,EACf,WAAW,SAAsB,GAChC,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAEhE;AAED,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,QAAQ,EACZ,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,KAAK,EAChB,OAAO,UAAQ,GACd,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAyIhE"}
1
+ {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/ingest/claude.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAYxE,OAAO,KAAK,EAAkB,KAAK,EAAE,MAAM,mBAAmB,CAAA;AA+D9D,wBAAsB,YAAY,CAChC,EAAE,EAAE,QAAQ,EACZ,OAAO,UAAQ,EACf,WAAW,SAAsB,GAChC,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,EACf,WAAW,SAAsB,GAChC,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAEhE;AAED,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,QAAQ,EACZ,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,KAAK,EAChB,OAAO,UAAQ,GACd,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAyIhE"}
@@ -1,4 +1,4 @@
1
- import type { SqliteAdapter as Database } from '@hasna/cloud';
1
+ import type { SqliteAdapter as Database } from '../db/sqlite-adapter.js';
2
2
  export declare function ingestCodexQuota(db: Database, verbose?: boolean): Promise<{
3
3
  snapshots: number;
4
4
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"codex-quota.d.ts","sourceRoot":"","sources":["../../src/ingest/codex-quota.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AAmF7D,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAoFpG"}
1
+ {"version":3,"file":"codex-quota.d.ts","sourceRoot":"","sources":["../../src/ingest/codex-quota.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAmFxE,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAoFpG"}
@@ -1,4 +1,4 @@
1
- import type { SqliteAdapter as Database } from '@hasna/cloud';
1
+ import type { SqliteAdapter as Database } from '../db/sqlite-adapter.js';
2
2
  declare function readCodexModel(configPath?: string): string;
3
3
  export declare function ingestCodex(db: Database, verbose?: boolean): Promise<{
4
4
  sessions: number;
@@ -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;AA4F7D,iBAAS,cAAc,CAAC,UAAU,SAAoB,GAAG,MAAM,CAS9D;AAwGD,wBAAsB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA+FhH;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,yBAAyB,CAAA;AA4FxE,iBAAS,cAAc,CAAC,UAAU,SAAoB,GAAG,MAAM,CAS9D;AAwGD,wBAAsB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA+FhH;AAED,OAAO,EAAE,cAAc,EAAE,CAAA"}
@@ -1,4 +1,4 @@
1
- import type { SqliteAdapter as Database } from '@hasna/cloud';
1
+ import type { SqliteAdapter as Database } from '../db/sqlite-adapter.js';
2
2
  export declare function ingestCursor(db: Database, verbose?: boolean): Promise<{
3
3
  requests: number;
4
4
  snapshots: number;
@@ -1 +1 @@
1
- {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/ingest/cursor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AAsC7D,wBAAsB,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAqGlH"}
1
+ {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/ingest/cursor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAsCxE,wBAAsB,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAqGlH"}
@@ -1,4 +1,4 @@
1
- import type { SqliteAdapter as Database } from '@hasna/cloud';
1
+ import type { SqliteAdapter as Database } from '../db/sqlite-adapter.js';
2
2
  export declare function ingestGemini(db: Database, verbose?: boolean): Promise<{
3
3
  sessions: number;
4
4
  requests: number;
@@ -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;AAoG7D,wBAAsB,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAgInH"}
1
+ {"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/ingest/gemini.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAoGxE,wBAAsB,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAgInH"}
@@ -1,4 +1,4 @@
1
- import type { SqliteAdapter as Database } from '@hasna/cloud';
1
+ import type { SqliteAdapter as Database } from '../db/sqlite-adapter.js';
2
2
  export declare function ingestHermes(db: Database, verbose?: boolean): Promise<{
3
3
  sessions: number;
4
4
  requests: number;
@@ -1 +1 @@
1
- {"version":3,"file":"hermes.d.ts","sourceRoot":"","sources":["../../src/ingest/hermes.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AAiC7D,wBAAsB,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA2EjH"}
1
+ {"version":3,"file":"hermes.d.ts","sourceRoot":"","sources":["../../src/ingest/hermes.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAiCxE,wBAAsB,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA2EjH"}
@@ -0,0 +1,6 @@
1
+ import type { SqliteAdapter as Database } from '../db/sqlite-adapter.js';
2
+ export declare function ingestLoops(db: Database, verbose?: boolean): Promise<{
3
+ sessions: number;
4
+ requests: number;
5
+ }>;
6
+ //# sourceMappingURL=loops.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loops.d.ts","sourceRoot":"","sources":["../../src/ingest/loops.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AA4IxE,wBAAsB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA+FhH"}
@@ -1,4 +1,4 @@
1
- import type { SqliteAdapter as Database } from '@hasna/cloud';
1
+ import type { SqliteAdapter as Database } from '../db/sqlite-adapter.js';
2
2
  export declare function ingestOpenCode(db: Database, verbose?: boolean): Promise<{
3
3
  files: number;
4
4
  requests: number;
@@ -1 +1 @@
1
- {"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../../src/ingest/opencode.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AA8C7D,wBAAsB,cAAc,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAmFlI"}
1
+ {"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../../src/ingest/opencode.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AA8CxE,wBAAsB,cAAc,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAmFlI"}