@hasna/economy 0.3.7 → 0.3.8
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 +0 -4
- package/README.md +28 -61
- package/dashboard/README.md +17 -64
- package/dist/cli/index.js +221 -114
- package/dist/db/cloud.d.ts +3 -4
- package/dist/db/cloud.d.ts.map +1 -1
- package/dist/db/database.d.ts +2 -2
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/dialect.d.ts +13 -0
- package/dist/db/dialect.d.ts.map +1 -0
- package/dist/db/pg-migrate.d.ts +30 -0
- package/dist/db/pg-migrate.d.ts.map +1 -0
- package/dist/db/sqlite-adapter.d.ts +36 -0
- package/dist/db/sqlite-adapter.d.ts.map +1 -0
- package/dist/db/sync-pg.d.ts +1 -1
- package/dist/db/sync-pg.d.ts.map +1 -1
- package/dist/index.js +129 -56
- package/dist/ingest/billing.d.ts +1 -1
- package/dist/ingest/billing.d.ts.map +1 -1
- package/dist/ingest/claude-quota.d.ts +1 -1
- package/dist/ingest/claude-quota.d.ts.map +1 -1
- package/dist/ingest/claude.d.ts +1 -1
- package/dist/ingest/claude.d.ts.map +1 -1
- package/dist/ingest/codex-quota.d.ts +1 -1
- package/dist/ingest/codex-quota.d.ts.map +1 -1
- package/dist/ingest/codex.d.ts +1 -1
- package/dist/ingest/codex.d.ts.map +1 -1
- package/dist/ingest/cursor.d.ts +1 -1
- package/dist/ingest/cursor.d.ts.map +1 -1
- package/dist/ingest/gemini.d.ts +1 -1
- package/dist/ingest/gemini.d.ts.map +1 -1
- package/dist/ingest/hermes.d.ts +1 -1
- package/dist/ingest/hermes.d.ts.map +1 -1
- package/dist/ingest/loops.d.ts +1 -1
- package/dist/ingest/loops.d.ts.map +1 -1
- package/dist/ingest/opencode.d.ts +1 -1
- package/dist/ingest/opencode.d.ts.map +1 -1
- package/dist/ingest/otel.d.ts +1 -1
- package/dist/ingest/otel.d.ts.map +1 -1
- package/dist/ingest/pi.d.ts +1 -1
- package/dist/ingest/pi.d.ts.map +1 -1
- package/dist/ingest/plugin.d.ts +1 -1
- package/dist/ingest/plugin.d.ts.map +1 -1
- package/dist/lib/accounts.d.ts.map +1 -1
- package/dist/lib/analytics.d.ts +1 -1
- package/dist/lib/analytics.d.ts.map +1 -1
- package/dist/lib/billing-diff.d.ts +1 -1
- package/dist/lib/billing-diff.d.ts.map +1 -1
- package/dist/lib/brief.d.ts +1 -1
- package/dist/lib/brief.d.ts.map +1 -1
- package/dist/lib/open-projects.d.ts +10 -2
- package/dist/lib/open-projects.d.ts.map +1 -1
- package/dist/lib/pricing.d.ts +1 -1
- package/dist/lib/pricing.d.ts.map +1 -1
- package/dist/lib/savings.d.ts +1 -1
- package/dist/lib/savings.d.ts.map +1 -1
- package/dist/lib/spikes.d.ts +1 -1
- package/dist/lib/spikes.d.ts.map +1 -1
- package/dist/lib/sync-all.d.ts +1 -1
- package/dist/lib/sync-all.d.ts.map +1 -1
- package/dist/lib/webhooks.d.ts +1 -1
- package/dist/lib/webhooks.d.ts.map +1 -1
- package/dist/mcp/index.js +144 -94
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/openapi.d.ts.map +1 -1
- package/dist/otel/index.js +69 -4
- package/dist/server/index.js +248 -88
- package/dist/server/serve.d.ts +1 -1
- package/dist/server/serve.d.ts.map +1 -1
- package/docs/README.md +12 -0
- package/docs/cli.md +88 -0
- package/docs/configuration.md +86 -0
- package/docs/ingestion.md +47 -0
- package/docs/mcp.md +65 -0
- package/docs/otel.md +53 -0
- package/docs/rest-api.md +89 -0
- package/package.json +4 -5
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Configuration and deployment
|
|
2
|
+
|
|
3
|
+
## Local data
|
|
4
|
+
|
|
5
|
+
The default data directory is `~/.hasna/economy/` and the SQLite database is `~/.hasna/economy/economy.db`. On first access, regular files in an older `~/.economy/` directory are copied when the new directory does not yet exist.
|
|
6
|
+
|
|
7
|
+
| Variable | Effect |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| `HASNA_ECONOMY_DB_PATH` | SQLite path; takes precedence over `ECONOMY_DB`. |
|
|
10
|
+
| `ECONOMY_DB` | Alternate SQLite path. `:memory:` is useful for tests. |
|
|
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. |
|
|
13
|
+
| `ECONOMY_TAG` | Fallback attribution tag on locally written sessions/requests. |
|
|
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.
|
|
16
|
+
|
|
17
|
+
## Account attribution
|
|
18
|
+
|
|
19
|
+
For an agent token such as `CODEX`, Economy checks these explicit forms before consulting `@hasna/accounts`:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
ECONOMY_CODEX_ACCOUNT_KEY or ECONOMY_CODEX_ACCOUNT
|
|
23
|
+
ECONOMY_ACCOUNT_KEY or ECONOMY_ACCOUNT
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Values may be `tool:name` (for example `codex:work`) or a bare name/email. The structured form is:
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
ECONOMY_CODEX_ACCOUNT_TOOL / _NAME / _EMAIL
|
|
30
|
+
ECONOMY_ACCOUNT_TOOL / _NAME / _EMAIL
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The same agent-specific pattern applies to all eight supported agents. Account keys use the tool plus normalized email when available, otherwise the profile name.
|
|
34
|
+
|
|
35
|
+
## CLI/MCP cloud client
|
|
36
|
+
|
|
37
|
+
Local is the default. To route CLI and MCP data operations to a shared server, set:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
export HASNA_ECONOMY_API_URL=https://economy.example.com
|
|
41
|
+
export HASNA_ECONOMY_API_KEY='...'
|
|
42
|
+
```
|
|
43
|
+
|
|
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.
|
|
45
|
+
|
|
46
|
+
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
|
+
|
|
48
|
+
## REST server
|
|
49
|
+
|
|
50
|
+
Start the local server with either:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
economy serve --port 3456
|
|
54
|
+
economy-serve --port 3456
|
|
55
|
+
```
|
|
56
|
+
|
|
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`.
|
|
58
|
+
|
|
59
|
+
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
|
+
|
|
61
|
+
The server serves `dashboard/dist` and falls back to its `index.html` for non-API paths when those assets exist.
|
|
62
|
+
|
|
63
|
+
## Self-hosted server
|
|
64
|
+
|
|
65
|
+
The server switches to direct Postgres mode when `HASNA_ECONOMY_STORAGE_MODE=cloud` or when a DSN is present:
|
|
66
|
+
|
|
67
|
+
```text
|
|
68
|
+
HASNA_ECONOMY_DATABASE_URL
|
|
69
|
+
ECONOMY_DATABASE_URL
|
|
70
|
+
DATABASE_URL
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Apply migrations with `economy-serve migrate`. `ECONOMY_PG_POOL_MAX` defaults to 5. A non-loopback cloud 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.
|
|
74
|
+
|
|
75
|
+
See [REST API authentication](rest-api.md#authentication) for request headers and open probes.
|
|
76
|
+
|
|
77
|
+
## Other services
|
|
78
|
+
|
|
79
|
+
| Variable | Effect |
|
|
80
|
+
| --- | --- |
|
|
81
|
+
| `MCP_HTTP=1` | Run `economy-mcp` in Streamable HTTP mode instead of stdio. |
|
|
82
|
+
| `MCP_HTTP_PORT` | MCP HTTP port; default 8860 and overridden by `--port`. |
|
|
83
|
+
| `ECONOMY_OTEL_PORT` | OTLP sidecar port; default 4318 and overridden by `--port`. |
|
|
84
|
+
| `ECONOMY_OTEL_BIND` | OTLP sidecar bind host; default `127.0.0.1`. |
|
|
85
|
+
|
|
86
|
+
Source- and billing-specific environment variables are listed in [Ingestion](ingestion.md).
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Ingestion
|
|
2
|
+
|
|
3
|
+
`economy sync` imports local coding-agent usage into Economy's SQLite database. A sync with no source flag runs every source; one or more source flags limit the run. Reads such as `economy today` also auto-sync all local sources before querying.
|
|
4
|
+
|
|
5
|
+
In cloud-client mode, CLI and MCP reads/writes go directly to the shared HTTP API. Local `sync` and `billing sync` deliberately do nothing in that mode; ingest on a machine running in local mode, or use the authenticated server ingest endpoints.
|
|
6
|
+
|
|
7
|
+
## Sources
|
|
8
|
+
|
|
9
|
+
| Source | Default input | Notes and overrides |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| Claude Code | `~/.claude/projects/**/*.jsonl` | Imports assistant messages with usage, including cache tiers and supported pricing modifiers. Quota uses `~/.claude/.credentials.json`, `CLAUDE_OAUTH_TOKEN`, or `ANTHROPIC_OAUTH_TOKEN`. |
|
|
12
|
+
| Takumi | `~/.takumi/projects/**/*.jsonl` | Uses the same JSONL ingestion format as Claude. |
|
|
13
|
+
| Codex | `~/.codex/state_5.sqlite`, rollout JSONL, and `~/.codex/config.toml` | Overrides: `HASNA_ECONOMY_CODEX_DB_PATH`, `HASNA_ECONOMY_CODEX_CONFIG_PATH`. Quota uses `~/.codex/auth.json` or `CODEX_OAUTH_TOKEN`; `CODEX_USAGE_URL` can override the quota URL. |
|
|
14
|
+
| Codewith (reported as Codex) | `~/.codewith/state_5.sqlite` and `~/.codewith/config.toml` | Overrides: `HASNA_ECONOMY_CODEWITH_DB_PATH`, `HASNA_ECONOMY_CODEWITH_CONFIG_PATH`. An explicit Codex DB path disables default Codewith discovery unless a Codewith DB path is also explicit. |
|
|
15
|
+
| Gemini CLI | `~/.gemini/tmp` and `~/.gemini/history` | Overrides: `HASNA_ECONOMY_GEMINI_TMP_DIR`, `HASNA_ECONOMY_GEMINI_HISTORY_DIR`. |
|
|
16
|
+
| OpenCode | `~/.local/share/opencode/storage/message/**/*.json` | Imports assistant-message usage and uses the recorded cost when present. |
|
|
17
|
+
| Cursor | Cursor `/api/usage` and `/api/usage-summary` | Requires `CURSOR_SESSION_TOKEN` (or `CURSOR_API_TOKEN`). Creates daily usage snapshots and a subscription rollup when spend is present. |
|
|
18
|
+
| Pi | `~/.pi/agent/sessions/**/*.json` | Override with `PI_CODING_AGENT_SESSION_DIR`. Uses recorded turn cost; a missing cost remains zero until pricing is repaired or the source records one. |
|
|
19
|
+
| Hermes | `~/.hermes/state.db` | Imports session-level token and cost rollups. |
|
|
20
|
+
| OpenLoops | `~/.hasna/loops/loops.db` | Override with `HASNA_ECONOMY_LOOPS_DB_PATH`; price with `HASNA_ECONOMY_LOOPS_MODEL` or `ECONOMY_LOOPS_MODEL`. Imports orchestration/judge `goal_runs.tokens_used` only, into `loop:*` cost centers. |
|
|
21
|
+
|
|
22
|
+
Full, unfiltered sync also attempts to import active metadata from `@hasna/projects`. Missing files, optional registries, and unavailable quota credentials are skipped; use `--verbose` to see source-level diagnostics.
|
|
23
|
+
|
|
24
|
+
## Incremental and repair behavior
|
|
25
|
+
|
|
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
|
+
|
|
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.
|
|
30
|
+
- `--recalculate` prices token-bearing requests whose `cost_usd` is zero, then rerolls affected sessions. It reports buckets still missing usable pricing.
|
|
31
|
+
- Budget webhooks are checked after a CLI or REST sync. Failed deliveries remain eligible for a later retry.
|
|
32
|
+
|
|
33
|
+
## Account and cost-center attribution
|
|
34
|
+
|
|
35
|
+
Economy first checks agent-specific overrides such as `ECONOMY_CODEX_ACCOUNT`, then generic `ECONOMY_ACCOUNT` overrides, then matching `@hasna/accounts` env-dir/applied/current profiles. An override may be `tool:name`, an email, or separate `*_ACCOUNT_TOOL`, `*_ACCOUNT_NAME`, and `*_ACCOUNT_EMAIL` fields. See [configuration](configuration.md#account-attribution).
|
|
36
|
+
|
|
37
|
+
Apps and services can report explicit project, repository, account, attribution-tag, and cost-center fields through [`economy-otel`](otel.md). `ECONOMY_TAG` supplies a fallback attribution tag for records written through the local database helpers.
|
|
38
|
+
|
|
39
|
+
## Provider billing
|
|
40
|
+
|
|
41
|
+
`economy billing sync --days 31` imports ground-truth daily billing locally:
|
|
42
|
+
|
|
43
|
+
- Anthropic: `HASNAXYZ_ANTHROPIC_LIVE_ADMIN_API_KEY` or `ANTHROPIC_ADMIN_API_KEY`.
|
|
44
|
+
- OpenAI: `HASNAXYZ_OPENAI_LIVE_ADMIN_API_KEY` or `OPENAI_ADMIN_API_KEY`.
|
|
45
|
+
- Gemini export: `HASNA_ECONOMY_GEMINI_BILLING_EXPORT_PATH`, `HASNAXYZ_ECONOMY_GEMINI_BILLING_EXPORT_PATH`, or `GEMINI_BILLING_EXPORT_PATH`.
|
|
46
|
+
|
|
47
|
+
Gemini imports JSON arrays, objects with a `rows` array, JSONL, or simple CSV. `economy billing show` compares totals; `economy billing diff` applies the reconciliation threshold.
|
package/docs/mcp.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# MCP server
|
|
2
|
+
|
|
3
|
+
`economy-mcp` uses stdio by default:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
claude mcp add --transport stdio --scope user economy -- economy-mcp
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Codex configuration:
|
|
10
|
+
|
|
11
|
+
```toml
|
|
12
|
+
[mcp_servers.economy]
|
|
13
|
+
command = "economy-mcp"
|
|
14
|
+
args = []
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Gemini settings:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"mcpServers": {
|
|
22
|
+
"economy": { "command": "economy-mcp", "args": [] }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
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).
|
|
28
|
+
|
|
29
|
+
## Streamable HTTP
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
economy-mcp --http # http://127.0.0.1:8860/mcp
|
|
33
|
+
MCP_HTTP=1 economy-mcp # same
|
|
34
|
+
economy-mcp --http --port 8815
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`MCP_HTTP_PORT` supplies the default HTTP port. The transport binds to loopback, exposes `POST /mcp`, and exposes `GET /health`. Stdio remains the default when `--http`/`MCP_HTTP` is absent.
|
|
38
|
+
|
|
39
|
+
## Tools
|
|
40
|
+
|
|
41
|
+
Discovery helpers:
|
|
42
|
+
|
|
43
|
+
- `search_tools`, `describe_tools`
|
|
44
|
+
|
|
45
|
+
Cost and activity reads:
|
|
46
|
+
|
|
47
|
+
- `get_cost_summary`, `get_sessions`, `get_session_detail`, `get_top_sessions`
|
|
48
|
+
- `get_model_breakdown`, `get_project_breakdown`, `get_agent_breakdown`, `get_account_breakdown`, `get_cost_center_breakdown`
|
|
49
|
+
- `get_daily`, `list_machines`, `get_usage`, `get_savings`, `get_billing_summary`
|
|
50
|
+
|
|
51
|
+
Management and estimation:
|
|
52
|
+
|
|
53
|
+
- `get_budget_status`, `set_budget`, `remove_budget`
|
|
54
|
+
- `get_goals`, `set_goal`, `remove_goal`
|
|
55
|
+
- `get_pricing`, `set_pricing`, `remove_pricing`, `estimate_cost`
|
|
56
|
+
- `list_subscriptions`, `set_subscription`, `remove_subscription`
|
|
57
|
+
- `sync`, `send_feedback`
|
|
58
|
+
|
|
59
|
+
Shared agent-registry tools:
|
|
60
|
+
|
|
61
|
+
- `register_agent`, `heartbeat`, `set_focus`, `list_agents`
|
|
62
|
+
|
|
63
|
+
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
|
+
|
|
65
|
+
The `sync` tool accepts `all`, any supported coding agent, or `loops`. It ingests on-box files only in local mode. In cloud-client mode it returns an explanatory no-op because all other data tools already use the shared API.
|
package/docs/otel.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# OTLP/HTTP sidecar
|
|
2
|
+
|
|
3
|
+
`economy-otel` writes application/service metrics into the local Economy SQLite database:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
economy-otel --port 4318
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
It binds to `127.0.0.1` by default. Override the bind with `ECONOMY_OTEL_BIND` and the port with `ECONOMY_OTEL_PORT` or `--port`.
|
|
10
|
+
|
|
11
|
+
| Method | Path | Behavior |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| `GET` | `/health` | `{ status, service, version }`. |
|
|
14
|
+
| `POST` | `/v1/metrics` | Parse an OTLP JSON `resourceMetrics` payload. |
|
|
15
|
+
| `POST` | `/ingest` | Parse one simplified JSON cost/token event. |
|
|
16
|
+
|
|
17
|
+
Other methods return 405. Invalid JSON returns 400. A valid payload with no recognized positive cost/token metrics returns `{ "ingested": 0, "message": "no matching metrics" }`.
|
|
18
|
+
|
|
19
|
+
## Simplified event
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
curl -X POST http://127.0.0.1:4318/ingest \
|
|
23
|
+
-H 'content-type: application/json' \
|
|
24
|
+
-d '{
|
|
25
|
+
"source": "app",
|
|
26
|
+
"cost_center": "alumia",
|
|
27
|
+
"cost_center_kind": "app",
|
|
28
|
+
"project_path": "/workspace/alumia",
|
|
29
|
+
"model": "gpt-5-mini",
|
|
30
|
+
"cost_usd": 0.12,
|
|
31
|
+
"input_tokens": 1200,
|
|
32
|
+
"output_tokens": 300
|
|
33
|
+
}'
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Core fields are `agent`/`source`, `session_id`, `request_id` or `source_request_id`, `model`, `timestamp`, `cost_usd`, `input_tokens`, and `output_tokens`. An event must have positive cost or tokens.
|
|
37
|
+
|
|
38
|
+
Attribution fields include:
|
|
39
|
+
|
|
40
|
+
- `cost_center`, `cost_center_kind`, `cost_center_id`
|
|
41
|
+
- `attribution_tag`, `project_path`, `project_name`, `repo`
|
|
42
|
+
- `account_key`, `account_tool`, `account_name`, `account_email`, `account_source`
|
|
43
|
+
- `cost_basis` (`metered_api`, `subscription_included`, `estimated`, or `unknown`)
|
|
44
|
+
|
|
45
|
+
Cost-center kinds are `loop`, `app`, `repo`, `service`, and `team`. When kind and name are present, the sidecar creates a cost center such as `app:alumia` unless an explicit ID is supplied. Unknown agents normalize to the cost-center kind when it is a supported pseudo-agent (`app`, `service`, `repo`, `loop`), otherwise `service`.
|
|
46
|
+
|
|
47
|
+
## OTLP recognition
|
|
48
|
+
|
|
49
|
+
The OTLP parser reads sum or gauge data points. Metric names containing cost or `.usd` become cost; names containing input+token or `tokens.input` become input tokens; names containing output+token or `tokens.output` become output tokens. Points are joined by agent and request/event ID.
|
|
50
|
+
|
|
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
|
+
|
|
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.
|
package/docs/rest-api.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# REST API
|
|
2
|
+
|
|
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
|
+
|
|
5
|
+
The canonical application prefix is `/v1`. Equivalent `/api` routes remain available for the bundled dashboard and older clients. Successful application responses use:
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{ "data": {}, "meta": {} }
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Errors use `{ "error": "message" }`. Foundation probes return a direct `{ status, version, mode, service }` object instead of the data envelope.
|
|
12
|
+
|
|
13
|
+
## Discovery and probes
|
|
14
|
+
|
|
15
|
+
| Method | Path | Purpose |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| `GET` | `/health`, `/healthz` | Liveness. |
|
|
18
|
+
| `GET` | `/ready`, `/readyz` | Storage readiness; may return 503. |
|
|
19
|
+
| `GET` | `/version`, `/v1/version` | Version and deployment mode. |
|
|
20
|
+
| `GET` | `/openapi.json` | Runtime-versioned OpenAPI document. |
|
|
21
|
+
|
|
22
|
+
These routes are open. The checked-in OpenAPI source is [`openapi/economy.json`](../openapi/economy.json).
|
|
23
|
+
|
|
24
|
+
## Authentication
|
|
25
|
+
|
|
26
|
+
In local mode, set `ECONOMY_API_TOKEN` or `HASNA_ECONOMY_API_TOKEN` and send either:
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
Authorization: Bearer <token>
|
|
30
|
+
X-Economy-Token: <token>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
In self-hosted Postgres mode, send a valid Economy API key as `x-api-key` or a bearer token. All non-probe routes require a valid key when the server has an authenticator. Bulk ingest and feedback additionally request the `economy:write` scope.
|
|
34
|
+
|
|
35
|
+
## Read routes
|
|
36
|
+
|
|
37
|
+
| Method | Canonical path | Parameters/behavior |
|
|
38
|
+
| --- | --- | --- |
|
|
39
|
+
| `GET` | `/v1/summary` | `period` (default `today`), optional `machine`. |
|
|
40
|
+
| `GET` | `/v1/machines` | Machine rollups and current machine metadata. |
|
|
41
|
+
| `GET` | `/v1/fleet` | `period` (default `month`), optional `machine`; returns summary, machines, registry. |
|
|
42
|
+
| `GET` | `/v1/daily` | `days` (default 30), optional `machine`. |
|
|
43
|
+
| `GET` | `/v1/hourly` | Optional `machine`; `hours` must be 1–48. |
|
|
44
|
+
| `GET` | `/v1/sessions` | `agent`, `project`, `search`, `machine`, `account`, `limit` (50), `offset` (0), `since`, and comma-separated `fields`. |
|
|
45
|
+
| `GET` | `/v1/sessions/{id}/requests` | Full ID or unique prefix; returns 404 when absent. |
|
|
46
|
+
| `GET` | `/v1/top` | `n` (default 10), optional `agent` and `since`. |
|
|
47
|
+
| `GET` | `/v1/models` | Model breakdown. |
|
|
48
|
+
| `GET` | `/v1/projects` | Project breakdown; `period` defaults to `all`, optional `machine`. |
|
|
49
|
+
| `GET` | `/v1/projects/detail` | Detailed project query; `q` is required. |
|
|
50
|
+
| `GET` | `/v1/accounts` | Account breakdown; `period` defaults to `all`, optional `machine`. |
|
|
51
|
+
| `GET` | `/v1/breakdown` | `by=model|project|agent|account|cost-center|loop|app|repo|service|team`, optional `period` and `machine`. |
|
|
52
|
+
| `GET` | `/v1/usage` | `period` (default `month`), optional valid `agent`. |
|
|
53
|
+
| `GET` | `/v1/savings` | `period` (default `month`), optional valid `agent`. |
|
|
54
|
+
| `GET` | `/v1/billing` | `period` (default `month`). |
|
|
55
|
+
| `GET` | `/v1/billing/diff` | `period` (default `month`), `threshold` percentage (default 15). |
|
|
56
|
+
| `GET` | `/v1/budgets` | Budget statuses. |
|
|
57
|
+
| `GET` | `/v1/goals` | Goal statuses. |
|
|
58
|
+
| `GET` | `/v1/pricing` | Model pricing rows. |
|
|
59
|
+
| `GET` | `/v1/subscriptions` | Subscription plans. |
|
|
60
|
+
| `GET` | `/v1/project-registry` | Registered projects. |
|
|
61
|
+
| `GET` | `/v1/brief` | Fleet brief; optional `since` and `machine`. |
|
|
62
|
+
| `GET` | `/v1/export` | `type=sessions|requests`, `period` (default `month`); returns rows in JSON for clients to encode. |
|
|
63
|
+
| `GET` | `/v1/compare` | Required `from` and `to` dates (`YYYY-MM-DD`). |
|
|
64
|
+
| `GET` | `/v1/forecast` | Current-month projection. |
|
|
65
|
+
| `GET` | `/v1/efficiency` | Per-model token efficiency. |
|
|
66
|
+
| `GET` | `/v1/requests` | Requests after required ISO `since`; used by live watch. |
|
|
67
|
+
|
|
68
|
+
Common period values are `today`, `yesterday`, `week`, `month`, `year`, and `all`, but individual routes accept the subset documented above.
|
|
69
|
+
|
|
70
|
+
## Mutation routes
|
|
71
|
+
|
|
72
|
+
| Method | Canonical path | Body/behavior |
|
|
73
|
+
| --- | --- | --- |
|
|
74
|
+
| `POST` | `/v1/budgets` | Positive `limit_usd`; optional `period`, `alert_at_percent`, `project_path`, `agent`, `cost_center_id`. |
|
|
75
|
+
| `DELETE` | `/v1/budgets/{id}` | Delete a budget. |
|
|
76
|
+
| `POST` | `/v1/goals` | Positive `limit_usd`; `period=day|week|month|year`; optional `project_path`, `agent`. |
|
|
77
|
+
| `DELETE` | `/v1/goals/{id}` | Delete a goal. |
|
|
78
|
+
| `POST` | `/v1/pricing` | `model`, non-negative input/output/cache pricing fields. |
|
|
79
|
+
| `DELETE` | `/v1/pricing/{model}` | Delete a URL-encoded model row. |
|
|
80
|
+
| `POST` | `/v1/subscriptions` | Required `provider` and `plan`; optional ID, agent, fee/inclusion, cycle/reset, and active fields. |
|
|
81
|
+
| `DELETE` | `/v1/subscriptions/{id}` | Delete a plan. |
|
|
82
|
+
| `POST` | `/v1/project-registry` | Required `path`; optional `name`, `description`, `tags`. |
|
|
83
|
+
| `DELETE` | `/v1/project-registry/{path}` | Delete a URL-encoded project path. |
|
|
84
|
+
| `POST` | `/v1/sync` | `{ "sources": "all" }` or one of the eight agents/`loops`; triggers server-local ingestion. |
|
|
85
|
+
| `POST` | `/v1/billing/sync` | Optional `days` (1–366) and `providers` from `anthropic`, `openai`, `gemini`. Provider failures are returned per provider. |
|
|
86
|
+
| `POST` | `/v1/ingest` | Idempotently merge arrays named `requests`, `sessions`, `projects`, `budgets`, `goals`, `billing_daily`, `model_pricing`, `subscriptions`, and `usage_snapshots`. |
|
|
87
|
+
| `POST` | `/v1/feedback` | Required `message`; optional `email` and `category=bug|feature|general`. |
|
|
88
|
+
|
|
89
|
+
Agent fields accept `claude`, `takumi`, `codex`, `gemini`, `opencode`, `cursor`, `pi`, or `hermes`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/economy",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"description": "AI coding cost tracker — CLI + MCP server + REST API + web dashboard for Claude Code, Codex, Gemini, OpenCode, Cursor, Pi, and Hermes",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"files": [
|
|
29
29
|
"dist",
|
|
30
|
+
"docs",
|
|
30
31
|
"dashboard/dist",
|
|
31
32
|
"LICENSE",
|
|
32
33
|
"README.md",
|
|
@@ -69,13 +70,12 @@
|
|
|
69
70
|
"access": "public"
|
|
70
71
|
},
|
|
71
72
|
"dependencies": {
|
|
72
|
-
"@hasna/accounts": "0.
|
|
73
|
+
"@hasna/accounts": "0.2.23",
|
|
73
74
|
"@hasna/agent-registry": "^0.1.0",
|
|
74
|
-
"@hasna/cloud": "0.1.24",
|
|
75
75
|
"@hasna/contracts": "^0.4.2",
|
|
76
76
|
"@hasna/events": "^0.1.7",
|
|
77
77
|
"@hasna/mcp-harness": "^0.1.0",
|
|
78
|
-
"@hasna/projects": "0.1.
|
|
78
|
+
"@hasna/projects": "0.1.95",
|
|
79
79
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
80
80
|
"chalk": "^5.4.1",
|
|
81
81
|
"commander": "^13.1.0",
|
|
@@ -89,7 +89,6 @@
|
|
|
89
89
|
"typescript": "^5.7.2"
|
|
90
90
|
},
|
|
91
91
|
"trustedDependencies": [
|
|
92
|
-
"@hasna/cloud",
|
|
93
92
|
"@hasna/projects"
|
|
94
93
|
]
|
|
95
94
|
}
|