@hasna/economy 0.3.26 → 0.3.28
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 +15 -0
- package/README.md +3 -3
- package/dist/cli/index.js +189 -48
- package/dist/db/database.d.ts +39 -0
- package/dist/db/database.d.ts.map +1 -1
- package/dist/index.js +142 -14
- package/dist/ingest/loops.d.ts +10 -0
- package/dist/ingest/loops.d.ts.map +1 -1
- package/dist/lib/model-config.d.ts +1 -1
- package/dist/lib/model-config.d.ts.map +1 -1
- package/dist/lib/package-metadata.d.ts.map +1 -1
- package/dist/mcp/index.js +170 -34
- package/dist/otel/index.js +142 -14
- package/dist/server/index.js +161 -25
- package/package.json +8 -2
- package/postinstall.js +31 -0
package/dist/otel/index.js
CHANGED
|
@@ -594,7 +594,8 @@ import { execFileSync } from "child_process";
|
|
|
594
594
|
import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from "fs";
|
|
595
595
|
import { hostname, platform } from "os";
|
|
596
596
|
import { homedir } from "os";
|
|
597
|
-
import { join } from "path";
|
|
597
|
+
import { join, resolve } from "path";
|
|
598
|
+
import { dataDir } from "@hasna/paths";
|
|
598
599
|
function normalizeMachineId(value) {
|
|
599
600
|
const id = value?.trim().toLowerCase().split(".")[0];
|
|
600
601
|
return id && id.length > 0 ? id : null;
|
|
@@ -625,21 +626,48 @@ function getMachineId() {
|
|
|
625
626
|
return macHostMachineId() ?? hostMachine;
|
|
626
627
|
return hostMachine;
|
|
627
628
|
}
|
|
628
|
-
function
|
|
629
|
+
function getHomeDir() {
|
|
629
630
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir();
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
631
|
+
if (!home)
|
|
632
|
+
throw new Error("Could not resolve the user home directory");
|
|
633
|
+
return home;
|
|
634
|
+
}
|
|
635
|
+
function getResolverDataRoot() {
|
|
636
|
+
return dataDir({
|
|
637
|
+
app: "economy",
|
|
638
|
+
home: process.env["HOME"] || process.env["USERPROFILE"] || undefined
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
function getLegacyDataRoot() {
|
|
642
|
+
return join(getHomeDir(), ".hasna", "economy");
|
|
643
|
+
}
|
|
644
|
+
function adoptResolverDataRoot(resolved, env = process.env) {
|
|
645
|
+
const dataOverride = env.HASNA_DATA_HOME;
|
|
646
|
+
if (typeof dataOverride === "string" && dataOverride.trim().length > 0)
|
|
647
|
+
return true;
|
|
648
|
+
return existsSync(join(resolved, "economy.db"));
|
|
649
|
+
}
|
|
650
|
+
function getExactDataRoot() {
|
|
651
|
+
const dir = process.env["HASNA_ECONOMY_HOME"] ?? process.env["ECONOMY_HOME"];
|
|
652
|
+
if (dir && dir.trim())
|
|
653
|
+
return dir.trim();
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
function getDataDir() {
|
|
657
|
+
const exact = getExactDataRoot();
|
|
658
|
+
const effective = exact ? resolve(exact) : adoptResolverDataRoot(getResolverDataRoot()) ? resolve(getResolverDataRoot()) : getLegacyDataRoot();
|
|
659
|
+
const oldDir = join(getHomeDir(), ".economy");
|
|
660
|
+
if (existsSync(oldDir) && !existsSync(effective)) {
|
|
661
|
+
mkdirSync(effective, { recursive: true });
|
|
634
662
|
for (const file of readdirSync(oldDir)) {
|
|
635
663
|
const oldPath = join(oldDir, file);
|
|
636
664
|
if (statSync(oldPath).isFile()) {
|
|
637
|
-
copyFileSync(oldPath, join(
|
|
665
|
+
copyFileSync(oldPath, join(effective, file));
|
|
638
666
|
}
|
|
639
667
|
}
|
|
640
668
|
}
|
|
641
|
-
mkdirSync(
|
|
642
|
-
return
|
|
669
|
+
mkdirSync(effective, { recursive: true });
|
|
670
|
+
return effective;
|
|
643
671
|
}
|
|
644
672
|
function getDbPath() {
|
|
645
673
|
if (process.env["HASNA_ECONOMY_DB_PATH"])
|
|
@@ -1385,18 +1413,118 @@ async function ingestOtelRows(db, rows) {
|
|
|
1385
1413
|
rollupSession(db, sessionId);
|
|
1386
1414
|
return { requests, sessions: sessions.size };
|
|
1387
1415
|
}
|
|
1416
|
+
// package.json
|
|
1417
|
+
var package_default = {
|
|
1418
|
+
name: "@hasna/economy",
|
|
1419
|
+
version: "0.3.28",
|
|
1420
|
+
description: "AI coding cost tracker \u2014 CLI + MCP server + REST API + web dashboard for Claude Code, Codex, OpenCode, Cursor, Pi, and Hermes, with legacy Gemini CLI session and Gemini API billing ingestion",
|
|
1421
|
+
repository: {
|
|
1422
|
+
type: "git",
|
|
1423
|
+
url: "git+https://github.com/hasna/economy.git"
|
|
1424
|
+
},
|
|
1425
|
+
bugs: {
|
|
1426
|
+
url: "https://github.com/hasna/economy/issues"
|
|
1427
|
+
},
|
|
1428
|
+
homepage: "https://github.com/hasna/economy#readme",
|
|
1429
|
+
type: "module",
|
|
1430
|
+
main: "dist/index.js",
|
|
1431
|
+
types: "dist/index.d.ts",
|
|
1432
|
+
bin: {
|
|
1433
|
+
economy: "dist/cli/index.js",
|
|
1434
|
+
"economy-mcp": "dist/mcp/index.js",
|
|
1435
|
+
"economy-serve": "dist/server/index.js",
|
|
1436
|
+
"economy-otel": "dist/otel/index.js"
|
|
1437
|
+
},
|
|
1438
|
+
exports: {
|
|
1439
|
+
".": {
|
|
1440
|
+
types: "./dist/index.d.ts",
|
|
1441
|
+
import: "./dist/index.js"
|
|
1442
|
+
},
|
|
1443
|
+
"./sdk": {
|
|
1444
|
+
types: "./dist/index.d.ts",
|
|
1445
|
+
import: "./dist/index.js"
|
|
1446
|
+
}
|
|
1447
|
+
},
|
|
1448
|
+
files: [
|
|
1449
|
+
"dist",
|
|
1450
|
+
"docs",
|
|
1451
|
+
"dashboard/dist",
|
|
1452
|
+
"postinstall.js",
|
|
1453
|
+
"LICENSE",
|
|
1454
|
+
"README.md",
|
|
1455
|
+
"CHANGELOG.md",
|
|
1456
|
+
"SECURITY.md",
|
|
1457
|
+
"CONTRIBUTING.md",
|
|
1458
|
+
"CODE_OF_CONDUCT.md"
|
|
1459
|
+
],
|
|
1460
|
+
scripts: {
|
|
1461
|
+
build: "cd dashboard && bun install --frozen-lockfile && bun run build && cd .. && bun build src/cli/index.ts --outdir dist/cli --target bun --packages external && bun build src/mcp/index.ts --outdir dist/mcp --target bun --packages external && bun build src/server/index.ts --outdir dist/server --target bun --packages external && bun build src/db/pg-sync-worker.ts --outdir dist/server --target bun --packages external && bun build src/otel/index.ts --outdir dist/otel --target bun --packages external && bun build src/index.ts --outdir dist --target bun --packages external && tsc --emitDeclarationOnly --outDir dist",
|
|
1462
|
+
"build:cli": "bun build src/cli/index.ts --outdir dist/cli --target bun --packages external",
|
|
1463
|
+
"build:mcp": "bun build src/mcp/index.ts --outdir dist/mcp --target bun --packages external",
|
|
1464
|
+
"build:server": "bun build src/server/index.ts --outdir dist/server --target bun --packages external",
|
|
1465
|
+
"build:lib": "bun build src/index.ts --outdir dist --target bun --packages external",
|
|
1466
|
+
"build:dashboard": "cd dashboard && bun run build",
|
|
1467
|
+
"artifact-scan": "bun scripts/contracts-cli.mjs artifact-scan dist",
|
|
1468
|
+
prepack: "bun run build && bun run artifact-scan",
|
|
1469
|
+
typecheck: "tsc --noEmit",
|
|
1470
|
+
test: "bun test",
|
|
1471
|
+
"dev:cli": "bun run src/cli/index.ts",
|
|
1472
|
+
"dev:mcp": "bun run src/mcp/index.ts",
|
|
1473
|
+
"dev:serve": "bun run src/server/index.ts",
|
|
1474
|
+
"dev:otel": "bun run src/otel/index.ts",
|
|
1475
|
+
postinstall: "node postinstall.js"
|
|
1476
|
+
},
|
|
1477
|
+
keywords: [
|
|
1478
|
+
"economy",
|
|
1479
|
+
"cost",
|
|
1480
|
+
"ai",
|
|
1481
|
+
"claude",
|
|
1482
|
+
"codex",
|
|
1483
|
+
"gemini",
|
|
1484
|
+
"mcp",
|
|
1485
|
+
"cli",
|
|
1486
|
+
"budget",
|
|
1487
|
+
"tracking"
|
|
1488
|
+
],
|
|
1489
|
+
author: "hasna",
|
|
1490
|
+
license: "Apache-2.0",
|
|
1491
|
+
publishConfig: {
|
|
1492
|
+
registry: "https://registry.npmjs.org",
|
|
1493
|
+
access: "public"
|
|
1494
|
+
},
|
|
1495
|
+
dependencies: {
|
|
1496
|
+
"@hasna/accounts": "0.2.23",
|
|
1497
|
+
"@hasna/agent-registry": "^0.1.0",
|
|
1498
|
+
"@hasna/contracts": "0.13.4",
|
|
1499
|
+
"@hasna/events": "^0.1.16",
|
|
1500
|
+
"@hasna/mcp-harness": "^0.1.0",
|
|
1501
|
+
"@hasna/paths": "0.1.0",
|
|
1502
|
+
"@hasna/projects": "1.0.0",
|
|
1503
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
1504
|
+
chalk: "^5.4.1",
|
|
1505
|
+
commander: "^13.1.0",
|
|
1506
|
+
pg: "8.13.1",
|
|
1507
|
+
zod: "^3.24.2"
|
|
1508
|
+
},
|
|
1509
|
+
devDependencies: {
|
|
1510
|
+
"@types/bun": "latest",
|
|
1511
|
+
"@types/pg": "^8.20.0",
|
|
1512
|
+
"bun-types": "latest",
|
|
1513
|
+
typescript: "^5.7.2"
|
|
1514
|
+
},
|
|
1515
|
+
trustedDependencies: [
|
|
1516
|
+
"@hasna/projects"
|
|
1517
|
+
]
|
|
1518
|
+
};
|
|
1388
1519
|
|
|
1389
1520
|
// src/lib/package-metadata.ts
|
|
1390
|
-
import { readFileSync } from "fs";
|
|
1391
1521
|
var cachedMetadata = null;
|
|
1392
1522
|
function getPackageMetadata() {
|
|
1393
1523
|
if (cachedMetadata)
|
|
1394
1524
|
return cachedMetadata;
|
|
1395
|
-
const raw = readFileSync(new URL("../../package.json", import.meta.url), "utf8");
|
|
1396
|
-
const parsed = JSON.parse(raw);
|
|
1397
1525
|
cachedMetadata = {
|
|
1398
|
-
name:
|
|
1399
|
-
version:
|
|
1526
|
+
name: package_default.name ?? "@hasna/economy",
|
|
1527
|
+
version: package_default.version ?? "0.0.0"
|
|
1400
1528
|
};
|
|
1401
1529
|
return cachedMetadata;
|
|
1402
1530
|
}
|
package/dist/server/index.js
CHANGED
|
@@ -595,7 +595,8 @@ import { execFileSync } from "child_process";
|
|
|
595
595
|
import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from "fs";
|
|
596
596
|
import { hostname, platform } from "os";
|
|
597
597
|
import { homedir } from "os";
|
|
598
|
-
import { join } from "path";
|
|
598
|
+
import { join, resolve } from "path";
|
|
599
|
+
import { dataDir } from "@hasna/paths";
|
|
599
600
|
function normalizeMachineId(value) {
|
|
600
601
|
const id = value?.trim().toLowerCase().split(".")[0];
|
|
601
602
|
return id && id.length > 0 ? id : null;
|
|
@@ -626,21 +627,48 @@ function getMachineId() {
|
|
|
626
627
|
return macHostMachineId() ?? hostMachine;
|
|
627
628
|
return hostMachine;
|
|
628
629
|
}
|
|
629
|
-
function
|
|
630
|
+
function getHomeDir() {
|
|
630
631
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir();
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
632
|
+
if (!home)
|
|
633
|
+
throw new Error("Could not resolve the user home directory");
|
|
634
|
+
return home;
|
|
635
|
+
}
|
|
636
|
+
function getResolverDataRoot() {
|
|
637
|
+
return dataDir({
|
|
638
|
+
app: "economy",
|
|
639
|
+
home: process.env["HOME"] || process.env["USERPROFILE"] || undefined
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
function getLegacyDataRoot() {
|
|
643
|
+
return join(getHomeDir(), ".hasna", "economy");
|
|
644
|
+
}
|
|
645
|
+
function adoptResolverDataRoot(resolved, env = process.env) {
|
|
646
|
+
const dataOverride = env.HASNA_DATA_HOME;
|
|
647
|
+
if (typeof dataOverride === "string" && dataOverride.trim().length > 0)
|
|
648
|
+
return true;
|
|
649
|
+
return existsSync(join(resolved, "economy.db"));
|
|
650
|
+
}
|
|
651
|
+
function getExactDataRoot() {
|
|
652
|
+
const dir = process.env["HASNA_ECONOMY_HOME"] ?? process.env["ECONOMY_HOME"];
|
|
653
|
+
if (dir && dir.trim())
|
|
654
|
+
return dir.trim();
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
function getDataDir() {
|
|
658
|
+
const exact = getExactDataRoot();
|
|
659
|
+
const effective = exact ? resolve(exact) : adoptResolverDataRoot(getResolverDataRoot()) ? resolve(getResolverDataRoot()) : getLegacyDataRoot();
|
|
660
|
+
const oldDir = join(getHomeDir(), ".economy");
|
|
661
|
+
if (existsSync(oldDir) && !existsSync(effective)) {
|
|
662
|
+
mkdirSync(effective, { recursive: true });
|
|
635
663
|
for (const file of readdirSync(oldDir)) {
|
|
636
664
|
const oldPath = join(oldDir, file);
|
|
637
665
|
if (statSync(oldPath).isFile()) {
|
|
638
|
-
copyFileSync(oldPath, join(
|
|
666
|
+
copyFileSync(oldPath, join(effective, file));
|
|
639
667
|
}
|
|
640
668
|
}
|
|
641
669
|
}
|
|
642
|
-
mkdirSync(
|
|
643
|
-
return
|
|
670
|
+
mkdirSync(effective, { recursive: true });
|
|
671
|
+
return effective;
|
|
644
672
|
}
|
|
645
673
|
function getDbPath() {
|
|
646
674
|
if (process.env["HASNA_ECONOMY_DB_PATH"])
|
|
@@ -2874,7 +2902,7 @@ __export(exports_billing, {
|
|
|
2874
2902
|
syncGeminiBilling: () => syncGeminiBilling,
|
|
2875
2903
|
syncAnthropicBilling: () => syncAnthropicBilling
|
|
2876
2904
|
});
|
|
2877
|
-
import { readFileSync as
|
|
2905
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
2878
2906
|
function getAnthropicAdminKey() {
|
|
2879
2907
|
return process.env["HASNAXYZ_ANTHROPIC_LIVE_ADMIN_API_KEY"] ?? process.env["ANTHROPIC_ADMIN_API_KEY"] ?? null;
|
|
2880
2908
|
}
|
|
@@ -3101,7 +3129,7 @@ async function syncGeminiBilling(db, opts = {}) {
|
|
|
3101
3129
|
const start = opts.fromDate ? new Date(opts.fromDate) : new Date(end.getTime() - days * 24 * 3600000);
|
|
3102
3130
|
const fromDateStr = toISODate(start);
|
|
3103
3131
|
const toDateStr = toISODate(end);
|
|
3104
|
-
const rows = parseBillingRows(
|
|
3132
|
+
const rows = parseBillingRows(readFileSync8(exportPath, "utf-8"));
|
|
3105
3133
|
clearBillingRange(db, "gemini", fromDateStr, toDateStr);
|
|
3106
3134
|
const updatedAt = new Date().toISOString();
|
|
3107
3135
|
let totalUsd = 0;
|
|
@@ -3165,7 +3193,7 @@ var init_open_projects = __esm(() => {
|
|
|
3165
3193
|
});
|
|
3166
3194
|
|
|
3167
3195
|
// src/lib/config.ts
|
|
3168
|
-
import { existsSync as existsSync12, readFileSync as
|
|
3196
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
|
|
3169
3197
|
import { dirname as dirname2, join as join11 } from "path";
|
|
3170
3198
|
function getConfigPath() {
|
|
3171
3199
|
return process.env["HASNA_ECONOMY_CONFIG_PATH"] ?? join11(getDataDir(), "config.json");
|
|
@@ -3174,7 +3202,7 @@ function loadConfig() {
|
|
|
3174
3202
|
try {
|
|
3175
3203
|
const configPath = getConfigPath();
|
|
3176
3204
|
if (existsSync12(configPath)) {
|
|
3177
|
-
const raw =
|
|
3205
|
+
const raw = readFileSync9(configPath, "utf-8");
|
|
3178
3206
|
return { ...DEFAULTS, ...JSON.parse(raw) };
|
|
3179
3207
|
}
|
|
3180
3208
|
} catch {}
|
|
@@ -4599,10 +4627,18 @@ import { existsSync as existsSync8 } from "fs";
|
|
|
4599
4627
|
import { homedir as homedir8 } from "os";
|
|
4600
4628
|
import { join as join8 } from "path";
|
|
4601
4629
|
import { Database as BunDatabase2 } from "bun:sqlite";
|
|
4602
|
-
|
|
4630
|
+
import { dataDir as dataDir2 } from "@hasna/paths";
|
|
4603
4631
|
var LOOPS_INGEST_VERSION = "goal-runs-orchestration-v1";
|
|
4632
|
+
function defaultLoopsDbPath() {
|
|
4633
|
+
const resolved = dataDir2({
|
|
4634
|
+
app: "loops",
|
|
4635
|
+
home: process.env["HOME"] || process.env["USERPROFILE"] || undefined
|
|
4636
|
+
});
|
|
4637
|
+
const legacy = join8(process.env["HOME"] || process.env["USERPROFILE"] || homedir8(), ".hasna", "loops", "loops.db");
|
|
4638
|
+
return existsSync8(join8(resolved, "loops.db")) ? join8(resolved, "loops.db") : legacy;
|
|
4639
|
+
}
|
|
4604
4640
|
function loopsDbPath() {
|
|
4605
|
-
return process.env["HASNA_ECONOMY_LOOPS_DB_PATH"] ??
|
|
4641
|
+
return process.env["HASNA_ECONOMY_LOOPS_DB_PATH"] ?? defaultLoopsDbPath();
|
|
4606
4642
|
}
|
|
4607
4643
|
function loopsModel() {
|
|
4608
4644
|
return process.env["HASNA_ECONOMY_LOOPS_MODEL"] ?? process.env["ECONOMY_LOOPS_MODEL"] ?? "gpt-5.3-codex";
|
|
@@ -4786,11 +4822,11 @@ import { existsSync as existsSync9, readFileSync as readFileSync6 } from "fs";
|
|
|
4786
4822
|
// src/lib/paths.ts
|
|
4787
4823
|
import { homedir as homedir9 } from "os";
|
|
4788
4824
|
import { join as join9 } from "path";
|
|
4789
|
-
function
|
|
4825
|
+
function getHomeDir2() {
|
|
4790
4826
|
return process.env["USERPROFILE"] ?? process.env["HOME"] ?? homedir9();
|
|
4791
4827
|
}
|
|
4792
4828
|
function agentPaths() {
|
|
4793
|
-
const home =
|
|
4829
|
+
const home = getHomeDir2();
|
|
4794
4830
|
return {
|
|
4795
4831
|
claudeProjects: join9(home, ".claude", "projects"),
|
|
4796
4832
|
claudeCredentials: join9(home, ".claude", ".credentials.json"),
|
|
@@ -5975,19 +6011,119 @@ function isAuthorizedRequest(req, path) {
|
|
|
5975
6011
|
// src/server/serve.ts
|
|
5976
6012
|
import { randomUUID } from "crypto";
|
|
5977
6013
|
import { existsSync as existsSync13 } from "fs";
|
|
5978
|
-
import { resolve, sep } from "path";
|
|
6014
|
+
import { resolve as resolve2, sep } from "path";
|
|
6015
|
+
// package.json
|
|
6016
|
+
var package_default = {
|
|
6017
|
+
name: "@hasna/economy",
|
|
6018
|
+
version: "0.3.28",
|
|
6019
|
+
description: "AI coding cost tracker \u2014 CLI + MCP server + REST API + web dashboard for Claude Code, Codex, OpenCode, Cursor, Pi, and Hermes, with legacy Gemini CLI session and Gemini API billing ingestion",
|
|
6020
|
+
repository: {
|
|
6021
|
+
type: "git",
|
|
6022
|
+
url: "git+https://github.com/hasna/economy.git"
|
|
6023
|
+
},
|
|
6024
|
+
bugs: {
|
|
6025
|
+
url: "https://github.com/hasna/economy/issues"
|
|
6026
|
+
},
|
|
6027
|
+
homepage: "https://github.com/hasna/economy#readme",
|
|
6028
|
+
type: "module",
|
|
6029
|
+
main: "dist/index.js",
|
|
6030
|
+
types: "dist/index.d.ts",
|
|
6031
|
+
bin: {
|
|
6032
|
+
economy: "dist/cli/index.js",
|
|
6033
|
+
"economy-mcp": "dist/mcp/index.js",
|
|
6034
|
+
"economy-serve": "dist/server/index.js",
|
|
6035
|
+
"economy-otel": "dist/otel/index.js"
|
|
6036
|
+
},
|
|
6037
|
+
exports: {
|
|
6038
|
+
".": {
|
|
6039
|
+
types: "./dist/index.d.ts",
|
|
6040
|
+
import: "./dist/index.js"
|
|
6041
|
+
},
|
|
6042
|
+
"./sdk": {
|
|
6043
|
+
types: "./dist/index.d.ts",
|
|
6044
|
+
import: "./dist/index.js"
|
|
6045
|
+
}
|
|
6046
|
+
},
|
|
6047
|
+
files: [
|
|
6048
|
+
"dist",
|
|
6049
|
+
"docs",
|
|
6050
|
+
"dashboard/dist",
|
|
6051
|
+
"postinstall.js",
|
|
6052
|
+
"LICENSE",
|
|
6053
|
+
"README.md",
|
|
6054
|
+
"CHANGELOG.md",
|
|
6055
|
+
"SECURITY.md",
|
|
6056
|
+
"CONTRIBUTING.md",
|
|
6057
|
+
"CODE_OF_CONDUCT.md"
|
|
6058
|
+
],
|
|
6059
|
+
scripts: {
|
|
6060
|
+
build: "cd dashboard && bun install --frozen-lockfile && bun run build && cd .. && bun build src/cli/index.ts --outdir dist/cli --target bun --packages external && bun build src/mcp/index.ts --outdir dist/mcp --target bun --packages external && bun build src/server/index.ts --outdir dist/server --target bun --packages external && bun build src/db/pg-sync-worker.ts --outdir dist/server --target bun --packages external && bun build src/otel/index.ts --outdir dist/otel --target bun --packages external && bun build src/index.ts --outdir dist --target bun --packages external && tsc --emitDeclarationOnly --outDir dist",
|
|
6061
|
+
"build:cli": "bun build src/cli/index.ts --outdir dist/cli --target bun --packages external",
|
|
6062
|
+
"build:mcp": "bun build src/mcp/index.ts --outdir dist/mcp --target bun --packages external",
|
|
6063
|
+
"build:server": "bun build src/server/index.ts --outdir dist/server --target bun --packages external",
|
|
6064
|
+
"build:lib": "bun build src/index.ts --outdir dist --target bun --packages external",
|
|
6065
|
+
"build:dashboard": "cd dashboard && bun run build",
|
|
6066
|
+
"artifact-scan": "bun scripts/contracts-cli.mjs artifact-scan dist",
|
|
6067
|
+
prepack: "bun run build && bun run artifact-scan",
|
|
6068
|
+
typecheck: "tsc --noEmit",
|
|
6069
|
+
test: "bun test",
|
|
6070
|
+
"dev:cli": "bun run src/cli/index.ts",
|
|
6071
|
+
"dev:mcp": "bun run src/mcp/index.ts",
|
|
6072
|
+
"dev:serve": "bun run src/server/index.ts",
|
|
6073
|
+
"dev:otel": "bun run src/otel/index.ts",
|
|
6074
|
+
postinstall: "node postinstall.js"
|
|
6075
|
+
},
|
|
6076
|
+
keywords: [
|
|
6077
|
+
"economy",
|
|
6078
|
+
"cost",
|
|
6079
|
+
"ai",
|
|
6080
|
+
"claude",
|
|
6081
|
+
"codex",
|
|
6082
|
+
"gemini",
|
|
6083
|
+
"mcp",
|
|
6084
|
+
"cli",
|
|
6085
|
+
"budget",
|
|
6086
|
+
"tracking"
|
|
6087
|
+
],
|
|
6088
|
+
author: "hasna",
|
|
6089
|
+
license: "Apache-2.0",
|
|
6090
|
+
publishConfig: {
|
|
6091
|
+
registry: "https://registry.npmjs.org",
|
|
6092
|
+
access: "public"
|
|
6093
|
+
},
|
|
6094
|
+
dependencies: {
|
|
6095
|
+
"@hasna/accounts": "0.2.23",
|
|
6096
|
+
"@hasna/agent-registry": "^0.1.0",
|
|
6097
|
+
"@hasna/contracts": "0.13.4",
|
|
6098
|
+
"@hasna/events": "^0.1.16",
|
|
6099
|
+
"@hasna/mcp-harness": "^0.1.0",
|
|
6100
|
+
"@hasna/paths": "0.1.0",
|
|
6101
|
+
"@hasna/projects": "1.0.0",
|
|
6102
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
6103
|
+
chalk: "^5.4.1",
|
|
6104
|
+
commander: "^13.1.0",
|
|
6105
|
+
pg: "8.13.1",
|
|
6106
|
+
zod: "^3.24.2"
|
|
6107
|
+
},
|
|
6108
|
+
devDependencies: {
|
|
6109
|
+
"@types/bun": "latest",
|
|
6110
|
+
"@types/pg": "^8.20.0",
|
|
6111
|
+
"bun-types": "latest",
|
|
6112
|
+
typescript: "^5.7.2"
|
|
6113
|
+
},
|
|
6114
|
+
trustedDependencies: [
|
|
6115
|
+
"@hasna/projects"
|
|
6116
|
+
]
|
|
6117
|
+
};
|
|
5979
6118
|
|
|
5980
6119
|
// src/lib/package-metadata.ts
|
|
5981
|
-
import { readFileSync as readFileSync8 } from "fs";
|
|
5982
6120
|
var cachedMetadata = null;
|
|
5983
6121
|
function getPackageMetadata() {
|
|
5984
6122
|
if (cachedMetadata)
|
|
5985
6123
|
return cachedMetadata;
|
|
5986
|
-
const raw = readFileSync8(new URL("../../package.json", import.meta.url), "utf8");
|
|
5987
|
-
const parsed = JSON.parse(raw);
|
|
5988
6124
|
cachedMetadata = {
|
|
5989
|
-
name:
|
|
5990
|
-
version:
|
|
6125
|
+
name: package_default.name ?? "@hasna/economy",
|
|
6126
|
+
version: package_default.version ?? "0.0.0"
|
|
5991
6127
|
};
|
|
5992
6128
|
return cachedMetadata;
|
|
5993
6129
|
}
|
|
@@ -7709,8 +7845,8 @@ function dashboardPath(root, pathname) {
|
|
|
7709
7845
|
return null;
|
|
7710
7846
|
}
|
|
7711
7847
|
const relativePath = decoded === "/" ? "index.html" : decoded.replace(/^\/+/, "");
|
|
7712
|
-
const rootPath =
|
|
7713
|
-
const filePath =
|
|
7848
|
+
const rootPath = resolve2(root);
|
|
7849
|
+
const filePath = resolve2(rootPath, relativePath);
|
|
7714
7850
|
return filePath === rootPath || filePath.startsWith(rootPath + sep) ? filePath : null;
|
|
7715
7851
|
}
|
|
7716
7852
|
var FOUNDATION_PATHS = new Set(["/health", "/healthz", "/ready", "/readyz", "/version", "/openapi.json"]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/economy",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.28",
|
|
4
4
|
"description": "AI coding cost tracker — CLI + MCP server + REST API + web dashboard for Claude Code, Codex, OpenCode, Cursor, Pi, and Hermes, with legacy Gemini CLI session and Gemini API billing ingestion",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -23,12 +23,17 @@
|
|
|
23
23
|
".": {
|
|
24
24
|
"types": "./dist/index.d.ts",
|
|
25
25
|
"import": "./dist/index.js"
|
|
26
|
+
},
|
|
27
|
+
"./sdk": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"import": "./dist/index.js"
|
|
26
30
|
}
|
|
27
31
|
},
|
|
28
32
|
"files": [
|
|
29
33
|
"dist",
|
|
30
34
|
"docs",
|
|
31
35
|
"dashboard/dist",
|
|
36
|
+
"postinstall.js",
|
|
32
37
|
"LICENSE",
|
|
33
38
|
"README.md",
|
|
34
39
|
"CHANGELOG.md",
|
|
@@ -51,7 +56,7 @@
|
|
|
51
56
|
"dev:mcp": "bun run src/mcp/index.ts",
|
|
52
57
|
"dev:serve": "bun run src/server/index.ts",
|
|
53
58
|
"dev:otel": "bun run src/otel/index.ts",
|
|
54
|
-
"postinstall": "
|
|
59
|
+
"postinstall": "node postinstall.js"
|
|
55
60
|
},
|
|
56
61
|
"keywords": [
|
|
57
62
|
"economy",
|
|
@@ -77,6 +82,7 @@
|
|
|
77
82
|
"@hasna/contracts": "0.13.4",
|
|
78
83
|
"@hasna/events": "^0.1.16",
|
|
79
84
|
"@hasna/mcp-harness": "^0.1.0",
|
|
85
|
+
"@hasna/paths": "0.1.0",
|
|
80
86
|
"@hasna/projects": "1.0.0",
|
|
81
87
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
82
88
|
"chalk": "^5.4.1",
|
package/postinstall.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Best-effort install-time creation of the economy data home, resolving the
|
|
2
|
+
// SAME effective data root the runtime uses (src/db/database.ts getDataDir):
|
|
3
|
+
// an exact-app override (HASNA_ECONOMY_HOME / ECONOMY_HOME) wins; otherwise
|
|
4
|
+
// the @hasna/paths XDG data home once adopted (HASNA_DATA_HOME set, or
|
|
5
|
+
// economy.db already migrated there); otherwise the legacy
|
|
6
|
+
// ~/.hasna/economy default. Failures are non-fatal: the runtime getDataDir()
|
|
7
|
+
// creates the same directories on first use.
|
|
8
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
|
|
12
|
+
const EXACT_OVERRIDE = (process.env["HASNA_ECONOMY_HOME"] || process.env["ECONOMY_HOME"] || "").trim();
|
|
13
|
+
const DATA_HOME_OVERRIDE = (process.env["HASNA_DATA_HOME"] || "").trim();
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
const { dataDir } = await import("@hasna/paths");
|
|
17
|
+
const resolved = dataDir({ app: "economy" });
|
|
18
|
+
let root;
|
|
19
|
+
if (EXACT_OVERRIDE) {
|
|
20
|
+
root = EXACT_OVERRIDE;
|
|
21
|
+
} else if (DATA_HOME_OVERRIDE || existsSync(join(resolved, "economy.db"))) {
|
|
22
|
+
root = resolved;
|
|
23
|
+
} else {
|
|
24
|
+
root = join(homedir(), ".hasna", "economy");
|
|
25
|
+
}
|
|
26
|
+
for (const dir of [root, join(root, "training")]) {
|
|
27
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
} catch {
|
|
30
|
+
// never fail an install over pre-created directories
|
|
31
|
+
}
|