@hasna/economy 0.2.22 → 0.2.23
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/README.md +28 -7
- package/dist/cli/index.js +44 -0
- package/dist/mcp/index.js +63 -0
- package/dist/server/index.js +50 -0
- package/dist/server/serve.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
# @hasna/economy
|
|
2
2
|
|
|
3
|
-
AI coding cost tracker for Claude Code, Takumi, Codex, and
|
|
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.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@hasna/economy)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
|
|
8
8
|
## Features
|
|
9
9
|
|
|
10
|
-
- Ingests local Claude Code, Takumi, Codex, and
|
|
10
|
+
- Ingests local Claude Code, Takumi, Codex, Gemini, OpenCode, Cursor, Pi, and Hermes usage.
|
|
11
11
|
- Tracks sessions, requests, projects, machines, models, cache tokens, budgets, goals, and provider billing.
|
|
12
12
|
- Attributes usage to `@hasna/accounts` profiles when agents run under managed account/profile config dirs.
|
|
13
|
+
- Breaks down API-equivalent, metered API, subscription-included, estimated, and unknown cost by account and coding agent.
|
|
13
14
|
- Seeds editable model pricing with input, output, cache-read, 5-minute cache-write, 1-hour cache-write, and context-cache storage rates.
|
|
14
15
|
- Handles tiered pricing such as Gemini long-prompt rates and OpenAI long-context rates.
|
|
15
16
|
- Reconciles estimates against Anthropic, OpenAI, and Gemini billing sources.
|
|
@@ -70,7 +71,7 @@ Gemini settings:
|
|
|
70
71
|
}
|
|
71
72
|
```
|
|
72
73
|
|
|
73
|
-
The MCP server exposes read tools for summaries, sessions, machines, pricing, daily spend, budgets, goals,
|
|
74
|
+
The MCP server exposes read tools for summaries, sessions, machines, pricing, daily spend, budgets, goals, provider billing, usage snapshots, savings, project/account/agent breakdowns, and subscriptions. It also exposes mutation tools for budgets, pricing rows, goals, and subscriptions so coding agents can manage Economy data through the same validated surface as the CLI and REST API.
|
|
74
75
|
|
|
75
76
|
## Ingest
|
|
76
77
|
|
|
@@ -87,6 +88,10 @@ economy sync --claude
|
|
|
87
88
|
economy sync --codex
|
|
88
89
|
economy sync --gemini
|
|
89
90
|
economy sync --takumi
|
|
91
|
+
economy sync --opencode
|
|
92
|
+
economy sync --cursor
|
|
93
|
+
economy sync --pi
|
|
94
|
+
economy sync --hermes
|
|
90
95
|
```
|
|
91
96
|
|
|
92
97
|
Useful repair options:
|
|
@@ -103,6 +108,15 @@ Account attribution is automatic when `@hasna/accounts` has a matching active, a
|
|
|
103
108
|
|
|
104
109
|
Account breakdowns report `api_equivalent_usd` for the API list-price value of the usage, plus `billable_usd`/`metered_api_usd` for known direct API spend and `subscription_included_usd` for usage covered by a subscription.
|
|
105
110
|
|
|
111
|
+
Subscription plans can be configured locally and are used by savings calculations:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
economy subscriptions set --provider cursor --plan pro --fee 20 --included 20 --agent cursor
|
|
115
|
+
economy subscriptions list
|
|
116
|
+
economy savings month
|
|
117
|
+
economy usage month --agent cursor
|
|
118
|
+
```
|
|
119
|
+
|
|
106
120
|
## Pricing
|
|
107
121
|
|
|
108
122
|
Default pricing is seeded into SQLite and can be edited locally:
|
|
@@ -150,7 +164,7 @@ economy config set webhook-url https://example.com/economy-webhook
|
|
|
150
164
|
economy config webhook-test
|
|
151
165
|
```
|
|
152
166
|
|
|
153
|
-
Budgets and goals can be global, project-scoped with `--project`, agent-scoped with `--agent`, or both. Valid agent scopes are `claude`, `takumi`, `codex`, and `
|
|
167
|
+
Budgets and goals can be global, project-scoped with `--project`, agent-scoped with `--agent`, or both. Valid agent scopes are `claude`, `takumi`, `codex`, `gemini`, `opencode`, `cursor`, `pi`, and `hermes`.
|
|
154
168
|
|
|
155
169
|
Budget webhooks fire after sync when the alert threshold is crossed. Failed webhook deliveries are not marked as fired, so the next sync can retry them.
|
|
156
170
|
|
|
@@ -169,7 +183,14 @@ Common endpoints:
|
|
|
169
183
|
- `GET /api/sessions?agent=codex&limit=20`
|
|
170
184
|
- `GET /api/sessions/:id/requests`
|
|
171
185
|
- `GET /api/models`
|
|
172
|
-
- `GET /api/projects`
|
|
186
|
+
- `GET /api/projects?period=month`
|
|
187
|
+
- `GET /api/breakdown?by=agent&period=month`
|
|
188
|
+
- `GET /api/accounts?period=month`
|
|
189
|
+
- `GET /api/usage?period=month`
|
|
190
|
+
- `GET /api/savings?period=month`
|
|
191
|
+
- `GET /api/subscriptions`
|
|
192
|
+
- `POST /api/subscriptions`
|
|
193
|
+
- `DELETE /api/subscriptions/:id`
|
|
173
194
|
- `GET /api/budgets`
|
|
174
195
|
- `POST /api/budgets`
|
|
175
196
|
- `DELETE /api/budgets/:id`
|
|
@@ -183,13 +204,13 @@ Common endpoints:
|
|
|
183
204
|
- `POST /api/sync`
|
|
184
205
|
- `POST /api/billing/sync`
|
|
185
206
|
|
|
186
|
-
Budget and
|
|
207
|
+
Budget, goal, and subscription mutation endpoints validate agent scopes against `claude`, `takumi`, `codex`, `gemini`, `opencode`, `cursor`, `pi`, and `hermes`.
|
|
187
208
|
|
|
188
209
|
The server also serves the built dashboard when `dashboard/dist` is present.
|
|
189
210
|
|
|
190
211
|
## Native macOS Menubar
|
|
191
212
|
|
|
192
|
-
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`. The default server URL is `http://127.0.0.1:3456`.
|
|
213
|
+
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, subscription savings, usage snapshots, recent sessions, and fleet status. The default server URL is `http://127.0.0.1:3456`.
|
|
193
214
|
|
|
194
215
|
Build it on macOS:
|
|
195
216
|
|
package/dist/cli/index.js
CHANGED
|
@@ -4574,6 +4574,50 @@ function createHandler(db) {
|
|
|
4574
4574
|
const agent = url.searchParams.get("agent") ?? undefined;
|
|
4575
4575
|
return ok(querySavingsSummary(db, period, agent && isAgent(agent) ? agent : undefined));
|
|
4576
4576
|
}
|
|
4577
|
+
if (path === "/api/subscriptions" && method === "GET") {
|
|
4578
|
+
return ok(listSubscriptions(db));
|
|
4579
|
+
}
|
|
4580
|
+
if (path === "/api/subscriptions" && method === "POST") {
|
|
4581
|
+
const body = await jsonBody(req);
|
|
4582
|
+
if (!body)
|
|
4583
|
+
return err("invalid JSON body");
|
|
4584
|
+
const provider = optionalString(body["provider"])?.trim();
|
|
4585
|
+
const plan = optionalString(body["plan"])?.trim();
|
|
4586
|
+
if (!provider)
|
|
4587
|
+
return err("provider is required");
|
|
4588
|
+
if (!plan)
|
|
4589
|
+
return err("plan is required");
|
|
4590
|
+
const monthlyFee = finiteNumber(body["monthly_fee_usd"] ?? body["fee_usd"] ?? 0);
|
|
4591
|
+
const includedUsage = finiteNumber(body["included_usage_usd"] ?? 0);
|
|
4592
|
+
if (monthlyFee == null || monthlyFee < 0)
|
|
4593
|
+
return err("monthly_fee_usd must be a non-negative number");
|
|
4594
|
+
if (includedUsage == null || includedUsage < 0)
|
|
4595
|
+
return err("included_usage_usd must be a non-negative number");
|
|
4596
|
+
const agent = optionalAgent(body["agent"]);
|
|
4597
|
+
if (agent === undefined)
|
|
4598
|
+
return err(AGENT_ERROR);
|
|
4599
|
+
const now = new Date().toISOString();
|
|
4600
|
+
const subscription = {
|
|
4601
|
+
id: optionalString(body["id"])?.trim() || randomUUID2(),
|
|
4602
|
+
agent,
|
|
4603
|
+
provider,
|
|
4604
|
+
plan,
|
|
4605
|
+
monthly_fee_usd: monthlyFee,
|
|
4606
|
+
included_usage_usd: includedUsage,
|
|
4607
|
+
billing_cycle_start: optionalString(body["billing_cycle_start"]),
|
|
4608
|
+
reset_policy: optionalString(body["reset_policy"]) ?? "monthly",
|
|
4609
|
+
active: body["active"] === false || body["active"] === 0 ? 0 : 1,
|
|
4610
|
+
created_at: optionalString(body["created_at"]) ?? now,
|
|
4611
|
+
updated_at: now
|
|
4612
|
+
};
|
|
4613
|
+
upsertSubscription(db, subscription);
|
|
4614
|
+
return ok(subscription);
|
|
4615
|
+
}
|
|
4616
|
+
const subscriptionMatch = path.match(/^\/api\/subscriptions\/(.+)$/);
|
|
4617
|
+
if (subscriptionMatch && method === "DELETE") {
|
|
4618
|
+
deleteSubscription(db, decodeURIComponent(subscriptionMatch[1]));
|
|
4619
|
+
return ok({ ok: true });
|
|
4620
|
+
}
|
|
4577
4621
|
const sessionRequestsMatch = path.match(/^\/api\/sessions\/([^/]+)\/requests$/);
|
|
4578
4622
|
if (sessionRequestsMatch && method === "GET") {
|
|
4579
4623
|
const sessionId = decodeURIComponent(sessionRequestsMatch[1]);
|
package/dist/mcp/index.js
CHANGED
|
@@ -1343,6 +1343,12 @@ function upsertSubscription(db, sub) {
|
|
|
1343
1343
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1344
1344
|
`).run(sub.id, sub.agent, sub.provider, sub.plan, sub.monthly_fee_usd, sub.included_usage_usd, sub.billing_cycle_start, sub.reset_policy, sub.active, sub.created_at, sub.updated_at);
|
|
1345
1345
|
}
|
|
1346
|
+
function listSubscriptions(db) {
|
|
1347
|
+
return db.prepare(`SELECT * FROM subscriptions ORDER BY provider, plan`).all();
|
|
1348
|
+
}
|
|
1349
|
+
function deleteSubscription(db, id) {
|
|
1350
|
+
db.prepare(`DELETE FROM subscriptions WHERE id = ?`).run(id);
|
|
1351
|
+
}
|
|
1346
1352
|
function upsertUsageSnapshot(db, snap) {
|
|
1347
1353
|
const now = snap.updated_at ?? new Date().toISOString();
|
|
1348
1354
|
const id = snap.id ?? `${snap.agent}-${snap.date}-${snap.metric}-${snap.machine_id}`;
|
|
@@ -3314,6 +3320,9 @@ var TOOL_NAMES = [
|
|
|
3314
3320
|
"get_session_detail",
|
|
3315
3321
|
"get_usage",
|
|
3316
3322
|
"get_savings",
|
|
3323
|
+
"list_subscriptions",
|
|
3324
|
+
"set_subscription",
|
|
3325
|
+
"remove_subscription",
|
|
3317
3326
|
"estimate_cost",
|
|
3318
3327
|
"sync",
|
|
3319
3328
|
"search_tools",
|
|
@@ -3348,6 +3357,9 @@ var TOOL_DESCRIPTIONS = {
|
|
|
3348
3357
|
get_session_detail: "session_id(prefix ok) -> per-request breakdown with model, tokens, cost",
|
|
3349
3358
|
get_usage: `period(today|week|month), agent?(${AGENTS.join("|")}) -> usage snapshots and all-machine summary`,
|
|
3350
3359
|
get_savings: `period(today|week|month|year|all), agent?(${AGENTS.join("|")}) -> subscription/API-equivalent savings`,
|
|
3360
|
+
list_subscriptions: "no params -> configured subscription plans and included usage",
|
|
3361
|
+
set_subscription: `provider, plan, monthly_fee_usd?, included_usage_usd?, agent?(${AGENTS.join("|")}) -> create/update subscription plan`,
|
|
3362
|
+
remove_subscription: "id -> delete subscription plan",
|
|
3351
3363
|
estimate_cost: "model, input_tokens?, output_tokens? -> pre-flight token cost estimate",
|
|
3352
3364
|
sync: `sources(all|${AGENTS.join("|")}) -> ingest latest cost data`,
|
|
3353
3365
|
search_tools: "query substring -> tool name list",
|
|
@@ -3608,6 +3620,57 @@ server.tool("get_usage", "Usage snapshots and fleet summary. period: today|week|
|
|
|
3608
3620
|
server.tool("get_savings", "Subscription vs API savings summary", { period: z.enum(["today", "week", "month", "year", "all"]).optional(), agent: z.enum(AGENTS).optional() }, async ({ period, agent }) => {
|
|
3609
3621
|
return text(JSON.stringify(querySavingsSummary(db, period ?? "month", agent), null, 2));
|
|
3610
3622
|
});
|
|
3623
|
+
server.tool("list_subscriptions", "List configured subscription plans and included usage caps. No params.", {}, async () => {
|
|
3624
|
+
const rows = listSubscriptions(db);
|
|
3625
|
+
if (rows.length === 0)
|
|
3626
|
+
return text("No subscriptions configured.");
|
|
3627
|
+
const lines = ["id provider plan agent fee included active"];
|
|
3628
|
+
for (const row of rows) {
|
|
3629
|
+
lines.push(`${String(row["id"]).slice(0, 8).padEnd(9)}` + `${String(row["provider"]).slice(0, 12).padEnd(13)}` + `${String(row["plan"]).slice(0, 10).padEnd(11)}` + `${String(row["agent"] ?? "all").slice(0, 10).padEnd(11)}` + `${fmtUsd(Number(row["monthly_fee_usd"] ?? 0)).padEnd(10)}` + `${fmtUsd(Number(row["included_usage_usd"] ?? 0)).padEnd(10)}` + `${Number(row["active"] ?? 0) ? "yes" : "no"}`);
|
|
3630
|
+
}
|
|
3631
|
+
return text(lines.join(`
|
|
3632
|
+
`));
|
|
3633
|
+
});
|
|
3634
|
+
server.tool("set_subscription", `Create or update a subscription plan. agent may be ${AGENTS.join("|")}.`, {
|
|
3635
|
+
id: z.string().optional(),
|
|
3636
|
+
provider: z.string(),
|
|
3637
|
+
plan: z.string(),
|
|
3638
|
+
agent: z.enum(AGENTS).optional(),
|
|
3639
|
+
monthly_fee_usd: z.number().optional(),
|
|
3640
|
+
included_usage_usd: z.number().optional(),
|
|
3641
|
+
billing_cycle_start: z.string().optional(),
|
|
3642
|
+
reset_policy: z.string().optional(),
|
|
3643
|
+
active: z.boolean().optional()
|
|
3644
|
+
}, async (input) => {
|
|
3645
|
+
if (input.monthly_fee_usd != null && input.monthly_fee_usd < 0)
|
|
3646
|
+
return text("monthly_fee_usd must be non-negative");
|
|
3647
|
+
if (input.included_usage_usd != null && input.included_usage_usd < 0)
|
|
3648
|
+
return text("included_usage_usd must be non-negative");
|
|
3649
|
+
const now = new Date().toISOString();
|
|
3650
|
+
const subscription = {
|
|
3651
|
+
id: input.id?.trim() || randomUUID(),
|
|
3652
|
+
agent: input.agent ?? null,
|
|
3653
|
+
provider: input.provider.trim(),
|
|
3654
|
+
plan: input.plan.trim(),
|
|
3655
|
+
monthly_fee_usd: input.monthly_fee_usd ?? 0,
|
|
3656
|
+
included_usage_usd: input.included_usage_usd ?? 0,
|
|
3657
|
+
billing_cycle_start: input.billing_cycle_start ?? null,
|
|
3658
|
+
reset_policy: input.reset_policy ?? "monthly",
|
|
3659
|
+
active: input.active === false ? 0 : 1,
|
|
3660
|
+
created_at: now,
|
|
3661
|
+
updated_at: now
|
|
3662
|
+
};
|
|
3663
|
+
if (!subscription.provider)
|
|
3664
|
+
return text("provider is required");
|
|
3665
|
+
if (!subscription.plan)
|
|
3666
|
+
return text("plan is required");
|
|
3667
|
+
upsertSubscription(db, subscription);
|
|
3668
|
+
return text(JSON.stringify(subscription, null, 2));
|
|
3669
|
+
});
|
|
3670
|
+
server.tool("remove_subscription", "Remove a subscription plan by id.", { id: z.string() }, async ({ id }) => {
|
|
3671
|
+
deleteSubscription(db, id);
|
|
3672
|
+
return text(`Removed subscription ${id}`);
|
|
3673
|
+
});
|
|
3611
3674
|
server.tool("estimate_cost", "Pre-flight cost estimate for token counts", { model: z.string(), input_tokens: z.number().optional(), output_tokens: z.number().optional() }, async ({ model, input_tokens, output_tokens }) => {
|
|
3612
3675
|
const cost = computeCostFromDb(db, model, input_tokens ?? 0, output_tokens ?? 0, 0, 0, 0);
|
|
3613
3676
|
return text(`${model}: ${fmtUsd(cost)} (${input_tokens ?? 0} in / ${output_tokens ?? 0} out)`);
|
package/dist/server/index.js
CHANGED
|
@@ -1364,6 +1364,12 @@ function upsertSubscription(db, sub) {
|
|
|
1364
1364
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1365
1365
|
`).run(sub.id, sub.agent, sub.provider, sub.plan, sub.monthly_fee_usd, sub.included_usage_usd, sub.billing_cycle_start, sub.reset_policy, sub.active, sub.created_at, sub.updated_at);
|
|
1366
1366
|
}
|
|
1367
|
+
function listSubscriptions(db) {
|
|
1368
|
+
return db.prepare(`SELECT * FROM subscriptions ORDER BY provider, plan`).all();
|
|
1369
|
+
}
|
|
1370
|
+
function deleteSubscription(db, id) {
|
|
1371
|
+
db.prepare(`DELETE FROM subscriptions WHERE id = ?`).run(id);
|
|
1372
|
+
}
|
|
1367
1373
|
function upsertUsageSnapshot(db, snap) {
|
|
1368
1374
|
const now = snap.updated_at ?? new Date().toISOString();
|
|
1369
1375
|
const id = snap.id ?? `${snap.agent}-${snap.date}-${snap.metric}-${snap.machine_id}`;
|
|
@@ -4112,6 +4118,50 @@ function createHandler(db) {
|
|
|
4112
4118
|
const agent = url.searchParams.get("agent") ?? undefined;
|
|
4113
4119
|
return ok(querySavingsSummary(db, period, agent && isAgent(agent) ? agent : undefined));
|
|
4114
4120
|
}
|
|
4121
|
+
if (path === "/api/subscriptions" && method === "GET") {
|
|
4122
|
+
return ok(listSubscriptions(db));
|
|
4123
|
+
}
|
|
4124
|
+
if (path === "/api/subscriptions" && method === "POST") {
|
|
4125
|
+
const body = await jsonBody(req);
|
|
4126
|
+
if (!body)
|
|
4127
|
+
return err("invalid JSON body");
|
|
4128
|
+
const provider = optionalString(body["provider"])?.trim();
|
|
4129
|
+
const plan = optionalString(body["plan"])?.trim();
|
|
4130
|
+
if (!provider)
|
|
4131
|
+
return err("provider is required");
|
|
4132
|
+
if (!plan)
|
|
4133
|
+
return err("plan is required");
|
|
4134
|
+
const monthlyFee = finiteNumber(body["monthly_fee_usd"] ?? body["fee_usd"] ?? 0);
|
|
4135
|
+
const includedUsage = finiteNumber(body["included_usage_usd"] ?? 0);
|
|
4136
|
+
if (monthlyFee == null || monthlyFee < 0)
|
|
4137
|
+
return err("monthly_fee_usd must be a non-negative number");
|
|
4138
|
+
if (includedUsage == null || includedUsage < 0)
|
|
4139
|
+
return err("included_usage_usd must be a non-negative number");
|
|
4140
|
+
const agent = optionalAgent(body["agent"]);
|
|
4141
|
+
if (agent === undefined)
|
|
4142
|
+
return err(AGENT_ERROR);
|
|
4143
|
+
const now = new Date().toISOString();
|
|
4144
|
+
const subscription = {
|
|
4145
|
+
id: optionalString(body["id"])?.trim() || randomUUID(),
|
|
4146
|
+
agent,
|
|
4147
|
+
provider,
|
|
4148
|
+
plan,
|
|
4149
|
+
monthly_fee_usd: monthlyFee,
|
|
4150
|
+
included_usage_usd: includedUsage,
|
|
4151
|
+
billing_cycle_start: optionalString(body["billing_cycle_start"]),
|
|
4152
|
+
reset_policy: optionalString(body["reset_policy"]) ?? "monthly",
|
|
4153
|
+
active: body["active"] === false || body["active"] === 0 ? 0 : 1,
|
|
4154
|
+
created_at: optionalString(body["created_at"]) ?? now,
|
|
4155
|
+
updated_at: now
|
|
4156
|
+
};
|
|
4157
|
+
upsertSubscription(db, subscription);
|
|
4158
|
+
return ok(subscription);
|
|
4159
|
+
}
|
|
4160
|
+
const subscriptionMatch = path.match(/^\/api\/subscriptions\/(.+)$/);
|
|
4161
|
+
if (subscriptionMatch && method === "DELETE") {
|
|
4162
|
+
deleteSubscription(db, decodeURIComponent(subscriptionMatch[1]));
|
|
4163
|
+
return ok({ ok: true });
|
|
4164
|
+
}
|
|
4115
4165
|
const sessionRequestsMatch = path.match(/^\/api\/sessions\/([^/]+)\/requests$/);
|
|
4116
4166
|
if (sessionRequestsMatch && method === "GET") {
|
|
4117
4167
|
const sessionId = decodeURIComponent(sessionRequestsMatch[1]);
|
|
@@ -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,cAAc,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,cAAc,CAAA;AAsC7D,UAAU,kBAAkB;IAC1B,EAAE,CAAC,EAAE,QAAQ,CAAA;IACb,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CAChC;AAqED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,EAAE,YAAY,SAAwB,IACzF,KAAK,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CAwB7D;AAQD,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,IACV,KAAK,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CAgW/D;AAED,wBAAgB,WAAW,CAAC,IAAI,SAAO,EAAE,OAAO,GAAE,kBAAuB,GAAG,UAAU,CAAC,OAAO,GAAG,CAAC,KAAK,CAAC,CAcvG"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/economy",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.23",
|
|
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
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|