@hasna/economy 0.3.28 → 0.5.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 (216) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/CONTRIBUTING.md +1 -1
  3. package/README.md +18 -22
  4. package/SECURITY.md +1 -1
  5. package/dist/chunks/agent-registry-store-tpnhx3wj.js +39 -0
  6. package/dist/chunks/billing-bkpxy4kv.js +13 -0
  7. package/dist/chunks/config-j9mv3ehk.js +15 -0
  8. package/dist/chunks/index-2hwzmfgv.js +3407 -0
  9. package/dist/chunks/index-axh32d30.js +1313 -0
  10. package/dist/chunks/index-emvs5ph2.js +2085 -0
  11. package/dist/chunks/index-ez9thsr7.js +3440 -0
  12. package/dist/chunks/index-hesxq90g.js +62 -0
  13. package/dist/chunks/index-k540hfww.js +258 -0
  14. package/dist/chunks/index-kgy9b6bf.js +451 -0
  15. package/dist/chunks/index-kn5572kd.js +124 -0
  16. package/dist/chunks/index-p35cyhc4.js +4 -0
  17. package/dist/chunks/index-w3bvv0ns.js +26 -0
  18. package/dist/chunks/index-zpvea08j.js +3372 -0
  19. package/dist/chunks/menubar-6xtkt17h.js +296 -0
  20. package/dist/chunks/open-projects-6z4pq6mx.js +39 -0
  21. package/dist/chunks/pricing-f80p81gr.js +29 -0
  22. package/dist/chunks/pricing-vf2pycmh.js +30 -0
  23. package/dist/chunks/pricing-y21mcjsa.js +30 -0
  24. package/dist/chunks/serve-0k69e4h9.js +4655 -0
  25. package/dist/chunks/sqlite-store-501f32ts.js +258 -0
  26. package/dist/chunks/sqlite-store-fq4natnf.js +256 -0
  27. package/dist/chunks/sqlite-store-st99garx.js +258 -0
  28. package/dist/chunks/third-party-sqlite-5e85fxa2.js +11 -0
  29. package/dist/chunks/tui-7f8f951w.js +22 -0
  30. package/dist/chunks/watch-mm1j39vy.js +196 -0
  31. package/dist/chunks/webhooks-k4fh2t9c.js +109 -0
  32. package/dist/cli/commands/brief.d.ts.map +1 -1
  33. package/dist/cli/commands/extras.d.ts.map +1 -1
  34. package/dist/cli/commands/journal.d.ts +3 -0
  35. package/dist/cli/commands/journal.d.ts.map +1 -0
  36. package/dist/cli/commands/menubar.d.ts +97 -4
  37. package/dist/cli/commands/menubar.d.ts.map +1 -1
  38. package/dist/cli/commands/tui.d.ts +1 -1
  39. package/dist/cli/commands/tui.d.ts.map +1 -1
  40. package/dist/cli/commands/watch.d.ts.map +1 -1
  41. package/dist/cli/index.js +1524 -9998
  42. package/dist/db/cloud.d.ts +18 -12
  43. package/dist/db/cloud.d.ts.map +1 -1
  44. package/dist/db/database.d.ts +33 -12
  45. package/dist/db/database.d.ts.map +1 -1
  46. package/dist/db/ingest-concurrency-worker.d.ts +2 -0
  47. package/dist/db/ingest-concurrency-worker.d.ts.map +1 -0
  48. package/dist/db/ingest-errors.d.ts +9 -0
  49. package/dist/db/ingest-errors.d.ts.map +1 -0
  50. package/dist/db/ingest-schema.d.ts +3 -0
  51. package/dist/db/ingest-schema.d.ts.map +1 -0
  52. package/dist/db/ingest-specs.d.ts +8 -0
  53. package/dist/db/ingest-specs.d.ts.map +1 -0
  54. package/dist/db/ingest-test-contract.d.ts +3 -0
  55. package/dist/db/ingest-test-contract.d.ts.map +1 -0
  56. package/dist/db/ingest-validation.d.ts +12 -0
  57. package/dist/db/ingest-validation.d.ts.map +1 -0
  58. package/dist/db/ingest.d.ts +27 -0
  59. package/dist/db/ingest.d.ts.map +1 -0
  60. package/dist/db/legacy-schema.d.ts +7 -0
  61. package/dist/db/legacy-schema.d.ts.map +1 -0
  62. package/dist/db/observation-contract.d.ts +3 -0
  63. package/dist/db/observation-contract.d.ts.map +1 -0
  64. package/dist/db/observation-provenance.d.ts +23 -0
  65. package/dist/db/observation-provenance.d.ts.map +1 -0
  66. package/dist/db/observation-schema.d.ts +24 -0
  67. package/dist/db/observation-schema.d.ts.map +1 -0
  68. package/dist/db/pg-connection.d.ts +10 -0
  69. package/dist/db/pg-connection.d.ts.map +1 -0
  70. package/dist/db/pg-migrate.d.ts +4 -9
  71. package/dist/db/pg-migrate.d.ts.map +1 -1
  72. package/dist/db/pg-migrations.d.ts.map +1 -1
  73. package/dist/db/pg-protocol.d.ts +28 -0
  74. package/dist/db/pg-protocol.d.ts.map +1 -0
  75. package/dist/db/sqlite-store.d.ts +4 -0
  76. package/dist/db/sqlite-store.d.ts.map +1 -0
  77. package/dist/db/sync-pg.d.ts +17 -21
  78. package/dist/db/sync-pg.d.ts.map +1 -1
  79. package/dist/db/third-party-sqlite.d.ts +10 -0
  80. package/dist/db/third-party-sqlite.d.ts.map +1 -0
  81. package/dist/index.d.ts +3 -0
  82. package/dist/index.d.ts.map +1 -1
  83. package/dist/index.js +1281 -2405
  84. package/dist/ingest/claude-quota.d.ts.map +1 -1
  85. package/dist/ingest/claude.d.ts.map +1 -1
  86. package/dist/ingest/codex-quota.d.ts.map +1 -1
  87. package/dist/ingest/codex.d.ts +25 -0
  88. package/dist/ingest/codex.d.ts.map +1 -1
  89. package/dist/ingest/collection-outcome.d.ts +17 -0
  90. package/dist/ingest/collection-outcome.d.ts.map +1 -0
  91. package/dist/ingest/cursor.d.ts.map +1 -1
  92. package/dist/ingest/gemini.d.ts.map +1 -1
  93. package/dist/ingest/hermes.d.ts +1 -1
  94. package/dist/ingest/hermes.d.ts.map +1 -1
  95. package/dist/ingest/loops.d.ts +9 -0
  96. package/dist/ingest/loops.d.ts.map +1 -1
  97. package/dist/ingest/opencode.d.ts +1 -1
  98. package/dist/ingest/opencode.d.ts.map +1 -1
  99. package/dist/ingest/pi.d.ts +1 -1
  100. package/dist/ingest/pi.d.ts.map +1 -1
  101. package/dist/ingest/quota-observation.d.ts +16 -0
  102. package/dist/ingest/quota-observation.d.ts.map +1 -0
  103. package/dist/ingest/source-time.d.ts +4 -0
  104. package/dist/ingest/source-time.d.ts.map +1 -0
  105. package/dist/lib/accounts-store.d.ts +109 -0
  106. package/dist/lib/accounts-store.d.ts.map +1 -0
  107. package/dist/lib/accounts.d.ts +2 -1
  108. package/dist/lib/accounts.d.ts.map +1 -1
  109. package/dist/lib/analytics.d.ts.map +1 -1
  110. package/dist/lib/api-display-url.d.ts +19 -0
  111. package/dist/lib/api-display-url.d.ts.map +1 -0
  112. package/dist/lib/autosync-gate.d.ts +22 -0
  113. package/dist/lib/autosync-gate.d.ts.map +1 -0
  114. package/dist/lib/billing-diff.d.ts +18 -40
  115. package/dist/lib/billing-diff.d.ts.map +1 -1
  116. package/dist/lib/brief.d.ts +2 -0
  117. package/dist/lib/brief.d.ts.map +1 -1
  118. package/dist/lib/budget-decimal.d.ts +14 -0
  119. package/dist/lib/budget-decimal.d.ts.map +1 -0
  120. package/dist/lib/cloud-ingest.d.ts +30 -1
  121. package/dist/lib/cloud-ingest.d.ts.map +1 -1
  122. package/dist/lib/cloud-storage.d.ts +166 -10
  123. package/dist/lib/cloud-storage.d.ts.map +1 -1
  124. package/dist/lib/gatherer.d.ts.map +1 -1
  125. package/dist/lib/ingest-batches.d.ts +21 -0
  126. package/dist/lib/ingest-batches.d.ts.map +1 -0
  127. package/dist/lib/ingest-journal-client.d.ts +37 -0
  128. package/dist/lib/ingest-journal-client.d.ts.map +1 -0
  129. package/dist/lib/ingest-journal-lock.d.ts +5 -0
  130. package/dist/lib/ingest-journal-lock.d.ts.map +1 -0
  131. package/dist/lib/ingest-journal-maintenance.d.ts +92 -0
  132. package/dist/lib/ingest-journal-maintenance.d.ts.map +1 -0
  133. package/dist/lib/ingest-journal-operation.d.ts +55 -0
  134. package/dist/lib/ingest-journal-operation.d.ts.map +1 -0
  135. package/dist/lib/ingest-journal.d.ts +73 -0
  136. package/dist/lib/ingest-journal.d.ts.map +1 -0
  137. package/dist/lib/money-display.d.ts +13 -0
  138. package/dist/lib/money-display.d.ts.map +1 -0
  139. package/dist/lib/periods.d.ts +12 -0
  140. package/dist/lib/periods.d.ts.map +1 -1
  141. package/dist/lib/pricing.d.ts +14 -0
  142. package/dist/lib/pricing.d.ts.map +1 -1
  143. package/dist/lib/savings.d.ts +64 -7
  144. package/dist/lib/savings.d.ts.map +1 -1
  145. package/dist/lib/serve-auth.d.ts.map +1 -1
  146. package/dist/lib/store/index.d.ts +29 -22
  147. package/dist/lib/store/index.d.ts.map +1 -1
  148. package/dist/lib/summary-contract.d.ts +32 -0
  149. package/dist/lib/summary-contract.d.ts.map +1 -0
  150. package/dist/lib/summary-test-contract.d.ts +3 -0
  151. package/dist/lib/summary-test-contract.d.ts.map +1 -0
  152. package/dist/lib/sync-all.d.ts +4 -0
  153. package/dist/lib/sync-all.d.ts.map +1 -1
  154. package/dist/lib/sync-maintenance.d.ts.map +1 -1
  155. package/dist/lib/sync-report.d.ts +4 -0
  156. package/dist/lib/sync-report.d.ts.map +1 -0
  157. package/dist/lib/test-keychain-fixture.d.ts +9 -0
  158. package/dist/lib/test-keychain-fixture.d.ts.map +1 -0
  159. package/dist/mcp/agent-registry-store.d.ts +30 -0
  160. package/dist/mcp/agent-registry-store.d.ts.map +1 -0
  161. package/dist/mcp/agent-registry.d.ts +78 -0
  162. package/dist/mcp/agent-registry.d.ts.map +1 -0
  163. package/dist/mcp/harness.d.ts +26 -0
  164. package/dist/mcp/harness.d.ts.map +1 -0
  165. package/dist/mcp/index.js +3219 -3357
  166. package/dist/mcp/ingest-journal-tool.d.ts +2 -0
  167. package/dist/mcp/ingest-journal-tool.d.ts.map +1 -0
  168. package/dist/mcp/server.d.ts.map +1 -1
  169. package/dist/openapi.d.ts.map +1 -1
  170. package/dist/otel/index.js +2197 -121
  171. package/dist/server/human-auth.d.ts +20 -0
  172. package/dist/server/human-auth.d.ts.map +1 -0
  173. package/dist/server/index.js +10477 -6378
  174. package/dist/server/legacy-inventory.d.ts +52 -0
  175. package/dist/server/legacy-inventory.d.ts.map +1 -0
  176. package/dist/server/legacy-operations.d.ts +64 -0
  177. package/dist/server/legacy-operations.d.ts.map +1 -0
  178. package/dist/server/legacy-plan.d.ts +55 -0
  179. package/dist/server/legacy-plan.d.ts.map +1 -0
  180. package/dist/server/legacy-preservation.d.ts +58 -0
  181. package/dist/server/legacy-preservation.d.ts.map +1 -0
  182. package/dist/server/pg-sync-worker.js +195 -21
  183. package/dist/server/serve.d.ts +20 -18
  184. package/dist/server/serve.d.ts.map +1 -1
  185. package/dist/server/service-admission.d.ts +10 -0
  186. package/dist/server/service-admission.d.ts.map +1 -0
  187. package/dist/server/service-dispatcher.d.ts +45 -0
  188. package/dist/server/service-dispatcher.d.ts.map +1 -0
  189. package/dist/server/service-operations.d.ts +5 -0
  190. package/dist/server/service-operations.d.ts.map +1 -0
  191. package/dist/server/service-protocol.d.ts +50 -0
  192. package/dist/server/service-protocol.d.ts.map +1 -0
  193. package/dist/server/service-worker.d.ts +2 -0
  194. package/dist/server/service-worker.d.ts.map +1 -0
  195. package/dist/server/service-worker.js +8029 -0
  196. package/dist/types/index.d.ts +66 -0
  197. package/dist/types/index.d.ts.map +1 -1
  198. package/docs/README.md +1 -1
  199. package/docs/cli.md +2 -3
  200. package/docs/configuration.md +25 -12
  201. package/docs/cost-reporting.md +49 -0
  202. package/docs/historical-data-reconciliation.md +203 -0
  203. package/docs/ingest-journal.md +108 -0
  204. package/docs/ingestion.md +61 -5
  205. package/docs/mcp.md +5 -3
  206. package/docs/native-budget-save.md +84 -0
  207. package/docs/native-human-auth.md +102 -0
  208. package/docs/otel.md +8 -2
  209. package/docs/rest-api.md +4 -4
  210. package/package.json +17 -20
  211. package/postinstall.js +52 -1
  212. package/dashboard/README.md +0 -26
  213. package/dist/cli/brains.d.ts +0 -3
  214. package/dist/cli/brains.d.ts.map +0 -1
  215. package/dist/lib/test-hermetic-accounts.d.ts +0 -6
  216. package/dist/lib/test-hermetic-accounts.d.ts.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,124 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Make PostgreSQL ingestion transactional, bounded and replay-safe. Preserve exact operation keys and unresolved client journals; verify receipts after a lost response instead of automatically sending the write again.
8
+ - Separate recorded charges, known API-equivalent values, subscription fees and incomplete pricing or billing evidence. Reports carry explicit periods, account identity and completeness instead of implying unknown amounts are zero or paid usage is savings.
9
+ - Preserve collector event, model, account and time provenance, and report scan freshness independently from the latest usage event.
10
+ - Bound high-cardinality CLI and MCP collection output, with explicit complete responses where supported.
11
+ - Add authenticated macOS and iPhone client source and release tooling in the repository, including searchable subscription reporting and recoverable API-cost alerts. Native applications are separate artifacts; this npm package does not install them.
12
+
13
+ ### Patch Changes
14
+
15
+ - Add explicit historical-data inventory, planning, preservation, restore rehearsal, quarantine and rollback commands. Preserve original rows and immutable operation receipts; bind operations to the effective database target, retain locks through restoration checks, and refuse stale rollback after later activity. These commands do not automatically rewrite historical production data.
16
+ - Commit each PostgreSQL migration and its ledger marker atomically on one connection, serialize runners for the same database/schema, and preserve explicit recovery after an interrupted attempt.
17
+ - Keep HTTP requests responsive during database stalls through bounded asynchronous workers.
18
+ - Keep hosted CLI and MCP clients from opening persistent local SQLite stores. Account attribution fails closed on configuration errors; hosted collection does not silently read another app's local store.
19
+ - Add compact journal inspection and recovery controls. Completed journals and selected stopped native app artifacts are removed through Trash, with recovery identifiers retained.
20
+ - Replace the retired Mac installer with explicit artifact verification, collision-safe installation and preserved quarantine.
21
+
22
+ The older dashboard-removal and path-resolver Changesets are consumed here for
23
+ release bookkeeping. The published 0.4.0 archive already excluded the dashboard
24
+ and included the same postinstall path resolver; neither is a new data migration
25
+ in 0.5.0.
26
+
27
+ ## 0.4.0
28
+
29
+ ### Minor Changes
30
+
31
+ - 00118b3: Resolve credentials through the `@hasna/contracts` 1.0.2 client chain (hasna/apps#1720).
32
+
33
+ The CLI, the MCP server and the `./sdk` store surface no longer carry a
34
+ credential chain of their own. All three route through the one resolver in
35
+ `@hasna/contracts` (bumped from 0.13.4 to the exact 1.0.2), which reads, per
36
+ call: an explicit `--api-key`/`--profile`, then `HASNA_ECONOMY_API_KEY_OVERRIDE`
37
+ / `HASNA_PROFILE` / `HASNA_ECONOMY_API_KEY_REF`, then the macOS Keychain item
38
+ `hasna.credentials.economy.api-key`, then `~/.hasna/economy/config/credentials`
39
+ (0600, `HASNA_ECONOMY_API_KEY=…`), then `HASNA_ECONOMY_API_KEY`. The authority
40
+ follows the same ladder — `HASNA_ECONOMY_API_URL`, the Keychain `api-url` item,
41
+ the credentials file — and now DEFAULTS to the fleet gateway
42
+ `https://api.hasna.com/economy` once a credential resolves, so a key alone is a
43
+ complete configuration. A long-lived MCP server re-resolves the credential on
44
+ every request, so a rotation heals without a restart.
45
+
46
+ What this removes:
47
+
48
+ - The app's own legacy env chain: the unprefixed serve token `ECONOMY_API_TOKEN`
49
+ (canonical `HASNA_ECONOMY_API_TOKEN` remains) and `ECONOMY_MACHINE_ID`
50
+ (canonical `HASNA_ECONOMY_MACHINE_ID`).
51
+ - Retired `*_MODE` / `*_STORAGE_MODE` switches stay a hard error, and the
52
+ DEPRECATED notice is gone: `HASNA_ECONOMY_API_KEY` is a legitimate resolver
53
+ tier, it just sits below the Keychain and the credentials file.
54
+ - Nothing reads `~/.hasna/fleet-env`, `~/.hasna/cloud`, `~/.config/hasna` or
55
+ `$XDG_CONFIG_HOME` — the resolver never consults those locations.
56
+
57
+ What this keeps and adds:
58
+
59
+ - Fail loud (owner directive 2026-09-04): hosted with no credential = non-zero
60
+ exit, no SQLite file, no `economy-local-fallback` event, and an error naming
61
+ every tier consulted. Local mode is served only by the explicit opt-in
62
+ `HASNA_ECONOMY_LOCAL=1` (alias `ECONOMY_LOCAL=1` for one release), which
63
+ yields to every hosted signal and now prints one `economy: local mode …` line
64
+ on stderr.
65
+ - `economy transport` (new CLI command): reports the resolved transport and the
66
+ credential SOURCE — `/v1` authority, `api_url_source`, `api_key_source`,
67
+ `api_key_tier` — never the key value; `--json` for the full report. It is the
68
+ one surface that reports a refusal instead of throwing.
69
+ - The `./sdk` store surface (`getStore`) is unchanged in shape, and it is the
70
+ ONLY SDK: the Store takes no `baseUrl`, so the package can never attach an
71
+ ambient fleet key to a caller-supplied one (hasna/apps#1794) — the authority
72
+ and the credential both come from the resolver, per call.
73
+ - `@hasna/contracts` stays a runtime dependency (economy builds with
74
+ `--packages external`), so the published declarations importing its types
75
+ resolve for consumers. The `/v1/machines` and `/v1/fleet` surfaces are
76
+ unchanged and keep working against the resolved authority.
77
+
78
+ ### Patch Changes
79
+
80
+ - 361d51b: Fail-closed / resolver validation fixes for the hosted-by-default client
81
+ (hasna/apps#1720, validation round 1).
82
+
83
+ - **MCP: no SQLite under the app home in hosted mode.** `economy-mcp` opened
84
+ the agent-lifecycle registry (`agent-registry.db` plus its WAL/SHM sidecars)
85
+ at startup — in hosted mode too, before any tool was called. The registry
86
+ store is now resolved on first tool use, and a hosted client keeps it in
87
+ memory for the life of the process; only the explicit `HASNA_ECONOMY_LOCAL=1`
88
+ opt-in persists `agent-registry.db` beside the local store
89
+ (`HASNA_AGENT_REGISTRY_DB_PATH` still names a file explicitly in either lane).
90
+ - **MCP: the fail-closed diagnostic is the first stderr line**
91
+ (`MCP server error: Economy fails closed …`, naming the Keychain item, the
92
+ credentials file and `HASNA_ECONOMY_API_KEY`) instead of Bun's code frame.
93
+ - **`economy transport` exits 1 when no credential resolves.** The report is
94
+ still printed (`--json` included), so the diagnostic stays readable while a
95
+ script can no longer take a refusal for a hosted transport.
96
+ - **`economy-otel` follows the storage seam.** With a resolved credential the
97
+ sidecar forwards the request/session rows of every accepted payload to the
98
+ shared API's `/v1/ingest` (the response gains `forwarded`; nothing is written
99
+ under `~/.hasna/economy`); under `HASNA_ECONOMY_LOCAL=1` it writes the
100
+ on-box store and announces local mode on stderr; with neither it fails
101
+ closed before binding. `economy-otel` stays undeclared in
102
+ `hasna.contract.json` because `-otel` is outside the contract kit's bin
103
+ allowlist.
104
+ - **Hosted `economy sync` keeps its mtime cache as a JSON file under the cache
105
+ root** (`HASNA_CACHE_HOME`, else `~/Library/Caches/Hasna/economy` on macOS /
106
+ `~/.cache/hasna/economy` elsewhere; `HASNA_ECONOMY_INGEST_CACHE` overrides)
107
+ instead of `~/.hasna/economy/ingest-cache.db`. An older cache file is simply
108
+ not read — one re-read, absorbed by the server's idempotent upserts — and can
109
+ be deleted.
110
+ - **Manifest:** the CLI and MCP surfaces declare `authMode: api-key`
111
+ (credential via the `@hasna/contracts` chain) and the SDK surface
112
+ `exportSubpath: ./sdk`.
113
+ - **No `-sdk` split package.** The unpublished in-tree `@hasna/economy-sdk`
114
+ (`sdk/`) is removed: the SDK is the `./sdk` export of this one package — the
115
+ Store abstraction, which takes no `baseUrl` and therefore never attaches an
116
+ ambient fleet key to a caller-supplied one (hasna/apps#1794).
117
+ - Test hygiene: `src/mcp/http.test.ts` restores the local opt-in it pins, so
118
+ the #1788 ambient-gate test passes in the full suite; new spawned-bin tests
119
+ cover the MCP hosted / fail-closed arms, the three `economy-otel` lanes and
120
+ the `economy transport` exit codes.
121
+
3
122
  ## 0.3.28
4
123
 
5
124
  ### Patch Changes
package/CONTRIBUTING.md CHANGED
@@ -13,7 +13,7 @@ bun run typecheck
13
13
  bun run build
14
14
  ```
15
15
 
16
- Dashboard work lives under `dashboard/` and SDK work lives under `sdk/`. Run package-local commands from those directories when changing those packages.
16
+ The SDK is the `./sdk` export subpath of this package (`src/index.ts`, the Store abstraction); there is no separate `sdk/` package to build or publish.
17
17
 
18
18
  ## Release And Package Hygiene
19
19
 
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @hasna/economy
2
2
 
3
- AI coding cost tracker for Claude Code, Takumi, Codex, Gemini, OpenCode, Cursor, Pi, and Hermes. It ships as a CLI, MCP server, REST API, web dashboard, and native macOS menu bar app.
3
+ AI coding cost tracker for Claude Code, Takumi, Codex, Gemini, OpenCode, Cursor, Pi, and Hermes. It ships as a CLI, MCP server, REST API, and native macOS menu bar app.
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/@hasna/economy)](https://www.npmjs.com/package/@hasna/economy)
6
6
  [![License](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE)
@@ -14,7 +14,7 @@ AI coding cost tracker for Claude Code, Takumi, Codex, Gemini, OpenCode, Cursor,
14
14
  - Seeds editable model pricing with input, output, cache-read, 5-minute cache-write, 1-hour cache-write, and context-cache storage rates.
15
15
  - Handles tiered pricing such as Gemini long-prompt rates and OpenAI long-context rates.
16
16
  - Reconciles estimates against Anthropic, OpenAI, and Gemini billing sources.
17
- - Exposes cost data through CLI commands, an MCP server, REST endpoints, and a dashboard.
17
+ - Exposes cost data through CLI commands, an MCP server, and REST endpoints.
18
18
  - Syncs project metadata from the `@hasna/projects` registry during full local sync.
19
19
  - Sends budget alert webhooks and retries failed deliveries on later syncs.
20
20
 
@@ -33,12 +33,6 @@ economy pricing list
33
33
  economy serve --port 3456
34
34
  ```
35
35
 
36
- Open the dashboard with:
37
-
38
- ```bash
39
- economy dashboard --port 3456
40
- ```
41
-
42
36
  ## Documentation
43
37
 
44
38
  - [CLI reference](docs/cli.md)
@@ -124,7 +118,7 @@ economy sync --hermes
124
118
  economy sync --loops
125
119
  ```
126
120
 
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.
121
+ `economy sync --loops` reads the OpenLoops store (`~/.hasna/loops/loops.db` by default, resolved by the in-package resolver to the 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. **This cross-app on-box read runs only in the local lane.** A hosted client (a resolved economy credential) refuses it with one stderr line — another app's on-box SQLite is never fleet data — so hosted stations see `loops ingest skipped` until the read is served by the loops API. 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
122
 
129
123
  Useful repair options:
130
124
 
@@ -136,7 +130,7 @@ economy sync --backfill-machine
136
130
 
137
131
  Full sync also imports active project metadata from `@hasna/projects` when the registry is available. The Codex source reads both `~/.codex/state_5.sqlite` and the Codewith store at `~/.codewith/state_5.sqlite` by default; explicit `HASNA_ECONOMY_CODEX_DB_PATH` and `HASNA_ECONOMY_CODEWITH_DB_PATH` values override those locations.
138
132
 
139
- Account attribution is automatic when `@hasna/accounts` has a matching active, applied, or env-dir profile for the agent. Account identity is the email address plus coding agent, so `work@example.com` under Codex and Claude is reported as two accounts. You can also force attribution for a process with `ECONOMY_ACCOUNT=tool:name` or agent-specific overrides such as `ECONOMY_CODEX_ACCOUNT=codex:work`.
133
+ Account attribution is automatic when the accounts API (`HASNA_ACCOUNTS_API_KEY`, the accounts credentials file, or the Keychain item `hasna.credentials.accounts.api-key`) resolves a matching active, applied, or env-dir profile for the agent. With **no** accounts credential the on-box registry `~/.hasna/accounts/accounts.json` is **not** read unless `HASNA_ECONOMY_LOCAL=1` is set: attribution is simply omitted, announced once on stderr, rather than filling hosted rows from an on-box file. Account identity is the email address plus coding agent, so `work@example.com` under Codex and Claude is reported as two accounts. You can also force attribution for a process with `ECONOMY_ACCOUNT=tool:name` or agent-specific overrides such as `ECONOMY_CODEX_ACCOUNT=codex:work`.
140
134
 
141
135
  Session drilldown can be scoped to an account key, account name, or email:
142
136
 
@@ -237,7 +231,7 @@ Start the server:
237
231
  economy-serve --port 3456
238
232
  ```
239
233
 
240
- The canonical API uses `/v1`; `/api` remains a legacy alias for the dashboard and older clients. For example:
234
+ The canonical API uses `/v1`; `/api` remains a legacy alias for older clients. For example:
241
235
 
242
236
  - `GET /health`, `/ready`, `/version`, and `/openapi.json`
243
237
  - `GET /v1/summary?period=today`
@@ -248,35 +242,38 @@ The canonical API uses `/v1`; `/api` remains a legacy alias for the dashboard an
248
242
 
249
243
  See the [REST API reference](docs/rest-api.md) for every route, response envelopes, authentication, and legacy aliases. The server publishes the current generated contract at `/openapi.json`.
250
244
 
251
- The server also serves the built dashboard when `dashboard/dist` is present. The dashboard includes account-scoped session filtering, subscription plan create/update/delete controls in Savings, and savings/usage/account tables for subscription-aware cost analysis.
252
-
253
245
  ## Native macOS Menubar
254
246
 
255
- The `menubar/` app is a native SwiftUI `MenuBarExtra` app, not Electron. It targets Swift 5.9+ and macOS 14+, and talks to the REST API exposed by `economy-serve`. It shows today/week/month spend, token and request counts, top agents, top accounts, top projects, active subscription plans, subscription savings, multi-agent usage snapshots, recent sessions, and fleet status. The default server URL is `http://127.0.0.1:3456`.
247
+ HasnaEconomy is a native SwiftUI menu-bar app for macOS 14 and later, with an iPhone companion. The rebuilt clients use `https://api.hasna.com/economy/v1`; usage allowances, metered API costs, reported provider charges and subscription fees stay separate. Native source and build instructions are in `native/`. The old `menubar/Sources` implementation is retained as historical source and is not the rebuilt release.
256
248
 
257
- Build it on macOS:
249
+ An agent installs a reviewed, signed and notarized release artifact. First obtain `HasnaEconomy.app` and its release receipt through the approved artifact delivery channel, then inspect it:
258
250
 
259
251
  ```bash
260
- cd menubar
261
- swift build -c release
252
+ economy menubar inspect --app /absolute/path/HasnaEconomy.app
262
253
  ```
263
254
 
264
- Release app helpers:
255
+ `inspect` reports a portable `hasna-economy-app-tree-v1` SHA-256 covering relative paths, entry kinds, permissions and file contents. Compare that digest and version with the release receipt. Installation requires those exact values:
265
256
 
266
257
  ```bash
267
- economy menubar install
258
+ economy menubar install --app /absolute/path/HasnaEconomy.app --app-version <reviewed-version> --sha256 <release-receipt-sha256>
268
259
  economy menubar start
269
260
  economy menubar stop
270
261
  economy menubar uninstall
271
262
  ```
272
263
 
264
+ The destination is `/Applications/HasnaEconomy.app`. The CLI verifies the bundle identifier, executable, version, architecture, Hasna Developer ID signature and macOS execution assessment before installation, then verifies a private staging bundle and retained source before atomically publishing without replacement and reading the installed bundle back. It preserves quarantine and extended attributes. It refuses occupied destinations, including empty directories and dangling symlinks; there is no overwrite flag or automatic quit. A failed copy leaves its private staging bundle for explicit reconciliation through Trash. A destination that appears during installation is preserved; it is never used as a copy target. Installation does not launch the app. `start` reports a launch request, which is not proof of a working menu or authenticated session.
265
+
266
+ `uninstall` requires the app to be stopped and invokes the installed Trash client with its own station credentials and identity. It never falls back to direct deletion. Check `trash pending` after an uncertain capture and use the returned entry ID for recovery. The legacy `/Applications/Economy Bar.app` is not changed by these commands. Source builds and fixture captures are not signed distribution artifacts or evidence of a hosted installation.
267
+
273
268
  ## Data Directory
274
269
 
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.
270
+ Data is stored under a single data root resolved by the in-package 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
271
 
277
272
  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
273
 
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.
274
+ For shared deployments, CLI, MCP and `./sdk` can use a remote `/v1` API instead of local SQLite. Their credential is resolved by `@hasna/contracts` 1.0.2, fresh per request, from: an explicit `--api-key`/`--profile` argument, the env pointers `HASNA_ECONOMY_API_KEY_OVERRIDE` / `HASNA_PROFILE` / `HASNA_ECONOMY_API_KEY_REF`, the macOS Keychain item `hasna.credentials.economy.api-key`, the 0600 file `~/.hasna/economy/config/credentials` (`HASNA_ECONOMY_API_KEY=…`), then `HASNA_ECONOMY_API_KEY` in the environment. 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`. Unprefixed `ECONOMY_API_URL`/`ECONOMY_API_KEY` aliases are legacy, accepted for one release.
275
+
276
+ **A run without a credential fails closed**: non-zero exit, no SQLite file, no local-fallback event. The on-box store is served only with the explicit opt-in `HASNA_ECONOMY_LOCAL=1` (alias `ECONOMY_LOCAL=1`), which prints `economy: local mode …` on stderr. See [configuration](docs/configuration.md) for the full client resolution, server auth, Postgres mode, and all environment variables.
280
277
 
281
278
  ## Development
282
279
 
@@ -285,7 +282,6 @@ bun test
285
282
  bun run typecheck
286
283
  bun run build
287
284
  bun scripts/sync-openapi.ts
288
- cd dashboard && bun run lint
289
285
  cd menubar && swift build -c release
290
286
  ```
291
287
 
package/SECURITY.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Supported Versions
4
4
 
5
- Security fixes are handled for the latest published versions of `@hasna/economy` and `@hasna/economy-sdk`.
5
+ Security fixes are handled for the latest published version of `@hasna/economy` (the CLI, MCP, server and `./sdk` surfaces ship in that one package).
6
6
 
7
7
  ## Reporting A Vulnerability
8
8
 
@@ -0,0 +1,39 @@
1
+ // @bun
2
+ import"./index-p35cyhc4.js";
3
+
4
+ // src/mcp/agent-registry-store.ts
5
+ import { Database } from "bun:sqlite";
6
+ import { dirname } from "path";
7
+ import { existsSync, mkdirSync } from "fs";
8
+ var MEMORY_REGISTRY_PATH = ":memory:";
9
+
10
+ class RegistryDb {
11
+ db;
12
+ constructor(path) {
13
+ if (path !== MEMORY_REGISTRY_PATH && dirname(path) && !existsSync(dirname(path))) {
14
+ mkdirSync(dirname(path), { recursive: true, mode: 448 });
15
+ }
16
+ this.db = new Database(path);
17
+ this.db.exec("PRAGMA journal_mode = WAL");
18
+ this.db.exec("PRAGMA busy_timeout = 10000");
19
+ }
20
+ get(sql, ...params) {
21
+ return this.db.prepare(sql).get(...params);
22
+ }
23
+ all(sql, ...params) {
24
+ return this.db.prepare(sql).all(...params);
25
+ }
26
+ run(sql, ...params) {
27
+ return this.db.prepare(sql).run(...params);
28
+ }
29
+ exec(sql) {
30
+ this.db.exec(sql);
31
+ }
32
+ close() {
33
+ this.db.close();
34
+ }
35
+ }
36
+ export {
37
+ RegistryDb,
38
+ MEMORY_REGISTRY_PATH
39
+ };
@@ -0,0 +1,13 @@
1
+ // @bun
2
+ import {
3
+ syncAnthropicBilling,
4
+ syncGeminiBilling,
5
+ syncOpenAIBilling
6
+ } from "./index-k540hfww.js";
7
+ import"./index-ez9thsr7.js";
8
+ import"./index-p35cyhc4.js";
9
+ export {
10
+ syncOpenAIBilling,
11
+ syncGeminiBilling,
12
+ syncAnthropicBilling
13
+ };
@@ -0,0 +1,15 @@
1
+ // @bun
2
+ import {
3
+ getConfigValue,
4
+ loadConfig,
5
+ saveConfig,
6
+ setConfigValue
7
+ } from "./index-hesxq90g.js";
8
+ import"./index-ez9thsr7.js";
9
+ import"./index-p35cyhc4.js";
10
+ export {
11
+ setConfigValue,
12
+ saveConfig,
13
+ loadConfig,
14
+ getConfigValue
15
+ };