@hasna/economy 0.2.22 → 0.2.24
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/commands/extras.d.ts.map +1 -1
- package/dist/cli/index.js +204 -48
- package/dist/db/database.d.ts.map +1 -1
- package/dist/index.js +67 -38
- package/dist/lib/periods.d.ts +6 -0
- package/dist/lib/periods.d.ts.map +1 -0
- package/dist/lib/savings.d.ts.map +1 -1
- package/dist/mcp/index.js +221 -47
- package/dist/server/index.js +209 -46
- package/dist/server/serve.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1056,23 +1056,20 @@ function labelForPath(projectPath, projectName) {
|
|
|
1056
1056
|
return segments[segments.length - 1] ?? projectPath;
|
|
1057
1057
|
}
|
|
1058
1058
|
function queryProjectBreakdown(db, period = "all") {
|
|
1059
|
-
const
|
|
1059
|
+
const requestWhere = requestPeriodWhere(period);
|
|
1060
|
+
const sessionWhere = sessionPeriodWhere(period);
|
|
1060
1061
|
const sessions = db.prepare(`
|
|
1061
1062
|
SELECT id, project_path, project_name, total_cost_usd, started_at
|
|
1062
1063
|
FROM sessions
|
|
1063
|
-
WHERE
|
|
1064
|
-
AND (project_path != '' OR project_name != '')
|
|
1064
|
+
WHERE project_path != '' OR project_name != ''
|
|
1065
1065
|
`).all();
|
|
1066
1066
|
const groups = new Map;
|
|
1067
1067
|
for (const s of sessions) {
|
|
1068
1068
|
const label = labelForPath(s.project_path, s.project_name);
|
|
1069
1069
|
if (!label)
|
|
1070
1070
|
continue;
|
|
1071
|
-
const g = groups.get(label) ?? { sessionIds: [], samplePath: s.project_path
|
|
1071
|
+
const g = groups.get(label) ?? { sessionIds: [], samplePath: s.project_path };
|
|
1072
1072
|
g.sessionIds.push(s.id);
|
|
1073
|
-
g.totalCost += s.total_cost_usd || 0;
|
|
1074
|
-
if (!g.lastActive || s.started_at > g.lastActive)
|
|
1075
|
-
g.lastActive = s.started_at;
|
|
1076
1073
|
if (!g.samplePath)
|
|
1077
1074
|
g.samplePath = s.project_path;
|
|
1078
1075
|
groups.set(label, g);
|
|
@@ -1082,32 +1079,52 @@ function queryProjectBreakdown(db, period = "all") {
|
|
|
1082
1079
|
const placeholders = g.sessionIds.map(() => "?").join(",");
|
|
1083
1080
|
const reqStats = placeholders.length ? db.prepare(`
|
|
1084
1081
|
SELECT
|
|
1082
|
+
COUNT(DISTINCT session_id) as sessions,
|
|
1085
1083
|
COUNT(*) as requests,
|
|
1086
1084
|
COALESCE(SUM(cost_usd), 0) as cost_usd,
|
|
1087
|
-
COALESCE(SUM(input_tokens + output_tokens + cache_read_tokens + cache_create_tokens), 0) as total_tokens
|
|
1088
|
-
|
|
1089
|
-
|
|
1085
|
+
COALESCE(SUM(input_tokens + output_tokens + cache_read_tokens + cache_create_tokens), 0) as total_tokens,
|
|
1086
|
+
MAX(timestamp) as last_active
|
|
1087
|
+
FROM requests
|
|
1088
|
+
WHERE session_id IN (${placeholders})
|
|
1089
|
+
AND ${requestWhere}
|
|
1090
|
+
`).get(...g.sessionIds) : { sessions: 0, requests: 0, cost_usd: 0, total_tokens: 0, last_active: null };
|
|
1091
|
+
const sessionOnlyStats = placeholders.length ? db.prepare(`
|
|
1092
|
+
SELECT
|
|
1093
|
+
COUNT(*) as sessions,
|
|
1094
|
+
COALESCE(SUM(request_count), 0) as requests,
|
|
1095
|
+
COALESCE(SUM(total_tokens), 0) as total_tokens,
|
|
1096
|
+
COALESCE(SUM(total_cost_usd), 0) as cost_usd,
|
|
1097
|
+
MAX(started_at) as last_active
|
|
1098
|
+
FROM sessions
|
|
1099
|
+
WHERE id IN (${placeholders})
|
|
1100
|
+
AND ${sessionWhere}
|
|
1101
|
+
AND id NOT IN (SELECT DISTINCT session_id FROM requests)
|
|
1102
|
+
`).get(...g.sessionIds) : { sessions: 0, requests: 0, total_tokens: 0, cost_usd: 0, last_active: null };
|
|
1103
|
+
const totalSessions = reqStats.sessions + sessionOnlyStats.sessions;
|
|
1104
|
+
if (totalSessions === 0)
|
|
1105
|
+
continue;
|
|
1106
|
+
const lastActive = [reqStats.last_active, sessionOnlyStats.last_active].filter(Boolean).sort().at(-1) ?? "";
|
|
1090
1107
|
result.push({
|
|
1091
1108
|
project_path: g.samplePath,
|
|
1092
1109
|
project_name: label,
|
|
1093
|
-
sessions:
|
|
1094
|
-
requests: reqStats.requests,
|
|
1095
|
-
total_tokens: reqStats.total_tokens,
|
|
1096
|
-
cost_usd: reqStats.cost_usd
|
|
1097
|
-
last_active:
|
|
1110
|
+
sessions: totalSessions,
|
|
1111
|
+
requests: reqStats.requests + sessionOnlyStats.requests,
|
|
1112
|
+
total_tokens: reqStats.total_tokens + sessionOnlyStats.total_tokens,
|
|
1113
|
+
cost_usd: reqStats.cost_usd + sessionOnlyStats.cost_usd,
|
|
1114
|
+
last_active: lastActive
|
|
1098
1115
|
});
|
|
1099
1116
|
}
|
|
1100
1117
|
result.sort((a, b) => b.cost_usd - a.cost_usd);
|
|
1101
1118
|
return result;
|
|
1102
1119
|
}
|
|
1103
1120
|
function queryAccountBreakdown(db, period = "all") {
|
|
1104
|
-
const
|
|
1121
|
+
const requestWhere = requestPeriodWhere(period);
|
|
1122
|
+
const sessionWhere = sessionPeriodWhere(period);
|
|
1105
1123
|
const sessions = db.prepare(`
|
|
1106
1124
|
SELECT id, account_key, account_tool, account_name, account_email, account_source,
|
|
1107
1125
|
total_cost_usd, total_tokens, request_count, started_at
|
|
1108
1126
|
FROM sessions
|
|
1109
|
-
WHERE
|
|
1110
|
-
AND (account_key != '' OR account_tool != '' OR account_name != '' OR account_email != '')
|
|
1127
|
+
WHERE account_key != '' OR account_tool != '' OR account_name != '' OR account_email != ''
|
|
1111
1128
|
`).all();
|
|
1112
1129
|
const groups = new Map;
|
|
1113
1130
|
for (const session of sessions) {
|
|
@@ -1119,18 +1136,9 @@ function queryAccountBreakdown(db, period = "all") {
|
|
|
1119
1136
|
account_tool: session.account_tool,
|
|
1120
1137
|
account_name: session.account_name,
|
|
1121
1138
|
account_email: session.account_email || null,
|
|
1122
|
-
account_source: session.account_source || "unknown"
|
|
1123
|
-
totalCost: 0,
|
|
1124
|
-
totalTokens: 0,
|
|
1125
|
-
requests: 0,
|
|
1126
|
-
lastActive: ""
|
|
1139
|
+
account_source: session.account_source || "unknown"
|
|
1127
1140
|
};
|
|
1128
1141
|
group.sessionIds.push(session.id);
|
|
1129
|
-
group.totalCost += session.total_cost_usd || 0;
|
|
1130
|
-
group.totalTokens += session.total_tokens || 0;
|
|
1131
|
-
group.requests += session.request_count || 0;
|
|
1132
|
-
if (!group.lastActive || session.started_at > group.lastActive)
|
|
1133
|
-
group.lastActive = session.started_at;
|
|
1134
1142
|
groups.set(key, group);
|
|
1135
1143
|
}
|
|
1136
1144
|
const result = [];
|
|
@@ -1138,36 +1146,57 @@ function queryAccountBreakdown(db, period = "all") {
|
|
|
1138
1146
|
const placeholders = group.sessionIds.map(() => "?").join(",");
|
|
1139
1147
|
const reqStats = placeholders ? db.prepare(`
|
|
1140
1148
|
SELECT
|
|
1149
|
+
COUNT(DISTINCT session_id) as sessions,
|
|
1141
1150
|
COUNT(*) as requests,
|
|
1142
1151
|
COALESCE(SUM(cost_usd), 0) as cost_usd,
|
|
1143
1152
|
COALESCE(SUM(input_tokens + output_tokens + cache_read_tokens + cache_create_tokens), 0) as total_tokens,
|
|
1144
1153
|
COALESCE(SUM(CASE WHEN cost_basis = 'metered_api' THEN cost_usd ELSE 0 END), 0) as metered_api_usd,
|
|
1145
1154
|
COALESCE(SUM(CASE WHEN cost_basis = 'subscription_included' THEN cost_usd ELSE 0 END), 0) as subscription_included_usd,
|
|
1146
1155
|
COALESCE(SUM(CASE WHEN COALESCE(cost_basis, 'estimated') = 'estimated' THEN cost_usd ELSE 0 END), 0) as estimated_usd,
|
|
1147
|
-
COALESCE(SUM(CASE WHEN cost_basis = 'unknown' THEN cost_usd ELSE 0 END), 0) as unknown_usd
|
|
1148
|
-
|
|
1156
|
+
COALESCE(SUM(CASE WHEN cost_basis = 'unknown' THEN cost_usd ELSE 0 END), 0) as unknown_usd,
|
|
1157
|
+
MAX(timestamp) as last_active
|
|
1158
|
+
FROM requests
|
|
1159
|
+
WHERE session_id IN (${placeholders})
|
|
1160
|
+
AND ${requestWhere}
|
|
1149
1161
|
`).get(...group.sessionIds) : {
|
|
1162
|
+
sessions: 0,
|
|
1150
1163
|
requests: 0,
|
|
1151
1164
|
cost_usd: 0,
|
|
1152
1165
|
total_tokens: 0,
|
|
1153
1166
|
metered_api_usd: 0,
|
|
1154
1167
|
subscription_included_usd: 0,
|
|
1155
1168
|
estimated_usd: 0,
|
|
1156
|
-
unknown_usd: 0
|
|
1169
|
+
unknown_usd: 0,
|
|
1170
|
+
last_active: null
|
|
1157
1171
|
};
|
|
1158
|
-
const
|
|
1159
|
-
|
|
1160
|
-
|
|
1172
|
+
const sessionOnlyStats = placeholders ? db.prepare(`
|
|
1173
|
+
SELECT
|
|
1174
|
+
COUNT(*) as sessions,
|
|
1175
|
+
COALESCE(SUM(request_count), 0) as requests,
|
|
1176
|
+
COALESCE(SUM(total_tokens), 0) as total_tokens,
|
|
1177
|
+
COALESCE(SUM(total_cost_usd), 0) as cost_usd,
|
|
1178
|
+
MAX(started_at) as last_active
|
|
1179
|
+
FROM sessions
|
|
1180
|
+
WHERE id IN (${placeholders})
|
|
1181
|
+
AND ${sessionWhere}
|
|
1182
|
+
AND id NOT IN (SELECT DISTINCT session_id FROM requests)
|
|
1183
|
+
`).get(...group.sessionIds) : { sessions: 0, requests: 0, total_tokens: 0, cost_usd: 0, last_active: null };
|
|
1184
|
+
const sessionsTotal = reqStats.sessions + sessionOnlyStats.sessions;
|
|
1185
|
+
if (sessionsTotal === 0)
|
|
1186
|
+
continue;
|
|
1187
|
+
const apiEquivalentUsd = reqStats.cost_usd + sessionOnlyStats.cost_usd;
|
|
1188
|
+
const estimatedUsd = reqStats.estimated_usd + sessionOnlyStats.cost_usd;
|
|
1161
1189
|
const billableUsd = reqStats.metered_api_usd;
|
|
1190
|
+
const lastActive = [reqStats.last_active, sessionOnlyStats.last_active].filter(Boolean).sort().at(-1) ?? "";
|
|
1162
1191
|
result.push({
|
|
1163
1192
|
account_key: key,
|
|
1164
1193
|
account_tool: group.account_tool,
|
|
1165
1194
|
account_name: group.account_name,
|
|
1166
1195
|
account_email: group.account_email,
|
|
1167
1196
|
account_source: group.account_source,
|
|
1168
|
-
sessions:
|
|
1169
|
-
requests: reqStats.requests
|
|
1170
|
-
total_tokens: reqStats.total_tokens
|
|
1197
|
+
sessions: sessionsTotal,
|
|
1198
|
+
requests: reqStats.requests + sessionOnlyStats.requests,
|
|
1199
|
+
total_tokens: reqStats.total_tokens + sessionOnlyStats.total_tokens,
|
|
1171
1200
|
api_equivalent_usd: apiEquivalentUsd,
|
|
1172
1201
|
billable_usd: billableUsd,
|
|
1173
1202
|
metered_api_usd: reqStats.metered_api_usd,
|
|
@@ -1175,7 +1204,7 @@ function queryAccountBreakdown(db, period = "all") {
|
|
|
1175
1204
|
estimated_usd: estimatedUsd,
|
|
1176
1205
|
unknown_usd: reqStats.unknown_usd,
|
|
1177
1206
|
cost_usd: apiEquivalentUsd,
|
|
1178
|
-
last_active:
|
|
1207
|
+
last_active: lastActive
|
|
1179
1208
|
});
|
|
1180
1209
|
}
|
|
1181
1210
|
result.sort((a, b) => b.cost_usd - a.cost_usd);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"periods.d.ts","sourceRoot":"","sources":["../../src/lib/periods.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAM/C,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAsB9F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"savings.d.ts","sourceRoot":"","sources":["../../src/lib/savings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC7D,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAE/C,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,kBAAkB,EAAE,MAAM,CAAA;IAC1B,oBAAoB,EAAE,MAAM,CAAA;IAC5B,qBAAqB,EAAE,MAAM,CAAA;IAC7B,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC,CAAA;CAClD;
|
|
1
|
+
{"version":3,"file":"savings.d.ts","sourceRoot":"","sources":["../../src/lib/savings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC7D,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAE/C,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,kBAAkB,EAAE,MAAM,CAAA;IAC1B,oBAAoB,EAAE,MAAM,CAAA;IAC5B,qBAAqB,EAAE,MAAM,CAAA;IAC7B,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC,CAAA;CAClD;AAoCD,6EAA6E;AAC7E,wBAAgB,eAAe,CAC7B,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,MAAM,GACtB,MAAM,CAER;AAED,wBAAgB,mBAAmB,CACjC,EAAE,EAAE,QAAQ,EACZ,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,KAAK,GACZ,cAAc,CAmHhB;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,CAIhE"}
|
package/dist/mcp/index.js
CHANGED
|
@@ -1057,23 +1057,20 @@ function labelForPath(projectPath, projectName) {
|
|
|
1057
1057
|
return segments[segments.length - 1] ?? projectPath;
|
|
1058
1058
|
}
|
|
1059
1059
|
function queryProjectBreakdown(db, period = "all") {
|
|
1060
|
-
const
|
|
1060
|
+
const requestWhere = requestPeriodWhere(period);
|
|
1061
|
+
const sessionWhere = sessionPeriodWhere(period);
|
|
1061
1062
|
const sessions = db.prepare(`
|
|
1062
1063
|
SELECT id, project_path, project_name, total_cost_usd, started_at
|
|
1063
1064
|
FROM sessions
|
|
1064
|
-
WHERE
|
|
1065
|
-
AND (project_path != '' OR project_name != '')
|
|
1065
|
+
WHERE project_path != '' OR project_name != ''
|
|
1066
1066
|
`).all();
|
|
1067
1067
|
const groups = new Map;
|
|
1068
1068
|
for (const s of sessions) {
|
|
1069
1069
|
const label = labelForPath(s.project_path, s.project_name);
|
|
1070
1070
|
if (!label)
|
|
1071
1071
|
continue;
|
|
1072
|
-
const g = groups.get(label) ?? { sessionIds: [], samplePath: s.project_path
|
|
1072
|
+
const g = groups.get(label) ?? { sessionIds: [], samplePath: s.project_path };
|
|
1073
1073
|
g.sessionIds.push(s.id);
|
|
1074
|
-
g.totalCost += s.total_cost_usd || 0;
|
|
1075
|
-
if (!g.lastActive || s.started_at > g.lastActive)
|
|
1076
|
-
g.lastActive = s.started_at;
|
|
1077
1074
|
if (!g.samplePath)
|
|
1078
1075
|
g.samplePath = s.project_path;
|
|
1079
1076
|
groups.set(label, g);
|
|
@@ -1083,32 +1080,52 @@ function queryProjectBreakdown(db, period = "all") {
|
|
|
1083
1080
|
const placeholders = g.sessionIds.map(() => "?").join(",");
|
|
1084
1081
|
const reqStats = placeholders.length ? db.prepare(`
|
|
1085
1082
|
SELECT
|
|
1083
|
+
COUNT(DISTINCT session_id) as sessions,
|
|
1086
1084
|
COUNT(*) as requests,
|
|
1087
1085
|
COALESCE(SUM(cost_usd), 0) as cost_usd,
|
|
1088
|
-
COALESCE(SUM(input_tokens + output_tokens + cache_read_tokens + cache_create_tokens), 0) as total_tokens
|
|
1089
|
-
|
|
1090
|
-
|
|
1086
|
+
COALESCE(SUM(input_tokens + output_tokens + cache_read_tokens + cache_create_tokens), 0) as total_tokens,
|
|
1087
|
+
MAX(timestamp) as last_active
|
|
1088
|
+
FROM requests
|
|
1089
|
+
WHERE session_id IN (${placeholders})
|
|
1090
|
+
AND ${requestWhere}
|
|
1091
|
+
`).get(...g.sessionIds) : { sessions: 0, requests: 0, cost_usd: 0, total_tokens: 0, last_active: null };
|
|
1092
|
+
const sessionOnlyStats = placeholders.length ? db.prepare(`
|
|
1093
|
+
SELECT
|
|
1094
|
+
COUNT(*) as sessions,
|
|
1095
|
+
COALESCE(SUM(request_count), 0) as requests,
|
|
1096
|
+
COALESCE(SUM(total_tokens), 0) as total_tokens,
|
|
1097
|
+
COALESCE(SUM(total_cost_usd), 0) as cost_usd,
|
|
1098
|
+
MAX(started_at) as last_active
|
|
1099
|
+
FROM sessions
|
|
1100
|
+
WHERE id IN (${placeholders})
|
|
1101
|
+
AND ${sessionWhere}
|
|
1102
|
+
AND id NOT IN (SELECT DISTINCT session_id FROM requests)
|
|
1103
|
+
`).get(...g.sessionIds) : { sessions: 0, requests: 0, total_tokens: 0, cost_usd: 0, last_active: null };
|
|
1104
|
+
const totalSessions = reqStats.sessions + sessionOnlyStats.sessions;
|
|
1105
|
+
if (totalSessions === 0)
|
|
1106
|
+
continue;
|
|
1107
|
+
const lastActive = [reqStats.last_active, sessionOnlyStats.last_active].filter(Boolean).sort().at(-1) ?? "";
|
|
1091
1108
|
result.push({
|
|
1092
1109
|
project_path: g.samplePath,
|
|
1093
1110
|
project_name: label,
|
|
1094
|
-
sessions:
|
|
1095
|
-
requests: reqStats.requests,
|
|
1096
|
-
total_tokens: reqStats.total_tokens,
|
|
1097
|
-
cost_usd: reqStats.cost_usd
|
|
1098
|
-
last_active:
|
|
1111
|
+
sessions: totalSessions,
|
|
1112
|
+
requests: reqStats.requests + sessionOnlyStats.requests,
|
|
1113
|
+
total_tokens: reqStats.total_tokens + sessionOnlyStats.total_tokens,
|
|
1114
|
+
cost_usd: reqStats.cost_usd + sessionOnlyStats.cost_usd,
|
|
1115
|
+
last_active: lastActive
|
|
1099
1116
|
});
|
|
1100
1117
|
}
|
|
1101
1118
|
result.sort((a, b) => b.cost_usd - a.cost_usd);
|
|
1102
1119
|
return result;
|
|
1103
1120
|
}
|
|
1104
1121
|
function queryAccountBreakdown(db, period = "all") {
|
|
1105
|
-
const
|
|
1122
|
+
const requestWhere = requestPeriodWhere(period);
|
|
1123
|
+
const sessionWhere = sessionPeriodWhere(period);
|
|
1106
1124
|
const sessions = db.prepare(`
|
|
1107
1125
|
SELECT id, account_key, account_tool, account_name, account_email, account_source,
|
|
1108
1126
|
total_cost_usd, total_tokens, request_count, started_at
|
|
1109
1127
|
FROM sessions
|
|
1110
|
-
WHERE
|
|
1111
|
-
AND (account_key != '' OR account_tool != '' OR account_name != '' OR account_email != '')
|
|
1128
|
+
WHERE account_key != '' OR account_tool != '' OR account_name != '' OR account_email != ''
|
|
1112
1129
|
`).all();
|
|
1113
1130
|
const groups = new Map;
|
|
1114
1131
|
for (const session of sessions) {
|
|
@@ -1120,18 +1137,9 @@ function queryAccountBreakdown(db, period = "all") {
|
|
|
1120
1137
|
account_tool: session.account_tool,
|
|
1121
1138
|
account_name: session.account_name,
|
|
1122
1139
|
account_email: session.account_email || null,
|
|
1123
|
-
account_source: session.account_source || "unknown"
|
|
1124
|
-
totalCost: 0,
|
|
1125
|
-
totalTokens: 0,
|
|
1126
|
-
requests: 0,
|
|
1127
|
-
lastActive: ""
|
|
1140
|
+
account_source: session.account_source || "unknown"
|
|
1128
1141
|
};
|
|
1129
1142
|
group.sessionIds.push(session.id);
|
|
1130
|
-
group.totalCost += session.total_cost_usd || 0;
|
|
1131
|
-
group.totalTokens += session.total_tokens || 0;
|
|
1132
|
-
group.requests += session.request_count || 0;
|
|
1133
|
-
if (!group.lastActive || session.started_at > group.lastActive)
|
|
1134
|
-
group.lastActive = session.started_at;
|
|
1135
1143
|
groups.set(key, group);
|
|
1136
1144
|
}
|
|
1137
1145
|
const result = [];
|
|
@@ -1139,36 +1147,57 @@ function queryAccountBreakdown(db, period = "all") {
|
|
|
1139
1147
|
const placeholders = group.sessionIds.map(() => "?").join(",");
|
|
1140
1148
|
const reqStats = placeholders ? db.prepare(`
|
|
1141
1149
|
SELECT
|
|
1150
|
+
COUNT(DISTINCT session_id) as sessions,
|
|
1142
1151
|
COUNT(*) as requests,
|
|
1143
1152
|
COALESCE(SUM(cost_usd), 0) as cost_usd,
|
|
1144
1153
|
COALESCE(SUM(input_tokens + output_tokens + cache_read_tokens + cache_create_tokens), 0) as total_tokens,
|
|
1145
1154
|
COALESCE(SUM(CASE WHEN cost_basis = 'metered_api' THEN cost_usd ELSE 0 END), 0) as metered_api_usd,
|
|
1146
1155
|
COALESCE(SUM(CASE WHEN cost_basis = 'subscription_included' THEN cost_usd ELSE 0 END), 0) as subscription_included_usd,
|
|
1147
1156
|
COALESCE(SUM(CASE WHEN COALESCE(cost_basis, 'estimated') = 'estimated' THEN cost_usd ELSE 0 END), 0) as estimated_usd,
|
|
1148
|
-
COALESCE(SUM(CASE WHEN cost_basis = 'unknown' THEN cost_usd ELSE 0 END), 0) as unknown_usd
|
|
1149
|
-
|
|
1157
|
+
COALESCE(SUM(CASE WHEN cost_basis = 'unknown' THEN cost_usd ELSE 0 END), 0) as unknown_usd,
|
|
1158
|
+
MAX(timestamp) as last_active
|
|
1159
|
+
FROM requests
|
|
1160
|
+
WHERE session_id IN (${placeholders})
|
|
1161
|
+
AND ${requestWhere}
|
|
1150
1162
|
`).get(...group.sessionIds) : {
|
|
1163
|
+
sessions: 0,
|
|
1151
1164
|
requests: 0,
|
|
1152
1165
|
cost_usd: 0,
|
|
1153
1166
|
total_tokens: 0,
|
|
1154
1167
|
metered_api_usd: 0,
|
|
1155
1168
|
subscription_included_usd: 0,
|
|
1156
1169
|
estimated_usd: 0,
|
|
1157
|
-
unknown_usd: 0
|
|
1170
|
+
unknown_usd: 0,
|
|
1171
|
+
last_active: null
|
|
1158
1172
|
};
|
|
1159
|
-
const
|
|
1160
|
-
|
|
1161
|
-
|
|
1173
|
+
const sessionOnlyStats = placeholders ? db.prepare(`
|
|
1174
|
+
SELECT
|
|
1175
|
+
COUNT(*) as sessions,
|
|
1176
|
+
COALESCE(SUM(request_count), 0) as requests,
|
|
1177
|
+
COALESCE(SUM(total_tokens), 0) as total_tokens,
|
|
1178
|
+
COALESCE(SUM(total_cost_usd), 0) as cost_usd,
|
|
1179
|
+
MAX(started_at) as last_active
|
|
1180
|
+
FROM sessions
|
|
1181
|
+
WHERE id IN (${placeholders})
|
|
1182
|
+
AND ${sessionWhere}
|
|
1183
|
+
AND id NOT IN (SELECT DISTINCT session_id FROM requests)
|
|
1184
|
+
`).get(...group.sessionIds) : { sessions: 0, requests: 0, total_tokens: 0, cost_usd: 0, last_active: null };
|
|
1185
|
+
const sessionsTotal = reqStats.sessions + sessionOnlyStats.sessions;
|
|
1186
|
+
if (sessionsTotal === 0)
|
|
1187
|
+
continue;
|
|
1188
|
+
const apiEquivalentUsd = reqStats.cost_usd + sessionOnlyStats.cost_usd;
|
|
1189
|
+
const estimatedUsd = reqStats.estimated_usd + sessionOnlyStats.cost_usd;
|
|
1162
1190
|
const billableUsd = reqStats.metered_api_usd;
|
|
1191
|
+
const lastActive = [reqStats.last_active, sessionOnlyStats.last_active].filter(Boolean).sort().at(-1) ?? "";
|
|
1163
1192
|
result.push({
|
|
1164
1193
|
account_key: key,
|
|
1165
1194
|
account_tool: group.account_tool,
|
|
1166
1195
|
account_name: group.account_name,
|
|
1167
1196
|
account_email: group.account_email,
|
|
1168
1197
|
account_source: group.account_source,
|
|
1169
|
-
sessions:
|
|
1170
|
-
requests: reqStats.requests
|
|
1171
|
-
total_tokens: reqStats.total_tokens
|
|
1198
|
+
sessions: sessionsTotal,
|
|
1199
|
+
requests: reqStats.requests + sessionOnlyStats.requests,
|
|
1200
|
+
total_tokens: reqStats.total_tokens + sessionOnlyStats.total_tokens,
|
|
1172
1201
|
api_equivalent_usd: apiEquivalentUsd,
|
|
1173
1202
|
billable_usd: billableUsd,
|
|
1174
1203
|
metered_api_usd: reqStats.metered_api_usd,
|
|
@@ -1176,7 +1205,7 @@ function queryAccountBreakdown(db, period = "all") {
|
|
|
1176
1205
|
estimated_usd: estimatedUsd,
|
|
1177
1206
|
unknown_usd: reqStats.unknown_usd,
|
|
1178
1207
|
cost_usd: apiEquivalentUsd,
|
|
1179
|
-
last_active:
|
|
1208
|
+
last_active: lastActive
|
|
1180
1209
|
});
|
|
1181
1210
|
}
|
|
1182
1211
|
result.sort((a, b) => b.cost_usd - a.cost_usd);
|
|
@@ -1343,6 +1372,12 @@ function upsertSubscription(db, sub) {
|
|
|
1343
1372
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1344
1373
|
`).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
1374
|
}
|
|
1375
|
+
function listSubscriptions(db) {
|
|
1376
|
+
return db.prepare(`SELECT * FROM subscriptions ORDER BY provider, plan`).all();
|
|
1377
|
+
}
|
|
1378
|
+
function deleteSubscription(db, id) {
|
|
1379
|
+
db.prepare(`DELETE FROM subscriptions WHERE id = ?`).run(id);
|
|
1380
|
+
}
|
|
1346
1381
|
function upsertUsageSnapshot(db, snap) {
|
|
1347
1382
|
const now = snap.updated_at ?? new Date().toISOString();
|
|
1348
1383
|
const id = snap.id ?? `${snap.agent}-${snap.date}-${snap.metric}-${snap.machine_id}`;
|
|
@@ -1626,6 +1661,10 @@ function prorateMonthlyFee(monthlyFee, period) {
|
|
|
1626
1661
|
return monthlyFee;
|
|
1627
1662
|
}
|
|
1628
1663
|
}
|
|
1664
|
+
function proratedIncludedConsumed(includedUsage, includedCap, period) {
|
|
1665
|
+
const cap = prorateMonthlyFee(includedCap, period);
|
|
1666
|
+
return cap > 0 ? Math.min(includedUsage, cap) : includedUsage;
|
|
1667
|
+
}
|
|
1629
1668
|
function computeSavedUsd(apiEquivalent, onDemand, subscriptionFee) {
|
|
1630
1669
|
return Math.max(0, apiEquivalent - onDemand - subscriptionFee);
|
|
1631
1670
|
}
|
|
@@ -1653,24 +1692,74 @@ function querySavingsSummary(db, period, agent) {
|
|
|
1653
1692
|
AND metric = 'on_demand_usd'
|
|
1654
1693
|
`).get(...params);
|
|
1655
1694
|
const subs = db.prepare(`
|
|
1656
|
-
SELECT
|
|
1695
|
+
SELECT
|
|
1696
|
+
COALESCE(SUM(monthly_fee_usd), 0) as fee,
|
|
1697
|
+
COALESCE(SUM(included_usage_usd), 0) as included
|
|
1657
1698
|
FROM subscriptions
|
|
1658
|
-
WHERE active = 1${agent ? " AND agent = ?" : ""}
|
|
1699
|
+
WHERE active = 1${agent ? " AND (agent = ? OR agent IS NULL)" : ""}
|
|
1659
1700
|
`).get(...agent ? [agent] : []);
|
|
1660
|
-
const subscriptionFee = prorateMonthlyFee(subs.
|
|
1701
|
+
const subscriptionFee = prorateMonthlyFee(subs.fee, period);
|
|
1661
1702
|
const apiEquivalent = apiRow.total + includedRow.total;
|
|
1703
|
+
const includedConsumed = proratedIncludedConsumed(includedRow.total, subs.included, period);
|
|
1662
1704
|
const onDemand = onDemandRow.total;
|
|
1663
1705
|
const saved = computeSavedUsd(apiEquivalent, onDemand, subscriptionFee);
|
|
1664
1706
|
const byAgent = {};
|
|
1665
1707
|
if (!agent) {
|
|
1708
|
+
const onDemandByAgent = new Map;
|
|
1709
|
+
for (const row of db.prepare(`
|
|
1710
|
+
SELECT agent, COALESCE(SUM(value), 0) as total
|
|
1711
|
+
FROM usage_snapshots
|
|
1712
|
+
WHERE ${subWhere}
|
|
1713
|
+
AND metric = 'on_demand_usd'
|
|
1714
|
+
GROUP BY agent
|
|
1715
|
+
`).all()) {
|
|
1716
|
+
onDemandByAgent.set(row.agent, row.total);
|
|
1717
|
+
}
|
|
1718
|
+
const subscriptionByAgent = new Map;
|
|
1719
|
+
for (const row of db.prepare(`
|
|
1720
|
+
SELECT agent,
|
|
1721
|
+
COALESCE(SUM(monthly_fee_usd), 0) as fee,
|
|
1722
|
+
COALESCE(SUM(included_usage_usd), 0) as included
|
|
1723
|
+
FROM subscriptions
|
|
1724
|
+
WHERE active = 1 AND agent IS NOT NULL
|
|
1725
|
+
GROUP BY agent
|
|
1726
|
+
`).all()) {
|
|
1727
|
+
subscriptionByAgent.set(row.agent, row);
|
|
1728
|
+
}
|
|
1729
|
+
const globalSubs = db.prepare(`
|
|
1730
|
+
SELECT
|
|
1731
|
+
COALESCE(SUM(monthly_fee_usd), 0) as fee,
|
|
1732
|
+
COALESCE(SUM(included_usage_usd), 0) as included
|
|
1733
|
+
FROM subscriptions
|
|
1734
|
+
WHERE active = 1 AND agent IS NULL
|
|
1735
|
+
`).get();
|
|
1736
|
+
const rows = db.prepare(`
|
|
1737
|
+
SELECT agent,
|
|
1738
|
+
COALESCE(SUM(cost_usd), 0) as api_eq,
|
|
1739
|
+
COALESCE(SUM(CASE WHEN cost_basis = 'subscription_included' THEN cost_usd ELSE 0 END), 0) as included
|
|
1740
|
+
FROM requests WHERE ${where}
|
|
1741
|
+
GROUP BY agent
|
|
1742
|
+
`).all();
|
|
1743
|
+
const totalAgentApiEq = rows.reduce((sum, row) => sum + row.api_eq, 0);
|
|
1666
1744
|
for (const row of db.prepare(`
|
|
1667
|
-
SELECT agent,
|
|
1745
|
+
SELECT agent,
|
|
1746
|
+
COALESCE(SUM(cost_usd), 0) as api_eq,
|
|
1747
|
+
COALESCE(SUM(CASE WHEN cost_basis = 'subscription_included' THEN cost_usd ELSE 0 END), 0) as included
|
|
1668
1748
|
FROM requests WHERE ${where}
|
|
1669
1749
|
GROUP BY agent
|
|
1670
1750
|
`).all()) {
|
|
1751
|
+
const agentSubs = subscriptionByAgent.get(row.agent) ?? { fee: 0, included: 0 };
|
|
1752
|
+
const globalShare = totalAgentApiEq > 0 ? row.api_eq / totalAgentApiEq : 0;
|
|
1753
|
+
const agentFee = prorateMonthlyFee(agentSubs.fee + globalSubs.fee * globalShare, period);
|
|
1754
|
+
const agentIncludedCap = agentSubs.included + globalSubs.included * globalShare;
|
|
1755
|
+
const agentIncludedConsumed = proratedIncludedConsumed(row.included, agentIncludedCap, period);
|
|
1756
|
+
const agentOnDemand = onDemandByAgent.get(row.agent) ?? 0;
|
|
1671
1757
|
byAgent[row.agent] = {
|
|
1672
1758
|
api_equivalent_usd: row.api_eq,
|
|
1673
|
-
|
|
1759
|
+
subscription_fee_usd: agentFee,
|
|
1760
|
+
included_consumed_usd: agentIncludedConsumed,
|
|
1761
|
+
on_demand_usd: agentOnDemand,
|
|
1762
|
+
saved_usd: computeSavedUsd(row.api_eq, agentOnDemand, agentFee)
|
|
1674
1763
|
};
|
|
1675
1764
|
}
|
|
1676
1765
|
}
|
|
@@ -1678,7 +1767,7 @@ function querySavingsSummary(db, period, agent) {
|
|
|
1678
1767
|
period,
|
|
1679
1768
|
api_equivalent_usd: apiEquivalent,
|
|
1680
1769
|
subscription_fee_usd: subscriptionFee,
|
|
1681
|
-
included_consumed_usd:
|
|
1770
|
+
included_consumed_usd: includedConsumed,
|
|
1682
1771
|
on_demand_usd: onDemand,
|
|
1683
1772
|
saved_usd: saved,
|
|
1684
1773
|
by_agent: byAgent
|
|
@@ -3266,6 +3355,34 @@ var AGENTS = [
|
|
|
3266
3355
|
"hermes"
|
|
3267
3356
|
];
|
|
3268
3357
|
|
|
3358
|
+
// src/lib/periods.ts
|
|
3359
|
+
function ymd(date) {
|
|
3360
|
+
return date.toISOString().substring(0, 10);
|
|
3361
|
+
}
|
|
3362
|
+
function usageSnapshotFilterForPeriod(period) {
|
|
3363
|
+
const now = new Date;
|
|
3364
|
+
switch (period) {
|
|
3365
|
+
case "today":
|
|
3366
|
+
return { date: ymd(now) };
|
|
3367
|
+
case "yesterday": {
|
|
3368
|
+
const yesterday = new Date(now);
|
|
3369
|
+
yesterday.setUTCDate(yesterday.getUTCDate() - 1);
|
|
3370
|
+
return { date: ymd(yesterday) };
|
|
3371
|
+
}
|
|
3372
|
+
case "week": {
|
|
3373
|
+
const weekAgo = new Date(now);
|
|
3374
|
+
weekAgo.setUTCDate(weekAgo.getUTCDate() - 7);
|
|
3375
|
+
return { since: ymd(weekAgo) };
|
|
3376
|
+
}
|
|
3377
|
+
case "month":
|
|
3378
|
+
return { since: ymd(new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1))) };
|
|
3379
|
+
case "year":
|
|
3380
|
+
return { since: ymd(new Date(Date.UTC(now.getUTCFullYear(), 0, 1))) };
|
|
3381
|
+
case "all":
|
|
3382
|
+
return {};
|
|
3383
|
+
}
|
|
3384
|
+
}
|
|
3385
|
+
|
|
3269
3386
|
// src/mcp/index.ts
|
|
3270
3387
|
init_database();
|
|
3271
3388
|
init_pricing();
|
|
@@ -3314,6 +3431,9 @@ var TOOL_NAMES = [
|
|
|
3314
3431
|
"get_session_detail",
|
|
3315
3432
|
"get_usage",
|
|
3316
3433
|
"get_savings",
|
|
3434
|
+
"list_subscriptions",
|
|
3435
|
+
"set_subscription",
|
|
3436
|
+
"remove_subscription",
|
|
3317
3437
|
"estimate_cost",
|
|
3318
3438
|
"sync",
|
|
3319
3439
|
"search_tools",
|
|
@@ -3346,8 +3466,11 @@ var TOOL_DESCRIPTIONS = {
|
|
|
3346
3466
|
get_daily: "days(30) -> daily cost table grouped by date and agent",
|
|
3347
3467
|
get_billing_summary: "period(today|yesterday|week|month|year|all) -> actual provider billing totals",
|
|
3348
3468
|
get_session_detail: "session_id(prefix ok) -> per-request breakdown with model, tokens, cost",
|
|
3349
|
-
get_usage: `period(today|week|month), agent?(${AGENTS.join("|")}) -> usage snapshots and all-machine summary`,
|
|
3469
|
+
get_usage: `period(today|week|month|year|all), agent?(${AGENTS.join("|")}) -> usage snapshots and all-machine summary`,
|
|
3350
3470
|
get_savings: `period(today|week|month|year|all), agent?(${AGENTS.join("|")}) -> subscription/API-equivalent savings`,
|
|
3471
|
+
list_subscriptions: "no params -> configured subscription plans and included usage",
|
|
3472
|
+
set_subscription: `provider, plan, monthly_fee_usd?, included_usage_usd?, agent?(${AGENTS.join("|")}) -> create/update subscription plan`,
|
|
3473
|
+
remove_subscription: "id -> delete subscription plan",
|
|
3351
3474
|
estimate_cost: "model, input_tokens?, output_tokens? -> pre-flight token cost estimate",
|
|
3352
3475
|
sync: `sources(all|${AGENTS.join("|")}) -> ingest latest cost data`,
|
|
3353
3476
|
search_tools: "query substring -> tool name list",
|
|
@@ -3599,15 +3722,66 @@ server.tool("sync", `Ingest new cost data. sources: all|${AGENTS.join("|")}`, {
|
|
|
3599
3722
|
const result = await syncAll(db, opts);
|
|
3600
3723
|
return text(JSON.stringify(result, null, 2));
|
|
3601
3724
|
});
|
|
3602
|
-
server.tool("get_usage", "Usage snapshots and fleet summary. period: today|week|month", { period: z.enum(["today", "week", "month"]).optional(), agent: z.enum(AGENTS).optional() }, async ({ period, agent }) => {
|
|
3725
|
+
server.tool("get_usage", "Usage snapshots and fleet summary. period: today|week|month|year|all", { period: z.enum(["today", "week", "month", "year", "all"]).optional(), agent: z.enum(AGENTS).optional() }, async ({ period, agent }) => {
|
|
3603
3726
|
const p = period ?? "month";
|
|
3604
|
-
const snaps = queryUsageSnapshots(db, { agent });
|
|
3727
|
+
const snaps = queryUsageSnapshots(db, { agent, ...usageSnapshotFilterForPeriod(p) });
|
|
3605
3728
|
const summary = querySummary(db, p, undefined, true);
|
|
3606
3729
|
return text(JSON.stringify({ snapshots: snaps, summary }, null, 2));
|
|
3607
3730
|
});
|
|
3608
3731
|
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
3732
|
return text(JSON.stringify(querySavingsSummary(db, period ?? "month", agent), null, 2));
|
|
3610
3733
|
});
|
|
3734
|
+
server.tool("list_subscriptions", "List configured subscription plans and included usage caps. No params.", {}, async () => {
|
|
3735
|
+
const rows = listSubscriptions(db);
|
|
3736
|
+
if (rows.length === 0)
|
|
3737
|
+
return text("No subscriptions configured.");
|
|
3738
|
+
const lines = ["id provider plan agent fee included active"];
|
|
3739
|
+
for (const row of rows) {
|
|
3740
|
+
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"}`);
|
|
3741
|
+
}
|
|
3742
|
+
return text(lines.join(`
|
|
3743
|
+
`));
|
|
3744
|
+
});
|
|
3745
|
+
server.tool("set_subscription", `Create or update a subscription plan. agent may be ${AGENTS.join("|")}.`, {
|
|
3746
|
+
id: z.string().optional(),
|
|
3747
|
+
provider: z.string(),
|
|
3748
|
+
plan: z.string(),
|
|
3749
|
+
agent: z.enum(AGENTS).optional(),
|
|
3750
|
+
monthly_fee_usd: z.number().optional(),
|
|
3751
|
+
included_usage_usd: z.number().optional(),
|
|
3752
|
+
billing_cycle_start: z.string().optional(),
|
|
3753
|
+
reset_policy: z.string().optional(),
|
|
3754
|
+
active: z.boolean().optional()
|
|
3755
|
+
}, async (input) => {
|
|
3756
|
+
if (input.monthly_fee_usd != null && input.monthly_fee_usd < 0)
|
|
3757
|
+
return text("monthly_fee_usd must be non-negative");
|
|
3758
|
+
if (input.included_usage_usd != null && input.included_usage_usd < 0)
|
|
3759
|
+
return text("included_usage_usd must be non-negative");
|
|
3760
|
+
const now = new Date().toISOString();
|
|
3761
|
+
const subscription = {
|
|
3762
|
+
id: input.id?.trim() || randomUUID(),
|
|
3763
|
+
agent: input.agent ?? null,
|
|
3764
|
+
provider: input.provider.trim(),
|
|
3765
|
+
plan: input.plan.trim(),
|
|
3766
|
+
monthly_fee_usd: input.monthly_fee_usd ?? 0,
|
|
3767
|
+
included_usage_usd: input.included_usage_usd ?? 0,
|
|
3768
|
+
billing_cycle_start: input.billing_cycle_start ?? null,
|
|
3769
|
+
reset_policy: input.reset_policy ?? "monthly",
|
|
3770
|
+
active: input.active === false ? 0 : 1,
|
|
3771
|
+
created_at: now,
|
|
3772
|
+
updated_at: now
|
|
3773
|
+
};
|
|
3774
|
+
if (!subscription.provider)
|
|
3775
|
+
return text("provider is required");
|
|
3776
|
+
if (!subscription.plan)
|
|
3777
|
+
return text("plan is required");
|
|
3778
|
+
upsertSubscription(db, subscription);
|
|
3779
|
+
return text(JSON.stringify(subscription, null, 2));
|
|
3780
|
+
});
|
|
3781
|
+
server.tool("remove_subscription", "Remove a subscription plan by id.", { id: z.string() }, async ({ id }) => {
|
|
3782
|
+
deleteSubscription(db, id);
|
|
3783
|
+
return text(`Removed subscription ${id}`);
|
|
3784
|
+
});
|
|
3611
3785
|
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
3786
|
const cost = computeCostFromDb(db, model, input_tokens ?? 0, output_tokens ?? 0, 0, 0, 0);
|
|
3613
3787
|
return text(`${model}: ${fmtUsd(cost)} (${input_tokens ?? 0} in / ${output_tokens ?? 0} out)`);
|