@hasna/economy 0.2.27 → 0.2.29

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.
@@ -11,14 +11,19 @@ export declare function querySessions(db: Database, filter?: SessionFilter): Eco
11
11
  export declare function queryTopSessions(db: Database, n?: number, agent?: string): EconomySession[];
12
12
  export declare function querySummary(db: Database, period: Period, machine?: string, allMachines?: boolean): CostSummary;
13
13
  export declare function queryModelBreakdown(db: Database): ModelBreakdown[];
14
- export declare function queryAgentBreakdown(db: Database, period?: Period): AgentBreakdown[];
15
- export declare function queryProjectBreakdown(db: Database, period?: Period): ProjectBreakdown[];
16
- export declare function queryAccountBreakdown(db: Database, period?: Period): AccountBreakdown[];
17
- export declare function queryDailyBreakdown(db: Database, days?: number): Array<{
14
+ export declare function queryAgentBreakdown(db: Database, period?: Period, machine?: string): AgentBreakdown[];
15
+ export declare function queryProjectBreakdown(db: Database, period?: Period, machine?: string): ProjectBreakdown[];
16
+ export declare function queryAccountBreakdown(db: Database, period?: Period, machine?: string): AccountBreakdown[];
17
+ export declare function queryDailyBreakdown(db: Database, days?: number, machine?: string): Array<{
18
18
  date: string;
19
19
  cost_usd: number;
20
20
  agent: string;
21
21
  }>;
22
+ export declare function queryHourlyBreakdown(db: Database, machine?: string): Array<{
23
+ hour: string;
24
+ cost_usd: number;
25
+ agent: string;
26
+ }>;
22
27
  export declare function upsertProject(db: Database, project: EconomyProject): void;
23
28
  export declare function getProject(db: Database, path: string): EconomyProject | null;
24
29
  export declare function listProjects(db: Database): EconomyProject[];
@@ -70,7 +75,7 @@ export interface MachineInfo {
70
75
  total_cost_usd: number;
71
76
  last_active: string;
72
77
  }
73
- export declare function listMachines(db: Database): MachineInfo[];
78
+ export declare function listMachines(db: Database, period?: Period): MachineInfo[];
74
79
  export interface DbModelPricing {
75
80
  model: string;
76
81
  input_per_1m: number;
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -565,6 +565,26 @@ function openDatabase(dbPath, skipSeed = false) {
565
565
  }
566
566
  return db;
567
567
  }
568
+ function quoteSqlIdent(identifier) {
569
+ return `"${identifier.replace(/"/g, '""')}"`;
570
+ }
571
+ function hasColumn(db, table, column) {
572
+ const columns = db.prepare(`PRAGMA table_info(${quoteSqlIdent(table)})`).all();
573
+ return columns.some((c) => c.name === column);
574
+ }
575
+ function addColumnIfMissing(db, table, column, definition) {
576
+ if (hasColumn(db, table, column))
577
+ return false;
578
+ try {
579
+ db.exec(`ALTER TABLE ${quoteSqlIdent(table)} ADD COLUMN ${quoteSqlIdent(column)} ${definition}`);
580
+ return true;
581
+ } catch (error) {
582
+ const message = error instanceof Error ? error.message : String(error);
583
+ if (/duplicate column name/i.test(message))
584
+ return true;
585
+ throw error;
586
+ }
587
+ }
568
588
  function initSchema(db) {
569
589
  db.exec(`
570
590
  CREATE TABLE IF NOT EXISTS requests (
@@ -735,59 +755,31 @@ function initSchema(db) {
735
755
  CREATE INDEX IF NOT EXISTS idx_usage_agent_date ON usage_snapshots(agent, date);
736
756
  CREATE INDEX IF NOT EXISTS idx_savings_date ON savings_daily(date);
737
757
  `);
738
- const cols = db.prepare(`PRAGMA table_info(requests)`).all();
739
- if (!cols.some((c) => c.name === "machine_id")) {
740
- db.exec(`ALTER TABLE requests ADD COLUMN machine_id TEXT DEFAULT ''`);
741
- db.exec(`ALTER TABLE sessions ADD COLUMN machine_id TEXT DEFAULT ''`);
742
- }
743
- if (!cols.some((c) => c.name === "cache_create_5m_tokens")) {
744
- db.exec(`ALTER TABLE requests ADD COLUMN cache_create_5m_tokens INTEGER DEFAULT 0`);
758
+ addColumnIfMissing(db, "requests", "machine_id", `TEXT DEFAULT ''`);
759
+ addColumnIfMissing(db, "sessions", "machine_id", `TEXT DEFAULT ''`);
760
+ if (addColumnIfMissing(db, "requests", "cache_create_5m_tokens", "INTEGER DEFAULT 0")) {
745
761
  db.exec(`UPDATE requests SET cache_create_5m_tokens = cache_create_tokens WHERE cache_create_5m_tokens = 0`);
746
762
  }
747
- if (!cols.some((c) => c.name === "cache_create_1h_tokens")) {
748
- db.exec(`ALTER TABLE requests ADD COLUMN cache_create_1h_tokens INTEGER DEFAULT 0`);
749
- }
750
- if (!cols.some((c) => c.name === "cost_basis")) {
751
- db.exec(`ALTER TABLE requests ADD COLUMN cost_basis TEXT DEFAULT 'estimated'`);
752
- }
753
- if (!cols.some((c) => c.name === "attribution_tag")) {
754
- db.exec(`ALTER TABLE requests ADD COLUMN attribution_tag TEXT DEFAULT ''`);
755
- }
756
- if (!cols.some((c) => c.name === "updated_at")) {
757
- db.exec(`ALTER TABLE requests ADD COLUMN updated_at TEXT DEFAULT ''`);
763
+ addColumnIfMissing(db, "requests", "cache_create_1h_tokens", "INTEGER DEFAULT 0");
764
+ addColumnIfMissing(db, "requests", "cost_basis", `TEXT DEFAULT 'estimated'`);
765
+ addColumnIfMissing(db, "requests", "attribution_tag", `TEXT DEFAULT ''`);
766
+ if (addColumnIfMissing(db, "requests", "updated_at", `TEXT DEFAULT ''`)) {
758
767
  db.exec(`UPDATE requests SET updated_at = timestamp WHERE updated_at = '' OR updated_at IS NULL`);
759
768
  }
760
- if (!cols.some((c) => c.name === "synced_at")) {
761
- db.exec(`ALTER TABLE requests ADD COLUMN synced_at TEXT DEFAULT ''`);
762
- }
769
+ addColumnIfMissing(db, "requests", "synced_at", `TEXT DEFAULT ''`);
763
770
  for (const column of ["account_key", "account_tool", "account_name", "account_email", "account_source"]) {
764
- if (!cols.some((c) => c.name === column)) {
765
- db.exec(`ALTER TABLE requests ADD COLUMN ${column} TEXT DEFAULT ''`);
766
- }
771
+ addColumnIfMissing(db, "requests", column, `TEXT DEFAULT ''`);
767
772
  }
768
- const sessionCols = db.prepare(`PRAGMA table_info(sessions)`).all();
769
- if (!sessionCols.some((c) => c.name === "attribution_tag")) {
770
- db.exec(`ALTER TABLE sessions ADD COLUMN attribution_tag TEXT DEFAULT ''`);
771
- }
772
- if (!sessionCols.some((c) => c.name === "updated_at")) {
773
- db.exec(`ALTER TABLE sessions ADD COLUMN updated_at TEXT DEFAULT ''`);
773
+ addColumnIfMissing(db, "sessions", "attribution_tag", `TEXT DEFAULT ''`);
774
+ if (addColumnIfMissing(db, "sessions", "updated_at", `TEXT DEFAULT ''`)) {
774
775
  db.exec(`UPDATE sessions SET updated_at = started_at WHERE updated_at = '' OR updated_at IS NULL`);
775
776
  }
776
- if (!sessionCols.some((c) => c.name === "synced_at")) {
777
- db.exec(`ALTER TABLE sessions ADD COLUMN synced_at TEXT DEFAULT ''`);
778
- }
777
+ addColumnIfMissing(db, "sessions", "synced_at", `TEXT DEFAULT ''`);
779
778
  for (const column of ["account_key", "account_tool", "account_name", "account_email", "account_source"]) {
780
- if (!sessionCols.some((c) => c.name === column)) {
781
- db.exec(`ALTER TABLE sessions ADD COLUMN ${column} TEXT DEFAULT ''`);
782
- }
783
- }
784
- const pricingCols = db.prepare(`PRAGMA table_info(model_pricing)`).all();
785
- if (!pricingCols.some((c) => c.name === "cache_write_1h_per_1m")) {
786
- db.exec(`ALTER TABLE model_pricing ADD COLUMN cache_write_1h_per_1m REAL NOT NULL DEFAULT 0`);
787
- }
788
- if (!pricingCols.some((c) => c.name === "cache_storage_per_1m_hour")) {
789
- db.exec(`ALTER TABLE model_pricing ADD COLUMN cache_storage_per_1m_hour REAL NOT NULL DEFAULT 0`);
779
+ addColumnIfMissing(db, "sessions", column, `TEXT DEFAULT ''`);
790
780
  }
781
+ addColumnIfMissing(db, "model_pricing", "cache_write_1h_per_1m", "REAL NOT NULL DEFAULT 0");
782
+ addColumnIfMissing(db, "model_pricing", "cache_storage_per_1m_hour", "REAL NOT NULL DEFAULT 0");
791
783
  db.exec(`
792
784
  CREATE INDEX IF NOT EXISTS idx_requests_machine ON requests(machine_id);
793
785
  CREATE INDEX IF NOT EXISTS idx_sessions_machine ON sessions(machine_id);
@@ -948,17 +940,22 @@ function querySummary(db, period, machine, allMachines = false) {
948
940
  const codexTotals = db.prepare(`
949
941
  SELECT COALESCE(SUM(total_cost_usd), 0) as cost_usd,
950
942
  COALESCE(SUM(total_tokens), 0) as tokens,
943
+ COALESCE(SUM(request_count), 0) as requests,
951
944
  COUNT(*) as sessions
952
945
  FROM sessions
953
946
  WHERE ${sWhere}${machineClause}
954
947
  AND id NOT IN (SELECT DISTINCT session_id FROM requests)
955
948
  `).get();
956
- const sessionCount = db.prepare(`SELECT COUNT(*) as sessions FROM sessions WHERE ${sWhere}${machineClause}`).get();
949
+ const requestSessionCount = db.prepare(`
950
+ SELECT COUNT(DISTINCT session_id) as sessions
951
+ FROM requests
952
+ WHERE ${rWhere}${machineClause}
953
+ `).get();
957
954
  return {
958
955
  total_usd: r.total_usd + codexTotals.cost_usd,
959
- requests: r.requests,
956
+ requests: r.requests + codexTotals.requests,
960
957
  tokens: r.tokens + codexTotals.tokens,
961
- sessions: sessionCount.sessions,
958
+ sessions: requestSessionCount.sessions + codexTotals.sessions,
962
959
  period
963
960
  };
964
961
  }
@@ -973,8 +970,10 @@ function queryModelBreakdown(db) {
973
970
  FROM requests GROUP BY model, agent ORDER BY cost_usd DESC
974
971
  `).all();
975
972
  }
976
- function queryAgentBreakdown(db, period = "all") {
973
+ function queryAgentBreakdown(db, period = "all", machine) {
977
974
  const requestWhere = requestPeriodWhere(period);
975
+ const machineClause = machine ? " AND machine_id = ?" : "";
976
+ const machineParams = machine ? [machine] : [];
978
977
  const groups = new Map;
979
978
  const requestRows = db.prepare(`
980
979
  SELECT agent,
@@ -990,10 +989,10 @@ function queryAgentBreakdown(db, period = "all") {
990
989
  COALESCE(SUM(cost_usd), 0) as cost_usd,
991
990
  MAX(timestamp) as last_active
992
991
  FROM requests
993
- WHERE ${requestWhere}
992
+ WHERE ${requestWhere}${machineClause}
994
993
  GROUP BY agent
995
994
  ORDER BY api_equivalent_usd DESC
996
- `).all();
995
+ `).all(...machineParams);
997
996
  for (const row of requestRows) {
998
997
  groups.set(row.agent, row);
999
998
  }
@@ -1006,10 +1005,10 @@ function queryAgentBreakdown(db, period = "all") {
1006
1005
  COALESCE(SUM(total_cost_usd), 0) as cost_usd,
1007
1006
  MAX(started_at) as last_active
1008
1007
  FROM sessions
1009
- WHERE ${sessionWhere}
1008
+ WHERE ${sessionWhere}${machineClause}
1010
1009
  AND id NOT IN (SELECT DISTINCT session_id FROM requests)
1011
1010
  GROUP BY agent
1012
- `).all();
1011
+ `).all(...machineParams);
1013
1012
  for (const row of sessionOnlyRows) {
1014
1013
  const existing = groups.get(row.agent) ?? {
1015
1014
  agent: row.agent,
@@ -1086,14 +1085,20 @@ function labelForPath(projectPath, projectName) {
1086
1085
  function groupKeyForPath(projectPath, projectName) {
1087
1086
  return labelForPath(projectPath, projectName).trim().toLowerCase();
1088
1087
  }
1089
- function queryProjectBreakdown(db, period = "all") {
1088
+ function queryProjectBreakdown(db, period = "all", machine) {
1090
1089
  const requestWhere = requestPeriodWhere(period);
1091
1090
  const sessionWhere = sessionPeriodWhere(period);
1091
+ const sessionMachineClause = machine ? " AND (machine_id = ? OR id IN (SELECT DISTINCT session_id FROM requests WHERE machine_id = ?))" : "";
1092
+ const requestMachineClause = machine ? " AND machine_id = ?" : "";
1093
+ const sessionMachineParams = machine ? [machine, machine] : [];
1094
+ const requestMachineParams = machine ? [machine] : [];
1095
+ const sessionOnlyMachineClause = machine ? " AND machine_id = ?" : "";
1096
+ const sessionOnlyMachineParams = machine ? [machine] : [];
1092
1097
  const sessions = db.prepare(`
1093
1098
  SELECT id, project_path, project_name, total_cost_usd, started_at
1094
1099
  FROM sessions
1095
- WHERE project_path != '' OR project_name != ''
1096
- `).all();
1100
+ WHERE (project_path != '' OR project_name != '')${sessionMachineClause}
1101
+ `).all(...sessionMachineParams);
1097
1102
  const groups = new Map;
1098
1103
  for (const s of sessions) {
1099
1104
  const label = labelForPath(s.project_path, s.project_name);
@@ -1119,7 +1124,8 @@ function queryProjectBreakdown(db, period = "all") {
1119
1124
  FROM requests
1120
1125
  WHERE session_id IN (${placeholders})
1121
1126
  AND ${requestWhere}
1122
- `).get(...g.sessionIds) : { sessions: 0, requests: 0, cost_usd: 0, total_tokens: 0, last_active: null };
1127
+ ${requestMachineClause}
1128
+ `).get(...g.sessionIds, ...requestMachineParams) : { sessions: 0, requests: 0, cost_usd: 0, total_tokens: 0, last_active: null };
1123
1129
  const sessionOnlyStats = placeholders.length ? db.prepare(`
1124
1130
  SELECT
1125
1131
  COUNT(*) as sessions,
@@ -1130,8 +1136,9 @@ function queryProjectBreakdown(db, period = "all") {
1130
1136
  FROM sessions
1131
1137
  WHERE id IN (${placeholders})
1132
1138
  AND ${sessionWhere}
1139
+ ${sessionOnlyMachineClause}
1133
1140
  AND id NOT IN (SELECT DISTINCT session_id FROM requests)
1134
- `).get(...g.sessionIds) : { sessions: 0, requests: 0, total_tokens: 0, cost_usd: 0, last_active: null };
1141
+ `).get(...g.sessionIds, ...sessionOnlyMachineParams) : { sessions: 0, requests: 0, total_tokens: 0, cost_usd: 0, last_active: null };
1135
1142
  const totalSessions = reqStats.sessions + sessionOnlyStats.sessions;
1136
1143
  if (totalSessions === 0)
1137
1144
  continue;
@@ -1149,107 +1156,167 @@ function queryProjectBreakdown(db, period = "all") {
1149
1156
  result.sort((a, b) => b.cost_usd - a.cost_usd);
1150
1157
  return result;
1151
1158
  }
1152
- function queryAccountBreakdown(db, period = "all") {
1159
+ function normalizeAccountEmail(email) {
1160
+ return (email ?? "").trim().toLowerCase();
1161
+ }
1162
+ function accountIdentityKey(agent, accountKey, accountName, accountEmail) {
1163
+ const identityAgent = (agent || "").trim();
1164
+ const normalizedEmail = normalizeAccountEmail(accountEmail);
1165
+ if (identityAgent && normalizedEmail)
1166
+ return `${identityAgent}:${normalizedEmail}`;
1167
+ if (identityAgent && accountName)
1168
+ return `${identityAgent}:${accountName}`;
1169
+ if (accountKey)
1170
+ return accountKey;
1171
+ return identityAgent ? `${identityAgent}:unknown` : "";
1172
+ }
1173
+ function addAccountBreakdownRow(groups, row, sessionOnly) {
1174
+ const agent = row.agent || row.account_tool;
1175
+ const email = normalizeAccountEmail(row.account_email);
1176
+ const accountName = row.account_name || email || row.account_key;
1177
+ const key = accountIdentityKey(agent, row.account_key, accountName, email);
1178
+ if (!key)
1179
+ return;
1180
+ const group = groups.get(key) ?? {
1181
+ account_key: key,
1182
+ account_tool: agent,
1183
+ account_name: accountName,
1184
+ account_email: email || null,
1185
+ account_source: row.account_source || "unknown",
1186
+ sessionIds: new Set,
1187
+ requests: 0,
1188
+ total_tokens: 0,
1189
+ api_equivalent_usd: 0,
1190
+ metered_api_usd: 0,
1191
+ subscription_included_usd: 0,
1192
+ estimated_usd: 0,
1193
+ unknown_usd: 0,
1194
+ last_active: ""
1195
+ };
1196
+ if (!group.account_email && email)
1197
+ group.account_email = email;
1198
+ if (!group.account_name && accountName)
1199
+ group.account_name = accountName;
1200
+ if ((!group.account_source || group.account_source === "unknown") && row.account_source && row.account_source !== "unknown") {
1201
+ group.account_source = row.account_source;
1202
+ }
1203
+ if (row.session_id)
1204
+ group.sessionIds.add(row.session_id);
1205
+ group.requests += row.requests;
1206
+ group.total_tokens += row.total_tokens;
1207
+ group.api_equivalent_usd += row.cost_usd;
1208
+ if (sessionOnly) {
1209
+ group.estimated_usd += row.cost_usd;
1210
+ } else if (row.cost_basis === "metered_api") {
1211
+ group.metered_api_usd += row.cost_usd;
1212
+ } else if (row.cost_basis === "subscription_included") {
1213
+ group.subscription_included_usd += row.cost_usd;
1214
+ } else if (row.cost_basis === "unknown") {
1215
+ group.unknown_usd += row.cost_usd;
1216
+ } else {
1217
+ group.estimated_usd += row.cost_usd;
1218
+ }
1219
+ if (!group.last_active || row.last_active > group.last_active)
1220
+ group.last_active = row.last_active;
1221
+ groups.set(key, group);
1222
+ }
1223
+ function queryAccountBreakdown(db, period = "all", machine) {
1153
1224
  const requestWhere = requestPeriodWhere(period);
1154
1225
  const sessionWhere = sessionPeriodWhere(period);
1155
- const sessions = db.prepare(`
1156
- SELECT id, account_key, account_tool, account_name, account_email, account_source,
1157
- total_cost_usd, total_tokens, request_count, started_at
1158
- FROM sessions
1159
- WHERE account_key != '' OR account_tool != '' OR account_name != '' OR account_email != ''
1160
- `).all();
1226
+ const requestMachineClause = machine ? " AND r.machine_id = ?" : "";
1227
+ const sessionMachineClause = machine ? " AND s.machine_id = ?" : "";
1228
+ const requestMachineParams = machine ? [machine] : [];
1229
+ const sessionMachineParams = machine ? [machine] : [];
1161
1230
  const groups = new Map;
1162
- for (const session of sessions) {
1163
- const key = session.account_key || `${session.account_tool}:${session.account_name}`;
1164
- if (!key || key === ":")
1165
- continue;
1166
- const group = groups.get(key) ?? {
1167
- sessionIds: [],
1168
- account_tool: session.account_tool,
1169
- account_name: session.account_name,
1170
- account_email: session.account_email || null,
1171
- account_source: session.account_source || "unknown"
1172
- };
1173
- group.sessionIds.push(session.id);
1174
- groups.set(key, group);
1175
- }
1176
- const result = [];
1177
- for (const [key, group] of groups.entries()) {
1178
- const placeholders = group.sessionIds.map(() => "?").join(",");
1179
- const reqStats = placeholders ? db.prepare(`
1180
- SELECT
1181
- COUNT(DISTINCT session_id) as sessions,
1182
- COUNT(*) as requests,
1183
- COALESCE(SUM(cost_usd), 0) as cost_usd,
1184
- COALESCE(SUM(input_tokens + output_tokens + cache_read_tokens + cache_create_tokens), 0) as total_tokens,
1185
- COALESCE(SUM(CASE WHEN cost_basis = 'metered_api' THEN cost_usd ELSE 0 END), 0) as metered_api_usd,
1186
- COALESCE(SUM(CASE WHEN cost_basis = 'subscription_included' THEN cost_usd ELSE 0 END), 0) as subscription_included_usd,
1187
- COALESCE(SUM(CASE WHEN COALESCE(cost_basis, 'estimated') = 'estimated' THEN cost_usd ELSE 0 END), 0) as estimated_usd,
1188
- COALESCE(SUM(CASE WHEN cost_basis = 'unknown' THEN cost_usd ELSE 0 END), 0) as unknown_usd,
1189
- MAX(timestamp) as last_active
1190
- FROM requests
1191
- WHERE session_id IN (${placeholders})
1192
- AND ${requestWhere}
1193
- `).get(...group.sessionIds) : {
1194
- sessions: 0,
1195
- requests: 0,
1196
- cost_usd: 0,
1197
- total_tokens: 0,
1198
- metered_api_usd: 0,
1199
- subscription_included_usd: 0,
1200
- estimated_usd: 0,
1201
- unknown_usd: 0,
1202
- last_active: null
1203
- };
1204
- const sessionOnlyStats = placeholders ? db.prepare(`
1205
- SELECT
1206
- COUNT(*) as sessions,
1207
- COALESCE(SUM(request_count), 0) as requests,
1208
- COALESCE(SUM(total_tokens), 0) as total_tokens,
1209
- COALESCE(SUM(total_cost_usd), 0) as cost_usd,
1210
- MAX(started_at) as last_active
1211
- FROM sessions
1212
- WHERE id IN (${placeholders})
1213
- AND ${sessionWhere}
1214
- AND id NOT IN (SELECT DISTINCT session_id FROM requests)
1215
- `).get(...group.sessionIds) : { sessions: 0, requests: 0, total_tokens: 0, cost_usd: 0, last_active: null };
1216
- const sessionsTotal = reqStats.sessions + sessionOnlyStats.sessions;
1217
- if (sessionsTotal === 0)
1218
- continue;
1219
- const apiEquivalentUsd = reqStats.cost_usd + sessionOnlyStats.cost_usd;
1220
- const estimatedUsd = reqStats.estimated_usd + sessionOnlyStats.cost_usd;
1221
- const billableUsd = reqStats.metered_api_usd;
1222
- const lastActive = [reqStats.last_active, sessionOnlyStats.last_active].filter(Boolean).sort().at(-1) ?? "";
1223
- result.push({
1224
- account_key: key,
1225
- account_tool: group.account_tool,
1226
- account_name: group.account_name,
1227
- account_email: group.account_email,
1228
- account_source: group.account_source,
1229
- sessions: sessionsTotal,
1230
- requests: reqStats.requests + sessionOnlyStats.requests,
1231
- total_tokens: reqStats.total_tokens + sessionOnlyStats.total_tokens,
1232
- api_equivalent_usd: apiEquivalentUsd,
1233
- billable_usd: billableUsd,
1234
- metered_api_usd: reqStats.metered_api_usd,
1235
- subscription_included_usd: reqStats.subscription_included_usd,
1236
- estimated_usd: estimatedUsd,
1237
- unknown_usd: reqStats.unknown_usd,
1238
- cost_usd: apiEquivalentUsd,
1239
- last_active: lastActive
1240
- });
1241
- }
1231
+ const requestRows = db.prepare(`
1232
+ SELECT
1233
+ r.session_id as session_id,
1234
+ COALESCE(NULLIF(r.agent, ''), NULLIF(s.agent, ''), '') as agent,
1235
+ COALESCE(NULLIF(r.account_key, ''), NULLIF(s.account_key, ''), '') as account_key,
1236
+ COALESCE(NULLIF(r.account_tool, ''), NULLIF(s.account_tool, ''), '') as account_tool,
1237
+ COALESCE(NULLIF(r.account_name, ''), NULLIF(s.account_name, ''), '') as account_name,
1238
+ COALESCE(NULLIF(r.account_email, ''), NULLIF(s.account_email, ''), '') as account_email,
1239
+ COALESCE(NULLIF(r.account_source, ''), NULLIF(s.account_source, ''), 'unknown') as account_source,
1240
+ 1 as requests,
1241
+ COALESCE(r.input_tokens + r.output_tokens + r.cache_read_tokens + r.cache_create_tokens, 0) as total_tokens,
1242
+ COALESCE(r.cost_usd, 0) as cost_usd,
1243
+ COALESCE(NULLIF(r.cost_basis, ''), 'estimated') as cost_basis,
1244
+ r.timestamp as last_active
1245
+ FROM requests r
1246
+ LEFT JOIN sessions s ON s.id = r.session_id
1247
+ WHERE ${requestWhere}${requestMachineClause}
1248
+ AND (
1249
+ COALESCE(NULLIF(r.account_key, ''), NULLIF(s.account_key, ''), '') != ''
1250
+ OR COALESCE(NULLIF(r.account_tool, ''), NULLIF(s.account_tool, ''), '') != ''
1251
+ OR COALESCE(NULLIF(r.account_name, ''), NULLIF(s.account_name, ''), '') != ''
1252
+ OR COALESCE(NULLIF(r.account_email, ''), NULLIF(s.account_email, ''), '') != ''
1253
+ )
1254
+ `).all(...requestMachineParams);
1255
+ for (const row of requestRows)
1256
+ addAccountBreakdownRow(groups, row, false);
1257
+ const sessionOnlyRows = db.prepare(`
1258
+ SELECT
1259
+ s.id as session_id,
1260
+ s.agent as agent,
1261
+ s.account_key as account_key,
1262
+ s.account_tool as account_tool,
1263
+ s.account_name as account_name,
1264
+ s.account_email as account_email,
1265
+ COALESCE(NULLIF(s.account_source, ''), 'unknown') as account_source,
1266
+ COALESCE(s.request_count, 0) as requests,
1267
+ COALESCE(s.total_tokens, 0) as total_tokens,
1268
+ COALESCE(s.total_cost_usd, 0) as cost_usd,
1269
+ 'estimated' as cost_basis,
1270
+ s.started_at as last_active
1271
+ FROM sessions s
1272
+ WHERE ${sessionWhere}${sessionMachineClause}
1273
+ AND s.id NOT IN (SELECT DISTINCT session_id FROM requests)
1274
+ AND (s.account_key != '' OR s.account_tool != '' OR s.account_name != '' OR s.account_email != '')
1275
+ `).all(...sessionMachineParams);
1276
+ for (const row of sessionOnlyRows)
1277
+ addAccountBreakdownRow(groups, row, true);
1278
+ const result = [...groups.values()].map((group) => ({
1279
+ account_key: group.account_key,
1280
+ account_tool: group.account_tool,
1281
+ account_name: group.account_name,
1282
+ account_email: group.account_email,
1283
+ account_source: group.account_source,
1284
+ sessions: group.sessionIds.size,
1285
+ requests: group.requests,
1286
+ total_tokens: group.total_tokens,
1287
+ api_equivalent_usd: group.api_equivalent_usd,
1288
+ billable_usd: group.metered_api_usd,
1289
+ metered_api_usd: group.metered_api_usd,
1290
+ subscription_included_usd: group.subscription_included_usd,
1291
+ estimated_usd: group.estimated_usd,
1292
+ unknown_usd: group.unknown_usd,
1293
+ cost_usd: group.api_equivalent_usd,
1294
+ last_active: group.last_active
1295
+ }));
1242
1296
  result.sort((a, b) => b.cost_usd - a.cost_usd);
1243
1297
  return result;
1244
1298
  }
1245
- function queryDailyBreakdown(db, days = 30) {
1299
+ function queryDailyBreakdown(db, days = 30, machine) {
1300
+ const machineClause = machine ? " AND machine_id = ?" : "";
1301
+ const params = machine ? [`-${days}`, machine] : [`-${days}`];
1246
1302
  return db.prepare(`
1247
1303
  SELECT DATE(timestamp) as date, agent, COALESCE(SUM(cost_usd), 0) as cost_usd
1248
1304
  FROM requests
1249
- WHERE timestamp >= DATE('now', ? || ' days')
1305
+ WHERE timestamp >= DATE('now', ? || ' days')${machineClause}
1250
1306
  GROUP BY DATE(timestamp), agent
1251
1307
  ORDER BY date ASC
1252
- `).all(`-${days}`);
1308
+ `).all(...params);
1309
+ }
1310
+ function queryHourlyBreakdown(db, machine) {
1311
+ const machineClause = machine ? " AND machine_id = ?" : "";
1312
+ const params = machine ? [machine] : [];
1313
+ return db.prepare(`
1314
+ SELECT STRFTIME('%H', timestamp) as hour, agent, COALESCE(SUM(cost_usd), 0) as cost_usd
1315
+ FROM requests
1316
+ WHERE DATE(timestamp) = DATE('now')${machineClause}
1317
+ GROUP BY STRFTIME('%H', timestamp), agent
1318
+ ORDER BY hour ASC
1319
+ `).all(...params);
1253
1320
  }
1254
1321
  function upsertProject(db, project) {
1255
1322
  db.prepare(`
@@ -1378,17 +1445,48 @@ function queryBillingSummary(db, period) {
1378
1445
  }
1379
1446
  return { total_usd: total, by_provider };
1380
1447
  }
1381
- function listMachines(db) {
1448
+ function listMachines(db, period = "all") {
1449
+ const rWhere = requestPeriodWhere(period);
1450
+ const sWhere = sessionPeriodWhere(period);
1382
1451
  return db.prepare(`
1452
+ WITH request_stats AS (
1453
+ SELECT
1454
+ machine_id,
1455
+ COUNT(DISTINCT session_id) as sessions,
1456
+ COUNT(*) as requests,
1457
+ COALESCE(SUM(cost_usd), 0) as total_cost_usd,
1458
+ MAX(timestamp) as last_active
1459
+ FROM requests
1460
+ WHERE machine_id != ''
1461
+ AND ${rWhere}
1462
+ GROUP BY machine_id
1463
+ ),
1464
+ session_only_stats AS (
1465
+ SELECT
1466
+ machine_id,
1467
+ COUNT(*) as sessions,
1468
+ COALESCE(SUM(request_count), 0) as requests,
1469
+ COALESCE(SUM(total_cost_usd), 0) as total_cost_usd,
1470
+ MAX(started_at) as last_active
1471
+ FROM sessions
1472
+ WHERE machine_id != ''
1473
+ AND ${sWhere}
1474
+ AND id NOT IN (SELECT DISTINCT session_id FROM requests)
1475
+ GROUP BY machine_id
1476
+ ),
1477
+ combined AS (
1478
+ SELECT * FROM request_stats
1479
+ UNION ALL
1480
+ SELECT * FROM session_only_stats
1481
+ )
1383
1482
  SELECT
1384
- s.machine_id,
1385
- COUNT(DISTINCT s.id) as sessions,
1386
- COALESCE((SELECT COUNT(*) FROM requests r WHERE r.machine_id = s.machine_id), 0) as requests,
1387
- COALESCE(SUM(s.total_cost_usd), 0) as total_cost_usd,
1388
- MAX(s.started_at) as last_active
1389
- FROM sessions s
1390
- WHERE s.machine_id != ''
1391
- GROUP BY s.machine_id
1483
+ machine_id,
1484
+ COALESCE(SUM(sessions), 0) as sessions,
1485
+ COALESCE(SUM(requests), 0) as requests,
1486
+ COALESCE(SUM(total_cost_usd), 0) as total_cost_usd,
1487
+ MAX(last_active) as last_active
1488
+ FROM combined
1489
+ GROUP BY machine_id
1392
1490
  ORDER BY total_cost_usd DESC
1393
1491
  `).all();
1394
1492
  }
@@ -2087,18 +2185,22 @@ var AGENT_ACCOUNT_TOOLS = {
2087
2185
  pi: ["pi"],
2088
2186
  hermes: ["hermes"]
2089
2187
  };
2090
- function accountKey(tool, name) {
2091
- return `${tool}:${name}`;
2188
+ function normalizeEmail(email) {
2189
+ return (email ?? "").trim().toLowerCase();
2190
+ }
2191
+ function accountKey(tool, name, email) {
2192
+ const normalizedEmail = normalizeEmail(email);
2193
+ return `${tool}:${normalizedEmail || name}`;
2092
2194
  }
2093
2195
  function normalizeDir(value) {
2094
2196
  return value.replace(/\/+$/, "");
2095
2197
  }
2096
2198
  function fromProfile(profile, source) {
2097
2199
  return {
2098
- account_key: accountKey(profile.tool, profile.name),
2200
+ account_key: accountKey(profile.tool, profile.name, profile.email),
2099
2201
  account_tool: profile.tool,
2100
2202
  account_name: profile.name,
2101
- ...profile.email ? { account_email: profile.email } : {},
2203
+ ...profile.email ? { account_email: normalizeEmail(profile.email) } : {},
2102
2204
  account_source: source
2103
2205
  };
2104
2206
  }
@@ -2110,10 +2212,12 @@ function fromOverride(raw, agent) {
2110
2212
  const [tool, name] = value.includes(":") ? value.split(":", 2) : [candidateTool, value];
2111
2213
  if (!tool || !name)
2112
2214
  return null;
2215
+ const email = name.includes("@") ? normalizeEmail(name) : undefined;
2113
2216
  return {
2114
- account_key: accountKey(tool, name),
2217
+ account_key: accountKey(tool, name, email),
2115
2218
  account_tool: tool,
2116
2219
  account_name: name,
2220
+ ...email ? { account_email: email } : {},
2117
2221
  account_source: "override"
2118
2222
  };
2119
2223
  }
@@ -2126,11 +2230,12 @@ function envOverride(agent, env) {
2126
2230
  const name = env[`ECONOMY_${agentPrefix}_ACCOUNT_NAME`] ?? env["ECONOMY_ACCOUNT_NAME"];
2127
2231
  if (!tool || !name)
2128
2232
  return null;
2233
+ const email = normalizeEmail(env[`ECONOMY_${agentPrefix}_ACCOUNT_EMAIL`] ?? env["ECONOMY_ACCOUNT_EMAIL"]);
2129
2234
  return {
2130
- account_key: accountKey(tool, name),
2235
+ account_key: accountKey(tool, name, email),
2131
2236
  account_tool: tool,
2132
2237
  account_name: name,
2133
- account_email: env[`ECONOMY_${agentPrefix}_ACCOUNT_EMAIL`] ?? env["ECONOMY_ACCOUNT_EMAIL"],
2238
+ ...email ? { account_email: email } : {},
2134
2239
  account_source: "override"
2135
2240
  };
2136
2241
  }
@@ -2774,6 +2879,7 @@ export {
2774
2879
  queryRequestsSince,
2775
2880
  queryProjectBreakdown,
2776
2881
  queryModelBreakdown,
2882
+ queryHourlyBreakdown,
2777
2883
  queryDailyBreakdown,
2778
2884
  queryBillingSummary,
2779
2885
  queryAgentBreakdown,