@hasna/economy 0.3.27 → 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.
- package/CHANGELOG.md +101 -0
- package/CONTRIBUTING.md +1 -1
- package/README.md +9 -16
- package/SECURITY.md +1 -1
- package/dist/cli/commands/extras.d.ts.map +1 -1
- package/dist/cli/index.js +1182 -862
- package/dist/db/cloud.d.ts +16 -8
- package/dist/db/cloud.d.ts.map +1 -1
- package/dist/db/database.d.ts +58 -0
- package/dist/db/database.d.ts.map +1 -1
- package/dist/index.js +236 -27
- package/dist/ingest/claude.d.ts.map +1 -1
- package/dist/ingest/loops.d.ts +19 -0
- package/dist/ingest/loops.d.ts.map +1 -1
- package/dist/lib/accounts-store.d.ts +83 -0
- package/dist/lib/accounts-store.d.ts.map +1 -0
- package/dist/lib/accounts.d.ts +2 -1
- package/dist/lib/accounts.d.ts.map +1 -1
- package/dist/lib/api-display-url.d.ts +19 -0
- package/dist/lib/api-display-url.d.ts.map +1 -0
- package/dist/lib/autosync-gate.d.ts +15 -0
- package/dist/lib/autosync-gate.d.ts.map +1 -0
- package/dist/lib/cloud-ingest.d.ts +15 -1
- package/dist/lib/cloud-ingest.d.ts.map +1 -1
- package/dist/lib/cloud-storage.d.ts +166 -10
- package/dist/lib/cloud-storage.d.ts.map +1 -1
- package/dist/lib/gatherer.d.ts.map +1 -1
- package/dist/lib/model-config.d.ts +1 -1
- package/dist/lib/model-config.d.ts.map +1 -1
- package/dist/lib/serve-auth.d.ts.map +1 -1
- package/dist/lib/store/index.d.ts +6 -3
- package/dist/lib/store/index.d.ts.map +1 -1
- package/dist/lib/test-hermetic-accounts.d.ts +3 -2
- package/dist/lib/test-hermetic-accounts.d.ts.map +1 -1
- package/dist/lib/test-hermetic-fleet-env.d.ts +4 -0
- package/dist/lib/test-hermetic-fleet-env.d.ts.map +1 -0
- package/dist/mcp/agent-registry.d.ts +85 -0
- package/dist/mcp/agent-registry.d.ts.map +1 -0
- package/dist/mcp/harness.d.ts +26 -0
- package/dist/mcp/harness.d.ts.map +1 -0
- package/dist/mcp/index.js +1387 -311
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/otel/index.js +468 -24
- package/dist/server/index.js +595 -164
- package/dist/server/serve.d.ts +0 -2
- package/dist/server/serve.d.ts.map +1 -1
- package/docs/cli.md +2 -3
- package/docs/configuration.md +23 -10
- package/docs/ingestion.md +1 -1
- package/docs/mcp.md +4 -2
- package/docs/otel.md +8 -2
- package/docs/rest-api.md +2 -2
- package/package.json +7 -11
- package/postinstall.js +82 -0
- package/dashboard/README.md +0 -26
- package/dist/cli/brains.d.ts +0 -3
- package/dist/cli/brains.d.ts.map +0 -1
package/dist/server/serve.d.ts
CHANGED
|
@@ -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;
|
|
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
|
|
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
|
|
package/docs/configuration.md
CHANGED
|
@@ -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
|
-
| `
|
|
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`
|
|
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
|
-
|
|
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
|
|
41
|
-
export HASNA_ECONOMY_API_KEY='...'
|
|
52
|
+
export HASNA_ECONOMY_LOCAL=1
|
|
42
53
|
```
|
|
43
54
|
|
|
44
|
-
|
|
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. `
|
|
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.
|
|
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 `
|
|
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;
|
|
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
|
|
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`
|
|
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
|
-
|
|
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
|
|
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 `
|
|
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.
|
|
4
|
-
"description": "AI coding cost tracker
|
|
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,7 @@
|
|
|
32
32
|
"files": [
|
|
33
33
|
"dist",
|
|
34
34
|
"docs",
|
|
35
|
-
"
|
|
35
|
+
"postinstall.js",
|
|
36
36
|
"LICENSE",
|
|
37
37
|
"README.md",
|
|
38
38
|
"CHANGELOG.md",
|
|
@@ -41,12 +41,11 @@
|
|
|
41
41
|
"CODE_OF_CONDUCT.md"
|
|
42
42
|
],
|
|
43
43
|
"scripts": {
|
|
44
|
-
"build": "
|
|
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",
|
|
45
45
|
"build:cli": "bun build src/cli/index.ts --outdir dist/cli --target bun --packages external",
|
|
46
46
|
"build:mcp": "bun build src/mcp/index.ts --outdir dist/mcp --target bun --packages external",
|
|
47
47
|
"build:server": "bun build src/server/index.ts --outdir dist/server --target bun --packages external",
|
|
48
48
|
"build:lib": "bun build src/index.ts --outdir dist --target bun --packages external",
|
|
49
|
-
"build:dashboard": "cd dashboard && bun run build",
|
|
50
49
|
"artifact-scan": "bun scripts/contracts-cli.mjs artifact-scan dist",
|
|
51
50
|
"prepack": "bun run build && bun run artifact-scan",
|
|
52
51
|
"typecheck": "tsc --noEmit",
|
|
@@ -55,7 +54,7 @@
|
|
|
55
54
|
"dev:mcp": "bun run src/mcp/index.ts",
|
|
56
55
|
"dev:serve": "bun run src/server/index.ts",
|
|
57
56
|
"dev:otel": "bun run src/otel/index.ts",
|
|
58
|
-
"postinstall": "
|
|
57
|
+
"postinstall": "node postinstall.js"
|
|
59
58
|
},
|
|
60
59
|
"keywords": [
|
|
61
60
|
"economy",
|
|
@@ -76,11 +75,8 @@
|
|
|
76
75
|
"access": "public"
|
|
77
76
|
},
|
|
78
77
|
"dependencies": {
|
|
79
|
-
"@hasna/
|
|
80
|
-
"@hasna/agent-registry": "^0.1.0",
|
|
81
|
-
"@hasna/contracts": "0.13.4",
|
|
78
|
+
"@hasna/contracts": "1.0.2",
|
|
82
79
|
"@hasna/events": "^0.1.16",
|
|
83
|
-
"@hasna/mcp-harness": "^0.1.0",
|
|
84
80
|
"@hasna/projects": "1.0.0",
|
|
85
81
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
86
82
|
"chalk": "^5.4.1",
|
|
@@ -89,7 +85,7 @@
|
|
|
89
85
|
"zod": "^3.24.2"
|
|
90
86
|
},
|
|
91
87
|
"devDependencies": {
|
|
92
|
-
"@types/bun": "
|
|
88
|
+
"@types/bun": "1.3.14",
|
|
93
89
|
"@types/pg": "^8.20.0",
|
|
94
90
|
"bun-types": "latest",
|
|
95
91
|
"typescript": "^5.7.2"
|
package/postinstall.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
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
|
+
// --- 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
|
+
}
|
|
60
|
+
import { homedir } from "node:os";
|
|
61
|
+
import { join } from "node:path";
|
|
62
|
+
|
|
63
|
+
const EXACT_OVERRIDE = (process.env["HASNA_ECONOMY_HOME"] || process.env["ECONOMY_HOME"] || "").trim();
|
|
64
|
+
const DATA_HOME_OVERRIDE = (process.env["HASNA_DATA_HOME"] || "").trim();
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
// (local resolver — @hasna/paths deleted, hasna/apps#1535)
|
|
68
|
+
const resolved = dataDir({ app: "economy" });
|
|
69
|
+
let root;
|
|
70
|
+
if (EXACT_OVERRIDE) {
|
|
71
|
+
root = EXACT_OVERRIDE;
|
|
72
|
+
} else if (DATA_HOME_OVERRIDE || existsSync(join(resolved, "economy.db"))) {
|
|
73
|
+
root = resolved;
|
|
74
|
+
} else {
|
|
75
|
+
root = join(homedir(), ".hasna", "economy");
|
|
76
|
+
}
|
|
77
|
+
for (const dir of [root, join(root, "training")]) {
|
|
78
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
79
|
+
}
|
|
80
|
+
} catch {
|
|
81
|
+
// never fail an install over pre-created directories
|
|
82
|
+
}
|
package/dashboard/README.md
DELETED
|
@@ -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.
|
package/dist/cli/brains.d.ts
DELETED
package/dist/cli/brains.d.ts.map
DELETED
|
@@ -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"}
|