@hasna/economy 0.2.28 → 0.2.30

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.
@@ -0,0 +1,4 @@
1
+ export declare const MCP_NAME = "economy";
2
+ export declare const DEFAULT_MCP_HTTP_PORT = 8860;
3
+ export declare function buildServer(): any;
4
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,QAAQ,YAAY,CAAA;AACjC,eAAO,MAAM,qBAAqB,OAAO,CAAA;AAEzC,wBAAgB,WAAW,IAAI,GAAG,CAssBjC"}
@@ -971,8 +971,10 @@ function queryModelBreakdown(db) {
971
971
  FROM requests GROUP BY model, agent ORDER BY cost_usd DESC
972
972
  `).all();
973
973
  }
974
- function queryAgentBreakdown(db, period = "all") {
974
+ function queryAgentBreakdown(db, period = "all", machine) {
975
975
  const requestWhere = requestPeriodWhere(period);
976
+ const machineClause = machine ? " AND machine_id = ?" : "";
977
+ const machineParams = machine ? [machine] : [];
976
978
  const groups = new Map;
977
979
  const requestRows = db.prepare(`
978
980
  SELECT agent,
@@ -988,10 +990,10 @@ function queryAgentBreakdown(db, period = "all") {
988
990
  COALESCE(SUM(cost_usd), 0) as cost_usd,
989
991
  MAX(timestamp) as last_active
990
992
  FROM requests
991
- WHERE ${requestWhere}
993
+ WHERE ${requestWhere}${machineClause}
992
994
  GROUP BY agent
993
995
  ORDER BY api_equivalent_usd DESC
994
- `).all();
996
+ `).all(...machineParams);
995
997
  for (const row of requestRows) {
996
998
  groups.set(row.agent, row);
997
999
  }
@@ -1004,10 +1006,10 @@ function queryAgentBreakdown(db, period = "all") {
1004
1006
  COALESCE(SUM(total_cost_usd), 0) as cost_usd,
1005
1007
  MAX(started_at) as last_active
1006
1008
  FROM sessions
1007
- WHERE ${sessionWhere}
1009
+ WHERE ${sessionWhere}${machineClause}
1008
1010
  AND id NOT IN (SELECT DISTINCT session_id FROM requests)
1009
1011
  GROUP BY agent
1010
- `).all();
1012
+ `).all(...machineParams);
1011
1013
  for (const row of sessionOnlyRows) {
1012
1014
  const existing = groups.get(row.agent) ?? {
1013
1015
  agent: row.agent,
@@ -1084,14 +1086,20 @@ function labelForPath(projectPath, projectName) {
1084
1086
  function groupKeyForPath(projectPath, projectName) {
1085
1087
  return labelForPath(projectPath, projectName).trim().toLowerCase();
1086
1088
  }
1087
- function queryProjectBreakdown(db, period = "all") {
1089
+ function queryProjectBreakdown(db, period = "all", machine) {
1088
1090
  const requestWhere = requestPeriodWhere(period);
1089
1091
  const sessionWhere = sessionPeriodWhere(period);
1092
+ const sessionMachineClause = machine ? " AND (machine_id = ? OR id IN (SELECT DISTINCT session_id FROM requests WHERE machine_id = ?))" : "";
1093
+ const requestMachineClause = machine ? " AND machine_id = ?" : "";
1094
+ const sessionMachineParams = machine ? [machine, machine] : [];
1095
+ const requestMachineParams = machine ? [machine] : [];
1096
+ const sessionOnlyMachineClause = machine ? " AND machine_id = ?" : "";
1097
+ const sessionOnlyMachineParams = machine ? [machine] : [];
1090
1098
  const sessions = db.prepare(`
1091
1099
  SELECT id, project_path, project_name, total_cost_usd, started_at
1092
1100
  FROM sessions
1093
- WHERE project_path != '' OR project_name != ''
1094
- `).all();
1101
+ WHERE (project_path != '' OR project_name != '')${sessionMachineClause}
1102
+ `).all(...sessionMachineParams);
1095
1103
  const groups = new Map;
1096
1104
  for (const s of sessions) {
1097
1105
  const label = labelForPath(s.project_path, s.project_name);
@@ -1117,7 +1125,8 @@ function queryProjectBreakdown(db, period = "all") {
1117
1125
  FROM requests
1118
1126
  WHERE session_id IN (${placeholders})
1119
1127
  AND ${requestWhere}
1120
- `).get(...g.sessionIds) : { sessions: 0, requests: 0, cost_usd: 0, total_tokens: 0, last_active: null };
1128
+ ${requestMachineClause}
1129
+ `).get(...g.sessionIds, ...requestMachineParams) : { sessions: 0, requests: 0, cost_usd: 0, total_tokens: 0, last_active: null };
1121
1130
  const sessionOnlyStats = placeholders.length ? db.prepare(`
1122
1131
  SELECT
1123
1132
  COUNT(*) as sessions,
@@ -1128,8 +1137,9 @@ function queryProjectBreakdown(db, period = "all") {
1128
1137
  FROM sessions
1129
1138
  WHERE id IN (${placeholders})
1130
1139
  AND ${sessionWhere}
1140
+ ${sessionOnlyMachineClause}
1131
1141
  AND id NOT IN (SELECT DISTINCT session_id FROM requests)
1132
- `).get(...g.sessionIds) : { sessions: 0, requests: 0, total_tokens: 0, cost_usd: 0, last_active: null };
1142
+ `).get(...g.sessionIds, ...sessionOnlyMachineParams) : { sessions: 0, requests: 0, total_tokens: 0, cost_usd: 0, last_active: null };
1133
1143
  const totalSessions = reqStats.sessions + sessionOnlyStats.sessions;
1134
1144
  if (totalSessions === 0)
1135
1145
  continue;
@@ -1147,107 +1157,167 @@ function queryProjectBreakdown(db, period = "all") {
1147
1157
  result.sort((a, b) => b.cost_usd - a.cost_usd);
1148
1158
  return result;
1149
1159
  }
1150
- function queryAccountBreakdown(db, period = "all") {
1160
+ function normalizeAccountEmail(email) {
1161
+ return (email ?? "").trim().toLowerCase();
1162
+ }
1163
+ function accountIdentityKey(agent, accountKey, accountName, accountEmail) {
1164
+ const identityAgent = (agent || "").trim();
1165
+ const normalizedEmail = normalizeAccountEmail(accountEmail);
1166
+ if (identityAgent && normalizedEmail)
1167
+ return `${identityAgent}:${normalizedEmail}`;
1168
+ if (identityAgent && accountName)
1169
+ return `${identityAgent}:${accountName}`;
1170
+ if (accountKey)
1171
+ return accountKey;
1172
+ return identityAgent ? `${identityAgent}:unknown` : "";
1173
+ }
1174
+ function addAccountBreakdownRow(groups, row, sessionOnly) {
1175
+ const agent = row.agent || row.account_tool;
1176
+ const email = normalizeAccountEmail(row.account_email);
1177
+ const accountName = row.account_name || email || row.account_key;
1178
+ const key = accountIdentityKey(agent, row.account_key, accountName, email);
1179
+ if (!key)
1180
+ return;
1181
+ const group = groups.get(key) ?? {
1182
+ account_key: key,
1183
+ account_tool: agent,
1184
+ account_name: accountName,
1185
+ account_email: email || null,
1186
+ account_source: row.account_source || "unknown",
1187
+ sessionIds: new Set,
1188
+ requests: 0,
1189
+ total_tokens: 0,
1190
+ api_equivalent_usd: 0,
1191
+ metered_api_usd: 0,
1192
+ subscription_included_usd: 0,
1193
+ estimated_usd: 0,
1194
+ unknown_usd: 0,
1195
+ last_active: ""
1196
+ };
1197
+ if (!group.account_email && email)
1198
+ group.account_email = email;
1199
+ if (!group.account_name && accountName)
1200
+ group.account_name = accountName;
1201
+ if ((!group.account_source || group.account_source === "unknown") && row.account_source && row.account_source !== "unknown") {
1202
+ group.account_source = row.account_source;
1203
+ }
1204
+ if (row.session_id)
1205
+ group.sessionIds.add(row.session_id);
1206
+ group.requests += row.requests;
1207
+ group.total_tokens += row.total_tokens;
1208
+ group.api_equivalent_usd += row.cost_usd;
1209
+ if (sessionOnly) {
1210
+ group.estimated_usd += row.cost_usd;
1211
+ } else if (row.cost_basis === "metered_api") {
1212
+ group.metered_api_usd += row.cost_usd;
1213
+ } else if (row.cost_basis === "subscription_included") {
1214
+ group.subscription_included_usd += row.cost_usd;
1215
+ } else if (row.cost_basis === "unknown") {
1216
+ group.unknown_usd += row.cost_usd;
1217
+ } else {
1218
+ group.estimated_usd += row.cost_usd;
1219
+ }
1220
+ if (!group.last_active || row.last_active > group.last_active)
1221
+ group.last_active = row.last_active;
1222
+ groups.set(key, group);
1223
+ }
1224
+ function queryAccountBreakdown(db, period = "all", machine) {
1151
1225
  const requestWhere = requestPeriodWhere(period);
1152
1226
  const sessionWhere = sessionPeriodWhere(period);
1153
- const sessions = db.prepare(`
1154
- SELECT id, account_key, account_tool, account_name, account_email, account_source,
1155
- total_cost_usd, total_tokens, request_count, started_at
1156
- FROM sessions
1157
- WHERE account_key != '' OR account_tool != '' OR account_name != '' OR account_email != ''
1158
- `).all();
1227
+ const requestMachineClause = machine ? " AND r.machine_id = ?" : "";
1228
+ const sessionMachineClause = machine ? " AND s.machine_id = ?" : "";
1229
+ const requestMachineParams = machine ? [machine] : [];
1230
+ const sessionMachineParams = machine ? [machine] : [];
1159
1231
  const groups = new Map;
1160
- for (const session of sessions) {
1161
- const key = session.account_key || `${session.account_tool}:${session.account_name}`;
1162
- if (!key || key === ":")
1163
- continue;
1164
- const group = groups.get(key) ?? {
1165
- sessionIds: [],
1166
- account_tool: session.account_tool,
1167
- account_name: session.account_name,
1168
- account_email: session.account_email || null,
1169
- account_source: session.account_source || "unknown"
1170
- };
1171
- group.sessionIds.push(session.id);
1172
- groups.set(key, group);
1173
- }
1174
- const result = [];
1175
- for (const [key, group] of groups.entries()) {
1176
- const placeholders = group.sessionIds.map(() => "?").join(",");
1177
- const reqStats = placeholders ? db.prepare(`
1178
- SELECT
1179
- COUNT(DISTINCT session_id) as sessions,
1180
- COUNT(*) as requests,
1181
- COALESCE(SUM(cost_usd), 0) as cost_usd,
1182
- COALESCE(SUM(input_tokens + output_tokens + cache_read_tokens + cache_create_tokens), 0) as total_tokens,
1183
- COALESCE(SUM(CASE WHEN cost_basis = 'metered_api' THEN cost_usd ELSE 0 END), 0) as metered_api_usd,
1184
- COALESCE(SUM(CASE WHEN cost_basis = 'subscription_included' THEN cost_usd ELSE 0 END), 0) as subscription_included_usd,
1185
- COALESCE(SUM(CASE WHEN COALESCE(cost_basis, 'estimated') = 'estimated' THEN cost_usd ELSE 0 END), 0) as estimated_usd,
1186
- COALESCE(SUM(CASE WHEN cost_basis = 'unknown' THEN cost_usd ELSE 0 END), 0) as unknown_usd,
1187
- MAX(timestamp) as last_active
1188
- FROM requests
1189
- WHERE session_id IN (${placeholders})
1190
- AND ${requestWhere}
1191
- `).get(...group.sessionIds) : {
1192
- sessions: 0,
1193
- requests: 0,
1194
- cost_usd: 0,
1195
- total_tokens: 0,
1196
- metered_api_usd: 0,
1197
- subscription_included_usd: 0,
1198
- estimated_usd: 0,
1199
- unknown_usd: 0,
1200
- last_active: null
1201
- };
1202
- const sessionOnlyStats = placeholders ? db.prepare(`
1203
- SELECT
1204
- COUNT(*) as sessions,
1205
- COALESCE(SUM(request_count), 0) as requests,
1206
- COALESCE(SUM(total_tokens), 0) as total_tokens,
1207
- COALESCE(SUM(total_cost_usd), 0) as cost_usd,
1208
- MAX(started_at) as last_active
1209
- FROM sessions
1210
- WHERE id IN (${placeholders})
1211
- AND ${sessionWhere}
1212
- AND id NOT IN (SELECT DISTINCT session_id FROM requests)
1213
- `).get(...group.sessionIds) : { sessions: 0, requests: 0, total_tokens: 0, cost_usd: 0, last_active: null };
1214
- const sessionsTotal = reqStats.sessions + sessionOnlyStats.sessions;
1215
- if (sessionsTotal === 0)
1216
- continue;
1217
- const apiEquivalentUsd = reqStats.cost_usd + sessionOnlyStats.cost_usd;
1218
- const estimatedUsd = reqStats.estimated_usd + sessionOnlyStats.cost_usd;
1219
- const billableUsd = reqStats.metered_api_usd;
1220
- const lastActive = [reqStats.last_active, sessionOnlyStats.last_active].filter(Boolean).sort().at(-1) ?? "";
1221
- result.push({
1222
- account_key: key,
1223
- account_tool: group.account_tool,
1224
- account_name: group.account_name,
1225
- account_email: group.account_email,
1226
- account_source: group.account_source,
1227
- sessions: sessionsTotal,
1228
- requests: reqStats.requests + sessionOnlyStats.requests,
1229
- total_tokens: reqStats.total_tokens + sessionOnlyStats.total_tokens,
1230
- api_equivalent_usd: apiEquivalentUsd,
1231
- billable_usd: billableUsd,
1232
- metered_api_usd: reqStats.metered_api_usd,
1233
- subscription_included_usd: reqStats.subscription_included_usd,
1234
- estimated_usd: estimatedUsd,
1235
- unknown_usd: reqStats.unknown_usd,
1236
- cost_usd: apiEquivalentUsd,
1237
- last_active: lastActive
1238
- });
1239
- }
1232
+ const requestRows = db.prepare(`
1233
+ SELECT
1234
+ r.session_id as session_id,
1235
+ COALESCE(NULLIF(r.agent, ''), NULLIF(s.agent, ''), '') as agent,
1236
+ COALESCE(NULLIF(r.account_key, ''), NULLIF(s.account_key, ''), '') as account_key,
1237
+ COALESCE(NULLIF(r.account_tool, ''), NULLIF(s.account_tool, ''), '') as account_tool,
1238
+ COALESCE(NULLIF(r.account_name, ''), NULLIF(s.account_name, ''), '') as account_name,
1239
+ COALESCE(NULLIF(r.account_email, ''), NULLIF(s.account_email, ''), '') as account_email,
1240
+ COALESCE(NULLIF(r.account_source, ''), NULLIF(s.account_source, ''), 'unknown') as account_source,
1241
+ 1 as requests,
1242
+ COALESCE(r.input_tokens + r.output_tokens + r.cache_read_tokens + r.cache_create_tokens, 0) as total_tokens,
1243
+ COALESCE(r.cost_usd, 0) as cost_usd,
1244
+ COALESCE(NULLIF(r.cost_basis, ''), 'estimated') as cost_basis,
1245
+ r.timestamp as last_active
1246
+ FROM requests r
1247
+ LEFT JOIN sessions s ON s.id = r.session_id
1248
+ WHERE ${requestWhere}${requestMachineClause}
1249
+ AND (
1250
+ COALESCE(NULLIF(r.account_key, ''), NULLIF(s.account_key, ''), '') != ''
1251
+ OR COALESCE(NULLIF(r.account_tool, ''), NULLIF(s.account_tool, ''), '') != ''
1252
+ OR COALESCE(NULLIF(r.account_name, ''), NULLIF(s.account_name, ''), '') != ''
1253
+ OR COALESCE(NULLIF(r.account_email, ''), NULLIF(s.account_email, ''), '') != ''
1254
+ )
1255
+ `).all(...requestMachineParams);
1256
+ for (const row of requestRows)
1257
+ addAccountBreakdownRow(groups, row, false);
1258
+ const sessionOnlyRows = db.prepare(`
1259
+ SELECT
1260
+ s.id as session_id,
1261
+ s.agent as agent,
1262
+ s.account_key as account_key,
1263
+ s.account_tool as account_tool,
1264
+ s.account_name as account_name,
1265
+ s.account_email as account_email,
1266
+ COALESCE(NULLIF(s.account_source, ''), 'unknown') as account_source,
1267
+ COALESCE(s.request_count, 0) as requests,
1268
+ COALESCE(s.total_tokens, 0) as total_tokens,
1269
+ COALESCE(s.total_cost_usd, 0) as cost_usd,
1270
+ 'estimated' as cost_basis,
1271
+ s.started_at as last_active
1272
+ FROM sessions s
1273
+ WHERE ${sessionWhere}${sessionMachineClause}
1274
+ AND s.id NOT IN (SELECT DISTINCT session_id FROM requests)
1275
+ AND (s.account_key != '' OR s.account_tool != '' OR s.account_name != '' OR s.account_email != '')
1276
+ `).all(...sessionMachineParams);
1277
+ for (const row of sessionOnlyRows)
1278
+ addAccountBreakdownRow(groups, row, true);
1279
+ const result = [...groups.values()].map((group) => ({
1280
+ account_key: group.account_key,
1281
+ account_tool: group.account_tool,
1282
+ account_name: group.account_name,
1283
+ account_email: group.account_email,
1284
+ account_source: group.account_source,
1285
+ sessions: group.sessionIds.size,
1286
+ requests: group.requests,
1287
+ total_tokens: group.total_tokens,
1288
+ api_equivalent_usd: group.api_equivalent_usd,
1289
+ billable_usd: group.metered_api_usd,
1290
+ metered_api_usd: group.metered_api_usd,
1291
+ subscription_included_usd: group.subscription_included_usd,
1292
+ estimated_usd: group.estimated_usd,
1293
+ unknown_usd: group.unknown_usd,
1294
+ cost_usd: group.api_equivalent_usd,
1295
+ last_active: group.last_active
1296
+ }));
1240
1297
  result.sort((a, b) => b.cost_usd - a.cost_usd);
1241
1298
  return result;
1242
1299
  }
1243
- function queryDailyBreakdown(db, days = 30) {
1300
+ function queryDailyBreakdown(db, days = 30, machine) {
1301
+ const machineClause = machine ? " AND machine_id = ?" : "";
1302
+ const params = machine ? [`-${days}`, machine] : [`-${days}`];
1244
1303
  return db.prepare(`
1245
1304
  SELECT DATE(timestamp) as date, agent, COALESCE(SUM(cost_usd), 0) as cost_usd
1246
1305
  FROM requests
1247
- WHERE timestamp >= DATE('now', ? || ' days')
1306
+ WHERE timestamp >= DATE('now', ? || ' days')${machineClause}
1248
1307
  GROUP BY DATE(timestamp), agent
1249
1308
  ORDER BY date ASC
1250
- `).all(`-${days}`);
1309
+ `).all(...params);
1310
+ }
1311
+ function queryHourlyBreakdown(db, machine) {
1312
+ const machineClause = machine ? " AND machine_id = ?" : "";
1313
+ const params = machine ? [machine] : [];
1314
+ return db.prepare(`
1315
+ SELECT STRFTIME('%H', timestamp) as hour, agent, COALESCE(SUM(cost_usd), 0) as cost_usd
1316
+ FROM requests
1317
+ WHERE DATE(timestamp) = DATE('now')${machineClause}
1318
+ GROUP BY STRFTIME('%H', timestamp), agent
1319
+ ORDER BY hour ASC
1320
+ `).all(...params);
1251
1321
  }
1252
1322
  function upsertProject(db, project) {
1253
1323
  db.prepare(`
@@ -2283,18 +2353,22 @@ var AGENT_ACCOUNT_TOOLS = {
2283
2353
  pi: ["pi"],
2284
2354
  hermes: ["hermes"]
2285
2355
  };
2286
- function accountKey(tool, name) {
2287
- return `${tool}:${name}`;
2356
+ function normalizeEmail(email) {
2357
+ return (email ?? "").trim().toLowerCase();
2358
+ }
2359
+ function accountKey(tool, name, email) {
2360
+ const normalizedEmail = normalizeEmail(email);
2361
+ return `${tool}:${normalizedEmail || name}`;
2288
2362
  }
2289
2363
  function normalizeDir(value) {
2290
2364
  return value.replace(/\/+$/, "");
2291
2365
  }
2292
2366
  function fromProfile(profile, source) {
2293
2367
  return {
2294
- account_key: accountKey(profile.tool, profile.name),
2368
+ account_key: accountKey(profile.tool, profile.name, profile.email),
2295
2369
  account_tool: profile.tool,
2296
2370
  account_name: profile.name,
2297
- ...profile.email ? { account_email: profile.email } : {},
2371
+ ...profile.email ? { account_email: normalizeEmail(profile.email) } : {},
2298
2372
  account_source: source
2299
2373
  };
2300
2374
  }
@@ -2306,10 +2380,12 @@ function fromOverride(raw, agent) {
2306
2380
  const [tool, name] = value.includes(":") ? value.split(":", 2) : [candidateTool, value];
2307
2381
  if (!tool || !name)
2308
2382
  return null;
2383
+ const email = name.includes("@") ? normalizeEmail(name) : undefined;
2309
2384
  return {
2310
- account_key: accountKey(tool, name),
2385
+ account_key: accountKey(tool, name, email),
2311
2386
  account_tool: tool,
2312
2387
  account_name: name,
2388
+ ...email ? { account_email: email } : {},
2313
2389
  account_source: "override"
2314
2390
  };
2315
2391
  }
@@ -2322,11 +2398,12 @@ function envOverride(agent, env) {
2322
2398
  const name = env[`ECONOMY_${agentPrefix}_ACCOUNT_NAME`] ?? env["ECONOMY_ACCOUNT_NAME"];
2323
2399
  if (!tool || !name)
2324
2400
  return null;
2401
+ const email = normalizeEmail(env[`ECONOMY_${agentPrefix}_ACCOUNT_EMAIL`] ?? env["ECONOMY_ACCOUNT_EMAIL"]);
2325
2402
  return {
2326
- account_key: accountKey(tool, name),
2403
+ account_key: accountKey(tool, name, email),
2327
2404
  account_tool: tool,
2328
2405
  account_name: name,
2329
- account_email: env[`ECONOMY_${agentPrefix}_ACCOUNT_EMAIL`] ?? env["ECONOMY_ACCOUNT_EMAIL"],
2406
+ ...email ? { account_email: email } : {},
2330
2407
  account_source: "override"
2331
2408
  };
2332
2409
  }
@@ -4085,8 +4162,9 @@ function createHandler(db) {
4085
4162
  }
4086
4163
  if (path === "/api/fleet" && method === "GET") {
4087
4164
  const period = url.searchParams.get("period") ?? "month";
4165
+ const machine = url.searchParams.get("machine") ?? undefined;
4088
4166
  return ok({
4089
- summary: querySummary(db, period, undefined, true),
4167
+ summary: querySummary(db, period, machine),
4090
4168
  machines: listMachines(db, period),
4091
4169
  registry: listMachineRegistry(db),
4092
4170
  current_machine: getMachineId()
@@ -4094,7 +4172,12 @@ function createHandler(db) {
4094
4172
  }
4095
4173
  if (path === "/api/daily" && method === "GET") {
4096
4174
  const days = Number(url.searchParams.get("days") ?? 30);
4097
- return ok(queryDailyBreakdown(db, days));
4175
+ const machine = url.searchParams.get("machine") ?? undefined;
4176
+ return ok(queryDailyBreakdown(db, days, machine));
4177
+ }
4178
+ if (path === "/api/hourly" && method === "GET") {
4179
+ const machine = url.searchParams.get("machine") ?? undefined;
4180
+ return ok(queryHourlyBreakdown(db, machine));
4098
4181
  }
4099
4182
  if (path === "/api/sessions" && method === "GET") {
4100
4183
  const agent = url.searchParams.get("agent");
@@ -4164,21 +4247,24 @@ function createHandler(db) {
4164
4247
  }
4165
4248
  if (path === "/api/projects" && method === "GET") {
4166
4249
  const period = url.searchParams.get("period") ?? "all";
4167
- return ok(queryProjectBreakdown(db, period));
4250
+ const machine = url.searchParams.get("machine") ?? undefined;
4251
+ return ok(queryProjectBreakdown(db, period, machine));
4168
4252
  }
4169
4253
  if (path === "/api/accounts" && method === "GET") {
4170
4254
  const period = url.searchParams.get("period") ?? "all";
4171
- return ok(queryAccountBreakdown(db, period));
4255
+ const machine = url.searchParams.get("machine") ?? undefined;
4256
+ return ok(queryAccountBreakdown(db, period, machine));
4172
4257
  }
4173
4258
  if (path === "/api/breakdown" && method === "GET") {
4174
4259
  const by = url.searchParams.get("by") ?? "model";
4175
4260
  const period = url.searchParams.get("period") ?? "all";
4261
+ const machine = url.searchParams.get("machine") ?? undefined;
4176
4262
  if (by === "project")
4177
- return ok(queryProjectBreakdown(db, period));
4263
+ return ok(queryProjectBreakdown(db, period, machine));
4178
4264
  if (by === "agent")
4179
- return ok(queryAgentBreakdown(db, period));
4265
+ return ok(queryAgentBreakdown(db, period, machine));
4180
4266
  if (by === "account")
4181
- return ok(queryAccountBreakdown(db, period));
4267
+ return ok(queryAccountBreakdown(db, period, machine));
4182
4268
  return ok(queryModelBreakdown(db));
4183
4269
  }
4184
4270
  if (path === "/api/budgets" && method === "GET") {
@@ -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;AAuC7D,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,CAkW/D;AAED,wBAAgB,WAAW,CAAC,IAAI,SAAO,EAAE,OAAO,GAAE,kBAAuB,GAAG,UAAU,CAAC,OAAO,GAAG,CAAC,KAAK,CAAC,CAcvG"}
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;AAuC7D,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,CA4W/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.28",
3
+ "version": "0.2.30",
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",