paygate-mcp 7.3.0 → 7.5.0
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 +130 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +226 -0
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -100,6 +100,8 @@ Agent → PayGate (auth + billing) → Your MCP Server (stdio or HTTP)
|
|
|
100
100
|
- **Scheduled Actions** — `POST /keys/schedule` creates future-dated actions (revoke/suspend/topup) on API keys, `GET /keys/schedule` lists pending schedules with optional `?key=` filter, `DELETE /keys/schedule?id=...` cancels a schedule — max 20 per key, alias support, background execution timer, audit trail
|
|
101
101
|
- **Key Activity Timeline** — `GET /keys/activity?key=...` returns a unified chronological feed of audit events and usage events for a specific key — newest first, optional `?since=` and `?limit=` filters, alias support
|
|
102
102
|
- **Credit Reservations** — `POST /keys/reserve` holds credits, `POST /keys/reserve/commit` deducts held credits, `POST /keys/reserve/release` frees the hold, `GET /keys/reserve` lists active reservations — prevents overcommit, configurable TTL (10s–1h), max 50 per key, auto-expiry, audit trail
|
|
103
|
+
- **Request Log** — `GET /requests` queryable log of every tool call with timing, credits charged, status (allowed/denied), deny reason, key, and request ID — filter by key/tool/status/since, pagination, summary statistics (totals + avg duration), 5000-entry ring buffer
|
|
104
|
+
- **Tool Stats** — `GET /tools/stats` per-tool analytics: call counts, success rate, avg/p95 latency, credits consumed, deny reason breakdown, top 10 consumers — optional `?tool=` for detailed single-tool view, `?since=` filter
|
|
103
105
|
- **Config Hot Reload** — `POST /config/reload` reloads pricing, rate limits, webhooks, quotas, and behavior flags from config file without server restart
|
|
104
106
|
- **Webhook Events** — POST batched usage events to any URL for external billing/alerting
|
|
105
107
|
- **Config File Mode** — Load all settings from a JSON file (`--config`)
|
|
@@ -2075,6 +2077,134 @@ curl "http://localhost:3402/keys/reserve" -H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
|
2075
2077
|
|
|
2076
2078
|
TTL range: 10s to 1h (default 5 min). Max 50 reservations per key. Expired reservations auto-cleanup. Alias support. Rejects revoked/suspended keys. Audit trail (`credits.reserved` / `credits.committed` / `credits.released`).
|
|
2077
2079
|
|
|
2080
|
+
### Request Log
|
|
2081
|
+
|
|
2082
|
+
Queryable log of every MCP tool call with timing, credits, status, and deny reason:
|
|
2083
|
+
|
|
2084
|
+
```bash
|
|
2085
|
+
# Get all requests (newest first)
|
|
2086
|
+
curl "http://localhost:3402/requests" -H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2087
|
+
|
|
2088
|
+
# Filter by tool name
|
|
2089
|
+
curl "http://localhost:3402/requests?tool=my_tool" -H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2090
|
+
|
|
2091
|
+
# Filter by status (allowed or denied)
|
|
2092
|
+
curl "http://localhost:3402/requests?status=denied" -H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2093
|
+
|
|
2094
|
+
# Filter by key (partial match on masked key)
|
|
2095
|
+
curl "http://localhost:3402/requests?key=pg_abc1" -H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2096
|
+
|
|
2097
|
+
# Filter by time + pagination
|
|
2098
|
+
curl "http://localhost:3402/requests?since=2025-03-01T00:00:00Z&limit=50&offset=0" \
|
|
2099
|
+
-H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2100
|
+
|
|
2101
|
+
# Combine filters
|
|
2102
|
+
curl "http://localhost:3402/requests?tool=my_tool&status=allowed&limit=10" \
|
|
2103
|
+
-H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2104
|
+
```
|
|
2105
|
+
|
|
2106
|
+
**Response:**
|
|
2107
|
+
|
|
2108
|
+
```json
|
|
2109
|
+
{
|
|
2110
|
+
"total": 42,
|
|
2111
|
+
"offset": 0,
|
|
2112
|
+
"limit": 100,
|
|
2113
|
+
"summary": {
|
|
2114
|
+
"totalAllowed": 38,
|
|
2115
|
+
"totalDenied": 4,
|
|
2116
|
+
"totalCredits": 190,
|
|
2117
|
+
"avgDurationMs": 45
|
|
2118
|
+
},
|
|
2119
|
+
"requests": [
|
|
2120
|
+
{
|
|
2121
|
+
"id": 42,
|
|
2122
|
+
"timestamp": "2025-03-16T10:30:00Z",
|
|
2123
|
+
"tool": "my_tool",
|
|
2124
|
+
"key": "pg_abc1...2345",
|
|
2125
|
+
"status": "allowed",
|
|
2126
|
+
"credits": 5,
|
|
2127
|
+
"durationMs": 32,
|
|
2128
|
+
"requestId": "req_a1b2c3d4e5f6g7h8"
|
|
2129
|
+
},
|
|
2130
|
+
{
|
|
2131
|
+
"id": 41,
|
|
2132
|
+
"timestamp": "2025-03-16T10:29:55Z",
|
|
2133
|
+
"tool": "my_tool",
|
|
2134
|
+
"key": "pg_xyz9...8765",
|
|
2135
|
+
"status": "denied",
|
|
2136
|
+
"credits": 0,
|
|
2137
|
+
"durationMs": 1,
|
|
2138
|
+
"denyReason": "insufficient_credits",
|
|
2139
|
+
"requestId": "req_i9j0k1l2m3n4o5p6"
|
|
2140
|
+
}
|
|
2141
|
+
]
|
|
2142
|
+
}
|
|
2143
|
+
```
|
|
2144
|
+
|
|
2145
|
+
5000-entry ring buffer. Summary statistics are computed on filtered results. Deny reasons: `insufficient_credits`, `rate_limited`, `invalid_api_key`, `key_suspended`, `api_key_expired`, `tool_not_allowed`, `quota_exceeded`.
|
|
2146
|
+
|
|
2147
|
+
### Tool Stats
|
|
2148
|
+
|
|
2149
|
+
Per-tool analytics derived from the request log:
|
|
2150
|
+
|
|
2151
|
+
```bash
|
|
2152
|
+
# Overview of all tools
|
|
2153
|
+
curl "http://localhost:3402/tools/stats" -H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2154
|
+
|
|
2155
|
+
# Detailed stats for a specific tool
|
|
2156
|
+
curl "http://localhost:3402/tools/stats?tool=my_tool" -H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2157
|
+
|
|
2158
|
+
# Filter by time range
|
|
2159
|
+
curl "http://localhost:3402/tools/stats?since=2025-03-01T00:00:00Z" \
|
|
2160
|
+
-H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2161
|
+
```
|
|
2162
|
+
|
|
2163
|
+
**Response (overview):**
|
|
2164
|
+
|
|
2165
|
+
```json
|
|
2166
|
+
{
|
|
2167
|
+
"totalTools": 3,
|
|
2168
|
+
"totalCalls": 150,
|
|
2169
|
+
"tools": [
|
|
2170
|
+
{
|
|
2171
|
+
"tool": "my_tool",
|
|
2172
|
+
"totalCalls": 100,
|
|
2173
|
+
"allowed": 95,
|
|
2174
|
+
"denied": 5,
|
|
2175
|
+
"successRate": 95,
|
|
2176
|
+
"totalCredits": 475,
|
|
2177
|
+
"avgDurationMs": 42
|
|
2178
|
+
}
|
|
2179
|
+
]
|
|
2180
|
+
}
|
|
2181
|
+
```
|
|
2182
|
+
|
|
2183
|
+
**Response (detailed `?tool=my_tool`):**
|
|
2184
|
+
|
|
2185
|
+
```json
|
|
2186
|
+
{
|
|
2187
|
+
"tool": "my_tool",
|
|
2188
|
+
"totalCalls": 100,
|
|
2189
|
+
"allowed": 95,
|
|
2190
|
+
"denied": 5,
|
|
2191
|
+
"successRate": 95,
|
|
2192
|
+
"totalCredits": 475,
|
|
2193
|
+
"avgDurationMs": 42,
|
|
2194
|
+
"p95DurationMs": 120,
|
|
2195
|
+
"denyReasons": {
|
|
2196
|
+
"insufficient_credits": 3,
|
|
2197
|
+
"rate_limited": 2
|
|
2198
|
+
},
|
|
2199
|
+
"topConsumers": [
|
|
2200
|
+
{ "key": "pg_abc1...2345", "calls": 50, "credits": 250 },
|
|
2201
|
+
{ "key": "pg_xyz9...8765", "calls": 30, "credits": 150 }
|
|
2202
|
+
]
|
|
2203
|
+
}
|
|
2204
|
+
```
|
|
2205
|
+
|
|
2206
|
+
Top consumers limited to 10. Tools sorted by call count in overview. Data sourced from request log (5000-entry ring buffer).
|
|
2207
|
+
|
|
2078
2208
|
### IP Allowlisting
|
|
2079
2209
|
|
|
2080
2210
|
Restrict API keys to specific IP addresses or CIDR ranges:
|
package/dist/server.d.ts
CHANGED
|
@@ -117,6 +117,12 @@ export declare class PayGateServer {
|
|
|
117
117
|
private creditReservations;
|
|
118
118
|
/** Auto-incrementing reservation ID counter */
|
|
119
119
|
private nextReservationId;
|
|
120
|
+
/** Request log — ring buffer of tool call entries */
|
|
121
|
+
private requestLog;
|
|
122
|
+
/** Next request log entry ID */
|
|
123
|
+
private nextRequestLogId;
|
|
124
|
+
/** Max request log entries (ring buffer) */
|
|
125
|
+
private readonly maxRequestLogEntries;
|
|
120
126
|
/** Number of in-flight /mcp requests */
|
|
121
127
|
private inflight;
|
|
122
128
|
/** Config file path for hot reload (null if not using config file) */
|
|
@@ -308,6 +314,10 @@ export declare class PayGateServer {
|
|
|
308
314
|
* @returns Resolves when fully stopped.
|
|
309
315
|
*/
|
|
310
316
|
gracefulStop(timeoutMs?: number): Promise<void>;
|
|
317
|
+
private handleRequestLog;
|
|
318
|
+
private handleToolStats;
|
|
319
|
+
/** Calculate percentile from an array of numbers. */
|
|
320
|
+
private percentile;
|
|
311
321
|
}
|
|
312
322
|
export {};
|
|
313
323
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -235,6 +235,12 @@ class PayGateServer {
|
|
|
235
235
|
creditReservations = new Map();
|
|
236
236
|
/** Auto-incrementing reservation ID counter */
|
|
237
237
|
nextReservationId = 1;
|
|
238
|
+
/** Request log — ring buffer of tool call entries */
|
|
239
|
+
requestLog = [];
|
|
240
|
+
/** Next request log entry ID */
|
|
241
|
+
nextRequestLogId = 1;
|
|
242
|
+
/** Max request log entries (ring buffer) */
|
|
243
|
+
maxRequestLogEntries = 5000;
|
|
238
244
|
/** Number of in-flight /mcp requests */
|
|
239
245
|
inflight = 0;
|
|
240
246
|
/** Config file path for hot reload (null if not using config file) */
|
|
@@ -666,6 +672,12 @@ class PayGateServer {
|
|
|
666
672
|
return this.handleAuditExport(req, res);
|
|
667
673
|
case '/audit/stats':
|
|
668
674
|
return this.handleAuditStats(req, res);
|
|
675
|
+
case '/requests':
|
|
676
|
+
if (req.method === 'GET')
|
|
677
|
+
return this.handleRequestLog(req, res);
|
|
678
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
679
|
+
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
680
|
+
return;
|
|
669
681
|
// ─── Registry / Discovery endpoints ──────────────────────────────
|
|
670
682
|
case '/.well-known/mcp-payment':
|
|
671
683
|
return this.handlePaymentMetadata(req, res);
|
|
@@ -675,6 +687,12 @@ class PayGateServer {
|
|
|
675
687
|
return this.handleMetrics(req, res);
|
|
676
688
|
case '/analytics':
|
|
677
689
|
return this.handleAnalytics(req, res);
|
|
690
|
+
case '/tools/stats':
|
|
691
|
+
if (req.method === 'GET')
|
|
692
|
+
return this.handleToolStats(req, res);
|
|
693
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
694
|
+
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
695
|
+
return;
|
|
678
696
|
case '/alerts':
|
|
679
697
|
if (req.method === 'GET')
|
|
680
698
|
return this.handleGetAlerts(req, res);
|
|
@@ -915,6 +933,7 @@ class PayGateServer {
|
|
|
915
933
|
const pluginCtx = { apiKey, toolName, toolArgs, request };
|
|
916
934
|
pluginRequest = await this.plugins.executeBeforeToolCall(pluginCtx);
|
|
917
935
|
}
|
|
936
|
+
const toolCallStartTime = Date.now();
|
|
918
937
|
let response = await this.handler.handleRequest(pluginRequest, apiKey, clientIp, scopedTokenTools);
|
|
919
938
|
// Plugin: afterToolCall — let plugins modify the response
|
|
920
939
|
if (this.plugins.count > 0 && request.method === 'tools/call') {
|
|
@@ -982,6 +1001,49 @@ class PayGateServer {
|
|
|
982
1001
|
}
|
|
983
1002
|
}
|
|
984
1003
|
}
|
|
1004
|
+
// Record request log entry for tools/call
|
|
1005
|
+
if (request.method === 'tools/call') {
|
|
1006
|
+
const toolName = request.params?.name || 'unknown';
|
|
1007
|
+
const durationMs = Date.now() - toolCallStartTime;
|
|
1008
|
+
const isError = !!response.error;
|
|
1009
|
+
let denyReason;
|
|
1010
|
+
if (isError) {
|
|
1011
|
+
const msg = response.error.message || '';
|
|
1012
|
+
if (msg.includes('rate_limited'))
|
|
1013
|
+
denyReason = 'rate_limited';
|
|
1014
|
+
else if (msg.includes('insufficient_credits'))
|
|
1015
|
+
denyReason = 'insufficient_credits';
|
|
1016
|
+
else if (msg.includes('invalid_api_key'))
|
|
1017
|
+
denyReason = 'invalid_api_key';
|
|
1018
|
+
else if (msg.includes('key_suspended'))
|
|
1019
|
+
denyReason = 'key_suspended';
|
|
1020
|
+
else if (msg.includes('api_key_expired'))
|
|
1021
|
+
denyReason = 'api_key_expired';
|
|
1022
|
+
else if (msg.includes('tool_not_allowed'))
|
|
1023
|
+
denyReason = 'tool_not_allowed';
|
|
1024
|
+
else if (msg.includes('quota_exceeded'))
|
|
1025
|
+
denyReason = 'quota_exceeded';
|
|
1026
|
+
else
|
|
1027
|
+
denyReason = msg || 'denied';
|
|
1028
|
+
}
|
|
1029
|
+
const creditsCharged = isError ? 0 : this.gate.getToolPrice(toolName, request.params?.arguments);
|
|
1030
|
+
const logEntry = {
|
|
1031
|
+
id: this.nextRequestLogId++,
|
|
1032
|
+
timestamp: new Date().toISOString(),
|
|
1033
|
+
tool: toolName,
|
|
1034
|
+
key: (0, audit_1.maskKeyForAudit)(apiKey || 'anonymous'),
|
|
1035
|
+
status: (isError ? 'denied' : 'allowed'),
|
|
1036
|
+
credits: creditsCharged,
|
|
1037
|
+
durationMs,
|
|
1038
|
+
...(denyReason ? { denyReason } : {}),
|
|
1039
|
+
requestId,
|
|
1040
|
+
};
|
|
1041
|
+
this.requestLog.push(logEntry);
|
|
1042
|
+
// Enforce ring buffer size
|
|
1043
|
+
if (this.requestLog.length > this.maxRequestLogEntries) {
|
|
1044
|
+
this.requestLog = this.requestLog.slice(-this.maxRequestLogEntries);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
985
1047
|
// Build rate limit + credits headers for tools/call responses
|
|
986
1048
|
const rateLimitHeaders = this.buildRateLimitHeaders(apiKey, request);
|
|
987
1049
|
// Check if client accepts SSE
|
|
@@ -1239,6 +1301,8 @@ class PayGateServer {
|
|
|
1239
1301
|
keySchedule: 'GET /keys/schedule?key=... — List schedules + POST to create + DELETE to cancel (requires X-Admin-Key)',
|
|
1240
1302
|
keyActivity: 'GET /keys/activity?key=... — Unified activity timeline for a key (requires X-Admin-Key)',
|
|
1241
1303
|
creditReservations: 'POST /keys/reserve to hold credits, POST /keys/reserve/commit to deduct, POST /keys/reserve/release to release, GET /keys/reserve to list (requires X-Admin-Key)',
|
|
1304
|
+
requestLog: 'GET /requests — Queryable log of tool call requests with timing, credits, status (requires X-Admin-Key)',
|
|
1305
|
+
toolStats: 'GET /tools/stats — Per-tool call counts, success rates, latency, credits, and top consumers (requires X-Admin-Key)',
|
|
1242
1306
|
...(this.oauth ? {
|
|
1243
1307
|
oauthMetadata: 'GET /.well-known/oauth-authorization-server — OAuth 2.1 server metadata',
|
|
1244
1308
|
oauthRegister: 'POST /oauth/register — Register OAuth client',
|
|
@@ -6224,6 +6288,168 @@ class PayGateServer {
|
|
|
6224
6288
|
await this.redisSync.destroy();
|
|
6225
6289
|
}
|
|
6226
6290
|
}
|
|
6291
|
+
// ─── /requests — Request Log (queryable tool call log) ──────────────────────
|
|
6292
|
+
handleRequestLog(req, res) {
|
|
6293
|
+
if (!this.checkAdmin(req, res))
|
|
6294
|
+
return;
|
|
6295
|
+
const urlParts = req.url?.split('?') || [];
|
|
6296
|
+
const params = new URLSearchParams(urlParts[1] || '');
|
|
6297
|
+
// Filters
|
|
6298
|
+
const keyFilter = params.get('key');
|
|
6299
|
+
const toolFilter = params.get('tool');
|
|
6300
|
+
const statusFilter = params.get('status'); // 'allowed' | 'denied'
|
|
6301
|
+
const sinceFilter = params.get('since');
|
|
6302
|
+
const limit = Math.min(1000, Math.max(1, parseInt(params.get('limit') || '100', 10) || 100));
|
|
6303
|
+
const offset = Math.max(0, parseInt(params.get('offset') || '0', 10) || 0);
|
|
6304
|
+
let filtered = this.requestLog;
|
|
6305
|
+
// Filter by key (partial match on masked key)
|
|
6306
|
+
if (keyFilter) {
|
|
6307
|
+
const kf = keyFilter.toLowerCase();
|
|
6308
|
+
filtered = filtered.filter(e => e.key.toLowerCase().includes(kf));
|
|
6309
|
+
}
|
|
6310
|
+
// Filter by tool name (exact match)
|
|
6311
|
+
if (toolFilter) {
|
|
6312
|
+
filtered = filtered.filter(e => e.tool === toolFilter);
|
|
6313
|
+
}
|
|
6314
|
+
// Filter by status
|
|
6315
|
+
if (statusFilter === 'allowed' || statusFilter === 'denied') {
|
|
6316
|
+
filtered = filtered.filter(e => e.status === statusFilter);
|
|
6317
|
+
}
|
|
6318
|
+
// Filter by since timestamp
|
|
6319
|
+
if (sinceFilter) {
|
|
6320
|
+
const sinceTime = new Date(sinceFilter).getTime();
|
|
6321
|
+
if (!isNaN(sinceTime)) {
|
|
6322
|
+
filtered = filtered.filter(e => new Date(e.timestamp).getTime() >= sinceTime);
|
|
6323
|
+
}
|
|
6324
|
+
}
|
|
6325
|
+
const total = filtered.length;
|
|
6326
|
+
// Return newest first
|
|
6327
|
+
const reversed = [...filtered].reverse();
|
|
6328
|
+
const page = reversed.slice(offset, offset + limit);
|
|
6329
|
+
// Compute summary stats
|
|
6330
|
+
const totalAllowed = filtered.filter(e => e.status === 'allowed').length;
|
|
6331
|
+
const totalDenied = filtered.filter(e => e.status === 'denied').length;
|
|
6332
|
+
const totalCredits = filtered.reduce((sum, e) => sum + e.credits, 0);
|
|
6333
|
+
const avgDurationMs = filtered.length > 0
|
|
6334
|
+
? Math.round(filtered.reduce((sum, e) => sum + e.durationMs, 0) / filtered.length)
|
|
6335
|
+
: 0;
|
|
6336
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
6337
|
+
res.end(JSON.stringify({
|
|
6338
|
+
total,
|
|
6339
|
+
offset,
|
|
6340
|
+
limit,
|
|
6341
|
+
summary: {
|
|
6342
|
+
totalAllowed,
|
|
6343
|
+
totalDenied,
|
|
6344
|
+
totalCredits,
|
|
6345
|
+
avgDurationMs,
|
|
6346
|
+
},
|
|
6347
|
+
requests: page,
|
|
6348
|
+
}));
|
|
6349
|
+
}
|
|
6350
|
+
// ─── /tools/stats — Per-tool analytics from request log ────────────────────
|
|
6351
|
+
handleToolStats(req, res) {
|
|
6352
|
+
if (!this.checkAdmin(req, res))
|
|
6353
|
+
return;
|
|
6354
|
+
const urlParts = req.url?.split('?') || [];
|
|
6355
|
+
const params = new URLSearchParams(urlParts[1] || '');
|
|
6356
|
+
const sinceFilter = params.get('since');
|
|
6357
|
+
const toolFilter = params.get('tool');
|
|
6358
|
+
let entries = this.requestLog;
|
|
6359
|
+
// Filter by since
|
|
6360
|
+
if (sinceFilter) {
|
|
6361
|
+
const sinceTime = new Date(sinceFilter).getTime();
|
|
6362
|
+
if (!isNaN(sinceTime)) {
|
|
6363
|
+
entries = entries.filter(e => new Date(e.timestamp).getTime() >= sinceTime);
|
|
6364
|
+
}
|
|
6365
|
+
}
|
|
6366
|
+
// If specific tool requested, return detailed stats for just that tool
|
|
6367
|
+
if (toolFilter) {
|
|
6368
|
+
const toolEntries = entries.filter(e => e.tool === toolFilter);
|
|
6369
|
+
const allowed = toolEntries.filter(e => e.status === 'allowed');
|
|
6370
|
+
const denied = toolEntries.filter(e => e.status === 'denied');
|
|
6371
|
+
const totalCredits = toolEntries.reduce((sum, e) => sum + e.credits, 0);
|
|
6372
|
+
const avgDurationMs = toolEntries.length > 0
|
|
6373
|
+
? Math.round(toolEntries.reduce((sum, e) => sum + e.durationMs, 0) / toolEntries.length)
|
|
6374
|
+
: 0;
|
|
6375
|
+
const p95 = this.percentile(toolEntries.map(e => e.durationMs), 95);
|
|
6376
|
+
// Deny reason breakdown
|
|
6377
|
+
const denyReasons = {};
|
|
6378
|
+
for (const e of denied) {
|
|
6379
|
+
const reason = e.denyReason || 'unknown';
|
|
6380
|
+
denyReasons[reason] = (denyReasons[reason] || 0) + 1;
|
|
6381
|
+
}
|
|
6382
|
+
// Top consumers by call count
|
|
6383
|
+
const consumerCalls = {};
|
|
6384
|
+
const consumerCredits = {};
|
|
6385
|
+
for (const e of toolEntries) {
|
|
6386
|
+
consumerCalls[e.key] = (consumerCalls[e.key] || 0) + 1;
|
|
6387
|
+
consumerCredits[e.key] = (consumerCredits[e.key] || 0) + e.credits;
|
|
6388
|
+
}
|
|
6389
|
+
const topConsumers = Object.entries(consumerCalls)
|
|
6390
|
+
.sort((a, b) => b[1] - a[1])
|
|
6391
|
+
.slice(0, 10)
|
|
6392
|
+
.map(([key, calls]) => ({ key, calls, credits: consumerCredits[key] || 0 }));
|
|
6393
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
6394
|
+
res.end(JSON.stringify({
|
|
6395
|
+
tool: toolFilter,
|
|
6396
|
+
totalCalls: toolEntries.length,
|
|
6397
|
+
allowed: allowed.length,
|
|
6398
|
+
denied: denied.length,
|
|
6399
|
+
successRate: toolEntries.length > 0
|
|
6400
|
+
? Math.round((allowed.length / toolEntries.length) * 10000) / 100
|
|
6401
|
+
: 0,
|
|
6402
|
+
totalCredits,
|
|
6403
|
+
avgDurationMs,
|
|
6404
|
+
p95DurationMs: p95,
|
|
6405
|
+
denyReasons,
|
|
6406
|
+
topConsumers,
|
|
6407
|
+
}));
|
|
6408
|
+
return;
|
|
6409
|
+
}
|
|
6410
|
+
// Aggregate stats per tool
|
|
6411
|
+
const toolMap = {};
|
|
6412
|
+
for (const e of entries) {
|
|
6413
|
+
if (!toolMap[e.tool]) {
|
|
6414
|
+
toolMap[e.tool] = { calls: 0, allowed: 0, denied: 0, credits: 0, totalDurationMs: 0 };
|
|
6415
|
+
}
|
|
6416
|
+
const t = toolMap[e.tool];
|
|
6417
|
+
t.calls++;
|
|
6418
|
+
if (e.status === 'allowed')
|
|
6419
|
+
t.allowed++;
|
|
6420
|
+
else
|
|
6421
|
+
t.denied++;
|
|
6422
|
+
t.credits += e.credits;
|
|
6423
|
+
t.totalDurationMs += e.durationMs;
|
|
6424
|
+
}
|
|
6425
|
+
const tools = Object.entries(toolMap)
|
|
6426
|
+
.map(([tool, stats]) => ({
|
|
6427
|
+
tool,
|
|
6428
|
+
totalCalls: stats.calls,
|
|
6429
|
+
allowed: stats.allowed,
|
|
6430
|
+
denied: stats.denied,
|
|
6431
|
+
successRate: stats.calls > 0
|
|
6432
|
+
? Math.round((stats.allowed / stats.calls) * 10000) / 100
|
|
6433
|
+
: 0,
|
|
6434
|
+
totalCredits: stats.credits,
|
|
6435
|
+
avgDurationMs: stats.calls > 0 ? Math.round(stats.totalDurationMs / stats.calls) : 0,
|
|
6436
|
+
}))
|
|
6437
|
+
.sort((a, b) => b.totalCalls - a.totalCalls);
|
|
6438
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
6439
|
+
res.end(JSON.stringify({
|
|
6440
|
+
totalTools: tools.length,
|
|
6441
|
+
totalCalls: entries.length,
|
|
6442
|
+
tools,
|
|
6443
|
+
}));
|
|
6444
|
+
}
|
|
6445
|
+
/** Calculate percentile from an array of numbers. */
|
|
6446
|
+
percentile(values, p) {
|
|
6447
|
+
if (values.length === 0)
|
|
6448
|
+
return 0;
|
|
6449
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
6450
|
+
const idx = Math.ceil((p / 100) * sorted.length) - 1;
|
|
6451
|
+
return sorted[Math.max(0, idx)];
|
|
6452
|
+
}
|
|
6227
6453
|
}
|
|
6228
6454
|
exports.PayGateServer = PayGateServer;
|
|
6229
6455
|
//# sourceMappingURL=server.js.map
|