@hasna/economy 0.3.28 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +95 -0
  2. package/CONTRIBUTING.md +1 -1
  3. package/README.md +6 -13
  4. package/SECURITY.md +1 -1
  5. package/dist/cli/commands/extras.d.ts.map +1 -1
  6. package/dist/cli/index.js +1135 -853
  7. package/dist/db/cloud.d.ts +16 -8
  8. package/dist/db/cloud.d.ts.map +1 -1
  9. package/dist/db/database.d.ts +19 -0
  10. package/dist/db/database.d.ts.map +1 -1
  11. package/dist/index.js +198 -19
  12. package/dist/ingest/claude.d.ts.map +1 -1
  13. package/dist/ingest/loops.d.ts +9 -0
  14. package/dist/ingest/loops.d.ts.map +1 -1
  15. package/dist/lib/accounts-store.d.ts +83 -0
  16. package/dist/lib/accounts-store.d.ts.map +1 -0
  17. package/dist/lib/accounts.d.ts +2 -1
  18. package/dist/lib/accounts.d.ts.map +1 -1
  19. package/dist/lib/api-display-url.d.ts +19 -0
  20. package/dist/lib/api-display-url.d.ts.map +1 -0
  21. package/dist/lib/autosync-gate.d.ts +15 -0
  22. package/dist/lib/autosync-gate.d.ts.map +1 -0
  23. package/dist/lib/cloud-ingest.d.ts +15 -1
  24. package/dist/lib/cloud-ingest.d.ts.map +1 -1
  25. package/dist/lib/cloud-storage.d.ts +166 -10
  26. package/dist/lib/cloud-storage.d.ts.map +1 -1
  27. package/dist/lib/gatherer.d.ts.map +1 -1
  28. package/dist/lib/serve-auth.d.ts.map +1 -1
  29. package/dist/lib/store/index.d.ts +6 -3
  30. package/dist/lib/store/index.d.ts.map +1 -1
  31. package/dist/lib/test-hermetic-accounts.d.ts +3 -2
  32. package/dist/lib/test-hermetic-accounts.d.ts.map +1 -1
  33. package/dist/lib/test-hermetic-fleet-env.d.ts +4 -0
  34. package/dist/lib/test-hermetic-fleet-env.d.ts.map +1 -0
  35. package/dist/mcp/agent-registry.d.ts +85 -0
  36. package/dist/mcp/agent-registry.d.ts.map +1 -0
  37. package/dist/mcp/harness.d.ts +26 -0
  38. package/dist/mcp/harness.d.ts.map +1 -0
  39. package/dist/mcp/index.js +1340 -302
  40. package/dist/mcp/server.d.ts.map +1 -1
  41. package/dist/otel/index.js +430 -16
  42. package/dist/server/index.js +548 -155
  43. package/dist/server/serve.d.ts +0 -2
  44. package/dist/server/serve.d.ts.map +1 -1
  45. package/docs/cli.md +2 -3
  46. package/docs/configuration.md +23 -10
  47. package/docs/ingestion.md +1 -1
  48. package/docs/mcp.md +4 -2
  49. package/docs/otel.md +8 -2
  50. package/docs/rest-api.md +2 -2
  51. package/package.json +5 -11
  52. package/postinstall.js +52 -1
  53. package/dashboard/README.md +0 -26
  54. package/dist/cli/brains.d.ts +0 -3
  55. package/dist/cli/brains.d.ts.map +0 -1
@@ -20,11 +20,9 @@ export interface ApiAuthenticator {
20
20
  }
21
21
  interface StartServerOptions {
22
22
  db?: Database;
23
- dashboardDir?: string;
24
23
  hostname?: string;
25
24
  log?: (message: string) => void;
26
25
  }
27
- export declare function createServerFetch(apiHandler: (req: Request) => Promise<Response>, dashboardDir?: string): (req: Request) => Promise<Response>;
28
26
  export interface HandlerOptions {
29
27
  /** API-key verifier (from `@hasna/contracts/auth`). When present, every
30
28
  * request outside the open foundation probes must present a valid
@@ -1 +1 @@
1
- {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/server/serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAgDxE,gFAAgF;AAChF,wBAAgB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAGzD;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,YAAY,CACV,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,GAC7F,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC/E;AAuBD,UAAU,kBAAkB;IAC1B,EAAE,CAAC,EAAE,QAAQ,CAAA;IACb,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CAChC;AA6ED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,EAAE,YAAY,SAAwB,IACzF,KAAK,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CAwB7D;AAQD,MAAM,WAAW,cAAc;IAC7B;;wEAEoE;IACpE,aAAa,CAAC,EAAE,gBAAgB,CAAA;IAChC,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAChE;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,GAAE,cAAmB,IACxC,KAAK,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CAmhB/D;AAMD,wBAAgB,WAAW,CAAC,IAAI,SAAO,EAAE,OAAO,GAAE,kBAAuB,GAAG,UAAU,CAAC,OAAO,GAAG,CAAC,KAAK,CAAC,CAqDvG"}
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/server/serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AA8CxE,gFAAgF;AAChF,wBAAgB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAGzD;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,YAAY,CACV,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,GAC7F,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC/E;AAsBD,UAAU,kBAAkB;IAC1B,EAAE,CAAC,EAAE,QAAQ,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CAChC;AAmED,MAAM,WAAW,cAAc;IAC7B;;wEAEoE;IACpE,aAAa,CAAC,EAAE,gBAAgB,CAAA;IAChC,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAChE;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,GAAE,cAAmB,IACxC,KAAK,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CAmhB/D;AAMD,wBAAgB,WAAW,CAAC,IAAI,SAAO,EAAE,OAAO,GAAE,kBAAuB,GAAG,UAAU,CAAC,OAAO,GAAG,CAAC,KAAK,CAAC,CAqDvG"}
package/docs/cli.md CHANGED
@@ -6,7 +6,7 @@ Installing `@hasna/economy` provides four binaries:
6
6
  | --- | --- |
7
7
  | `economy` | Ingest, query, and manage Economy data. |
8
8
  | `economy-mcp` | Run the MCP server over stdio or Streamable HTTP. |
9
- | `economy-serve` | Serve the REST API and built dashboard, or migrate a self-hosted database. |
9
+ | `economy-serve` | Serve the REST API, or migrate a self-hosted database. |
10
10
  | `economy-otel` | Ingest OTLP/HTTP metrics or simplified cost events into local SQLite. |
11
11
 
12
12
  Use `<binary> --help` and `economy <command> --help` for the exact help emitted by the installed version.
@@ -32,6 +32,7 @@ The supported coding-agent values are `claude`, `takumi`, `codex`, `gemini`, `op
32
32
  | `economy watch` | Poll recent costs, or use `--daemon` to sync watched local paths. Also accepts `--interval`, `--agent`, and macOS `--notify`. In cloud mode it streams the API and does not ingest local files. |
33
33
  | `economy status` | Print one-line spend, fleet, storage, top-agent, and available quota status. |
34
34
  | `economy doctor` | Check source paths/token availability, storage mode, pricing gaps, deduplication, and billing drift. |
35
+ | `economy transport` | Report the resolved client transport and credential SOURCE (never the key): the `/v1` authority, the URL/key source names, and the credential tier from the `@hasna/contracts` chain; `--json` for the full report. Exits 0 even when unconfigured — the refusal is the report. |
35
36
  | `economy init` | Print first-run local and cloud-client setup hints. |
36
37
 
37
38
  Human output for high-cardinality commands is intentionally capped. Use the command's `--json`, `--verbose`, or `--limit` option when available. JSON output is complete; `--verbose` has command-specific semantics, so consult `--help`.
@@ -67,11 +68,9 @@ Human output for high-cardinality commands is intentionally capped. Use the comm
67
68
  | Command | Behavior |
68
69
  | --- | --- |
69
70
  | `economy serve --port <port>` | Start the REST API in-process. Equivalent server controls are documented under [`economy-serve`](configuration.md#rest-server). |
70
- | `economy dashboard --port <port>` | Start the server when necessary and open the dashboard URL. |
71
71
  | `economy mcp` | Print Claude Code, Codex, and Gemini MCP configuration; select one or use `--all`. |
72
72
  | `economy completion <shell>` | Print completion for `bash`, `zsh`, or `fish`. |
73
73
  | `economy menubar` | `install [--force]`, `start`, `stop`, or `uninstall` the macOS Economy Bar app. |
74
- | `economy brains` | `gather`, `train`, `model [set|clear]`, and `status`. Fine-tuning requires the optional `@hasna/brains` package; gathering does not. |
75
74
  | `economy events`, `economy webhooks` | Event emit/list/replay and event-subscription commands supplied by `@hasna/events`; use the nested `--help` for options. |
76
75
  | `economy todos` | Display the bundled Economy roadmap, filter tasks, or show a task ID. This is project planning data, not live service status. |
77
76
 
@@ -9,10 +9,10 @@ The default data directory is `~/.hasna/economy/` and the SQLite database is `~/
9
9
  | `HASNA_ECONOMY_DB_PATH` | SQLite path; takes precedence over `ECONOMY_DB`. |
10
10
  | `ECONOMY_DB` | Alternate SQLite path. `:memory:` is useful for tests. |
11
11
  | `HASNA_ECONOMY_CONFIG_PATH` | Path to `config.json`; defaults under the data directory. |
12
- | `ECONOMY_MACHINE_ID` | Machine identifier; otherwise Economy uses the normalized hostname. |
12
+ | `HASNA_ECONOMY_MACHINE_ID` | Machine identifier; otherwise Economy uses the normalized hostname. |
13
13
  | `ECONOMY_TAG` | Fallback attribution tag on locally written sessions/requests. |
14
14
 
15
- `economy config` reads and writes `config.json`. The defaults are `port=3456`, `default-period=today`, `auto-sync=true`, `sync-interval=30`, `alert-thresholds=[5,10,25,50,100]`, and `webhook-url=null`. At present, `webhook-url` drives budget notifications and `activeModel` drives `economy brains`; the other stored values are compatibility/settings metadata. Binary ports, periods, and watch intervals still come from command options or the environment described below.
15
+ `economy config` reads and writes `config.json`. The defaults are `port=3456`, `default-period=today`, `auto-sync=true`, `sync-interval=30`, `alert-thresholds=[5,10,25,50,100]`, and `webhook-url=null`. At present, `webhook-url` drives budget notifications and `activeModel` records the selected model used by AI analysis; the other stored values are compatibility/settings metadata. Binary ports, periods, and watch intervals still come from command options or the environment described below.
16
16
 
17
17
  ## Account attribution
18
18
 
@@ -34,14 +34,27 @@ The same agent-specific pattern applies to all eight supported agents. Account k
34
34
 
35
35
  ## CLI/MCP cloud client
36
36
 
37
- Local is the default. To route CLI and MCP data operations to a shared server, set:
37
+ The CLI and MCP server resolve their credential through the `@hasna/contracts` 1.0.2 client resolver, FRESH ON EVERY CALL (and per request inside a long-lived MCP server, so a key rotation heals without a restart). The tiers, in order:
38
+
39
+ 1. an explicit `--api-key` / `--profile` argument (CLI flags only)
40
+ 2. a deliberate env pointer — `HASNA_ECONOMY_API_KEY_OVERRIDE`, `HASNA_PROFILE`, `HASNA_ECONOMY_API_KEY_REF`
41
+ 3. the macOS Keychain — item `hasna.credentials.economy.api-key`, account `HASNA_STATION` → `hostname -s` → `$USER`
42
+ 4. disk — `~/.hasna/economy/config/credentials` (0600, `HASNA_ECONOMY_API_KEY=…`; `HASNA_HOME` / `HASNA_CONFIG_HOME` move the root; XDG locations are never read)
43
+ 5. `HASNA_ECONOMY_API_KEY` in the environment — legitimate, no deprecation notice
44
+
45
+ The authority follows the same ladder — `HASNA_ECONOMY_API_URL`, the Keychain `api-url` item, the credentials file — and DEFAULTS to the fleet gateway `https://api.hasna.com/economy` once a credential resolves, so a key alone is a complete configuration. An existing `/v1` suffix is normalized, otherwise it is appended. The unprefixed `ECONOMY_API_URL` / `ECONOMY_API_KEY` spellings are legacy aliases, accepted for one release at lower precedence.
46
+
47
+ **Fail closed (owner directive 2026-09-04).** A run without a credential from any tier exits non-zero, creates no SQLite file, and emits no `economy-local-fallback` event: an unconfigured client refuses to guess which dataset it serves. The error names every tier consulted.
48
+
49
+ **Local mode (the on-box SQLite store) is reachable only by explicit opt-in:**
38
50
 
39
51
  ```bash
40
- export HASNA_ECONOMY_API_URL=https://economy.example.com
41
- export HASNA_ECONOMY_API_KEY='...'
52
+ export HASNA_ECONOMY_LOCAL=1
42
53
  ```
43
54
 
44
- URL plus key is itself a cloud-mode signal. You may explicitly set `HASNA_ECONOMY_STORAGE_MODE=cloud`; `self_hosted`, `remote`, and `hybrid` are accepted deprecated aliases. The resolver also accepts `HASNA_ECONOMY_MODE`, `ECONOMY_STORAGE_MODE`, and `ECONOMY_MODE`, plus unprefixed `ECONOMY_API_URL`/`ECONOMY_API_KEY` aliases. An existing `/v1` suffix is normalized, otherwise it is appended. Cloud mode with no key or an invalid URL fails rather than reading an unintended local dataset.
55
+ The unprefixed `ECONOMY_LOCAL=1` alias is accepted. The opt-in yields to every hosted signal (a URL, a key, or a pointer in the environment outranks it), and a local run prints one line on stderr — `economy: local mode (HASNA_ECONOMY_LOCAL=1) …` — so an unhosted run is never mistaken for a hosted one that came back empty.
56
+
57
+ Retired `*_STORAGE_MODE` / `*_MODE` variables no longer exist as selectors: `HASNA_ECONOMY_STORAGE_MODE` (and `HASNA_ECONOMY_MODE`, `ECONOMY_STORAGE_MODE`, `ECONOMY_MODE`, plus the accounts variants `HASNA_ACCOUNTS_*_MODE`) are a hard error on the client — delete them and let the resolved credential do the routing.
45
58
 
46
59
  In cloud-client mode, data commands use the HTTP API, and local auto-sync, explicit `economy sync`, and `economy billing sync` are skipped. Clients never need or use a Postgres DSN.
47
60
 
@@ -54,12 +67,10 @@ economy serve --port 3456
54
67
  economy-serve --port 3456
55
68
  ```
56
69
 
57
- `ECONOMY_PORT` supplies the `economy-serve` default. `ECONOMY_BIND` (or `ECONOMY_HOST`) controls the local bind host. `ECONOMY_API_TOKEN` (or `HASNA_ECONOMY_API_TOKEN`) enables the local shared-token check; send it as `Authorization: Bearer ...` or `X-Economy-Token`.
70
+ `ECONOMY_PORT` supplies the `economy-serve` default. `ECONOMY_BIND` (or `ECONOMY_HOST`) controls the local bind host. `HASNA_ECONOMY_API_TOKEN` enables the local shared-token check; send it as `Authorization: Bearer ...` or `X-Economy-Token`. (The unprefixed `ECONOMY_API_TOKEN` spelling is retired.)
58
71
 
59
72
  Without a local token, the current server defaults to `0.0.0.0` and API routes are unauthenticated. Set a token and an intentional bind address before exposing a local-mode server to another host.
60
73
 
61
- The server serves `dashboard/dist` and falls back to its `index.html` for non-API paths when those assets exist.
62
-
63
74
  ## Self-hosted server
64
75
 
65
76
  The server backend follows the database URL alone — `postgresql` when one of these is set, `sqlite` when none is:
@@ -70,7 +81,7 @@ ECONOMY_DATABASE_URL
70
81
  DATABASE_URL
71
82
  ```
72
83
 
73
- `HASNA_ECONOMY_STORAGE_MODE` (and `HASNA_ECONOMY_MODE`, `ECONOMY_STORAGE_MODE`, `ECONOMY_MODE`) no longer selects a backend: the server refuses to start and prints a migration hint. Delete it and set a DSN instead. This is server-only — the CLI/MCP client still reads the mode variable described above.
84
+ `HASNA_ECONOMY_STORAGE_MODE` (and `HASNA_ECONOMY_MODE`, `ECONOMY_STORAGE_MODE`, `ECONOMY_MODE`) no longer selects a backend: the server refuses to start and prints a migration hint. Delete it and set a DSN instead. The client follows the same rule — the variables are a hard error there too, and API routing comes from the URL + key pair above.
74
85
 
75
86
  Apply migrations with `economy-serve migrate`. `ECONOMY_PG_POOL_MAX` defaults to 5. A non-loopback server also requires one of `HASNA_ECONOMY_API_SIGNING_KEY`, `HASNA_API_SIGNING_KEY`, or `API_KEY_SIGNING_SECRET`; API keys are then verified by `@hasna/contracts`. The signing secret belongs only on the server.
76
87
 
@@ -84,5 +95,7 @@ See [REST API authentication](rest-api.md#authentication) for request headers an
84
95
  | `MCP_HTTP_PORT` | MCP HTTP port; default 8860 and overridden by `--port`. |
85
96
  | `ECONOMY_OTEL_PORT` | OTLP sidecar port; default 4318 and overridden by `--port`. |
86
97
  | `ECONOMY_OTEL_BIND` | OTLP sidecar bind host; default `127.0.0.1`. |
98
+ | `HASNA_ECONOMY_INGEST_CACHE` | Path of the hosted `economy sync` mtime cache (a JSON file, never SQLite). Default `<cache root>/economy/ingest-cache.json`, where the cache root is `HASNA_CACHE_HOME`, else `~/Library/Caches/Hasna` (macOS) or `~/.cache/hasna`. Losing it costs one re-read; the server upserts are idempotent. |
99
+ | `HASNA_AGENT_REGISTRY_DB_PATH` | File for the MCP agent registry. Default: in memory when hosted, `agent-registry.db` under the data directory under `HASNA_ECONOMY_LOCAL=1`. |
87
100
 
88
101
  Source- and billing-specific environment variables are listed in [Ingestion](ingestion.md).
package/docs/ingestion.md CHANGED
@@ -26,7 +26,7 @@ Full, unfiltered sync also attempts to import active metadata from `@hasna/proje
26
26
  File/database state is cached in the `ingest_state` table, and request IDs are upserted and deduplicated. Consequently, normal repeated syncs are incremental.
27
27
 
28
28
  - `--force` clears the ingest-state entries for the supported sources and reprocesses them.
29
- - `--backfill-machine` fills empty `machine_id` values with `ECONOMY_MACHINE_ID` or the normalized hostname.
29
+ - `--backfill-machine` fills empty `machine_id` values with `HASNA_ECONOMY_MACHINE_ID` or the normalized hostname.
30
30
  - `--recalculate` prices token-bearing requests whose `cost_usd` is zero, then rerolls affected sessions. It reports buckets still missing usable pricing.
31
31
  - Budget webhooks are checked after a CLI or REST sync. Failed deliveries remain eligible for a later retry.
32
32
 
package/docs/mcp.md CHANGED
@@ -24,7 +24,7 @@ Gemini settings:
24
24
  }
25
25
  ```
26
26
 
27
- `economy mcp --all` prints these snippets. The MCP server uses the same local-versus-cloud Store selection as the CLI; configure a shared API with `HASNA_ECONOMY_API_URL` and `HASNA_ECONOMY_API_KEY` as described in [configuration](configuration.md#climcp-cloud-client).
27
+ `economy mcp --all` prints these snippets. The MCP server uses the same local-versus-cloud Store selection as the CLI; the credential resolves through the `@hasna/contracts` chain as described in [configuration](configuration.md#climcp-cloud-client).
28
28
 
29
29
  ## Streamable HTTP
30
30
 
@@ -56,10 +56,12 @@ Management and estimation:
56
56
  - `list_subscriptions`, `set_subscription`, `remove_subscription`
57
57
  - `sync`, `send_feedback`
58
58
 
59
- Shared agent-registry tools:
59
+ Shared agent lifecycle tools:
60
60
 
61
61
  - `register_agent`, `heartbeat`, `set_focus`, `list_agents`
62
62
 
63
+ The agent registry behind these tools is opened on first use, never at startup. A hosted server (a resolved credential) keeps it in memory for the life of the process — no SQLite file is created under `~/.hasna/economy` in hosted mode; under the explicit local opt-in (`HASNA_ECONOMY_LOCAL=1`) it persists as `agent-registry.db` beside the local store and is shared by every MCP process on the box. `HASNA_AGENT_REGISTRY_DB_PATH` names a file explicitly in either lane.
64
+
63
65
  High-cardinality tools return compact text by default. Where the schema offers them, use `limit`, `verbose=true`, or `json=true`. Limits are clamped to 100 for MCP calls.
64
66
 
65
67
  The `sync` tool accepts `all`, any supported coding agent, or `loops`. It ingests on-box files (in local mode into the local SQLite; in cloud-client mode it reads the on-box files on this machine and pushes the rows to the shared API).
package/docs/otel.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # OTLP/HTTP sidecar
2
2
 
3
- `economy-otel` writes application/service metrics into the local Economy SQLite database:
3
+ `economy-otel` ingests application/service metrics into Economy — forwarded to the hosted API when a credential resolves, or written to the on-box SQLite store under the explicit local opt-in (see [Storage](#storage)):
4
4
 
5
5
  ```bash
6
6
  economy-otel --port 4318
@@ -50,4 +50,10 @@ The OTLP parser reads sum or gauge data points. Metric names containing cost or
50
50
 
51
51
  Resource and point attributes may use plain names (`model`, `session_id`, `request_id`, `project_path`, and the attribution fields above) or common dotted aliases such as `ai.model`, `session.id`, `event.id`, `service.name`, `economy.cost_center`, and `account.email`.
52
52
 
53
- The sidecar always opens the local SQLite database. It does not forward to a cloud API; use the authenticated REST bulk-ingest path for remote ingestion.
53
+ ## Storage
54
+
55
+ The sidecar follows the same storage seam as the CLI and the MCP server ([configuration](configuration.md#climcp-cloud-client)), decided once at startup, before the listener binds:
56
+
57
+ - **Hosted** — a credential resolves through the `@hasna/contracts` chain (Keychain, `~/.hasna/economy/config/credentials`, `HASNA_ECONOMY_API_KEY`): every accepted payload is ingested into a scratch in-memory store and its request/session rows are pushed to the shared API's `/v1/ingest` (idempotent upserts). The response adds `forwarded` (rows the server accepted). Nothing is written under `~/.hasna/economy`; the `cost_centers` table is not part of `/v1/ingest`, so pushed rows carry their `cost_center_id` but the server's cost-center registry is not updated by the sidecar. A failed push answers 502 and the payload is not retried.
58
+ - **Local** — `HASNA_ECONOMY_LOCAL=1`: rows are written to the on-box `economy.db`, and the sidecar prints `economy: local mode …` on stderr once.
59
+ - **Neither** — the sidecar fails closed: exit 1 with the resolver's diagnostic and no listener, no SQLite file.
package/docs/rest-api.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Start the service with `economy-serve` or `economy serve`. The default local origin is `http://127.0.0.1:3456`; the bind behavior is detailed in [configuration](configuration.md#rest-server).
4
4
 
5
- The canonical application prefix is `/v1`. Equivalent `/api` routes remain available for the bundled dashboard and older clients. Successful application responses use:
5
+ The canonical application prefix is `/v1`. Equivalent `/api` routes remain available for older clients. Successful application responses use:
6
6
 
7
7
  ```json
8
8
  { "data": {}, "meta": {} }
@@ -23,7 +23,7 @@ These routes are open. The checked-in OpenAPI source is [`openapi/economy.json`]
23
23
 
24
24
  ## Authentication
25
25
 
26
- In local mode, set `ECONOMY_API_TOKEN` or `HASNA_ECONOMY_API_TOKEN` and send either:
26
+ In local mode, set `HASNA_ECONOMY_API_TOKEN` (the unprefixed `ECONOMY_API_TOKEN` spelling is retired) and send either:
27
27
 
28
28
  ```text
29
29
  Authorization: Bearer <token>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hasna/economy",
3
- "version": "0.3.28",
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",
3
+ "version": "0.4.0",
4
+ "description": "AI coding cost tracker \u2014 CLI + MCP server + REST API 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",
7
7
  "url": "git+https://github.com/hasna/economy.git"
@@ -32,7 +32,6 @@
32
32
  "files": [
33
33
  "dist",
34
34
  "docs",
35
- "dashboard/dist",
36
35
  "postinstall.js",
37
36
  "LICENSE",
38
37
  "README.md",
@@ -42,12 +41,11 @@
42
41
  "CODE_OF_CONDUCT.md"
43
42
  ],
44
43
  "scripts": {
45
- "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",
44
+ "build": "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",
46
45
  "build:cli": "bun build src/cli/index.ts --outdir dist/cli --target bun --packages external",
47
46
  "build:mcp": "bun build src/mcp/index.ts --outdir dist/mcp --target bun --packages external",
48
47
  "build:server": "bun build src/server/index.ts --outdir dist/server --target bun --packages external",
49
48
  "build:lib": "bun build src/index.ts --outdir dist --target bun --packages external",
50
- "build:dashboard": "cd dashboard && bun run build",
51
49
  "artifact-scan": "bun scripts/contracts-cli.mjs artifact-scan dist",
52
50
  "prepack": "bun run build && bun run artifact-scan",
53
51
  "typecheck": "tsc --noEmit",
@@ -77,12 +75,8 @@
77
75
  "access": "public"
78
76
  },
79
77
  "dependencies": {
80
- "@hasna/accounts": "0.2.23",
81
- "@hasna/agent-registry": "^0.1.0",
82
- "@hasna/contracts": "0.13.4",
78
+ "@hasna/contracts": "1.0.2",
83
79
  "@hasna/events": "^0.1.16",
84
- "@hasna/mcp-harness": "^0.1.0",
85
- "@hasna/paths": "0.1.0",
86
80
  "@hasna/projects": "1.0.0",
87
81
  "@modelcontextprotocol/sdk": "^1.12.1",
88
82
  "chalk": "^5.4.1",
@@ -91,7 +85,7 @@
91
85
  "zod": "^3.24.2"
92
86
  },
93
87
  "devDependencies": {
94
- "@types/bun": "latest",
88
+ "@types/bun": "1.3.14",
95
89
  "@types/pg": "^8.20.0",
96
90
  "bun-types": "latest",
97
91
  "typescript": "^5.7.2"
package/postinstall.js CHANGED
@@ -6,6 +6,57 @@
6
6
  // ~/.hasna/economy default. Failures are non-fatal: the runtime getDataDir()
7
7
  // creates the same directories on first use.
8
8
  import { existsSync, mkdirSync } from "node:fs";
9
+ // --- Local path resolver -------------------------------------------------
10
+ // @hasna/paths was deleted (hasna/apps#1535, 2026-09-03); this in-package
11
+ // implementation preserves the resolver contract (XDG / macOS home layout
12
+ // honoring HASNA_{CONFIG,DATA,STATE,CACHE}_HOME, with the same env-override
13
+ // and home-override semantics the deleted package had).
14
+ import { homedir as pathsResolverHomedir } from "node:os";
15
+ import { join as pathsResolverJoin } from "node:path";
16
+
17
+ const PATHS_RESOLVER_KIND_ENV = {
18
+ config: "HASNA_CONFIG_HOME",
19
+ data: "HASNA_DATA_HOME",
20
+ state: "HASNA_STATE_HOME",
21
+ cache: "HASNA_CACHE_HOME",
22
+ };
23
+
24
+ function pathsResolverBaseDir(kind, options) {
25
+ const env = options.env ?? process.env;
26
+ const override = env[PATHS_RESOLVER_KIND_ENV[kind]];
27
+ if (typeof override === "string" && override.length > 0) return override;
28
+ const home = options.home ?? pathsResolverHomedir();
29
+ const platform = options.platform ?? process.platform;
30
+ if (platform === "darwin") {
31
+ switch (kind) {
32
+ case "config":
33
+ case "data":
34
+ return pathsResolverJoin(home, "Library", "Application Support", "Hasna");
35
+ case "cache":
36
+ return pathsResolverJoin(home, "Library", "Caches", "Hasna");
37
+ case "state":
38
+ return pathsResolverJoin(home, "Library", "Logs", "Hasna");
39
+ }
40
+ }
41
+ switch (kind) {
42
+ case "config":
43
+ return pathsResolverJoin(home, ".config", "hasna");
44
+ case "data":
45
+ return pathsResolverJoin(home, ".local", "share", "hasna");
46
+ case "state":
47
+ return pathsResolverJoin(home, ".local", "state", "hasna");
48
+ case "cache":
49
+ return pathsResolverJoin(home, ".cache", "hasna");
50
+ }
51
+ }
52
+
53
+ function pathsResolverResolve(kind, options) {
54
+ const appSegment = options.internal === true ? pathsResolverJoin("internal", options.app) : options.app;
55
+ return pathsResolverJoin(pathsResolverBaseDir(kind, options), appSegment);
56
+ }
57
+ function dataDir(options) {
58
+ return pathsResolverResolve("data", options);
59
+ }
9
60
  import { homedir } from "node:os";
10
61
  import { join } from "node:path";
11
62
 
@@ -13,7 +64,7 @@ const EXACT_OVERRIDE = (process.env["HASNA_ECONOMY_HOME"] || process.env["ECONOM
13
64
  const DATA_HOME_OVERRIDE = (process.env["HASNA_DATA_HOME"] || "").trim();
14
65
 
15
66
  try {
16
- const { dataDir } = await import("@hasna/paths");
67
+ // (local resolver — @hasna/paths deleted, hasna/apps#1535)
17
68
  const resolved = dataDir({ app: "economy" });
18
69
  let root;
19
70
  if (EXACT_OVERRIDE) {
@@ -1,26 +0,0 @@
1
- # Economy dashboard
2
-
3
- The dashboard is the React/Vite UI served by `economy-serve`. It covers overview, sessions, models, projects, budgets, goals, usage, accounts, savings, fleet, reconciliation, pricing, and provider billing.
4
-
5
- ## Development
6
-
7
- Run the API from the repository root, then start Vite:
8
-
9
- ```bash
10
- economy-serve --port 3456
11
- cd dashboard
12
- bun install
13
- bun run dev
14
- ```
15
-
16
- The development build uses `http://localhost:3456` by default. Set `VITE_API_URL` at build/dev time to use another origin. The dashboard currently calls the server's legacy-compatible `/api` routes; the public canonical API is `/v1`.
17
-
18
- ## Validation
19
-
20
- ```bash
21
- bun test
22
- bun run lint
23
- bun run build
24
- ```
25
-
26
- The root package build writes production assets to `dashboard/dist`; `economy-serve` serves those assets and provides SPA fallback routing.
@@ -1,3 +0,0 @@
1
- import { Command } from 'commander';
2
- export declare function registerBrainsCommand(program: Command): void;
3
- //# sourceMappingURL=brains.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"brains.d.ts","sourceRoot":"","sources":["../../src/cli/brains.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAQnC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAoL5D"}