@hasna/economy 0.3.7 → 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.
- package/CHANGELOG.md +0 -4
- package/README.md +28 -61
- package/dashboard/README.md +17 -64
- package/dist/cli/index.js +221 -114
- package/dist/db/cloud.d.ts +3 -4
- package/dist/db/cloud.d.ts.map +1 -1
- package/dist/db/database.d.ts +2 -2
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/dialect.d.ts +13 -0
- package/dist/db/dialect.d.ts.map +1 -0
- package/dist/db/pg-migrate.d.ts +30 -0
- package/dist/db/pg-migrate.d.ts.map +1 -0
- package/dist/db/sqlite-adapter.d.ts +36 -0
- package/dist/db/sqlite-adapter.d.ts.map +1 -0
- package/dist/db/sync-pg.d.ts +1 -1
- package/dist/db/sync-pg.d.ts.map +1 -1
- package/dist/index.js +129 -56
- package/dist/ingest/billing.d.ts +1 -1
- package/dist/ingest/billing.d.ts.map +1 -1
- package/dist/ingest/claude-quota.d.ts +1 -1
- package/dist/ingest/claude-quota.d.ts.map +1 -1
- package/dist/ingest/claude.d.ts +1 -1
- package/dist/ingest/claude.d.ts.map +1 -1
- package/dist/ingest/codex-quota.d.ts +1 -1
- package/dist/ingest/codex-quota.d.ts.map +1 -1
- package/dist/ingest/codex.d.ts +1 -1
- package/dist/ingest/codex.d.ts.map +1 -1
- package/dist/ingest/cursor.d.ts +1 -1
- package/dist/ingest/cursor.d.ts.map +1 -1
- package/dist/ingest/gemini.d.ts +1 -1
- package/dist/ingest/gemini.d.ts.map +1 -1
- package/dist/ingest/hermes.d.ts +1 -1
- package/dist/ingest/hermes.d.ts.map +1 -1
- package/dist/ingest/loops.d.ts +1 -1
- package/dist/ingest/loops.d.ts.map +1 -1
- package/dist/ingest/opencode.d.ts +1 -1
- package/dist/ingest/opencode.d.ts.map +1 -1
- package/dist/ingest/otel.d.ts +1 -1
- package/dist/ingest/otel.d.ts.map +1 -1
- package/dist/ingest/pi.d.ts +1 -1
- package/dist/ingest/pi.d.ts.map +1 -1
- package/dist/ingest/plugin.d.ts +1 -1
- package/dist/ingest/plugin.d.ts.map +1 -1
- package/dist/lib/accounts.d.ts.map +1 -1
- package/dist/lib/analytics.d.ts +1 -1
- package/dist/lib/analytics.d.ts.map +1 -1
- package/dist/lib/billing-diff.d.ts +1 -1
- package/dist/lib/billing-diff.d.ts.map +1 -1
- package/dist/lib/brief.d.ts +1 -1
- package/dist/lib/brief.d.ts.map +1 -1
- package/dist/lib/open-projects.d.ts +10 -2
- package/dist/lib/open-projects.d.ts.map +1 -1
- package/dist/lib/pricing.d.ts +1 -1
- package/dist/lib/pricing.d.ts.map +1 -1
- package/dist/lib/savings.d.ts +1 -1
- package/dist/lib/savings.d.ts.map +1 -1
- package/dist/lib/spikes.d.ts +1 -1
- package/dist/lib/spikes.d.ts.map +1 -1
- package/dist/lib/sync-all.d.ts +1 -1
- package/dist/lib/sync-all.d.ts.map +1 -1
- package/dist/lib/webhooks.d.ts +1 -1
- package/dist/lib/webhooks.d.ts.map +1 -1
- package/dist/mcp/index.js +144 -94
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/openapi.d.ts.map +1 -1
- package/dist/otel/index.js +69 -4
- package/dist/server/index.js +248 -88
- package/dist/server/serve.d.ts +1 -1
- package/dist/server/serve.d.ts.map +1 -1
- package/docs/README.md +12 -0
- package/docs/cli.md +88 -0
- package/docs/configuration.md +86 -0
- package/docs/ingestion.md +47 -0
- package/docs/mcp.md +65 -0
- package/docs/otel.md +53 -0
- package/docs/rest-api.md +89 -0
- package/package.json +4 -5
package/dist/cli/index.js
CHANGED
|
@@ -17,6 +17,62 @@ var __export = (target, all) => {
|
|
|
17
17
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
18
18
|
var __require = import.meta.require;
|
|
19
19
|
|
|
20
|
+
// src/db/sqlite-adapter.ts
|
|
21
|
+
import { Database } from "bun:sqlite";
|
|
22
|
+
|
|
23
|
+
class SqliteAdapter {
|
|
24
|
+
db;
|
|
25
|
+
constructor(path) {
|
|
26
|
+
this.db = new Database(path, { create: true });
|
|
27
|
+
this.db.exec("PRAGMA journal_mode=WAL");
|
|
28
|
+
this.db.exec("PRAGMA foreign_keys=ON");
|
|
29
|
+
}
|
|
30
|
+
run(sql, ...params) {
|
|
31
|
+
const result = this.db.prepare(sql).run(...params);
|
|
32
|
+
return { changes: result.changes, lastInsertRowid: result.lastInsertRowid };
|
|
33
|
+
}
|
|
34
|
+
get(sql, ...params) {
|
|
35
|
+
return this.db.prepare(sql).get(...params);
|
|
36
|
+
}
|
|
37
|
+
all(sql, ...params) {
|
|
38
|
+
return this.db.prepare(sql).all(...params);
|
|
39
|
+
}
|
|
40
|
+
exec(sql) {
|
|
41
|
+
this.db.exec(sql);
|
|
42
|
+
}
|
|
43
|
+
query(sql) {
|
|
44
|
+
return this.db.query(sql);
|
|
45
|
+
}
|
|
46
|
+
prepare(sql) {
|
|
47
|
+
const stmt = this.db.prepare(sql);
|
|
48
|
+
return {
|
|
49
|
+
run(...params) {
|
|
50
|
+
const r = stmt.run(...params);
|
|
51
|
+
return { changes: r.changes, lastInsertRowid: r.lastInsertRowid };
|
|
52
|
+
},
|
|
53
|
+
get(...params) {
|
|
54
|
+
return stmt.get(...params);
|
|
55
|
+
},
|
|
56
|
+
all(...params) {
|
|
57
|
+
return stmt.all(...params);
|
|
58
|
+
},
|
|
59
|
+
finalize() {
|
|
60
|
+
stmt.finalize();
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
close() {
|
|
65
|
+
this.db.close();
|
|
66
|
+
}
|
|
67
|
+
transaction(fn) {
|
|
68
|
+
return this.db.transaction(fn)();
|
|
69
|
+
}
|
|
70
|
+
get raw() {
|
|
71
|
+
return this.db;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
var init_sqlite_adapter = () => {};
|
|
75
|
+
|
|
20
76
|
// src/lib/pricing.ts
|
|
21
77
|
var exports_pricing = {};
|
|
22
78
|
__export(exports_pricing, {
|
|
@@ -596,7 +652,6 @@ __export(exports_database, {
|
|
|
596
652
|
clearBillingRange: () => clearBillingRange,
|
|
597
653
|
bulkIngest: () => bulkIngest
|
|
598
654
|
});
|
|
599
|
-
import { SqliteAdapter as Database } from "@hasna/cloud";
|
|
600
655
|
import { execFileSync } from "child_process";
|
|
601
656
|
import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from "fs";
|
|
602
657
|
import { hostname, platform } from "os";
|
|
@@ -689,7 +744,7 @@ function openDatabase(dbPath, skipSeed = false) {
|
|
|
689
744
|
mkdirSync(dir, { recursive: true });
|
|
690
745
|
}
|
|
691
746
|
const db = withSqliteBusyRetry(() => {
|
|
692
|
-
const opened = new
|
|
747
|
+
const opened = new SqliteAdapter(path);
|
|
693
748
|
try {
|
|
694
749
|
opened.exec("PRAGMA busy_timeout = 10000");
|
|
695
750
|
opened.exec("PRAGMA journal_mode = WAL");
|
|
@@ -1295,9 +1350,12 @@ function labelForPath(projectPath, projectName) {
|
|
|
1295
1350
|
function groupKeyForPath(projectPath, projectName) {
|
|
1296
1351
|
return labelForPath(projectPath, projectName).trim().toLowerCase();
|
|
1297
1352
|
}
|
|
1298
|
-
function
|
|
1299
|
-
|
|
1300
|
-
|
|
1353
|
+
function laterTimestamp(current, candidate) {
|
|
1354
|
+
if (!candidate)
|
|
1355
|
+
return current;
|
|
1356
|
+
return candidate > current ? candidate : current;
|
|
1357
|
+
}
|
|
1358
|
+
function queryProjectBreakdownScoped(db, requestWhere, sessionWhere, requestScopeParams = [], sessionScopeParams = [], machine) {
|
|
1301
1359
|
const sessionMachineClause = machine ? " AND (machine_id = ? OR id IN (SELECT DISTINCT session_id FROM requests WHERE machine_id = ?))" : "";
|
|
1302
1360
|
const requestMachineClause = machine ? " AND machine_id = ?" : "";
|
|
1303
1361
|
const sessionMachineParams = machine ? [machine, machine] : [];
|
|
@@ -1305,67 +1363,89 @@ function queryProjectBreakdown(db, period = "all", machine) {
|
|
|
1305
1363
|
const sessionOnlyMachineClause = machine ? " AND machine_id = ?" : "";
|
|
1306
1364
|
const sessionOnlyMachineParams = machine ? [machine] : [];
|
|
1307
1365
|
const sessions = db.prepare(`
|
|
1308
|
-
SELECT id, project_path, project_name
|
|
1366
|
+
SELECT id, project_path, project_name
|
|
1309
1367
|
FROM sessions
|
|
1310
1368
|
WHERE (project_path != '' OR project_name != '')${sessionMachineClause}
|
|
1311
1369
|
`).all(...sessionMachineParams);
|
|
1312
1370
|
const groups = new Map;
|
|
1371
|
+
const groupKeyBySession = new Map;
|
|
1313
1372
|
for (const s of sessions) {
|
|
1314
1373
|
const label = labelForPath(s.project_path, s.project_name);
|
|
1315
1374
|
if (!label)
|
|
1316
1375
|
continue;
|
|
1317
1376
|
const key = groupKeyForPath(s.project_path, s.project_name);
|
|
1318
|
-
const g = groups.get(key) ?? { label,
|
|
1319
|
-
g.sessionIds.push(s.id);
|
|
1377
|
+
const g = groups.get(key) ?? { label, samplePath: s.project_path, sessions: 0, requests: 0, total_tokens: 0, cost_usd: 0, last_active: "" };
|
|
1320
1378
|
if (!g.samplePath)
|
|
1321
1379
|
g.samplePath = s.project_path;
|
|
1322
1380
|
groups.set(key, g);
|
|
1381
|
+
groupKeyBySession.set(s.id, key);
|
|
1382
|
+
}
|
|
1383
|
+
const groupFor = (sessionId) => {
|
|
1384
|
+
const key = groupKeyBySession.get(sessionId);
|
|
1385
|
+
return key === undefined ? undefined : groups.get(key);
|
|
1386
|
+
};
|
|
1387
|
+
const requestRows = db.prepare(`
|
|
1388
|
+
SELECT session_id,
|
|
1389
|
+
COUNT(*) as requests,
|
|
1390
|
+
COALESCE(SUM(cost_usd), 0) as cost_usd,
|
|
1391
|
+
COALESCE(SUM(input_tokens + output_tokens + cache_read_tokens + cache_create_tokens), 0) as total_tokens,
|
|
1392
|
+
MAX(timestamp) as last_active
|
|
1393
|
+
FROM requests
|
|
1394
|
+
WHERE ${requestWhere}${requestMachineClause}
|
|
1395
|
+
GROUP BY session_id
|
|
1396
|
+
`).all(...requestScopeParams, ...requestMachineParams);
|
|
1397
|
+
for (const row of requestRows) {
|
|
1398
|
+
const g = groupFor(row.session_id);
|
|
1399
|
+
if (!g)
|
|
1400
|
+
continue;
|
|
1401
|
+
g.sessions += 1;
|
|
1402
|
+
g.requests += row.requests;
|
|
1403
|
+
g.cost_usd += row.cost_usd;
|
|
1404
|
+
g.total_tokens += row.total_tokens;
|
|
1405
|
+
g.last_active = laterTimestamp(g.last_active, row.last_active);
|
|
1406
|
+
}
|
|
1407
|
+
const sessionsWithRequests = new Set(db.prepare(`SELECT DISTINCT session_id FROM requests`).all().map((row) => row.session_id));
|
|
1408
|
+
const sessionOnlyRows = db.prepare(`
|
|
1409
|
+
SELECT id,
|
|
1410
|
+
COALESCE(request_count, 0) as request_count,
|
|
1411
|
+
COALESCE(total_tokens, 0) as total_tokens,
|
|
1412
|
+
COALESCE(total_cost_usd, 0) as total_cost_usd,
|
|
1413
|
+
started_at
|
|
1414
|
+
FROM sessions
|
|
1415
|
+
WHERE ${sessionWhere}${sessionOnlyMachineClause}
|
|
1416
|
+
`).all(...sessionScopeParams, ...sessionOnlyMachineParams);
|
|
1417
|
+
for (const row of sessionOnlyRows) {
|
|
1418
|
+
if (sessionsWithRequests.has(row.id))
|
|
1419
|
+
continue;
|
|
1420
|
+
const g = groupFor(row.id);
|
|
1421
|
+
if (!g)
|
|
1422
|
+
continue;
|
|
1423
|
+
g.sessions += 1;
|
|
1424
|
+
g.requests += row.request_count;
|
|
1425
|
+
g.total_tokens += row.total_tokens;
|
|
1426
|
+
g.cost_usd += row.total_cost_usd;
|
|
1427
|
+
g.last_active = laterTimestamp(g.last_active, row.started_at);
|
|
1323
1428
|
}
|
|
1324
1429
|
const result = [];
|
|
1325
1430
|
for (const g of groups.values()) {
|
|
1326
|
-
|
|
1327
|
-
const reqStats = placeholders.length ? db.prepare(`
|
|
1328
|
-
SELECT
|
|
1329
|
-
COUNT(DISTINCT session_id) as sessions,
|
|
1330
|
-
COUNT(*) as requests,
|
|
1331
|
-
COALESCE(SUM(cost_usd), 0) as cost_usd,
|
|
1332
|
-
COALESCE(SUM(input_tokens + output_tokens + cache_read_tokens + cache_create_tokens), 0) as total_tokens,
|
|
1333
|
-
MAX(timestamp) as last_active
|
|
1334
|
-
FROM requests
|
|
1335
|
-
WHERE session_id IN (${placeholders})
|
|
1336
|
-
AND ${requestWhere}
|
|
1337
|
-
${requestMachineClause}
|
|
1338
|
-
`).get(...g.sessionIds, ...requestMachineParams) : { sessions: 0, requests: 0, cost_usd: 0, total_tokens: 0, last_active: null };
|
|
1339
|
-
const sessionOnlyStats = placeholders.length ? db.prepare(`
|
|
1340
|
-
SELECT
|
|
1341
|
-
COUNT(*) as sessions,
|
|
1342
|
-
COALESCE(SUM(request_count), 0) as requests,
|
|
1343
|
-
COALESCE(SUM(total_tokens), 0) as total_tokens,
|
|
1344
|
-
COALESCE(SUM(total_cost_usd), 0) as cost_usd,
|
|
1345
|
-
MAX(started_at) as last_active
|
|
1346
|
-
FROM sessions
|
|
1347
|
-
WHERE id IN (${placeholders})
|
|
1348
|
-
AND ${sessionWhere}
|
|
1349
|
-
${sessionOnlyMachineClause}
|
|
1350
|
-
AND id NOT IN (SELECT DISTINCT session_id FROM requests)
|
|
1351
|
-
`).get(...g.sessionIds, ...sessionOnlyMachineParams) : { sessions: 0, requests: 0, total_tokens: 0, cost_usd: 0, last_active: null };
|
|
1352
|
-
const totalSessions = reqStats.sessions + sessionOnlyStats.sessions;
|
|
1353
|
-
if (totalSessions === 0)
|
|
1431
|
+
if (g.sessions === 0)
|
|
1354
1432
|
continue;
|
|
1355
|
-
const lastActive = [reqStats.last_active, sessionOnlyStats.last_active].filter(Boolean).sort().at(-1) ?? "";
|
|
1356
1433
|
result.push({
|
|
1357
1434
|
project_path: g.samplePath,
|
|
1358
1435
|
project_name: g.label,
|
|
1359
|
-
sessions:
|
|
1360
|
-
requests:
|
|
1361
|
-
total_tokens:
|
|
1362
|
-
cost_usd:
|
|
1363
|
-
last_active:
|
|
1436
|
+
sessions: g.sessions,
|
|
1437
|
+
requests: g.requests,
|
|
1438
|
+
total_tokens: g.total_tokens,
|
|
1439
|
+
cost_usd: g.cost_usd,
|
|
1440
|
+
last_active: g.last_active
|
|
1364
1441
|
});
|
|
1365
1442
|
}
|
|
1366
1443
|
result.sort((a, b) => b.cost_usd - a.cost_usd);
|
|
1367
1444
|
return result;
|
|
1368
1445
|
}
|
|
1446
|
+
function queryProjectBreakdown(db, period = "all", machine) {
|
|
1447
|
+
return queryProjectBreakdownScoped(db, requestPeriodWhere(period), sessionPeriodWhere(period), [], [], machine);
|
|
1448
|
+
}
|
|
1369
1449
|
function normalizeAccountEmail(email) {
|
|
1370
1450
|
return (email ?? "").trim().toLowerCase();
|
|
1371
1451
|
}
|
|
@@ -1639,17 +1719,8 @@ function queryCostCenterBreakdown(db, period = "all", filter = {}) {
|
|
|
1639
1719
|
filtered.sort((a, b) => b.cost_usd - a.cost_usd);
|
|
1640
1720
|
return filtered;
|
|
1641
1721
|
}
|
|
1642
|
-
function queryProjectBreakdownSince(db, since) {
|
|
1643
|
-
return db
|
|
1644
|
-
SELECT project_path, project_name,
|
|
1645
|
-
COUNT(*) as sessions,
|
|
1646
|
-
COALESCE(SUM(total_tokens), 0) as total_tokens,
|
|
1647
|
-
COALESCE(SUM(request_count), 0) as requests,
|
|
1648
|
-
COALESCE(SUM(total_cost_usd), 0) as cost_usd,
|
|
1649
|
-
MAX(started_at) as last_active
|
|
1650
|
-
FROM sessions WHERE started_at >= ?
|
|
1651
|
-
GROUP BY project_path ORDER BY cost_usd DESC
|
|
1652
|
-
`).all(since);
|
|
1722
|
+
function queryProjectBreakdownSince(db, since, machine) {
|
|
1723
|
+
return queryProjectBreakdownScoped(db, "timestamp >= ?", "started_at >= ?", [since], [since], machine);
|
|
1653
1724
|
}
|
|
1654
1725
|
function queryAgentBreakdownSince(db, since) {
|
|
1655
1726
|
return db.prepare(`
|
|
@@ -2353,7 +2424,9 @@ var numOr = (v, d = 0) => {
|
|
|
2353
2424
|
const num = Number(v);
|
|
2354
2425
|
return Number.isFinite(num) ? num : d;
|
|
2355
2426
|
}, strOr = (v, d = "") => v == null ? d : String(v);
|
|
2356
|
-
var init_database = () => {
|
|
2427
|
+
var init_database = __esm(() => {
|
|
2428
|
+
init_sqlite_adapter();
|
|
2429
|
+
});
|
|
2357
2430
|
|
|
2358
2431
|
// src/lib/package-metadata.ts
|
|
2359
2432
|
import { readFileSync } from "fs";
|
|
@@ -3833,7 +3906,7 @@ class LocalStore {
|
|
|
3833
3906
|
return query.since ? queryAgentBreakdownSince(this.db(), query.since) : queryAgentBreakdown(this.db(), query.period ?? "all", query.machine);
|
|
3834
3907
|
}
|
|
3835
3908
|
async projectBreakdown(query = {}) {
|
|
3836
|
-
return query.since ? queryProjectBreakdownSince(this.db(), query.since) : queryProjectBreakdown(this.db(), query.period ?? "all", query.machine);
|
|
3909
|
+
return query.since ? queryProjectBreakdownSince(this.db(), query.since, query.machine) : queryProjectBreakdown(this.db(), query.period ?? "all", query.machine);
|
|
3837
3910
|
}
|
|
3838
3911
|
async accountBreakdown(query = {}) {
|
|
3839
3912
|
return query.since ? queryAccountBreakdownSince(this.db(), query.since) : queryAccountBreakdown(this.db(), query.period ?? "all", query.machine);
|
|
@@ -4029,7 +4102,7 @@ class ApiStore {
|
|
|
4029
4102
|
return cloudListItems(this.cloud, "breakdown", q({ by: "agent", period: query.period, since: query.since }));
|
|
4030
4103
|
}
|
|
4031
4104
|
async projectBreakdown(query = {}) {
|
|
4032
|
-
return cloudListItems(this.cloud, "breakdown", q({ by: "project", period: query.period, since: query.since }));
|
|
4105
|
+
return cloudListItems(this.cloud, "breakdown", q({ by: "project", period: query.period, since: query.since, machine: query.machine }));
|
|
4033
4106
|
}
|
|
4034
4107
|
async accountBreakdown(query = {}) {
|
|
4035
4108
|
return cloudListItems(this.cloud, "breakdown", q({ by: "account", period: query.period, since: query.since }));
|
|
@@ -4393,6 +4466,7 @@ function agentPaths() {
|
|
|
4393
4466
|
var init_paths = () => {};
|
|
4394
4467
|
|
|
4395
4468
|
// src/lib/accounts.ts
|
|
4469
|
+
import { appliedProfileName, resolveStore } from "@hasna/accounts";
|
|
4396
4470
|
function normalizeEmail(email) {
|
|
4397
4471
|
return (email ?? "").trim().toLowerCase();
|
|
4398
4472
|
}
|
|
@@ -4447,57 +4521,35 @@ function envOverride(agent, env) {
|
|
|
4447
4521
|
account_source: "override"
|
|
4448
4522
|
};
|
|
4449
4523
|
}
|
|
4450
|
-
function
|
|
4451
|
-
try {
|
|
4452
|
-
return new Set(api.listTools().map((tool) => tool.id));
|
|
4453
|
-
} catch {
|
|
4454
|
-
return new Set;
|
|
4455
|
-
}
|
|
4456
|
-
}
|
|
4457
|
-
function profileForEnvDir(api, tool, env) {
|
|
4524
|
+
async function profileForEnvDir(store, tool, env) {
|
|
4458
4525
|
const configuredDir = env[tool.envVar];
|
|
4459
4526
|
if (!configuredDir)
|
|
4460
4527
|
return null;
|
|
4461
4528
|
const normalized = normalizeDir(configuredDir);
|
|
4462
|
-
|
|
4463
|
-
return api.listProfiles(tool.id).find((profile) => normalizeDir(profile.dir) === normalized) ?? null;
|
|
4464
|
-
} catch {
|
|
4465
|
-
return null;
|
|
4466
|
-
}
|
|
4529
|
+
return (await store.listProfiles(tool.id)).find((profile) => normalizeDir(profile.dir) === normalized) ?? null;
|
|
4467
4530
|
}
|
|
4468
4531
|
async function resolveAccountForAgent(agent, env = process.env) {
|
|
4469
4532
|
const override = envOverride(agent, env);
|
|
4470
4533
|
if (override)
|
|
4471
4534
|
return override;
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
api = await import("@hasna/accounts");
|
|
4475
|
-
} catch {
|
|
4476
|
-
return null;
|
|
4477
|
-
}
|
|
4478
|
-
const toolIds = knownToolIds(api);
|
|
4535
|
+
const store = resolveStore(env);
|
|
4536
|
+
const tools = new Map((await store.listTools()).map((tool) => [tool.id, tool]));
|
|
4479
4537
|
for (const toolId of AGENT_ACCOUNT_TOOLS[agent]) {
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
let tool;
|
|
4483
|
-
try {
|
|
4484
|
-
tool = api.getTool(toolId);
|
|
4485
|
-
} catch {
|
|
4538
|
+
const tool = tools.get(toolId);
|
|
4539
|
+
if (!tool)
|
|
4486
4540
|
continue;
|
|
4487
|
-
|
|
4488
|
-
const envProfile = profileForEnvDir(api, tool, env);
|
|
4541
|
+
const envProfile = await profileForEnvDir(store, tool, env);
|
|
4489
4542
|
if (envProfile)
|
|
4490
4543
|
return fromProfile(envProfile, "env");
|
|
4491
|
-
|
|
4492
|
-
|
|
4544
|
+
const appliedName = appliedProfileName(toolId);
|
|
4545
|
+
if (appliedName) {
|
|
4546
|
+
const applied = await store.findProfile(appliedName, toolId);
|
|
4493
4547
|
if (applied)
|
|
4494
4548
|
return fromProfile(applied, "applied");
|
|
4495
|
-
}
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
return fromProfile(current, "current");
|
|
4500
|
-
} catch {}
|
|
4549
|
+
}
|
|
4550
|
+
const current = await store.currentProfile(toolId);
|
|
4551
|
+
if (current)
|
|
4552
|
+
return fromProfile(current, "current");
|
|
4501
4553
|
}
|
|
4502
4554
|
return null;
|
|
4503
4555
|
}
|
|
@@ -6363,13 +6415,14 @@ async function syncOpenProjectsRegistry(db, listActiveProjects) {
|
|
|
6363
6415
|
let imported = 0;
|
|
6364
6416
|
let skipped = 0;
|
|
6365
6417
|
for (const project of projects) {
|
|
6366
|
-
|
|
6418
|
+
const path = resolveProjectPath(project);
|
|
6419
|
+
if (!path) {
|
|
6367
6420
|
skipped++;
|
|
6368
6421
|
continue;
|
|
6369
6422
|
}
|
|
6370
6423
|
upsertProject(db, {
|
|
6371
6424
|
id: project.id,
|
|
6372
|
-
path
|
|
6425
|
+
path,
|
|
6373
6426
|
name: project.name,
|
|
6374
6427
|
description: project.description,
|
|
6375
6428
|
tags: project.tags ?? [],
|
|
@@ -6379,6 +6432,7 @@ async function syncOpenProjectsRegistry(db, listActiveProjects) {
|
|
|
6379
6432
|
}
|
|
6380
6433
|
return { imported, skipped };
|
|
6381
6434
|
}
|
|
6435
|
+
var resolveProjectPath = (project) => project.primary_path || project.path || "";
|
|
6382
6436
|
var init_open_projects = __esm(() => {
|
|
6383
6437
|
init_database();
|
|
6384
6438
|
});
|
|
@@ -6743,12 +6797,48 @@ var init_watch = __esm(() => {
|
|
|
6743
6797
|
init_notification();
|
|
6744
6798
|
});
|
|
6745
6799
|
|
|
6800
|
+
// src/db/dialect.ts
|
|
6801
|
+
function translateSql(sql, dialect) {
|
|
6802
|
+
if (dialect === "sqlite")
|
|
6803
|
+
return sql;
|
|
6804
|
+
return sqliteToPostgres(sql);
|
|
6805
|
+
}
|
|
6806
|
+
function translateParams(params) {
|
|
6807
|
+
const flat = params.length === 1 && Array.isArray(params[0]) ? params[0] : params;
|
|
6808
|
+
return flat.map((p) => p === undefined ? null : p);
|
|
6809
|
+
}
|
|
6810
|
+
function sqliteToPostgres(sql) {
|
|
6811
|
+
let out = sql;
|
|
6812
|
+
let paramIdx = 0;
|
|
6813
|
+
out = out.replace(/\?/g, () => `$${++paramIdx}`);
|
|
6814
|
+
out = out.replace(/datetime\s*\(\s*'now'\s*\)/gi, "NOW()");
|
|
6815
|
+
out = out.replace(/datetime\s*\(\s*'now'\s*,\s*'(-?\d+)\s+(minutes?|hours?|days?|seconds?)'\s*\)/gi, (_match, amount, unit) => {
|
|
6816
|
+
const n = parseInt(amount, 10);
|
|
6817
|
+
const absN = Math.abs(n);
|
|
6818
|
+
const normalizedUnit = unit.toLowerCase().replace(/s$/, "");
|
|
6819
|
+
const plural = absN === 1 ? normalizedUnit : `${normalizedUnit}s`;
|
|
6820
|
+
return n < 0 ? `NOW() - INTERVAL '${absN} ${plural}'` : `NOW() + INTERVAL '${absN} ${plural}'`;
|
|
6821
|
+
});
|
|
6822
|
+
out = out.replace(/lower\s*\(\s*hex\s*\(\s*randomblob\s*\(\s*\d+\s*\)\s*\)\s*\)/gi, "gen_random_uuid()::text");
|
|
6823
|
+
out = out.replace(/GROUP_CONCAT\s*\(/gi, "STRING_AGG(");
|
|
6824
|
+
out = out.replace(/json_extract\s*\(\s*(\w+)\s*,\s*'\$\.(\w+)'\s*\)/gi, (_match, col, key) => `${col}->>'${key}'`);
|
|
6825
|
+
out = out.replace(/\bAUTOINCREMENT\b/gi, "GENERATED ALWAYS AS IDENTITY");
|
|
6826
|
+
out = out.replace(/(?<![I])LIKE/gi, "ILIKE");
|
|
6827
|
+
out = out.replace(/\bIFNULL\s*\(/gi, "COALESCE(");
|
|
6828
|
+
out = out.replace(/INSERT\s+OR\s+REPLACE\s+INTO\s+"?(\w+)"?\s*\(([^)]+)\)\s*VALUES/gi, (_match, table2, colList) => `INSERT INTO "${table2}" (${colList}) VALUES`);
|
|
6829
|
+
out = out.replace(/INSERT\s+OR\s+REPLACE\s+INTO/gi, "INSERT INTO");
|
|
6830
|
+
if (/INSERT\s+OR\s+IGNORE\s+INTO/i.test(out)) {
|
|
6831
|
+
out = out.replace(/INSERT\s+OR\s+IGNORE\s+INTO/gi, "INSERT INTO");
|
|
6832
|
+
out = out.replace(/;?\s*$/, " ON CONFLICT DO NOTHING");
|
|
6833
|
+
}
|
|
6834
|
+
return out;
|
|
6835
|
+
}
|
|
6836
|
+
|
|
6746
6837
|
// src/db/sync-pg.ts
|
|
6747
6838
|
import { Worker, MessageChannel, receiveMessageOnPort } from "worker_threads";
|
|
6748
6839
|
import { fileURLToPath } from "url";
|
|
6749
6840
|
import { dirname as dirname3, join as join14 } from "path";
|
|
6750
6841
|
import { existsSync as existsSync15 } from "fs";
|
|
6751
|
-
import { translateSql, translateParams } from "@hasna/cloud";
|
|
6752
6842
|
function translateSqliteDates(sql) {
|
|
6753
6843
|
let s = sql;
|
|
6754
6844
|
const isoDate = (expr) => `to_char(${expr}, 'YYYY-MM-DD')`;
|
|
@@ -6870,11 +6960,18 @@ class SyncPgAdapter {
|
|
|
6870
6960
|
}
|
|
6871
6961
|
var init_sync_pg = () => {};
|
|
6872
6962
|
|
|
6963
|
+
// src/db/pg-migrate.ts
|
|
6964
|
+
import pg from "pg";
|
|
6965
|
+
function resolvePgSsl(dsn) {
|
|
6966
|
+
return dsn.includes("sslmode=require") || dsn.includes("ssl=true") ? { rejectUnauthorized: false } : undefined;
|
|
6967
|
+
}
|
|
6968
|
+
var init_pg_migrate = () => {};
|
|
6969
|
+
|
|
6873
6970
|
// src/db/pg-migrations.ts
|
|
6874
6971
|
var init_pg_migrations = () => {};
|
|
6875
6972
|
|
|
6876
6973
|
// src/db/cloud.ts
|
|
6877
|
-
import
|
|
6974
|
+
import pg2 from "pg";
|
|
6878
6975
|
function getCloudDatabaseUrl() {
|
|
6879
6976
|
return process.env["HASNA_ECONOMY_DATABASE_URL"]?.trim() || process.env["ECONOMY_DATABASE_URL"]?.trim() || process.env["DATABASE_URL"]?.trim() || undefined;
|
|
6880
6977
|
}
|
|
@@ -6898,8 +6995,7 @@ function openCloudDatabase(dsn = getCloudDatabaseUrl()) {
|
|
|
6898
6995
|
function createCloudPool(dsn = getCloudDatabaseUrl()) {
|
|
6899
6996
|
if (!dsn)
|
|
6900
6997
|
throw new Error("economy cloud mode requires a Postgres DSN (HASNA_ECONOMY_DATABASE_URL)");
|
|
6901
|
-
const
|
|
6902
|
-
const pool = new pg.Pool({ connectionString: dsn, ssl, max: Number(process.env["ECONOMY_PG_POOL_MAX"] ?? "5"), connectionTimeoutMillis: 1e4 });
|
|
6998
|
+
const pool = new pg2.Pool({ connectionString: dsn, ssl: resolvePgSsl(dsn), max: Number(process.env["ECONOMY_PG_POOL_MAX"] ?? "5"), connectionTimeoutMillis: 1e4 });
|
|
6903
6999
|
pool.on("error", (err) => {
|
|
6904
7000
|
console.error(JSON.stringify({ evt: "pg_pool_error", message: err.message }));
|
|
6905
7001
|
});
|
|
@@ -6922,6 +7018,7 @@ function authClientFromPool(pool) {
|
|
|
6922
7018
|
}
|
|
6923
7019
|
var init_cloud = __esm(() => {
|
|
6924
7020
|
init_sync_pg();
|
|
7021
|
+
init_pg_migrate();
|
|
6925
7022
|
init_pg_migrations();
|
|
6926
7023
|
});
|
|
6927
7024
|
|
|
@@ -7690,6 +7787,15 @@ var init_openapi = __esm(() => {
|
|
|
7690
7787
|
type: "string"
|
|
7691
7788
|
}
|
|
7692
7789
|
},
|
|
7790
|
+
{
|
|
7791
|
+
name: "since",
|
|
7792
|
+
in: "query",
|
|
7793
|
+
description: "ISO timestamp lower bound; takes precedence over period",
|
|
7794
|
+
schema: {
|
|
7795
|
+
type: "string",
|
|
7796
|
+
format: "date-time"
|
|
7797
|
+
}
|
|
7798
|
+
},
|
|
7693
7799
|
{
|
|
7694
7800
|
name: "machine",
|
|
7695
7801
|
in: "query",
|
|
@@ -8783,9 +8889,10 @@ function createHandler(db, options = {}) {
|
|
|
8783
8889
|
if (path === "/api/breakdown" && method === "GET") {
|
|
8784
8890
|
const by = url.searchParams.get("by") ?? "model";
|
|
8785
8891
|
const period = url.searchParams.get("period") ?? "all";
|
|
8892
|
+
const since = url.searchParams.get("since") ?? undefined;
|
|
8786
8893
|
const machine = url.searchParams.get("machine") ?? undefined;
|
|
8787
8894
|
if (by === "project")
|
|
8788
|
-
return ok(queryProjectBreakdown(db, period, machine));
|
|
8895
|
+
return ok(since ? queryProjectBreakdownSince(db, since, machine) : queryProjectBreakdown(db, period, machine));
|
|
8789
8896
|
if (by === "agent")
|
|
8790
8897
|
return ok(queryAgentBreakdown(db, period, machine));
|
|
8791
8898
|
if (by === "account")
|
|
@@ -10038,7 +10145,7 @@ var ROADMAP_PHASES = [
|
|
|
10038
10145
|
{
|
|
10039
10146
|
id: "phase-9",
|
|
10040
10147
|
title: "Multi-machine auto sync (4-machine fleet)",
|
|
10041
|
-
summary: "Investigation: machine_id + listMachines + manual cloud push/pull exist;
|
|
10148
|
+
summary: "Investigation: machine_id + listMachines + manual cloud push/pull exist; the (since-retired) shared sync package had incremental sync, conflict resolution, and launchd/systemd schedulers \u2014 but economy does not use them, requests/sessions lack updated_at, ingest is local-only, RDS is hardcoded, and default commands never pull before query.",
|
|
10042
10149
|
tasks: [
|
|
10043
10150
|
{
|
|
10044
10151
|
id: "9.1",
|
|
@@ -10053,7 +10160,7 @@ var ROADMAP_PHASES = [
|
|
|
10053
10160
|
},
|
|
10054
10161
|
{
|
|
10055
10162
|
id: "9.3",
|
|
10056
|
-
title: "Switch cloud sync to
|
|
10163
|
+
title: "Switch cloud sync to incrementalSyncPush/Pull with _sync_meta (replace full table scans)",
|
|
10057
10164
|
status: "done",
|
|
10058
10165
|
deps: ["9.2"]
|
|
10059
10166
|
},
|
|
@@ -10077,7 +10184,7 @@ var ROADMAP_PHASES = [
|
|
|
10077
10184
|
},
|
|
10078
10185
|
{
|
|
10079
10186
|
id: "9.7",
|
|
10080
|
-
title: "CLI economy cloud schedule install|status|remove \u2014 wrap
|
|
10187
|
+
title: "CLI economy cloud schedule install|status|remove \u2014 wrap registerSyncSchedule (launchd/systemd every 5\u201315m)",
|
|
10081
10188
|
status: "done",
|
|
10082
10189
|
deps: ["9.5"]
|
|
10083
10190
|
},
|
|
@@ -10397,10 +10504,10 @@ function printTodosHelp() {
|
|
|
10397
10504
|
console.log(" economy todos list --phase <id> Tasks for one phase (e.g. phase-9)");
|
|
10398
10505
|
console.log(" economy todos show <task-id> Task detail (e.g. 9.7)");
|
|
10399
10506
|
console.log();
|
|
10400
|
-
console.log(chalk2.bold(" Multi-machine
|
|
10401
|
-
console.log("
|
|
10402
|
-
console.log("
|
|
10403
|
-
console.log("
|
|
10507
|
+
console.log(chalk2.bold(" Multi-machine storage"));
|
|
10508
|
+
console.log(" local on-box SQLite; economy sync ingests this machine's agent data");
|
|
10509
|
+
console.log(" cloud CLI/MCP reads and writes the shared /v1 API directly with URL + key");
|
|
10510
|
+
console.log(" fleet machine_id keeps shared summaries and per-machine filters distinct");
|
|
10404
10511
|
console.log();
|
|
10405
10512
|
console.log(chalk2.bold(" Agents covered"));
|
|
10406
10513
|
console.log(" claude Claude Code telemetry + Pro/Max quota (partial)");
|
|
@@ -10765,7 +10872,7 @@ function registerExtendedCommands(program) {
|
|
|
10765
10872
|
${chalk4.dim(spendLabel)} ${fmt2(summary.total_usd)}`);
|
|
10766
10873
|
console.log();
|
|
10767
10874
|
});
|
|
10768
|
-
program.command("savings [period]").description("Subscription vs API-equivalent savings breakdown").option("--agent <agent>",
|
|
10875
|
+
program.command("savings [period]").description("Subscription vs API-equivalent savings breakdown").option("--agent <agent>", `Filter by agent (${AGENTS.join("|")})`).option("--json", "Output JSON").action(async (periodArg, opts) => {
|
|
10769
10876
|
const period = parsePeriod(periodArg, "month");
|
|
10770
10877
|
const agent = parseAgent(opts.agent, "--agent");
|
|
10771
10878
|
const savings = await getStore().savings(period, agent);
|
|
@@ -10784,7 +10891,7 @@ function registerExtendedCommands(program) {
|
|
|
10784
10891
|
console.log();
|
|
10785
10892
|
});
|
|
10786
10893
|
const subsCmd = program.command("subscriptions").description("Manage subscription plans");
|
|
10787
|
-
subsCmd.command("set").description("Set a subscription plan").requiredOption("--provider <name>", "Provider name").requiredOption("--plan <name>", "Plan name").option("--agent <agent>",
|
|
10894
|
+
subsCmd.command("set").description("Set a subscription plan").requiredOption("--provider <name>", "Provider name").requiredOption("--plan <name>", "Plan name").option("--agent <agent>", `Agent scope (${AGENTS.join("|")})`).option("--fee <usd>", "Monthly fee USD", "0").option("--included <usd>", "Included usage USD", "0").action(async (opts) => {
|
|
10788
10895
|
const agent = parseAgent(opts.agent, "--agent") ?? null;
|
|
10789
10896
|
await getStore().setSubscription({
|
|
10790
10897
|
agent,
|
|
@@ -11115,7 +11222,7 @@ init_store();
|
|
|
11115
11222
|
init_transport();
|
|
11116
11223
|
import { execSync } from "child_process";
|
|
11117
11224
|
var program = new Command;
|
|
11118
|
-
program.name("economy").description("AI coding cost tracker \u2014 Claude, Codex, Gemini, OpenCode, Cursor, Pi, Hermes").version(packageMetadata.version);
|
|
11225
|
+
program.name("economy").description("AI coding cost tracker \u2014 Claude, Takumi, Codex, Gemini, OpenCode, Cursor, Pi, Hermes").version(packageMetadata.version);
|
|
11119
11226
|
async function autoSync(opts = {}) {
|
|
11120
11227
|
if (isCloudStore())
|
|
11121
11228
|
return;
|
|
@@ -11429,7 +11536,7 @@ program.command("month").description("Cost summary for this month").action(async
|
|
|
11429
11536
|
await autoSync();
|
|
11430
11537
|
await printSummary("This Month", "month");
|
|
11431
11538
|
});
|
|
11432
|
-
program.command("sessions").description("List coding sessions with costs").option("--agent <agent>",
|
|
11539
|
+
program.command("sessions").description("List coding sessions with costs").option("--agent <agent>", `Filter by agent (${AGENTS.join("|")})`).option("--project <path>", "Filter by project path").option("--account <query>", "Filter by account key, name, or email").option("--machine <id>", "Filter by machine hostname (e.g. spark01, apple01)").option("--limit <n>", "Number of sessions", "20").option("--format <fmt>", "Output format: table|compact|csv|json", "table").option("--since <date>", "Filter sessions since date or relative (e.g. 2026-03-01, 7d, 30d)").option("--search <query>", "Search by project name, session id prefix, or agent").action(async (opts) => {
|
|
11433
11540
|
const limit = parsePositiveCliInteger(opts.limit ?? "20", "--limit");
|
|
11434
11541
|
const agent = parseOptionalCliAgent(opts.agent);
|
|
11435
11542
|
await autoSync();
|
|
@@ -11476,7 +11583,7 @@ program.command("sessions").description("List coding sessions with costs").optio
|
|
|
11476
11583
|
]));
|
|
11477
11584
|
console.log();
|
|
11478
11585
|
});
|
|
11479
|
-
program.command("top").description("Most expensive sessions").option("-n <n>", "Number of sessions", "10").option("--agent <agent>",
|
|
11586
|
+
program.command("top").description("Most expensive sessions").option("-n <n>", "Number of sessions", "10").option("--agent <agent>", `Filter by agent (${AGENTS.join("|")})`).option("--since <date>", "Filter sessions since date or relative (e.g. 2026-03-01, 7d, 30d)").action(async (opts) => {
|
|
11480
11587
|
const count = parsePositiveCliInteger(opts.n ?? "10", "-n");
|
|
11481
11588
|
const agent = parseOptionalCliAgent(opts.agent);
|
|
11482
11589
|
const sinceDate = opts.since ? parseSinceDate2(opts.since) : undefined;
|
|
@@ -11607,7 +11714,7 @@ program.command("accounts [period]").description("List account usage by email ad
|
|
|
11607
11714
|
printHiddenRowsHint(rows.length, visibleRows.length, "Use --limit <n>, --verbose, or --json.");
|
|
11608
11715
|
console.log();
|
|
11609
11716
|
});
|
|
11610
|
-
program.command("watch").description("Live stream of incoming costs").option("--interval <seconds>", "Poll interval in seconds", "10").option("--daemon", "Watch agent data directories and sync on change").option("--agent <agent>",
|
|
11717
|
+
program.command("watch").description("Live stream of incoming costs").option("--interval <seconds>", "Poll interval in seconds", "10").option("--daemon", "Watch agent data directories and sync on change").option("--agent <agent>", `Filter by agent (${AGENTS.join("|")})`).option("--notify <amount>", "Fire macOS notification when cumulative cost crosses this USD threshold").action(async (opts) => {
|
|
11611
11718
|
const { watchCosts: watchCosts2 } = await Promise.resolve().then(() => (init_watch(), exports_watch));
|
|
11612
11719
|
await watchCosts2({
|
|
11613
11720
|
interval: parsePositiveCliInteger(opts.interval ?? "10", "--interval"),
|
|
@@ -11617,7 +11724,7 @@ program.command("watch").description("Live stream of incoming costs").option("--
|
|
|
11617
11724
|
});
|
|
11618
11725
|
});
|
|
11619
11726
|
var budgetCmd = program.command("budget").description("Manage spending budgets");
|
|
11620
|
-
budgetCmd.command("set").description("Set a budget").option("--project <path>", "Project path (omit for global)").option("--cost-center <id>", "Cost center id (for example loop:fleet-evaluator)").option("--period <period>", "Period: daily|weekly|monthly", "monthly").option("--limit <usd>", "Budget limit in USD").option("--alert <percent>", "Alert threshold %", "80").option("--agent <agent>",
|
|
11727
|
+
budgetCmd.command("set").description("Set a budget").option("--project <path>", "Project path (omit for global)").option("--cost-center <id>", "Cost center id (for example loop:fleet-evaluator)").option("--period <period>", "Period: daily|weekly|monthly", "monthly").option("--limit <usd>", "Budget limit in USD").option("--alert <percent>", "Alert threshold %", "80").option("--agent <agent>", `Limit to agent (${AGENTS.join("|")})`).action(async (opts) => {
|
|
11621
11728
|
const limitUsd = parsePositiveCliNumber(opts.limit, "--limit");
|
|
11622
11729
|
const alertAtPercent = parsePositiveCliNumber(opts.alert ?? "80", "--alert");
|
|
11623
11730
|
if (alertAtPercent > 100)
|
|
@@ -12126,7 +12233,7 @@ menubarCmd.command("stop").description("Quit Economy Bar").action(async () => {
|
|
|
12126
12233
|
menubarStop2();
|
|
12127
12234
|
});
|
|
12128
12235
|
var goalCmd = program.command("goal").description("Manage spending goals");
|
|
12129
|
-
goalCmd.command("set").description("Set a spending goal").option("--period <period>", "Period: day|week|month|year", "month").option("--limit <usd>", "Goal limit in USD").option("--project <path>", "Scope to project path").option("--agent <agent>",
|
|
12236
|
+
goalCmd.command("set").description("Set a spending goal").option("--period <period>", "Period: day|week|month|year", "month").option("--limit <usd>", "Goal limit in USD").option("--project <path>", "Scope to project path").option("--agent <agent>", `Scope to agent (${AGENTS.join("|")})`).action(async (opts) => {
|
|
12130
12237
|
const limitUsd = parsePositiveCliNumber(opts.limit, "--limit");
|
|
12131
12238
|
const period = requireCliChoice(opts.period, "--period", ["day", "week", "month", "year"]);
|
|
12132
12239
|
const agent = parseOptionalCliAgent(opts.agent) ?? null;
|
package/dist/db/cloud.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { DbAdapter } from '@hasna/cloud';
|
|
2
1
|
import type { AuthQueryClient } from '@hasna/contracts/auth';
|
|
3
2
|
import pg from 'pg';
|
|
4
|
-
import type { SqliteAdapter as Database } from '
|
|
3
|
+
import type { DbAdapter, SqliteAdapter as Database } from './sqlite-adapter.js';
|
|
5
4
|
/** Resolve the cloud Postgres DSN from the standard env aliases. */
|
|
6
5
|
export declare function getCloudDatabaseUrl(): string | undefined;
|
|
7
6
|
/** True when the serve should run in cloud (RDS-direct) mode. */
|
|
@@ -10,7 +9,7 @@ export declare function isCloudMode(): boolean;
|
|
|
10
9
|
export declare function resolveSigningSecret(): string | undefined;
|
|
11
10
|
/**
|
|
12
11
|
* Open a pooled Postgres connection to the shared RDS. Returns a value typed as
|
|
13
|
-
* the SQLite `Database` the query layer expects — safe because `
|
|
12
|
+
* the SQLite `Database` the query layer expects — safe because `SyncPgAdapter`
|
|
14
13
|
* implements the identical `DbAdapter` surface those functions rely on.
|
|
15
14
|
*/
|
|
16
15
|
export declare function openCloudDatabase(dsn?: string | undefined): Database;
|
|
@@ -19,7 +18,7 @@ export declare function createCloudPool(dsn?: string | undefined): pg.Pool;
|
|
|
19
18
|
/**
|
|
20
19
|
* Adapt a pg Pool to the `@hasna/contracts/auth` `AuthQueryClient` surface
|
|
21
20
|
* (many/get/execute). This backs the `ApiKeyStore` without pulling in the
|
|
22
|
-
* generic vendored kit — economy's storage is
|
|
21
|
+
* generic vendored kit — economy's storage is this pg pool.
|
|
23
22
|
*/
|
|
24
23
|
export declare function authClientFromPool(pool: pg.Pool): AuthQueryClient;
|
|
25
24
|
/** Ordered PG migrations applied by the migration runner (index = version). */
|
package/dist/db/cloud.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloud.d.ts","sourceRoot":"","sources":["../../src/db/cloud.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"cloud.d.ts","sourceRoot":"","sources":["../../src/db/cloud.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC5D,OAAO,EAAE,MAAM,IAAI,CAAA;AACnB,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAK/E,oEAAoE;AACpE,wBAAgB,mBAAmB,IAAI,MAAM,GAAG,SAAS,CAOxD;AAED,iEAAiE;AACjE,wBAAgB,WAAW,IAAI,OAAO,CAKrC;AAED,oFAAoF;AACpF,wBAAgB,oBAAoB,IAAI,MAAM,GAAG,SAAS,CAOzD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,qBAAwB,GAAG,QAAQ,CASvE;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,GAAG,qBAAwB,GAAG,EAAE,CAAC,IAAI,CAWpE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,eAAe,CAcjE;AAED,+EAA+E;AAC/E,wBAAgB,eAAe,IAAI,MAAM,EAAE,CAE1C;AAED,YAAY,EAAE,SAAS,EAAE,CAAA"}
|
package/dist/db/database.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SqliteAdapter as Database } from '
|
|
1
|
+
import { SqliteAdapter as Database } from './sqlite-adapter.js';
|
|
2
2
|
import type { EconomyRequest, EconomySession, EconomyProject, CostCenter, CostCenterBreakdown, CostCenterKind, Budget, BudgetStatus, CostSummary, ZeroCostModelBreakdown, ModelBreakdown, ProjectBreakdown, AgentBreakdown, AccountBreakdown, Period, Agent, SessionFilter } from '../types/index.js';
|
|
3
3
|
export declare function getMachineId(): string;
|
|
4
4
|
export declare function getDataDir(): string;
|
|
@@ -22,7 +22,7 @@ export declare function queryCostCenterBreakdown(db: Database, period?: Period,
|
|
|
22
22
|
machine?: string;
|
|
23
23
|
since?: string;
|
|
24
24
|
}): CostCenterBreakdown[];
|
|
25
|
-
export declare function queryProjectBreakdownSince(db: Database, since: string): ProjectBreakdown[];
|
|
25
|
+
export declare function queryProjectBreakdownSince(db: Database, since: string, machine?: string): ProjectBreakdown[];
|
|
26
26
|
export declare function queryAgentBreakdownSince(db: Database, since: string): AgentBreakdown[];
|
|
27
27
|
export declare function queryModelBreakdownSince(db: Database, since: string): ModelBreakdown[];
|
|
28
28
|
export declare function queryAccountBreakdownSince(db: Database, since: string): AccountBreakdown[];
|