@hasna/economy 0.3.27 → 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 CHANGED
@@ -1,5 +1,11 @@
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
+
3
9
  ## 0.3.27
4
10
 
5
11
  ### 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 in `~/.hasna/economy/`.
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`. 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`.
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 getDataDir() {
630
+ function getHomeDir() {
630
631
  const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir();
631
- const newDir = join(home, ".hasna", "economy");
632
- const oldDir = join(home, ".economy");
633
- if (existsSync(oldDir) && !existsSync(newDir)) {
634
- mkdirSync(newDir, { recursive: true });
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(newDir, file));
666
+ copyFileSync(oldPath, join(effective, file));
639
667
  }
640
668
  }
641
669
  }
642
- mkdirSync(newDir, { recursive: true });
643
- return newDir;
670
+ mkdirSync(effective, { recursive: true });
671
+ return effective;
644
672
  }
645
673
  function getDbPath() {
646
674
  if (process.env["HASNA_ECONOMY_DB_PATH"])
@@ -2379,7 +2407,7 @@ var package_default;
2379
2407
  var init_package = __esm(() => {
2380
2408
  package_default = {
2381
2409
  name: "@hasna/economy",
2382
- version: "0.3.27",
2410
+ version: "0.3.28",
2383
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",
2384
2412
  repository: {
2385
2413
  type: "git",
@@ -2412,6 +2440,7 @@ var init_package = __esm(() => {
2412
2440
  "dist",
2413
2441
  "docs",
2414
2442
  "dashboard/dist",
2443
+ "postinstall.js",
2415
2444
  "LICENSE",
2416
2445
  "README.md",
2417
2446
  "CHANGELOG.md",
@@ -2434,7 +2463,7 @@ var init_package = __esm(() => {
2434
2463
  "dev:mcp": "bun run src/mcp/index.ts",
2435
2464
  "dev:serve": "bun run src/server/index.ts",
2436
2465
  "dev:otel": "bun run src/otel/index.ts",
2437
- postinstall: "mkdir -p $HOME/.hasna/economy/training 2>/dev/null || true"
2466
+ postinstall: "node postinstall.js"
2438
2467
  },
2439
2468
  keywords: [
2440
2469
  "economy",
@@ -2460,6 +2489,7 @@ var init_package = __esm(() => {
2460
2489
  "@hasna/contracts": "0.13.4",
2461
2490
  "@hasna/events": "^0.1.16",
2462
2491
  "@hasna/mcp-harness": "^0.1.0",
2492
+ "@hasna/paths": "0.1.0",
2463
2493
  "@hasna/projects": "1.0.0",
2464
2494
  "@modelcontextprotocol/sdk": "^1.12.1",
2465
2495
  chalk: "^5.4.1",
@@ -4093,11 +4123,11 @@ var init_agents = __esm(() => {
4093
4123
  // src/lib/paths.ts
4094
4124
  import { homedir as homedir2 } from "os";
4095
4125
  import { join as join4 } from "path";
4096
- function getHomeDir() {
4126
+ function getHomeDir2() {
4097
4127
  return process.env["USERPROFILE"] ?? process.env["HOME"] ?? homedir2();
4098
4128
  }
4099
4129
  function agentPaths() {
4100
- const home = getHomeDir();
4130
+ const home = getHomeDir2();
4101
4131
  return {
4102
4132
  claudeProjects: join4(home, ".claude", "projects"),
4103
4133
  claudeCredentials: join4(home, ".claude", ".credentials.json"),
@@ -5270,8 +5300,17 @@ import { existsSync as existsSync10 } from "fs";
5270
5300
  import { homedir as homedir9 } from "os";
5271
5301
  import { join as join12 } from "path";
5272
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
+ }
5273
5312
  function loopsDbPath() {
5274
- return process.env["HASNA_ECONOMY_LOOPS_DB_PATH"] ?? DEFAULT_LOOPS_DB_PATH;
5313
+ return process.env["HASNA_ECONOMY_LOOPS_DB_PATH"] ?? defaultLoopsDbPath();
5275
5314
  }
5276
5315
  function loopsModel() {
5277
5316
  return process.env["HASNA_ECONOMY_LOOPS_MODEL"] ?? process.env["ECONOMY_LOOPS_MODEL"] ?? "gpt-5.3-codex";
@@ -5447,11 +5486,10 @@ async function ingestLoops(db, verbose = false) {
5447
5486
  rollupSession(db, sessionId);
5448
5487
  return { sessions: sessions.size, requests };
5449
5488
  }
5450
- var DEFAULT_LOOPS_DB_PATH, LOOPS_INGEST_VERSION = "goal-runs-orchestration-v1";
5489
+ var LOOPS_INGEST_VERSION = "goal-runs-orchestration-v1";
5451
5490
  var init_loops = __esm(() => {
5452
5491
  init_database();
5453
5492
  init_pricing();
5454
- DEFAULT_LOOPS_DB_PATH = join12(homedir9(), ".hasna", "loops", "loops.db");
5455
5493
  });
5456
5494
 
5457
5495
  // src/ingest/claude-quota.ts
@@ -8332,7 +8370,7 @@ __export(exports_serve, {
8332
8370
  });
8333
8371
  import { randomUUID as randomUUID2 } from "crypto";
8334
8372
  import { existsSync as existsSync16 } from "fs";
8335
- import { resolve, sep } from "path";
8373
+ import { resolve as resolve2, sep } from "path";
8336
8374
  import { verifyApiKey, ApiKeyStore } from "@hasna/contracts/auth";
8337
8375
  function openApiDocument() {
8338
8376
  const spec = openApiSpec;
@@ -8401,8 +8439,8 @@ function dashboardPath(root, pathname) {
8401
8439
  return null;
8402
8440
  }
8403
8441
  const relativePath = decoded === "/" ? "index.html" : decoded.replace(/^\/+/, "");
8404
- const rootPath = resolve(root);
8405
- const filePath = resolve(rootPath, relativePath);
8442
+ const rootPath = resolve2(root);
8443
+ const filePath = resolve2(rootPath, relativePath);
8406
8444
  return filePath === rootPath || filePath.startsWith(rootPath + sep) ? filePath : null;
8407
8445
  }
8408
8446
  function createServerFetch(apiHandler, dashboardDir = DEFAULT_DASHBOARD_DIR) {
@@ -9351,7 +9389,7 @@ function clearActiveModel() {
9351
9389
  init_database();
9352
9390
  function registerBrainsCommand(program) {
9353
9391
  const brainsCmd = program.command("brains").description("Fine-tune an AI model on your economy cost data");
9354
- brainsCmd.command("gather").description("Gather training data from economy cost data and write to ~/.hasna/economy/training/").option("--limit <n>", "Maximum number of training examples", "500").option("--output <path>", "Output file path (default: ~/.hasna/economy/training/training-<timestamp>.jsonl)").action(async (opts) => {
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) => {
9355
9393
  const limit = opts.limit ? parseInt(opts.limit, 10) : 500;
9356
9394
  console.log(chalk.cyan(`Gathering up to ${limit} training examples from economy data...`));
9357
9395
  try {
@@ -9377,7 +9415,7 @@ ${chalk.dim("Next step:")} economy brains train --base-model gpt-4o-mini`);
9377
9415
  process.exit(1);
9378
9416
  }
9379
9417
  });
9380
- 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 ~/.hasna/economy/training/)").action(async (opts) => {
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) => {
9381
9419
  const baseModel = opts.baseModel ?? "gpt-4o-mini";
9382
9420
  const name = opts.name ?? "economy-assistant";
9383
9421
  console.log(chalk.cyan("Starting fine-tuning job..."));
@@ -11840,8 +11878,8 @@ program.command("dashboard").description("Open the web dashboard (auto-starts se
11840
11878
  if (!serverRunning) {
11841
11879
  console.log(chalk7.cyan(`\u2192 Starting economy server on port ${port}...`));
11842
11880
  const { spawn } = await import("child_process");
11843
- const { resolve: resolve2, dirname: dirname4 } = await import("path");
11844
- const serveScript = resolve2(dirname4(process.argv[1]), "..", "server", "index.js");
11881
+ const { resolve: resolve3, dirname: dirname4 } = await import("path");
11882
+ const serveScript = resolve3(dirname4(process.argv[1]), "..", "server", "index.js");
11845
11883
  const child = spawn(process.execPath, [serveScript], {
11846
11884
  detached: true,
11847
11885
  stdio: "ignore",
@@ -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":"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"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -593,7 +593,8 @@ import { execFileSync } from "child_process";
593
593
  import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from "fs";
594
594
  import { hostname, platform } from "os";
595
595
  import { homedir } from "os";
596
- import { join } from "path";
596
+ import { join, resolve } from "path";
597
+ import { dataDir } from "@hasna/paths";
597
598
  function normalizeMachineId(value) {
598
599
  const id = value?.trim().toLowerCase().split(".")[0];
599
600
  return id && id.length > 0 ? id : null;
@@ -624,21 +625,48 @@ function getMachineId() {
624
625
  return macHostMachineId() ?? hostMachine;
625
626
  return hostMachine;
626
627
  }
627
- function getDataDir() {
628
+ function getHomeDir() {
628
629
  const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir();
629
- const newDir = join(home, ".hasna", "economy");
630
- const oldDir = join(home, ".economy");
631
- if (existsSync(oldDir) && !existsSync(newDir)) {
632
- mkdirSync(newDir, { recursive: true });
630
+ if (!home)
631
+ throw new Error("Could not resolve the user home directory");
632
+ return home;
633
+ }
634
+ function getResolverDataRoot() {
635
+ return dataDir({
636
+ app: "economy",
637
+ home: process.env["HOME"] || process.env["USERPROFILE"] || undefined
638
+ });
639
+ }
640
+ function getLegacyDataRoot() {
641
+ return join(getHomeDir(), ".hasna", "economy");
642
+ }
643
+ function adoptResolverDataRoot(resolved, env = process.env) {
644
+ const dataOverride = env.HASNA_DATA_HOME;
645
+ if (typeof dataOverride === "string" && dataOverride.trim().length > 0)
646
+ return true;
647
+ return existsSync(join(resolved, "economy.db"));
648
+ }
649
+ function getExactDataRoot() {
650
+ const dir = process.env["HASNA_ECONOMY_HOME"] ?? process.env["ECONOMY_HOME"];
651
+ if (dir && dir.trim())
652
+ return dir.trim();
653
+ return;
654
+ }
655
+ function getDataDir() {
656
+ const exact = getExactDataRoot();
657
+ const effective = exact ? resolve(exact) : adoptResolverDataRoot(getResolverDataRoot()) ? resolve(getResolverDataRoot()) : getLegacyDataRoot();
658
+ const oldDir = join(getHomeDir(), ".economy");
659
+ if (existsSync(oldDir) && !existsSync(effective)) {
660
+ mkdirSync(effective, { recursive: true });
633
661
  for (const file of readdirSync(oldDir)) {
634
662
  const oldPath = join(oldDir, file);
635
663
  if (statSync(oldPath).isFile()) {
636
- copyFileSync(oldPath, join(newDir, file));
664
+ copyFileSync(oldPath, join(effective, file));
637
665
  }
638
666
  }
639
667
  }
640
- mkdirSync(newDir, { recursive: true });
641
- return newDir;
668
+ mkdirSync(effective, { recursive: true });
669
+ return effective;
642
670
  }
643
671
  function getDbPath() {
644
672
  if (process.env["HASNA_ECONOMY_DB_PATH"])
@@ -1993,7 +2021,7 @@ import { randomUUID } from "crypto";
1993
2021
  // package.json
1994
2022
  var package_default = {
1995
2023
  name: "@hasna/economy",
1996
- version: "0.3.27",
2024
+ version: "0.3.28",
1997
2025
  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",
1998
2026
  repository: {
1999
2027
  type: "git",
@@ -2026,6 +2054,7 @@ var package_default = {
2026
2054
  "dist",
2027
2055
  "docs",
2028
2056
  "dashboard/dist",
2057
+ "postinstall.js",
2029
2058
  "LICENSE",
2030
2059
  "README.md",
2031
2060
  "CHANGELOG.md",
@@ -2048,7 +2077,7 @@ var package_default = {
2048
2077
  "dev:mcp": "bun run src/mcp/index.ts",
2049
2078
  "dev:serve": "bun run src/server/index.ts",
2050
2079
  "dev:otel": "bun run src/otel/index.ts",
2051
- postinstall: "mkdir -p $HOME/.hasna/economy/training 2>/dev/null || true"
2080
+ postinstall: "node postinstall.js"
2052
2081
  },
2053
2082
  keywords: [
2054
2083
  "economy",
@@ -2074,6 +2103,7 @@ var package_default = {
2074
2103
  "@hasna/contracts": "0.13.4",
2075
2104
  "@hasna/events": "^0.1.16",
2076
2105
  "@hasna/mcp-harness": "^0.1.0",
2106
+ "@hasna/paths": "0.1.0",
2077
2107
  "@hasna/projects": "1.0.0",
2078
2108
  "@modelcontextprotocol/sdk": "^1.12.1",
2079
2109
  chalk: "^5.4.1",
@@ -1,4 +1,14 @@
1
1
  import type { SqliteAdapter as Database } from '../db/sqlite-adapter.js';
2
+ /**
3
+ * The default OpenLoops store path, resolved through the @hasna/paths resolver
4
+ * (XDG / macOS home layout): the resolver data root for `loops` once its store
5
+ * has been migrated there (`loops.db` present), otherwise the legacy
6
+ * `~/.hasna/loops/loops.db` — a legacy-read fallback, so economy keeps reading
7
+ * loops' store whichever side of the XDG migration loops is on. The exact-app
8
+ * override `HASNA_ECONOMY_LOOPS_DB_PATH` still wins unconditionally.
9
+ */
10
+ export declare function defaultLoopsDbPath(): string;
11
+ export declare function loopsDbPath(): string;
2
12
  export declare function ingestLoops(db: Database, verbose?: boolean): Promise<{
3
13
  sessions: number;
4
14
  requests: number;
@@ -1 +1 @@
1
- {"version":3,"file":"loops.d.ts","sourceRoot":"","sources":["../../src/ingest/loops.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AA4IxE,wBAAsB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA+FhH"}
1
+ {"version":3,"file":"loops.d.ts","sourceRoot":"","sources":["../../src/ingest/loops.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAiCxE;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAc3C;AAED,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAwGD,wBAAsB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA+FhH"}
@@ -1,7 +1,7 @@
1
1
  export declare const DEFAULT_MODEL = "gpt-4o-mini";
2
2
  /** Returns the active fine-tuned model ID, or DEFAULT_MODEL if none set. */
3
3
  export declare function getActiveModel(): string;
4
- /** Persists the active fine-tuned model ID to ~/.hasna/economy/config.json. */
4
+ /** Persists the active fine-tuned model ID to the economy data root (config.json). */
5
5
  export declare function setActiveModel(id: string): void;
6
6
  /** Clears the active model, falling back to DEFAULT_MODEL. */
7
7
  export declare function clearActiveModel(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"model-config.d.ts","sourceRoot":"","sources":["../../src/lib/model-config.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,aAAa,gBAAgB,CAAA;AA8B1C,4EAA4E;AAC5E,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAI/C;AAED,8DAA8D;AAC9D,wBAAgB,gBAAgB,IAAI,IAAI,CAIvC"}
1
+ {"version":3,"file":"model-config.d.ts","sourceRoot":"","sources":["../../src/lib/model-config.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,aAAa,gBAAgB,CAAA;AA8B1C,4EAA4E;AAC5E,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,sFAAsF;AACtF,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAI/C;AAED,8DAA8D;AAC9D,wBAAgB,gBAAgB,IAAI,IAAI,CAIvC"}
package/dist/mcp/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 getDataDir() {
630
+ function getHomeDir() {
630
631
  const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir();
631
- const newDir = join(home, ".hasna", "economy");
632
- const oldDir = join(home, ".economy");
633
- if (existsSync(oldDir) && !existsSync(newDir)) {
634
- mkdirSync(newDir, { recursive: true });
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(newDir, file));
666
+ copyFileSync(oldPath, join(effective, file));
639
667
  }
640
668
  }
641
669
  }
642
- mkdirSync(newDir, { recursive: true });
643
- return newDir;
670
+ mkdirSync(effective, { recursive: true });
671
+ return effective;
644
672
  }
645
673
  function getDbPath() {
646
674
  if (process.env["HASNA_ECONOMY_DB_PATH"])
@@ -2085,7 +2113,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
2085
2113
  // package.json
2086
2114
  var package_default = {
2087
2115
  name: "@hasna/economy",
2088
- version: "0.3.27",
2116
+ version: "0.3.28",
2089
2117
  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",
2090
2118
  repository: {
2091
2119
  type: "git",
@@ -2118,6 +2146,7 @@ var package_default = {
2118
2146
  "dist",
2119
2147
  "docs",
2120
2148
  "dashboard/dist",
2149
+ "postinstall.js",
2121
2150
  "LICENSE",
2122
2151
  "README.md",
2123
2152
  "CHANGELOG.md",
@@ -2140,7 +2169,7 @@ var package_default = {
2140
2169
  "dev:mcp": "bun run src/mcp/index.ts",
2141
2170
  "dev:serve": "bun run src/server/index.ts",
2142
2171
  "dev:otel": "bun run src/otel/index.ts",
2143
- postinstall: "mkdir -p $HOME/.hasna/economy/training 2>/dev/null || true"
2172
+ postinstall: "node postinstall.js"
2144
2173
  },
2145
2174
  keywords: [
2146
2175
  "economy",
@@ -2166,6 +2195,7 @@ var package_default = {
2166
2195
  "@hasna/contracts": "0.13.4",
2167
2196
  "@hasna/events": "^0.1.16",
2168
2197
  "@hasna/mcp-harness": "^0.1.0",
2198
+ "@hasna/paths": "0.1.0",
2169
2199
  "@hasna/projects": "1.0.0",
2170
2200
  "@modelcontextprotocol/sdk": "^1.12.1",
2171
2201
  chalk: "^5.4.1",
@@ -3486,10 +3516,18 @@ import { existsSync as existsSync8 } from "fs";
3486
3516
  import { homedir as homedir8 } from "os";
3487
3517
  import { join as join8 } from "path";
3488
3518
  import { Database as BunDatabase2 } from "bun:sqlite";
3489
- var DEFAULT_LOOPS_DB_PATH = join8(homedir8(), ".hasna", "loops", "loops.db");
3519
+ import { dataDir as dataDir2 } from "@hasna/paths";
3490
3520
  var LOOPS_INGEST_VERSION = "goal-runs-orchestration-v1";
3521
+ function defaultLoopsDbPath() {
3522
+ const resolved = dataDir2({
3523
+ app: "loops",
3524
+ home: process.env["HOME"] || process.env["USERPROFILE"] || undefined
3525
+ });
3526
+ const legacy = join8(process.env["HOME"] || process.env["USERPROFILE"] || homedir8(), ".hasna", "loops", "loops.db");
3527
+ return existsSync8(join8(resolved, "loops.db")) ? join8(resolved, "loops.db") : legacy;
3528
+ }
3491
3529
  function loopsDbPath() {
3492
- return process.env["HASNA_ECONOMY_LOOPS_DB_PATH"] ?? DEFAULT_LOOPS_DB_PATH;
3530
+ return process.env["HASNA_ECONOMY_LOOPS_DB_PATH"] ?? defaultLoopsDbPath();
3493
3531
  }
3494
3532
  function loopsModel() {
3495
3533
  return process.env["HASNA_ECONOMY_LOOPS_MODEL"] ?? process.env["ECONOMY_LOOPS_MODEL"] ?? "gpt-5.3-codex";
@@ -3673,11 +3711,11 @@ import { existsSync as existsSync9, readFileSync as readFileSync6 } from "fs";
3673
3711
  // src/lib/paths.ts
3674
3712
  import { homedir as homedir9 } from "os";
3675
3713
  import { join as join9 } from "path";
3676
- function getHomeDir() {
3714
+ function getHomeDir2() {
3677
3715
  return process.env["USERPROFILE"] ?? process.env["HOME"] ?? homedir9();
3678
3716
  }
3679
3717
  function agentPaths() {
3680
- const home = getHomeDir();
3718
+ const home = getHomeDir2();
3681
3719
  return {
3682
3720
  claudeProjects: join9(home, ".claude", "projects"),
3683
3721
  claudeCredentials: join9(home, ".claude", ".credentials.json"),
@@ -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 getDataDir() {
629
+ function getHomeDir() {
629
630
  const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir();
630
- const newDir = join(home, ".hasna", "economy");
631
- const oldDir = join(home, ".economy");
632
- if (existsSync(oldDir) && !existsSync(newDir)) {
633
- mkdirSync(newDir, { recursive: true });
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(newDir, file));
665
+ copyFileSync(oldPath, join(effective, file));
638
666
  }
639
667
  }
640
668
  }
641
- mkdirSync(newDir, { recursive: true });
642
- return newDir;
669
+ mkdirSync(effective, { recursive: true });
670
+ return effective;
643
671
  }
644
672
  function getDbPath() {
645
673
  if (process.env["HASNA_ECONOMY_DB_PATH"])
@@ -1388,7 +1416,7 @@ async function ingestOtelRows(db, rows) {
1388
1416
  // package.json
1389
1417
  var package_default = {
1390
1418
  name: "@hasna/economy",
1391
- version: "0.3.27",
1419
+ version: "0.3.28",
1392
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",
1393
1421
  repository: {
1394
1422
  type: "git",
@@ -1421,6 +1449,7 @@ var package_default = {
1421
1449
  "dist",
1422
1450
  "docs",
1423
1451
  "dashboard/dist",
1452
+ "postinstall.js",
1424
1453
  "LICENSE",
1425
1454
  "README.md",
1426
1455
  "CHANGELOG.md",
@@ -1443,7 +1472,7 @@ var package_default = {
1443
1472
  "dev:mcp": "bun run src/mcp/index.ts",
1444
1473
  "dev:serve": "bun run src/server/index.ts",
1445
1474
  "dev:otel": "bun run src/otel/index.ts",
1446
- postinstall: "mkdir -p $HOME/.hasna/economy/training 2>/dev/null || true"
1475
+ postinstall: "node postinstall.js"
1447
1476
  },
1448
1477
  keywords: [
1449
1478
  "economy",
@@ -1469,6 +1498,7 @@ var package_default = {
1469
1498
  "@hasna/contracts": "0.13.4",
1470
1499
  "@hasna/events": "^0.1.16",
1471
1500
  "@hasna/mcp-harness": "^0.1.0",
1501
+ "@hasna/paths": "0.1.0",
1472
1502
  "@hasna/projects": "1.0.0",
1473
1503
  "@modelcontextprotocol/sdk": "^1.12.1",
1474
1504
  chalk: "^5.4.1",
@@ -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 getDataDir() {
630
+ function getHomeDir() {
630
631
  const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir();
631
- const newDir = join(home, ".hasna", "economy");
632
- const oldDir = join(home, ".economy");
633
- if (existsSync(oldDir) && !existsSync(newDir)) {
634
- mkdirSync(newDir, { recursive: true });
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(newDir, file));
666
+ copyFileSync(oldPath, join(effective, file));
639
667
  }
640
668
  }
641
669
  }
642
- mkdirSync(newDir, { recursive: true });
643
- return newDir;
670
+ mkdirSync(effective, { recursive: true });
671
+ return effective;
644
672
  }
645
673
  function getDbPath() {
646
674
  if (process.env["HASNA_ECONOMY_DB_PATH"])
@@ -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
- var DEFAULT_LOOPS_DB_PATH = join8(homedir8(), ".hasna", "loops", "loops.db");
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"] ?? DEFAULT_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 getHomeDir() {
4825
+ function getHomeDir2() {
4790
4826
  return process.env["USERPROFILE"] ?? process.env["HOME"] ?? homedir9();
4791
4827
  }
4792
4828
  function agentPaths() {
4793
- const home = getHomeDir();
4829
+ const home = getHomeDir2();
4794
4830
  return {
4795
4831
  claudeProjects: join9(home, ".claude", "projects"),
4796
4832
  claudeCredentials: join9(home, ".claude", ".credentials.json"),
@@ -5975,11 +6011,11 @@ 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";
5979
6015
  // package.json
5980
6016
  var package_default = {
5981
6017
  name: "@hasna/economy",
5982
- version: "0.3.27",
6018
+ version: "0.3.28",
5983
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",
5984
6020
  repository: {
5985
6021
  type: "git",
@@ -6012,6 +6048,7 @@ var package_default = {
6012
6048
  "dist",
6013
6049
  "docs",
6014
6050
  "dashboard/dist",
6051
+ "postinstall.js",
6015
6052
  "LICENSE",
6016
6053
  "README.md",
6017
6054
  "CHANGELOG.md",
@@ -6034,7 +6071,7 @@ var package_default = {
6034
6071
  "dev:mcp": "bun run src/mcp/index.ts",
6035
6072
  "dev:serve": "bun run src/server/index.ts",
6036
6073
  "dev:otel": "bun run src/otel/index.ts",
6037
- postinstall: "mkdir -p $HOME/.hasna/economy/training 2>/dev/null || true"
6074
+ postinstall: "node postinstall.js"
6038
6075
  },
6039
6076
  keywords: [
6040
6077
  "economy",
@@ -6060,6 +6097,7 @@ var package_default = {
6060
6097
  "@hasna/contracts": "0.13.4",
6061
6098
  "@hasna/events": "^0.1.16",
6062
6099
  "@hasna/mcp-harness": "^0.1.0",
6100
+ "@hasna/paths": "0.1.0",
6063
6101
  "@hasna/projects": "1.0.0",
6064
6102
  "@modelcontextprotocol/sdk": "^1.12.1",
6065
6103
  chalk: "^5.4.1",
@@ -7807,8 +7845,8 @@ function dashboardPath(root, pathname) {
7807
7845
  return null;
7808
7846
  }
7809
7847
  const relativePath = decoded === "/" ? "index.html" : decoded.replace(/^\/+/, "");
7810
- const rootPath = resolve(root);
7811
- const filePath = resolve(rootPath, relativePath);
7848
+ const rootPath = resolve2(root);
7849
+ const filePath = resolve2(rootPath, relativePath);
7812
7850
  return filePath === rootPath || filePath.startsWith(rootPath + sep) ? filePath : null;
7813
7851
  }
7814
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.27",
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",
@@ -33,6 +33,7 @@
33
33
  "dist",
34
34
  "docs",
35
35
  "dashboard/dist",
36
+ "postinstall.js",
36
37
  "LICENSE",
37
38
  "README.md",
38
39
  "CHANGELOG.md",
@@ -55,7 +56,7 @@
55
56
  "dev:mcp": "bun run src/mcp/index.ts",
56
57
  "dev:serve": "bun run src/server/index.ts",
57
58
  "dev:otel": "bun run src/otel/index.ts",
58
- "postinstall": "mkdir -p $HOME/.hasna/economy/training 2>/dev/null || true"
59
+ "postinstall": "node postinstall.js"
59
60
  },
60
61
  "keywords": [
61
62
  "economy",
@@ -81,6 +82,7 @@
81
82
  "@hasna/contracts": "0.13.4",
82
83
  "@hasna/events": "^0.1.16",
83
84
  "@hasna/mcp-harness": "^0.1.0",
85
+ "@hasna/paths": "0.1.0",
84
86
  "@hasna/projects": "1.0.0",
85
87
  "@modelcontextprotocol/sdk": "^1.12.1",
86
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
+ }