@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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.28
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Switch @hasna/economy local path reads/writes through the @hasna/paths resolver (XDG/macOS home layout). The legacy `~/.hasna/economy` data root (with the `HASNA_ECONOMY_HOME` / `ECONOMY_HOME` exact-app overrides layered on top of the existing `HASNA_ECONOMY_DB_PATH` / `ECONOMY_DB` store override) stays the effective data root until the store has actually been migrated to the XDG data home or the operator sets the data-kind override `HASNA_DATA_HOME` — an existing local store never becomes invisible on upgrade. The install-time postinstall now creates the same effective data root (and its `training` subdir) the runtime resolves. The OpenLoops ingest read (`economy sync --loops`) resolves the loops store through the resolver with a legacy-read fallback, so it keeps working whichever side of the XDG migration `@hasna/loops` is on. Dependency pinned exactly to `@hasna/paths@0.1.0` — the wave-wide pin for the hasna/apps resolver-switch lanes (XDG home migration, hotfixes plan 0f49f56a, task P3.3).
|
|
8
|
+
|
|
9
|
+
## 0.3.27
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Updated dependencies [6176948]
|
|
14
|
+
- Updated dependencies [7575de8]
|
|
15
|
+
- @hasna/contracts@0.14.0
|
|
16
|
+
- @hasna/projects@1.0.0
|
|
17
|
+
|
|
3
18
|
## 0.3.26
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -124,7 +124,7 @@ economy sync --hermes
|
|
|
124
124
|
economy sync --loops
|
|
125
125
|
```
|
|
126
126
|
|
|
127
|
-
`economy sync --loops` reads `~/.hasna/loops/loops.db` in read-only mode and imports OpenLoops orchestration/judge `goal_runs.tokens_used` into `loop:*` cost centers. It intentionally does not ingest dispatched coding-agent work from loops; heavy agent spend remains captured by the existing per-agent ingesters and can be analyzed alongside loop cost centers through account/profile attribution.
|
|
127
|
+
`economy sync --loops` reads the OpenLoops store (`~/.hasna/loops/loops.db` by default, resolved through the `@hasna/paths` XDG data home once the loops store has migrated there) in read-only mode and imports OpenLoops orchestration/judge `goal_runs.tokens_used` into `loop:*` cost centers. It intentionally does not ingest dispatched coding-agent work from loops; heavy agent spend remains captured by the existing per-agent ingesters and can be analyzed alongside loop cost centers through account/profile attribution.
|
|
128
128
|
|
|
129
129
|
Useful repair options:
|
|
130
130
|
|
|
@@ -272,9 +272,9 @@ economy menubar uninstall
|
|
|
272
272
|
|
|
273
273
|
## Data Directory
|
|
274
274
|
|
|
275
|
-
Data is stored
|
|
275
|
+
Data is stored under a single data root resolved through the `@hasna/paths` resolver (XDG/macOS home layout). The legacy `~/.hasna/economy/` stays the effective root until the store is actually migrated to the XDG data home or the operator sets the data-kind override `HASNA_DATA_HOME`; the exact-app overrides `HASNA_ECONOMY_HOME` / `ECONOMY_HOME` win unconditionally.
|
|
276
276
|
|
|
277
|
-
The main SQLite database lives at `~/.hasna/economy/economy.db
|
|
277
|
+
The main SQLite database lives at `<data-root>/economy.db` (`~/.hasna/economy/economy.db` by default). Older `~/.economy/` data is copied on first open when the new directory does not exist. Override the database path with `HASNA_ECONOMY_DB_PATH` or `ECONOMY_DB`.
|
|
278
278
|
|
|
279
279
|
For shared deployments, CLI and MCP can use a remote `/v1` API instead of local SQLite by setting `HASNA_ECONOMY_API_URL` and `HASNA_ECONOMY_API_KEY`. See [configuration](docs/configuration.md) for client resolution, server auth, Postgres mode, and all environment variables.
|
|
280
280
|
|
package/dist/cli/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"])
|
|
@@ -2374,21 +2402,126 @@ var init_database = __esm(() => {
|
|
|
2374
2402
|
init_sqlite_adapter();
|
|
2375
2403
|
});
|
|
2376
2404
|
|
|
2405
|
+
// package.json
|
|
2406
|
+
var package_default;
|
|
2407
|
+
var init_package = __esm(() => {
|
|
2408
|
+
package_default = {
|
|
2409
|
+
name: "@hasna/economy",
|
|
2410
|
+
version: "0.3.28",
|
|
2411
|
+
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",
|
|
2412
|
+
repository: {
|
|
2413
|
+
type: "git",
|
|
2414
|
+
url: "git+https://github.com/hasna/economy.git"
|
|
2415
|
+
},
|
|
2416
|
+
bugs: {
|
|
2417
|
+
url: "https://github.com/hasna/economy/issues"
|
|
2418
|
+
},
|
|
2419
|
+
homepage: "https://github.com/hasna/economy#readme",
|
|
2420
|
+
type: "module",
|
|
2421
|
+
main: "dist/index.js",
|
|
2422
|
+
types: "dist/index.d.ts",
|
|
2423
|
+
bin: {
|
|
2424
|
+
economy: "dist/cli/index.js",
|
|
2425
|
+
"economy-mcp": "dist/mcp/index.js",
|
|
2426
|
+
"economy-serve": "dist/server/index.js",
|
|
2427
|
+
"economy-otel": "dist/otel/index.js"
|
|
2428
|
+
},
|
|
2429
|
+
exports: {
|
|
2430
|
+
".": {
|
|
2431
|
+
types: "./dist/index.d.ts",
|
|
2432
|
+
import: "./dist/index.js"
|
|
2433
|
+
},
|
|
2434
|
+
"./sdk": {
|
|
2435
|
+
types: "./dist/index.d.ts",
|
|
2436
|
+
import: "./dist/index.js"
|
|
2437
|
+
}
|
|
2438
|
+
},
|
|
2439
|
+
files: [
|
|
2440
|
+
"dist",
|
|
2441
|
+
"docs",
|
|
2442
|
+
"dashboard/dist",
|
|
2443
|
+
"postinstall.js",
|
|
2444
|
+
"LICENSE",
|
|
2445
|
+
"README.md",
|
|
2446
|
+
"CHANGELOG.md",
|
|
2447
|
+
"SECURITY.md",
|
|
2448
|
+
"CONTRIBUTING.md",
|
|
2449
|
+
"CODE_OF_CONDUCT.md"
|
|
2450
|
+
],
|
|
2451
|
+
scripts: {
|
|
2452
|
+
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",
|
|
2453
|
+
"build:cli": "bun build src/cli/index.ts --outdir dist/cli --target bun --packages external",
|
|
2454
|
+
"build:mcp": "bun build src/mcp/index.ts --outdir dist/mcp --target bun --packages external",
|
|
2455
|
+
"build:server": "bun build src/server/index.ts --outdir dist/server --target bun --packages external",
|
|
2456
|
+
"build:lib": "bun build src/index.ts --outdir dist --target bun --packages external",
|
|
2457
|
+
"build:dashboard": "cd dashboard && bun run build",
|
|
2458
|
+
"artifact-scan": "bun scripts/contracts-cli.mjs artifact-scan dist",
|
|
2459
|
+
prepack: "bun run build && bun run artifact-scan",
|
|
2460
|
+
typecheck: "tsc --noEmit",
|
|
2461
|
+
test: "bun test",
|
|
2462
|
+
"dev:cli": "bun run src/cli/index.ts",
|
|
2463
|
+
"dev:mcp": "bun run src/mcp/index.ts",
|
|
2464
|
+
"dev:serve": "bun run src/server/index.ts",
|
|
2465
|
+
"dev:otel": "bun run src/otel/index.ts",
|
|
2466
|
+
postinstall: "node postinstall.js"
|
|
2467
|
+
},
|
|
2468
|
+
keywords: [
|
|
2469
|
+
"economy",
|
|
2470
|
+
"cost",
|
|
2471
|
+
"ai",
|
|
2472
|
+
"claude",
|
|
2473
|
+
"codex",
|
|
2474
|
+
"gemini",
|
|
2475
|
+
"mcp",
|
|
2476
|
+
"cli",
|
|
2477
|
+
"budget",
|
|
2478
|
+
"tracking"
|
|
2479
|
+
],
|
|
2480
|
+
author: "hasna",
|
|
2481
|
+
license: "Apache-2.0",
|
|
2482
|
+
publishConfig: {
|
|
2483
|
+
registry: "https://registry.npmjs.org",
|
|
2484
|
+
access: "public"
|
|
2485
|
+
},
|
|
2486
|
+
dependencies: {
|
|
2487
|
+
"@hasna/accounts": "0.2.23",
|
|
2488
|
+
"@hasna/agent-registry": "^0.1.0",
|
|
2489
|
+
"@hasna/contracts": "0.13.4",
|
|
2490
|
+
"@hasna/events": "^0.1.16",
|
|
2491
|
+
"@hasna/mcp-harness": "^0.1.0",
|
|
2492
|
+
"@hasna/paths": "0.1.0",
|
|
2493
|
+
"@hasna/projects": "1.0.0",
|
|
2494
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
2495
|
+
chalk: "^5.4.1",
|
|
2496
|
+
commander: "^13.1.0",
|
|
2497
|
+
pg: "8.13.1",
|
|
2498
|
+
zod: "^3.24.2"
|
|
2499
|
+
},
|
|
2500
|
+
devDependencies: {
|
|
2501
|
+
"@types/bun": "latest",
|
|
2502
|
+
"@types/pg": "^8.20.0",
|
|
2503
|
+
"bun-types": "latest",
|
|
2504
|
+
typescript: "^5.7.2"
|
|
2505
|
+
},
|
|
2506
|
+
trustedDependencies: [
|
|
2507
|
+
"@hasna/projects"
|
|
2508
|
+
]
|
|
2509
|
+
};
|
|
2510
|
+
});
|
|
2511
|
+
|
|
2377
2512
|
// src/lib/package-metadata.ts
|
|
2378
|
-
import { readFileSync } from "fs";
|
|
2379
2513
|
function getPackageMetadata() {
|
|
2380
2514
|
if (cachedMetadata)
|
|
2381
2515
|
return cachedMetadata;
|
|
2382
|
-
const raw = readFileSync(new URL("../../package.json", import.meta.url), "utf8");
|
|
2383
|
-
const parsed = JSON.parse(raw);
|
|
2384
2516
|
cachedMetadata = {
|
|
2385
|
-
name:
|
|
2386
|
-
version:
|
|
2517
|
+
name: package_default.name ?? "@hasna/economy",
|
|
2518
|
+
version: package_default.version ?? "0.0.0"
|
|
2387
2519
|
};
|
|
2388
2520
|
return cachedMetadata;
|
|
2389
2521
|
}
|
|
2390
2522
|
var cachedMetadata = null, packageMetadata;
|
|
2391
2523
|
var init_package_metadata = __esm(() => {
|
|
2524
|
+
init_package();
|
|
2392
2525
|
packageMetadata = getPackageMetadata();
|
|
2393
2526
|
});
|
|
2394
2527
|
|
|
@@ -3990,11 +4123,11 @@ var init_agents = __esm(() => {
|
|
|
3990
4123
|
// src/lib/paths.ts
|
|
3991
4124
|
import { homedir as homedir2 } from "os";
|
|
3992
4125
|
import { join as join4 } from "path";
|
|
3993
|
-
function
|
|
4126
|
+
function getHomeDir2() {
|
|
3994
4127
|
return process.env["USERPROFILE"] ?? process.env["HOME"] ?? homedir2();
|
|
3995
4128
|
}
|
|
3996
4129
|
function agentPaths() {
|
|
3997
|
-
const home =
|
|
4130
|
+
const home = getHomeDir2();
|
|
3998
4131
|
return {
|
|
3999
4132
|
claudeProjects: join4(home, ".claude", "projects"),
|
|
4000
4133
|
claudeCredentials: join4(home, ".claude", ".credentials.json"),
|
|
@@ -4128,7 +4261,7 @@ var init_accounts = __esm(() => {
|
|
|
4128
4261
|
});
|
|
4129
4262
|
|
|
4130
4263
|
// src/ingest/claude.ts
|
|
4131
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
4264
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync2, existsSync as existsSync4, statSync as statSync2 } from "fs";
|
|
4132
4265
|
import { homedir as homedir3 } from "os";
|
|
4133
4266
|
import { join as join6, basename } from "path";
|
|
4134
4267
|
function autoDetectProject(cwd, projects) {
|
|
@@ -4188,7 +4321,7 @@ async function ingestJsonlProjects(db, projectsDir, agentName, verbose = false)
|
|
|
4188
4321
|
continue;
|
|
4189
4322
|
let lines;
|
|
4190
4323
|
try {
|
|
4191
|
-
lines =
|
|
4324
|
+
lines = readFileSync2(filePath, "utf-8").split(`
|
|
4192
4325
|
`).filter((l) => l.trim());
|
|
4193
4326
|
} catch {
|
|
4194
4327
|
continue;
|
|
@@ -4314,7 +4447,7 @@ var init_claude = __esm(() => {
|
|
|
4314
4447
|
});
|
|
4315
4448
|
|
|
4316
4449
|
// src/ingest/codex.ts
|
|
4317
|
-
import { existsSync as existsSync5, readFileSync as
|
|
4450
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
4318
4451
|
import { homedir as homedir4 } from "os";
|
|
4319
4452
|
import { join as join7, basename as basename2 } from "path";
|
|
4320
4453
|
import { Database as BunDatabase } from "bun:sqlite";
|
|
@@ -4361,7 +4494,7 @@ function readCodexModel(configPath = codexConfigPath()) {
|
|
|
4361
4494
|
if (!existsSync5(configPath))
|
|
4362
4495
|
return "gpt-5-codex";
|
|
4363
4496
|
try {
|
|
4364
|
-
const content =
|
|
4497
|
+
const content = readFileSync3(configPath, "utf-8");
|
|
4365
4498
|
const match = content.match(/^model\s*=\s*"([^"]+)"/m);
|
|
4366
4499
|
return match?.[1] ?? "gpt-5-codex";
|
|
4367
4500
|
} catch {
|
|
@@ -4404,7 +4537,7 @@ function readTokenEvents(rolloutPath) {
|
|
|
4404
4537
|
const fallbackUsages = new Map;
|
|
4405
4538
|
let fallbackTimestamp;
|
|
4406
4539
|
let aggregate = null;
|
|
4407
|
-
for (const line of
|
|
4540
|
+
for (const line of readFileSync3(rolloutPath, "utf-8").split(`
|
|
4408
4541
|
`)) {
|
|
4409
4542
|
if (!line.trim())
|
|
4410
4543
|
continue;
|
|
@@ -4569,7 +4702,7 @@ var init_codex = __esm(() => {
|
|
|
4569
4702
|
});
|
|
4570
4703
|
|
|
4571
4704
|
// src/ingest/gemini.ts
|
|
4572
|
-
import { readdirSync as readdirSync3, readFileSync as
|
|
4705
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync4, existsSync as existsSync6, statSync as statSync3 } from "fs";
|
|
4573
4706
|
import { homedir as homedir5 } from "os";
|
|
4574
4707
|
import { join as join8, basename as basename3 } from "path";
|
|
4575
4708
|
function geminiTmpDir() {
|
|
@@ -4607,7 +4740,7 @@ function projectRoot(projectDir, chatData) {
|
|
|
4607
4740
|
const rootFile = join8(projectDir, ".project_root");
|
|
4608
4741
|
try {
|
|
4609
4742
|
if (existsSync6(rootFile))
|
|
4610
|
-
return
|
|
4743
|
+
return readFileSync4(rootFile, "utf-8").trim();
|
|
4611
4744
|
} catch {}
|
|
4612
4745
|
return "";
|
|
4613
4746
|
}
|
|
@@ -4648,7 +4781,7 @@ async function ingestGemini(db, verbose) {
|
|
|
4648
4781
|
continue;
|
|
4649
4782
|
let chatData;
|
|
4650
4783
|
try {
|
|
4651
|
-
chatData = JSON.parse(
|
|
4784
|
+
chatData = JSON.parse(readFileSync4(filePath, "utf-8"));
|
|
4652
4785
|
} catch {
|
|
4653
4786
|
continue;
|
|
4654
4787
|
}
|
|
@@ -4732,7 +4865,7 @@ var init_gemini = __esm(() => {
|
|
|
4732
4865
|
});
|
|
4733
4866
|
|
|
4734
4867
|
// src/ingest/opencode.ts
|
|
4735
|
-
import { existsSync as existsSync7, readFileSync as
|
|
4868
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5, readdirSync as readdirSync4, statSync as statSync4 } from "fs";
|
|
4736
4869
|
import { homedir as homedir6 } from "os";
|
|
4737
4870
|
import { join as join9 } from "path";
|
|
4738
4871
|
function walkJsonFiles(dir, acc = []) {
|
|
@@ -4774,7 +4907,7 @@ async function ingestOpenCode(db, verbose = false) {
|
|
|
4774
4907
|
continue;
|
|
4775
4908
|
let parsed;
|
|
4776
4909
|
try {
|
|
4777
|
-
parsed = JSON.parse(
|
|
4910
|
+
parsed = JSON.parse(readFileSync5(file, "utf-8"));
|
|
4778
4911
|
} catch {
|
|
4779
4912
|
continue;
|
|
4780
4913
|
}
|
|
@@ -4970,7 +5103,7 @@ var init_cursor = __esm(() => {
|
|
|
4970
5103
|
});
|
|
4971
5104
|
|
|
4972
5105
|
// src/ingest/pi.ts
|
|
4973
|
-
import { existsSync as existsSync8, readFileSync as
|
|
5106
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, readdirSync as readdirSync5, statSync as statSync5 } from "fs";
|
|
4974
5107
|
import { homedir as homedir7 } from "os";
|
|
4975
5108
|
import { join as join10 } from "path";
|
|
4976
5109
|
function walkSessions(dir, acc = []) {
|
|
@@ -4999,7 +5132,7 @@ async function ingestPi(db, verbose = false) {
|
|
|
4999
5132
|
continue;
|
|
5000
5133
|
let data;
|
|
5001
5134
|
try {
|
|
5002
|
-
data = JSON.parse(
|
|
5135
|
+
data = JSON.parse(readFileSync6(file, "utf-8"));
|
|
5003
5136
|
} catch {
|
|
5004
5137
|
continue;
|
|
5005
5138
|
}
|
|
@@ -5167,8 +5300,17 @@ import { existsSync as existsSync10 } from "fs";
|
|
|
5167
5300
|
import { homedir as homedir9 } from "os";
|
|
5168
5301
|
import { join as join12 } from "path";
|
|
5169
5302
|
import { Database as BunDatabase2 } from "bun:sqlite";
|
|
5303
|
+
import { dataDir as dataDir2 } from "@hasna/paths";
|
|
5304
|
+
function defaultLoopsDbPath() {
|
|
5305
|
+
const resolved = dataDir2({
|
|
5306
|
+
app: "loops",
|
|
5307
|
+
home: process.env["HOME"] || process.env["USERPROFILE"] || undefined
|
|
5308
|
+
});
|
|
5309
|
+
const legacy = join12(process.env["HOME"] || process.env["USERPROFILE"] || homedir9(), ".hasna", "loops", "loops.db");
|
|
5310
|
+
return existsSync10(join12(resolved, "loops.db")) ? join12(resolved, "loops.db") : legacy;
|
|
5311
|
+
}
|
|
5170
5312
|
function loopsDbPath() {
|
|
5171
|
-
return process.env["HASNA_ECONOMY_LOOPS_DB_PATH"] ??
|
|
5313
|
+
return process.env["HASNA_ECONOMY_LOOPS_DB_PATH"] ?? defaultLoopsDbPath();
|
|
5172
5314
|
}
|
|
5173
5315
|
function loopsModel() {
|
|
5174
5316
|
return process.env["HASNA_ECONOMY_LOOPS_MODEL"] ?? process.env["ECONOMY_LOOPS_MODEL"] ?? "gpt-5.3-codex";
|
|
@@ -5344,15 +5486,14 @@ async function ingestLoops(db, verbose = false) {
|
|
|
5344
5486
|
rollupSession(db, sessionId);
|
|
5345
5487
|
return { sessions: sessions.size, requests };
|
|
5346
5488
|
}
|
|
5347
|
-
var
|
|
5489
|
+
var LOOPS_INGEST_VERSION = "goal-runs-orchestration-v1";
|
|
5348
5490
|
var init_loops = __esm(() => {
|
|
5349
5491
|
init_database();
|
|
5350
5492
|
init_pricing();
|
|
5351
|
-
DEFAULT_LOOPS_DB_PATH = join12(homedir9(), ".hasna", "loops", "loops.db");
|
|
5352
5493
|
});
|
|
5353
5494
|
|
|
5354
5495
|
// src/ingest/claude-quota.ts
|
|
5355
|
-
import { existsSync as existsSync11, readFileSync as
|
|
5496
|
+
import { existsSync as existsSync11, readFileSync as readFileSync7 } from "fs";
|
|
5356
5497
|
function readClaudeToken() {
|
|
5357
5498
|
const fromEnv = process.env["CLAUDE_OAUTH_TOKEN"] ?? process.env["ANTHROPIC_OAUTH_TOKEN"];
|
|
5358
5499
|
if (fromEnv)
|
|
@@ -5360,7 +5501,7 @@ function readClaudeToken() {
|
|
|
5360
5501
|
if (!existsSync11(CREDENTIALS_PATH))
|
|
5361
5502
|
return null;
|
|
5362
5503
|
try {
|
|
5363
|
-
const creds = JSON.parse(
|
|
5504
|
+
const creds = JSON.parse(readFileSync7(CREDENTIALS_PATH, "utf-8"));
|
|
5364
5505
|
const oauth = creds.claudeAiOauth;
|
|
5365
5506
|
if (!oauth?.accessToken)
|
|
5366
5507
|
return null;
|
|
@@ -5501,7 +5642,7 @@ var init_claude_quota = __esm(() => {
|
|
|
5501
5642
|
});
|
|
5502
5643
|
|
|
5503
5644
|
// src/ingest/codex-quota.ts
|
|
5504
|
-
import { existsSync as existsSync12, readFileSync as
|
|
5645
|
+
import { existsSync as existsSync12, readFileSync as readFileSync8 } from "fs";
|
|
5505
5646
|
function readCodexAuth() {
|
|
5506
5647
|
const fromEnv = process.env["CODEX_OAUTH_TOKEN"];
|
|
5507
5648
|
if (fromEnv)
|
|
@@ -5510,7 +5651,7 @@ function readCodexAuth() {
|
|
|
5510
5651
|
if (!existsSync12(authPath))
|
|
5511
5652
|
return null;
|
|
5512
5653
|
try {
|
|
5513
|
-
const auth = JSON.parse(
|
|
5654
|
+
const auth = JSON.parse(readFileSync8(authPath, "utf-8"));
|
|
5514
5655
|
const token = auth.tokens?.access_token;
|
|
5515
5656
|
if (!token)
|
|
5516
5657
|
return null;
|
|
@@ -5695,7 +5836,7 @@ __export(exports_billing, {
|
|
|
5695
5836
|
syncGeminiBilling: () => syncGeminiBilling,
|
|
5696
5837
|
syncAnthropicBilling: () => syncAnthropicBilling
|
|
5697
5838
|
});
|
|
5698
|
-
import { readFileSync as
|
|
5839
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
5699
5840
|
function getAnthropicAdminKey() {
|
|
5700
5841
|
return process.env["HASNAXYZ_ANTHROPIC_LIVE_ADMIN_API_KEY"] ?? process.env["ANTHROPIC_ADMIN_API_KEY"] ?? null;
|
|
5701
5842
|
}
|
|
@@ -5922,7 +6063,7 @@ async function syncGeminiBilling(db, opts = {}) {
|
|
|
5922
6063
|
const start = opts.fromDate ? new Date(opts.fromDate) : new Date(end.getTime() - days * 24 * 3600000);
|
|
5923
6064
|
const fromDateStr = toISODate(start);
|
|
5924
6065
|
const toDateStr = toISODate(end);
|
|
5925
|
-
const rows = parseBillingRows(
|
|
6066
|
+
const rows = parseBillingRows(readFileSync9(exportPath, "utf-8"));
|
|
5926
6067
|
clearBillingRange(db, "gemini", fromDateStr, toDateStr);
|
|
5927
6068
|
const updatedAt = new Date().toISOString();
|
|
5928
6069
|
let totalUsd = 0;
|
|
@@ -5993,7 +6134,7 @@ __export(exports_config, {
|
|
|
5993
6134
|
loadConfig: () => loadConfig2,
|
|
5994
6135
|
getConfigValue: () => getConfigValue
|
|
5995
6136
|
});
|
|
5996
|
-
import { existsSync as existsSync13, readFileSync as
|
|
6137
|
+
import { existsSync as existsSync13, readFileSync as readFileSync10, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3 } from "fs";
|
|
5997
6138
|
import { dirname as dirname2, join as join14 } from "path";
|
|
5998
6139
|
function getConfigPath() {
|
|
5999
6140
|
return process.env["HASNA_ECONOMY_CONFIG_PATH"] ?? join14(getDataDir(), "config.json");
|
|
@@ -6002,7 +6143,7 @@ function loadConfig2() {
|
|
|
6002
6143
|
try {
|
|
6003
6144
|
const configPath = getConfigPath();
|
|
6004
6145
|
if (existsSync13(configPath)) {
|
|
6005
|
-
const raw =
|
|
6146
|
+
const raw = readFileSync10(configPath, "utf-8");
|
|
6006
6147
|
return { ...DEFAULTS, ...JSON.parse(raw) };
|
|
6007
6148
|
}
|
|
6008
6149
|
} catch {}
|
|
@@ -8229,7 +8370,7 @@ __export(exports_serve, {
|
|
|
8229
8370
|
});
|
|
8230
8371
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
8231
8372
|
import { existsSync as existsSync16 } from "fs";
|
|
8232
|
-
import { resolve, sep } from "path";
|
|
8373
|
+
import { resolve as resolve2, sep } from "path";
|
|
8233
8374
|
import { verifyApiKey, ApiKeyStore } from "@hasna/contracts/auth";
|
|
8234
8375
|
function openApiDocument() {
|
|
8235
8376
|
const spec = openApiSpec;
|
|
@@ -8298,8 +8439,8 @@ function dashboardPath(root, pathname) {
|
|
|
8298
8439
|
return null;
|
|
8299
8440
|
}
|
|
8300
8441
|
const relativePath = decoded === "/" ? "index.html" : decoded.replace(/^\/+/, "");
|
|
8301
|
-
const rootPath =
|
|
8302
|
-
const filePath =
|
|
8442
|
+
const rootPath = resolve2(root);
|
|
8443
|
+
const filePath = resolve2(rootPath, relativePath);
|
|
8303
8444
|
return filePath === rootPath || filePath.startsWith(rootPath + sep) ? filePath : null;
|
|
8304
8445
|
}
|
|
8305
8446
|
function createServerFetch(apiHandler, dashboardDir = DEFAULT_DASHBOARD_DIR) {
|
|
@@ -9207,7 +9348,7 @@ ${goals.map((g) => `- ${g.period} goal (${g.project_path ?? g.agent ?? "global"}
|
|
|
9207
9348
|
|
|
9208
9349
|
// src/lib/model-config.ts
|
|
9209
9350
|
init_database();
|
|
9210
|
-
import { existsSync as existsSync2, readFileSync
|
|
9351
|
+
import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
|
|
9211
9352
|
import { dirname, join as join2 } from "path";
|
|
9212
9353
|
var DEFAULT_MODEL = "gpt-4o-mini";
|
|
9213
9354
|
function getModelConfigPath() {
|
|
@@ -9217,7 +9358,7 @@ function loadConfig() {
|
|
|
9217
9358
|
try {
|
|
9218
9359
|
const configPath = getModelConfigPath();
|
|
9219
9360
|
if (existsSync2(configPath)) {
|
|
9220
|
-
return JSON.parse(
|
|
9361
|
+
return JSON.parse(readFileSync(configPath, "utf-8"));
|
|
9221
9362
|
}
|
|
9222
9363
|
} catch {}
|
|
9223
9364
|
return {};
|
|
@@ -9248,7 +9389,7 @@ function clearActiveModel() {
|
|
|
9248
9389
|
init_database();
|
|
9249
9390
|
function registerBrainsCommand(program) {
|
|
9250
9391
|
const brainsCmd = program.command("brains").description("Fine-tune an AI model on your economy cost data");
|
|
9251
|
-
brainsCmd.command("gather").description("Gather training data from economy cost data and write to
|
|
9392
|
+
brainsCmd.command("gather").description("Gather training data from economy cost data and write to the economy data root (training/)").option("--limit <n>", "Maximum number of training examples", "500").option("--output <path>", "Output file path (default: <data-root>/training/training-<timestamp>.jsonl)").action(async (opts) => {
|
|
9252
9393
|
const limit = opts.limit ? parseInt(opts.limit, 10) : 500;
|
|
9253
9394
|
console.log(chalk.cyan(`Gathering up to ${limit} training examples from economy data...`));
|
|
9254
9395
|
try {
|
|
@@ -9274,7 +9415,7 @@ ${chalk.dim("Next step:")} economy brains train --base-model gpt-4o-mini`);
|
|
|
9274
9415
|
process.exit(1);
|
|
9275
9416
|
}
|
|
9276
9417
|
});
|
|
9277
|
-
brainsCmd.command("train").description("Start a fine-tuning job using gathered training data").option("--base-model <model>", "Base model to fine-tune", "gpt-4o-mini").option("--name <name>", "Name for the fine-tuned model", "economy-assistant").option("--dataset <path>", "Path to JSONL training file (default: latest in
|
|
9418
|
+
brainsCmd.command("train").description("Start a fine-tuning job using gathered training data").option("--base-model <model>", "Base model to fine-tune", "gpt-4o-mini").option("--name <name>", "Name for the fine-tuned model", "economy-assistant").option("--dataset <path>", "Path to JSONL training file (default: latest in <data-root>/training/)").action(async (opts) => {
|
|
9278
9419
|
const baseModel = opts.baseModel ?? "gpt-4o-mini";
|
|
9279
9420
|
const name = opts.name ?? "economy-assistant";
|
|
9280
9421
|
console.log(chalk.cyan("Starting fine-tuning job..."));
|
|
@@ -11737,8 +11878,8 @@ program.command("dashboard").description("Open the web dashboard (auto-starts se
|
|
|
11737
11878
|
if (!serverRunning) {
|
|
11738
11879
|
console.log(chalk7.cyan(`\u2192 Starting economy server on port ${port}...`));
|
|
11739
11880
|
const { spawn } = await import("child_process");
|
|
11740
|
-
const { resolve:
|
|
11741
|
-
const serveScript =
|
|
11881
|
+
const { resolve: resolve3, dirname: dirname4 } = await import("path");
|
|
11882
|
+
const serveScript = resolve3(dirname4(process.argv[1]), "..", "server", "index.js");
|
|
11742
11883
|
const child = spawn(process.execPath, [serveScript], {
|
|
11743
11884
|
detached: true,
|
|
11744
11885
|
stdio: "ignore",
|
package/dist/db/database.d.ts
CHANGED
|
@@ -1,6 +1,45 @@
|
|
|
1
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
|
+
/**
|
|
5
|
+
* Resolve the user's home directory: $HOME, then $USERPROFILE (Windows), then
|
|
6
|
+
* the OS user database. A home that cannot be resolved is a hard error — never
|
|
7
|
+
* a literal "~" path (relative to cwd) and never an "undefined"-prefixed path.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getHomeDir(): string;
|
|
10
|
+
/**
|
|
11
|
+
* The @hasna/paths-resolved (XDG / macOS home layout) data root for economy.
|
|
12
|
+
* This is the forward-looking home the XDG migration (hotfixes plan
|
|
13
|
+
* `0f49f56a`, task P3.3) moves the store toward: `~/.local/share/hasna/economy`
|
|
14
|
+
* on Linux, `~/Library/Application Support/Hasna/economy` on macOS. The home
|
|
15
|
+
* override mirrors the pre-existing $HOME-first resolution so the resolver
|
|
16
|
+
* follows the same home the legacy path does.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getResolverDataRoot(): string;
|
|
19
|
+
/** The legacy (pre-XDG) data root: ~/.hasna/economy */
|
|
20
|
+
export declare function getLegacyDataRoot(): string;
|
|
21
|
+
/**
|
|
22
|
+
* Whether the resolver (XDG) data root should be adopted as the effective data
|
|
23
|
+
* root. The resolver root is adopted only when the operator has set
|
|
24
|
+
* `HASNA_DATA_HOME` (the data-kind override — a deliberate opt-in to the XDG
|
|
25
|
+
* layout) or the store has already been physically migrated there
|
|
26
|
+
* (`economy.db` exists). A machine that only redirects another kind (e.g.
|
|
27
|
+
* cache to tmpfs) must NOT have its data home moved, and a live store at the
|
|
28
|
+
* legacy home must never become invisible on upgrade.
|
|
29
|
+
*/
|
|
30
|
+
export declare function adoptResolverDataRoot(resolved: string, env?: Record<string, string | undefined>): boolean;
|
|
31
|
+
/** The exact-app override root, when set: `HASNA_ECONOMY_HOME`, then `ECONOMY_HOME`. */
|
|
32
|
+
export declare function getExactDataRoot(): string | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* The effective data root for economy: an exact-app override
|
|
35
|
+
* (`HASNA_ECONOMY_HOME`, then `ECONOMY_HOME`) wins unconditionally; otherwise
|
|
36
|
+
* the resolver (XDG) data root once adopted; otherwise the legacy
|
|
37
|
+
* `~/.hasna/economy` default — an existing store never becomes invisible on
|
|
38
|
+
* upgrade. The store path (`HASNA_ECONOMY_DB_PATH` / `ECONOMY_DB`) is layered
|
|
39
|
+
* on top of this by `getDbPath`, so an explicit store path always wins
|
|
40
|
+
* regardless. The legacy `~/.economy` files are auto-migrated into the
|
|
41
|
+
* effective root, preserving the historical behavior.
|
|
42
|
+
*/
|
|
4
43
|
export declare function getDataDir(): string;
|
|
5
44
|
export declare function getDbPath(): string;
|
|
6
45
|
export declare function openDatabase(dbPath?: string, skipSeed?: boolean): Database;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"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"}
|