paygate-mcp 7.5.0 → 7.7.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 +68 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +230 -0
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -102,6 +102,8 @@ Agent → PayGate (auth + billing) → Your MCP Server (stdio or HTTP)
|
|
|
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
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
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
|
|
105
|
+
- **Request Log Export** — `GET /requests/export` exports the full request log as JSON or CSV with Content-Disposition headers — filter by key/tool/status/since/until, combined time-window queries, no pagination limit
|
|
106
|
+
- **Tool Call Dry Run** — `POST /requests/dry-run` simulates a tool call without executing — checks key validity, ACL, rate limits, credits, and spending limits, returns predicted outcome with credits-after calculation and rate limit status
|
|
105
107
|
- **Config Hot Reload** — `POST /config/reload` reloads pricing, rate limits, webhooks, quotas, and behavior flags from config file without server restart
|
|
106
108
|
- **Webhook Events** — POST batched usage events to any URL for external billing/alerting
|
|
107
109
|
- **Config File Mode** — Load all settings from a JSON file (`--config`)
|
|
@@ -2205,6 +2207,72 @@ curl "http://localhost:3402/tools/stats?since=2025-03-01T00:00:00Z" \
|
|
|
2205
2207
|
|
|
2206
2208
|
Top consumers limited to 10. Tools sorted by call count in overview. Data sourced from request log (5000-entry ring buffer).
|
|
2207
2209
|
|
|
2210
|
+
### Request Log Export
|
|
2211
|
+
|
|
2212
|
+
Export the request log as JSON or CSV for offline analysis:
|
|
2213
|
+
|
|
2214
|
+
```bash
|
|
2215
|
+
# Export as JSON (default)
|
|
2216
|
+
curl "http://localhost:3402/requests/export" \
|
|
2217
|
+
-H "X-Admin-Key: YOUR_ADMIN_KEY" -o paygate-requests.json
|
|
2218
|
+
|
|
2219
|
+
# Export as CSV
|
|
2220
|
+
curl "http://localhost:3402/requests/export?format=csv" \
|
|
2221
|
+
-H "X-Admin-Key: YOUR_ADMIN_KEY" -o paygate-requests.csv
|
|
2222
|
+
|
|
2223
|
+
# Export with filters
|
|
2224
|
+
curl "http://localhost:3402/requests/export?tool=my_tool&status=denied&since=2025-03-01T00:00:00Z&until=2025-03-31T23:59:59Z" \
|
|
2225
|
+
-H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2226
|
+
```
|
|
2227
|
+
|
|
2228
|
+
| Parameter | Description |
|
|
2229
|
+
|-----------|-------------|
|
|
2230
|
+
| `format` | `json` (default) or `csv` |
|
|
2231
|
+
| `key` | Filter by API key (partial match) |
|
|
2232
|
+
| `tool` | Filter by tool name (exact match) |
|
|
2233
|
+
| `status` | `allowed` or `denied` |
|
|
2234
|
+
| `since` | ISO 8601 start timestamp |
|
|
2235
|
+
| `until` | ISO 8601 end timestamp |
|
|
2236
|
+
|
|
2237
|
+
Both formats include Content-Disposition headers for automatic file download. Unlike `/requests`, the export endpoint returns **all** matching entries (no pagination limit). CSV includes proper quoting for values with commas.
|
|
2238
|
+
|
|
2239
|
+
### Tool Call Dry Run
|
|
2240
|
+
|
|
2241
|
+
Simulate a tool call to check if it would be allowed — without deducting credits or incrementing rate limits:
|
|
2242
|
+
|
|
2243
|
+
```bash
|
|
2244
|
+
curl -X POST http://localhost:3402/requests/dry-run \
|
|
2245
|
+
-H "X-Admin-Key: YOUR_ADMIN_KEY" \
|
|
2246
|
+
-d '{"key": "pg_...", "tool": "my_tool"}'
|
|
2247
|
+
```
|
|
2248
|
+
|
|
2249
|
+
**Response (allowed):**
|
|
2250
|
+
|
|
2251
|
+
```json
|
|
2252
|
+
{
|
|
2253
|
+
"allowed": true,
|
|
2254
|
+
"tool": "my_tool",
|
|
2255
|
+
"creditsRequired": 5,
|
|
2256
|
+
"creditsAvailable": 100,
|
|
2257
|
+
"creditsAfter": 95,
|
|
2258
|
+
"rateLimit": { "used": 3, "limit": 60, "remaining": 57, "resetInMs": 45000 }
|
|
2259
|
+
}
|
|
2260
|
+
```
|
|
2261
|
+
|
|
2262
|
+
**Response (denied):**
|
|
2263
|
+
|
|
2264
|
+
```json
|
|
2265
|
+
{
|
|
2266
|
+
"allowed": false,
|
|
2267
|
+
"reason": "insufficient_credits: need 5, have 2",
|
|
2268
|
+
"tool": "my_tool",
|
|
2269
|
+
"creditsRequired": 5,
|
|
2270
|
+
"creditsAvailable": 2
|
|
2271
|
+
}
|
|
2272
|
+
```
|
|
2273
|
+
|
|
2274
|
+
Checks key validity, suspension, tool ACL, rate limits, credit balance, and spending limits. Supports alias keys. Useful for agents that want to pre-flight check a call before committing.
|
|
2275
|
+
|
|
2208
2276
|
### IP Allowlisting
|
|
2209
2277
|
|
|
2210
2278
|
Restrict API keys to specific IP addresses or CIDR ranges:
|
package/dist/server.d.ts
CHANGED
|
@@ -316,6 +316,8 @@ export declare class PayGateServer {
|
|
|
316
316
|
gracefulStop(timeoutMs?: number): Promise<void>;
|
|
317
317
|
private handleRequestLog;
|
|
318
318
|
private handleToolStats;
|
|
319
|
+
private handleRequestDryRun;
|
|
320
|
+
private handleRequestLogExport;
|
|
319
321
|
/** Calculate percentile from an array of numbers. */
|
|
320
322
|
private percentile;
|
|
321
323
|
}
|
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":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAgB,eAAe,EAA0B,MAAM,MAAM,CAAC;AAI7E,OAAO,EAAE,aAAa,EAAkB,mBAAmB,EAAkB,MAAM,SAAS,CAAC;AAU7F,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE7C,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,cAAc,EAAqD,MAAM,WAAW,CAAC;AAC9F,OAAO,EAAE,WAAW,EAAmB,MAAM,SAAS,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAS,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,eAAe,EAA6B,MAAM,cAAc,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,aAAa,EAAqB,MAAM,UAAU,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAKrD,0EAA0E;AAC1E,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,sFAAsF;AACtF,wBAAgB,YAAY,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,GAAG,SAAS,CAErE;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,eAAe,EAAE,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAsBvF;AAyCD,yCAAyC;AACzC,KAAK,YAAY,GAAG,QAAQ,GAAG,YAAY,CAAC;AAa5C,qBAAa,aAAa;IACxB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,0DAA0D;IAC1D,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC1C,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,oEAAoE;IACpE,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,mEAAmE;IACnE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,aAAa,CAAqC;IAC1D,wDAAwD;IACxD,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAQ;IAC5C,oDAAoD;IACpD,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,2BAA2B;IAC3B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,0CAA0C;IAC1C,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,8CAA8C;IAC9C,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,mCAAmC;IACnC,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,gCAAgC;IAChC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,yEAAyE;IACzE,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAAQ;IAC5C,4DAA4D;IAC5D,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;IACpC,qDAAqD;IACrD,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,oCAAoC;IACpC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,oDAAoD;IACpD,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IACvC,sCAAsC;IACtC,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC,yCAAyC;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsB;IAChD,gEAAgE;IAChE,OAAO,CAAC,QAAQ,CAAS;IACzB,wEAAwE;IACxE,OAAO,CAAC,eAAe,CAAS;IAChC,mDAAmD;IACnD,OAAO,CAAC,kBAAkB,CAAiC;IAC3D,kDAAkD;IAClD,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,gDAAgD;IAChD,OAAO,CAAC,iBAAiB,CAAqF;IAC9G,8CAA8C;IAC9C,OAAO,CAAC,wBAAwB,CAA+C;IAC/E,8BAA8B;IAC9B,OAAO,CAAC,gBAAgB,CAOhB;IACR,2CAA2C;IAC3C,OAAO,CAAC,aAAa,CAA+C;IACpE,4CAA4C;IAC5C,OAAO,CAAC,cAAc,CAAK;IAC3B,kCAAkC;IAClC,OAAO,CAAC,kBAAkB,CAOX;IACf,+CAA+C;IAC/C,OAAO,CAAC,iBAAiB,CAAK;IAC9B,qDAAqD;IACrD,OAAO,CAAC,UAAU,CAUV;IACR,gCAAgC;IAChC,OAAO,CAAC,gBAAgB,CAAK;IAC7B,4CAA4C;IAC5C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAQ;IAC7C,wCAAwC;IACxC,OAAO,CAAC,QAAQ,CAAK;IACrB,sEAAsE;IACtE,OAAO,CAAC,UAAU,CAAuB;IAEzC,0DAA0D;IAC1D,OAAO,KAAK,OAAO,GAElB;gBAGC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,EAC1D,QAAQ,CAAC,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,mBAAmB,CAAC,EAAE,MAAM,EAC5B,OAAO,CAAC,EAAE,mBAAmB,EAAE,EAC/B,QAAQ,CAAC,EAAE,MAAM;IAsMnB;;;OAGG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIjC;;;;;;;;;;;OAWG;IACH,GAAG,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IAK1B,KAAK,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;YA0C5C,aAAa;YAoUb,SAAS;IAmQvB;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IA+C1B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAyB9B;;;;OAIG;IACH,OAAO,CAAC,aAAa;IAyCrB;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAuC7B,OAAO,CAAC,UAAU;IA8GlB,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,YAAY;IAyCpB,OAAO,CAAC,UAAU;IAuElB,OAAO,CAAC,kBAAkB;IA0D1B,kEAAkE;IAClE,OAAO,CAAC,OAAO;YAWD,eAAe;IAqH7B,OAAO,CAAC,cAAc;YA0CR,WAAW;YAuEX,oBAAoB;YAwHpB,oBAAoB;IA4IlC,OAAO,CAAC,eAAe;YAoDT,eAAe;YAsEf,eAAe;YAsDf,gBAAgB;YAkEhB,eAAe;YAgEf,cAAc;YAuFd,cAAc;YAoEd,eAAe;YA0Df,YAAY;YAkDZ,eAAe;YAwDf,cAAc;YA+Dd,aAAa;YAsDb,oBAAoB;YAsDpB,qBAAqB;IAgCnC,OAAO,CAAC,cAAc;IA2CtB,OAAO,CAAC,kBAAkB;IAiC1B,OAAO,CAAC,cAAc;IAyEtB,OAAO,CAAC,qBAAqB;IAsD7B,OAAO,CAAC,iBAAiB;IAuEzB,OAAO,CAAC,mBAAmB;IA8C3B,OAAO,CAAC,sBAAsB;IAwD9B,OAAO,CAAC,mBAAmB;IAoG3B,OAAO,CAAC,eAAe;YAiJT,kBAAkB;IAoFhC,OAAO,CAAC,aAAa;YAuDP,YAAY;IAkD1B,OAAO,CAAC,WAAW;YA+CL,mBAAmB;IAmCjC,OAAO,CAAC,eAAe;IAYvB,+EAA+E;IAC/E,OAAO,CAAC,mBAAmB;IAU3B,oEAAoE;YACtD,mBAAmB;IA4DjC,yDAAyD;YAC3C,oBAAoB;IAuFlC,yCAAyC;YAC3B,gBAAgB;IA8E9B,uDAAuD;YACzC,iBAAiB;IAiC/B,sEAAsE;IACtE,OAAO,CAAC,kBAAkB;IAqB1B,OAAO,CAAC,qBAAqB;IAO7B,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,eAAe;IA0BvB,OAAO,CAAC,eAAe;YAYT,qBAAqB;IAmDnC,OAAO,CAAC,oBAAoB;IAiB5B,OAAO,CAAC,sBAAsB;YAwBhB,mBAAmB;IAoDjC,OAAO,CAAC,oBAAoB;IAgB5B,OAAO,CAAC,oBAAoB;IA0D5B,OAAO,CAAC,sBAAsB;IA2D9B,OAAO,CAAC,cAAc;IAyBtB,OAAO,CAAC,aAAa;IAiErB,OAAO,CAAC,gBAAgB;IAkDxB,OAAO,CAAC,kBAAkB;IA6B1B,OAAO,CAAC,oBAAoB;IAiG5B,OAAO,CAAC,oBAAoB;IAmC5B,gFAAgF;IAChF,OAAO,CAAC,uBAAuB;IAiD/B,OAAO,CAAC,iBAAiB;IAmGzB,OAAO,CAAC,sBAAsB;IAgC9B,OAAO,CAAC,uBAAuB;IAqG/B,OAAO,CAAC,uBAAuB;IAqE/B,OAAO,CAAC,wBAAwB;IA+ChC,uEAAuE;IACvE,OAAO,CAAC,cAAc;IAQtB,mCAAmC;IACnC,OAAO,CAAC,0BAA0B;YAWpB,kBAAkB;IA4IhC,OAAO,CAAC,kBAAkB;IA8B1B,OAAO,CAAC,gBAAgB;IA6CxB,OAAO,CAAC,kBAAkB;IAgC1B,OAAO,CAAC,mBAAmB;YAiCb,iBAAiB;IA6H/B,OAAO,CAAC,wBAAwB;YAclB,yBAAyB;YAsCzB,yBAAyB;YAiDzB,yBAAyB;IA4CvC,OAAO,CAAC,WAAW;IA0BnB,OAAO,CAAC,iBAAiB;IAgCzB,OAAO,CAAC,gBAAgB;IAcxB,OAAO,CAAC,UAAU;IAiClB,OAAO,CAAC,eAAe;YAiBT,gBAAgB;YA4ChB,gBAAgB;YA6ChB,gBAAgB;YAsChB,mBAAmB;YAsDnB,mBAAmB;IA8CjC,OAAO,CAAC,eAAe;IA8BvB,OAAO,CAAC,oBAAoB;YAgBd,iBAAiB;YAyDjB,iBAAiB;IAiE/B,OAAO,CAAC,uBAAuB;IAyB/B,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,gBAAgB;YAOV,iBAAiB;YA2CjB,iBAAiB;YAuDjB,iBAAiB;YAyCjB,sBAAsB;YAsDtB,wBAAwB;IAiDtC,OAAO,CAAC,mBAAmB;YAsBb,oBAAoB;YAwDpB,oBAAoB;IAwDlC,OAAO,CAAC,mBAAmB;YAQb,oBAAoB;YAsCpB,oBAAoB;IAuClC;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,eAAe;IAUvB,iFAAiF;IACjF,OAAO,CAAC,iBAAiB;IAuBzB,OAAO,CAAC,QAAQ;IAkBV,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAqC3B;;;;;;;OAOG;IACG,YAAY,CAAC,SAAS,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAgDrD,OAAO,CAAC,gBAAgB;IAuExB,OAAO,CAAC,eAAe;YA+GT,mBAAmB;IAgJjC,OAAO,CAAC,sBAAsB;IAwF9B,qDAAqD;IACrD,OAAO,CAAC,UAAU;CAMnB"}
|
package/dist/server.js
CHANGED
|
@@ -678,6 +678,18 @@ class PayGateServer {
|
|
|
678
678
|
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
679
679
|
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
680
680
|
return;
|
|
681
|
+
case '/requests/export':
|
|
682
|
+
if (req.method === 'GET')
|
|
683
|
+
return this.handleRequestLogExport(req, res);
|
|
684
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
685
|
+
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
686
|
+
return;
|
|
687
|
+
case '/requests/dry-run':
|
|
688
|
+
if (req.method === 'POST')
|
|
689
|
+
return this.handleRequestDryRun(req, res);
|
|
690
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
691
|
+
res.end(JSON.stringify({ error: 'Method not allowed. Use POST.' }));
|
|
692
|
+
return;
|
|
681
693
|
// ─── Registry / Discovery endpoints ──────────────────────────────
|
|
682
694
|
case '/.well-known/mcp-payment':
|
|
683
695
|
return this.handlePaymentMetadata(req, res);
|
|
@@ -1302,6 +1314,8 @@ class PayGateServer {
|
|
|
1302
1314
|
keyActivity: 'GET /keys/activity?key=... — Unified activity timeline for a key (requires X-Admin-Key)',
|
|
1303
1315
|
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
1316
|
requestLog: 'GET /requests — Queryable log of tool call requests with timing, credits, status (requires X-Admin-Key)',
|
|
1317
|
+
requestLogExport: 'GET /requests/export — Export request log as JSON or CSV with filters (requires X-Admin-Key)',
|
|
1318
|
+
requestDryRun: 'POST /requests/dry-run — Simulate a tool call without executing (requires X-Admin-Key)',
|
|
1305
1319
|
toolStats: 'GET /tools/stats — Per-tool call counts, success rates, latency, credits, and top consumers (requires X-Admin-Key)',
|
|
1306
1320
|
...(this.oauth ? {
|
|
1307
1321
|
oauthMetadata: 'GET /.well-known/oauth-authorization-server — OAuth 2.1 server metadata',
|
|
@@ -6442,6 +6456,222 @@ class PayGateServer {
|
|
|
6442
6456
|
tools,
|
|
6443
6457
|
}));
|
|
6444
6458
|
}
|
|
6459
|
+
// ─── /requests/dry-run — Simulate a tool call without executing ─────────────
|
|
6460
|
+
async handleRequestDryRun(req, res) {
|
|
6461
|
+
if (!this.checkAdmin(req, res))
|
|
6462
|
+
return;
|
|
6463
|
+
try {
|
|
6464
|
+
const raw = await this.readBody(req);
|
|
6465
|
+
try {
|
|
6466
|
+
const params = JSON.parse(raw);
|
|
6467
|
+
const apiKey = params.key;
|
|
6468
|
+
const toolName = params.tool;
|
|
6469
|
+
if (!apiKey || typeof apiKey !== 'string') {
|
|
6470
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
6471
|
+
res.end(JSON.stringify({ error: 'Missing required field: key' }));
|
|
6472
|
+
return;
|
|
6473
|
+
}
|
|
6474
|
+
if (!toolName || typeof toolName !== 'string') {
|
|
6475
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
6476
|
+
res.end(JSON.stringify({ error: 'Missing required field: tool' }));
|
|
6477
|
+
return;
|
|
6478
|
+
}
|
|
6479
|
+
// Step 1: Key lookup (resolveKeyRaw handles alias resolution)
|
|
6480
|
+
const keyRecord = this.gate.store.resolveKeyRaw(apiKey);
|
|
6481
|
+
if (!keyRecord) {
|
|
6482
|
+
const isExpired = this.gate.store.isExpired(apiKey);
|
|
6483
|
+
const reason = isExpired ? 'api_key_expired' : 'invalid_api_key';
|
|
6484
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
6485
|
+
res.end(JSON.stringify({
|
|
6486
|
+
allowed: false,
|
|
6487
|
+
reason,
|
|
6488
|
+
tool: toolName,
|
|
6489
|
+
creditsRequired: 0,
|
|
6490
|
+
creditsAvailable: 0,
|
|
6491
|
+
}));
|
|
6492
|
+
return;
|
|
6493
|
+
}
|
|
6494
|
+
// Step 2: Suspended?
|
|
6495
|
+
if (keyRecord.suspended) {
|
|
6496
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
6497
|
+
res.end(JSON.stringify({
|
|
6498
|
+
allowed: false,
|
|
6499
|
+
reason: 'key_suspended',
|
|
6500
|
+
tool: toolName,
|
|
6501
|
+
creditsRequired: 0,
|
|
6502
|
+
creditsAvailable: keyRecord.credits,
|
|
6503
|
+
}));
|
|
6504
|
+
return;
|
|
6505
|
+
}
|
|
6506
|
+
// Step 3: Tool ACL
|
|
6507
|
+
const effectiveAllowed = keyRecord.allowedTools || [];
|
|
6508
|
+
const effectiveDenied = keyRecord.deniedTools || [];
|
|
6509
|
+
if (effectiveDenied.includes(toolName)) {
|
|
6510
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
6511
|
+
res.end(JSON.stringify({
|
|
6512
|
+
allowed: false,
|
|
6513
|
+
reason: `tool_not_allowed: ${toolName} is in deniedTools`,
|
|
6514
|
+
tool: toolName,
|
|
6515
|
+
creditsRequired: 0,
|
|
6516
|
+
creditsAvailable: keyRecord.credits,
|
|
6517
|
+
}));
|
|
6518
|
+
return;
|
|
6519
|
+
}
|
|
6520
|
+
if (effectiveAllowed.length > 0 && !effectiveAllowed.includes(toolName)) {
|
|
6521
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
6522
|
+
res.end(JSON.stringify({
|
|
6523
|
+
allowed: false,
|
|
6524
|
+
reason: `tool_not_allowed: ${toolName} not in allowedTools`,
|
|
6525
|
+
tool: toolName,
|
|
6526
|
+
creditsRequired: 0,
|
|
6527
|
+
creditsAvailable: keyRecord.credits,
|
|
6528
|
+
}));
|
|
6529
|
+
return;
|
|
6530
|
+
}
|
|
6531
|
+
// Step 4: Rate limit check (read-only)
|
|
6532
|
+
const rateStatus = this.gate.rateLimiter.getStatus(keyRecord.key);
|
|
6533
|
+
if (rateStatus.limit > 0 && rateStatus.remaining <= 0) {
|
|
6534
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
6535
|
+
res.end(JSON.stringify({
|
|
6536
|
+
allowed: false,
|
|
6537
|
+
reason: 'rate_limited',
|
|
6538
|
+
tool: toolName,
|
|
6539
|
+
creditsRequired: 0,
|
|
6540
|
+
creditsAvailable: keyRecord.credits,
|
|
6541
|
+
rateLimit: rateStatus,
|
|
6542
|
+
}));
|
|
6543
|
+
return;
|
|
6544
|
+
}
|
|
6545
|
+
// Step 5: Credits check
|
|
6546
|
+
const creditsRequired = this.gate.getToolPrice(toolName, params.arguments);
|
|
6547
|
+
if (keyRecord.credits < creditsRequired) {
|
|
6548
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
6549
|
+
res.end(JSON.stringify({
|
|
6550
|
+
allowed: false,
|
|
6551
|
+
reason: `insufficient_credits: need ${creditsRequired}, have ${keyRecord.credits}`,
|
|
6552
|
+
tool: toolName,
|
|
6553
|
+
creditsRequired,
|
|
6554
|
+
creditsAvailable: keyRecord.credits,
|
|
6555
|
+
}));
|
|
6556
|
+
return;
|
|
6557
|
+
}
|
|
6558
|
+
// Step 6: Spending limit
|
|
6559
|
+
if (keyRecord.spendingLimit > 0) {
|
|
6560
|
+
const wouldSpend = keyRecord.totalSpent + creditsRequired;
|
|
6561
|
+
if (wouldSpend > keyRecord.spendingLimit) {
|
|
6562
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
6563
|
+
res.end(JSON.stringify({
|
|
6564
|
+
allowed: false,
|
|
6565
|
+
reason: `spending_limit_exceeded: limit ${keyRecord.spendingLimit}, spent ${keyRecord.totalSpent}, need ${creditsRequired}`,
|
|
6566
|
+
tool: toolName,
|
|
6567
|
+
creditsRequired,
|
|
6568
|
+
creditsAvailable: keyRecord.credits,
|
|
6569
|
+
}));
|
|
6570
|
+
return;
|
|
6571
|
+
}
|
|
6572
|
+
}
|
|
6573
|
+
// All checks passed — would be allowed
|
|
6574
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
6575
|
+
res.end(JSON.stringify({
|
|
6576
|
+
allowed: true,
|
|
6577
|
+
tool: toolName,
|
|
6578
|
+
creditsRequired,
|
|
6579
|
+
creditsAvailable: keyRecord.credits,
|
|
6580
|
+
creditsAfter: keyRecord.credits - creditsRequired,
|
|
6581
|
+
...(rateStatus.limit > 0 ? { rateLimit: rateStatus } : {}),
|
|
6582
|
+
}));
|
|
6583
|
+
}
|
|
6584
|
+
catch {
|
|
6585
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
6586
|
+
res.end(JSON.stringify({ error: 'Invalid JSON body' }));
|
|
6587
|
+
}
|
|
6588
|
+
}
|
|
6589
|
+
catch {
|
|
6590
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
6591
|
+
res.end(JSON.stringify({ error: 'Failed to read request body' }));
|
|
6592
|
+
}
|
|
6593
|
+
}
|
|
6594
|
+
// ─── /requests/export — Export request log as JSON or CSV ───────────────────
|
|
6595
|
+
handleRequestLogExport(req, res) {
|
|
6596
|
+
if (!this.checkAdmin(req, res))
|
|
6597
|
+
return;
|
|
6598
|
+
const urlParts = req.url?.split('?') || [];
|
|
6599
|
+
const params = new URLSearchParams(urlParts[1] || '');
|
|
6600
|
+
const format = params.get('format') || 'json';
|
|
6601
|
+
const keyFilter = params.get('key');
|
|
6602
|
+
const toolFilter = params.get('tool');
|
|
6603
|
+
const statusFilter = params.get('status');
|
|
6604
|
+
const sinceFilter = params.get('since');
|
|
6605
|
+
const untilFilter = params.get('until');
|
|
6606
|
+
let filtered = this.requestLog;
|
|
6607
|
+
// Filter by key (partial match on masked key)
|
|
6608
|
+
if (keyFilter) {
|
|
6609
|
+
const kf = keyFilter.toLowerCase();
|
|
6610
|
+
filtered = filtered.filter(e => e.key.toLowerCase().includes(kf));
|
|
6611
|
+
}
|
|
6612
|
+
// Filter by tool name (exact match)
|
|
6613
|
+
if (toolFilter) {
|
|
6614
|
+
filtered = filtered.filter(e => e.tool === toolFilter);
|
|
6615
|
+
}
|
|
6616
|
+
// Filter by status
|
|
6617
|
+
if (statusFilter === 'allowed' || statusFilter === 'denied') {
|
|
6618
|
+
filtered = filtered.filter(e => e.status === statusFilter);
|
|
6619
|
+
}
|
|
6620
|
+
// Filter by since timestamp
|
|
6621
|
+
if (sinceFilter) {
|
|
6622
|
+
const sinceTime = new Date(sinceFilter).getTime();
|
|
6623
|
+
if (!isNaN(sinceTime)) {
|
|
6624
|
+
filtered = filtered.filter(e => new Date(e.timestamp).getTime() >= sinceTime);
|
|
6625
|
+
}
|
|
6626
|
+
}
|
|
6627
|
+
// Filter by until timestamp
|
|
6628
|
+
if (untilFilter) {
|
|
6629
|
+
const untilTime = new Date(untilFilter).getTime();
|
|
6630
|
+
if (!isNaN(untilTime)) {
|
|
6631
|
+
filtered = filtered.filter(e => new Date(e.timestamp).getTime() <= untilTime);
|
|
6632
|
+
}
|
|
6633
|
+
}
|
|
6634
|
+
// Return newest first
|
|
6635
|
+
const sorted = [...filtered].reverse();
|
|
6636
|
+
if (format === 'csv') {
|
|
6637
|
+
const header = 'id,timestamp,tool,key,status,credits,durationMs,denyReason,requestId';
|
|
6638
|
+
const rows = sorted.map(e => {
|
|
6639
|
+
const escapeCsv = (v) => {
|
|
6640
|
+
if (v === undefined || v === null)
|
|
6641
|
+
return '';
|
|
6642
|
+
const s = String(v);
|
|
6643
|
+
if (s.includes(',') || s.includes('"') || s.includes('\n')) {
|
|
6644
|
+
return '"' + s.replace(/"/g, '""') + '"';
|
|
6645
|
+
}
|
|
6646
|
+
return s;
|
|
6647
|
+
};
|
|
6648
|
+
return [
|
|
6649
|
+
e.id,
|
|
6650
|
+
e.timestamp,
|
|
6651
|
+
escapeCsv(e.tool),
|
|
6652
|
+
escapeCsv(e.key),
|
|
6653
|
+
e.status,
|
|
6654
|
+
e.credits,
|
|
6655
|
+
e.durationMs,
|
|
6656
|
+
escapeCsv(e.denyReason),
|
|
6657
|
+
escapeCsv(e.requestId),
|
|
6658
|
+
].join(',');
|
|
6659
|
+
});
|
|
6660
|
+
const csv = [header, ...rows].join('\n');
|
|
6661
|
+
res.writeHead(200, {
|
|
6662
|
+
'Content-Type': 'text/csv',
|
|
6663
|
+
'Content-Disposition': 'attachment; filename="paygate-requests.csv"',
|
|
6664
|
+
});
|
|
6665
|
+
res.end(csv);
|
|
6666
|
+
}
|
|
6667
|
+
else {
|
|
6668
|
+
res.writeHead(200, {
|
|
6669
|
+
'Content-Type': 'application/json',
|
|
6670
|
+
'Content-Disposition': 'attachment; filename="paygate-requests.json"',
|
|
6671
|
+
});
|
|
6672
|
+
res.end(JSON.stringify({ count: sorted.length, requests: sorted }, null, 2));
|
|
6673
|
+
}
|
|
6674
|
+
}
|
|
6445
6675
|
/** Calculate percentile from an array of numbers. */
|
|
6446
6676
|
percentile(values, p) {
|
|
6447
6677
|
if (values.length === 0)
|