paygate-mcp 7.8.0 → 8.0.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 +59 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +263 -0
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -105,6 +105,8 @@ Agent → PayGate (auth + billing) → Your MCP Server (stdio or HTTP)
|
|
|
105
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
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
|
|
107
107
|
- **Batch Dry Run** — `POST /requests/dry-run/batch` simulates multiple tool calls at once — aggregate credit check, per-tool ACL validation, spending limit, returns per-tool results with total credits required and credits-after
|
|
108
|
+
- **Tool Availability** — `GET /tools/available?key=...` returns per-key tool availability with pricing, affordability (canAfford), ACL enforcement (accessible/denyReason), and per-tool + global rate limit status
|
|
109
|
+
- **Key Dashboard** — `GET /keys/dashboard?key=...` consolidated single-endpoint view with metadata, balance, health score, spending velocity, rate limits, quotas, usage summary, and recent activity timeline
|
|
108
110
|
- **Config Hot Reload** — `POST /config/reload` reloads pricing, rate limits, webhooks, quotas, and behavior flags from config file without server restart
|
|
109
111
|
- **Webhook Events** — POST batched usage events to any URL for external billing/alerting
|
|
110
112
|
- **Config File Mode** — Load all settings from a JSON file (`--config`)
|
|
@@ -2301,6 +2303,63 @@ curl -X POST http://localhost:3402/requests/dry-run/batch \
|
|
|
2301
2303
|
|
|
2302
2304
|
Performs aggregate credit check (sum of all tool prices vs balance), per-tool ACL validation, spending limit, and rate limit checks. Returns per-tool results so you can see which specific tools would fail. Max 100 tools per batch. Supports alias keys.
|
|
2303
2305
|
|
|
2306
|
+
### Tool Availability
|
|
2307
|
+
|
|
2308
|
+
Check per-key tool availability including pricing, affordability, and rate limit status:
|
|
2309
|
+
|
|
2310
|
+
```bash
|
|
2311
|
+
curl "http://localhost:3402/tools/available?key=pg_..." \
|
|
2312
|
+
-H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2313
|
+
```
|
|
2314
|
+
|
|
2315
|
+
**Response:**
|
|
2316
|
+
|
|
2317
|
+
```json
|
|
2318
|
+
{
|
|
2319
|
+
"key": "pg_c815...09a6",
|
|
2320
|
+
"creditsAvailable": 100,
|
|
2321
|
+
"totalTools": 3,
|
|
2322
|
+
"accessibleTools": 2,
|
|
2323
|
+
"globalRateLimit": { "limit": 60, "used": 5, "remaining": 55, "resetInMs": 45000 },
|
|
2324
|
+
"tools": [
|
|
2325
|
+
{ "tool": "tool_a", "accessible": true, "creditsPerCall": 10, "canAfford": true },
|
|
2326
|
+
{ "tool": "tool_b", "accessible": false, "denyReason": "denied_by_acl", "creditsPerCall": 5, "canAfford": true },
|
|
2327
|
+
{ "tool": "tool_c", "accessible": true, "creditsPerCall": 1, "canAfford": true, "rateLimit": { "limit": 10, "used": 3, "remaining": 7 } }
|
|
2328
|
+
]
|
|
2329
|
+
}
|
|
2330
|
+
```
|
|
2331
|
+
|
|
2332
|
+
Returns every discovered tool with: `accessible` (ACL check), `denyReason` (if blocked), `creditsPerCall`, `canAfford` (credits vs price), and per-tool `rateLimit` when configured. Includes global rate limit info. Supports alias keys. Works on suspended keys (informational). Read-only — does not deduct credits or increment rate counters.
|
|
2333
|
+
|
|
2334
|
+
### Key Dashboard
|
|
2335
|
+
|
|
2336
|
+
Get a consolidated overview of any API key in a single request:
|
|
2337
|
+
|
|
2338
|
+
```bash
|
|
2339
|
+
curl "http://localhost:3402/keys/dashboard?key=pg_..." \
|
|
2340
|
+
-H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2341
|
+
```
|
|
2342
|
+
|
|
2343
|
+
**Response:**
|
|
2344
|
+
|
|
2345
|
+
```json
|
|
2346
|
+
{
|
|
2347
|
+
"key": "pg_c815...09a6",
|
|
2348
|
+
"name": "production-agent",
|
|
2349
|
+
"status": "active",
|
|
2350
|
+
"namespace": "prod",
|
|
2351
|
+
"balance": { "credits": 850, "totalSpent": 150, "totalAllocated": 1000, "spendingLimit": 500 },
|
|
2352
|
+
"health": { "score": 92, "status": "good" },
|
|
2353
|
+
"velocity": { "creditsPerHour": 6.2, "creditsPerDay": 149, "estimatedDepletionDate": "2025-02-03T..." },
|
|
2354
|
+
"rateLimits": { "global": { "limit": 60, "used": 12, "remaining": 48, "resetInMs": 35000 } },
|
|
2355
|
+
"quotas": { "source": "global", "daily": { "callsUsed": 24, "callsLimit": 100 }, "monthly": { "callsUsed": 340, "callsLimit": 5000 } },
|
|
2356
|
+
"usage": { "totalCalls": 340, "totalAllowed": 330, "totalDenied": 10, "totalCredits": 150 },
|
|
2357
|
+
"recentActivity": [{ "timestamp": "...", "event": "gate.allowed", "tool": "search", "credits": 5 }]
|
|
2358
|
+
}
|
|
2359
|
+
```
|
|
2360
|
+
|
|
2361
|
+
Combines metadata (status/namespace/group/tags), balance (credits/spent/allocated/spendingLimit), health score (0-100 composite), spending velocity with depletion forecast, rate limit and quota status, usage summary, and last 10 audit events. Supports alias keys. Works on suspended/revoked/expired keys. Read-only.
|
|
2362
|
+
|
|
2304
2363
|
### IP Allowlisting
|
|
2305
2364
|
|
|
2306
2365
|
Restrict API keys to specific IP addresses or CIDR ranges:
|
package/dist/server.d.ts
CHANGED
|
@@ -211,6 +211,7 @@ export declare class PayGateServer {
|
|
|
211
211
|
private handleSpendingVelocity;
|
|
212
212
|
private handleKeyComparison;
|
|
213
213
|
private handleKeyHealth;
|
|
214
|
+
private handleKeyDashboard;
|
|
214
215
|
private handleSetAutoTopup;
|
|
215
216
|
private handleBalance;
|
|
216
217
|
private handleLimits;
|
|
@@ -319,6 +320,7 @@ export declare class PayGateServer {
|
|
|
319
320
|
private handleRequestDryRun;
|
|
320
321
|
private handleRequestDryRunBatch;
|
|
321
322
|
private handleRequestLogExport;
|
|
323
|
+
private handleToolAvailability;
|
|
322
324
|
/** Calculate percentile from an array of numbers. */
|
|
323
325
|
private percentile;
|
|
324
326
|
}
|
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
|
@@ -638,6 +638,12 @@ class PayGateServer {
|
|
|
638
638
|
return this.handleKeyComparison(req, res);
|
|
639
639
|
case '/keys/health':
|
|
640
640
|
return this.handleKeyHealth(req, res);
|
|
641
|
+
case '/keys/dashboard':
|
|
642
|
+
if (req.method === 'GET')
|
|
643
|
+
return this.handleKeyDashboard(req, res);
|
|
644
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
645
|
+
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
646
|
+
return;
|
|
641
647
|
case '/keys/templates':
|
|
642
648
|
if (req.method === 'GET')
|
|
643
649
|
return this.handleListTemplates(req, res);
|
|
@@ -711,6 +717,12 @@ class PayGateServer {
|
|
|
711
717
|
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
712
718
|
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
713
719
|
return;
|
|
720
|
+
case '/tools/available':
|
|
721
|
+
if (req.method === 'GET')
|
|
722
|
+
return this.handleToolAvailability(req, res);
|
|
723
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
724
|
+
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
725
|
+
return;
|
|
714
726
|
case '/alerts':
|
|
715
727
|
if (req.method === 'GET')
|
|
716
728
|
return this.handleGetAlerts(req, res);
|
|
@@ -1324,6 +1336,8 @@ class PayGateServer {
|
|
|
1324
1336
|
requestDryRun: 'POST /requests/dry-run — Simulate a tool call without executing (requires X-Admin-Key)',
|
|
1325
1337
|
requestDryRunBatch: 'POST /requests/dry-run/batch — Simulate multiple tool calls without executing (requires X-Admin-Key)',
|
|
1326
1338
|
toolStats: 'GET /tools/stats — Per-tool call counts, success rates, latency, credits, and top consumers (requires X-Admin-Key)',
|
|
1339
|
+
toolAvailability: 'GET /tools/available?key=... — Per-key tool availability with pricing, affordability, and rate limit status (requires X-Admin-Key)',
|
|
1340
|
+
keyDashboard: 'GET /keys/dashboard?key=... — Consolidated key overview with metadata, balance, health, velocity, rate limits, quotas, and recent activity (requires X-Admin-Key)',
|
|
1327
1341
|
...(this.oauth ? {
|
|
1328
1342
|
oauthMetadata: 'GET /.well-known/oauth-authorization-server — OAuth 2.1 server metadata',
|
|
1329
1343
|
oauthRegister: 'POST /oauth/register — Register OAuth client',
|
|
@@ -3282,6 +3296,188 @@ class PayGateServer {
|
|
|
3282
3296
|
},
|
|
3283
3297
|
}));
|
|
3284
3298
|
}
|
|
3299
|
+
// ─── /keys/dashboard — Consolidated key overview ──────────────────────────────
|
|
3300
|
+
handleKeyDashboard(req, res) {
|
|
3301
|
+
if (!this.checkAdmin(req, res))
|
|
3302
|
+
return;
|
|
3303
|
+
const urlParts = req.url?.split('?') || [];
|
|
3304
|
+
const params = new URLSearchParams(urlParts[1] || '');
|
|
3305
|
+
const keyParam = params.get('key');
|
|
3306
|
+
if (!keyParam) {
|
|
3307
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
3308
|
+
res.end(JSON.stringify({ error: 'Missing required parameter: key' }));
|
|
3309
|
+
return;
|
|
3310
|
+
}
|
|
3311
|
+
const record = this.gate.store.resolveKeyRaw(keyParam);
|
|
3312
|
+
if (!record) {
|
|
3313
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
3314
|
+
res.end(JSON.stringify({ error: 'Key not found' }));
|
|
3315
|
+
return;
|
|
3316
|
+
}
|
|
3317
|
+
const actualKey = record.key;
|
|
3318
|
+
const maskedKey = actualKey.slice(0, 7) + '...' + actualKey.slice(-4);
|
|
3319
|
+
// ── Metadata ──
|
|
3320
|
+
const isExpired = record.expiresAt ? new Date(record.expiresAt).getTime() < Date.now() : false;
|
|
3321
|
+
let status = 'active';
|
|
3322
|
+
if (!record.active)
|
|
3323
|
+
status = 'revoked';
|
|
3324
|
+
else if (record.suspended)
|
|
3325
|
+
status = 'suspended';
|
|
3326
|
+
else if (isExpired)
|
|
3327
|
+
status = 'expired';
|
|
3328
|
+
const metadata = {
|
|
3329
|
+
key: maskedKey,
|
|
3330
|
+
name: record.name || null,
|
|
3331
|
+
status,
|
|
3332
|
+
namespace: record.namespace || null,
|
|
3333
|
+
group: record.group || null,
|
|
3334
|
+
createdAt: record.createdAt || null,
|
|
3335
|
+
...(record.expiresAt ? { expiresAt: record.expiresAt } : {}),
|
|
3336
|
+
tags: record.tags && Object.keys(record.tags).length > 0 ? record.tags : undefined,
|
|
3337
|
+
};
|
|
3338
|
+
// ── Balance ──
|
|
3339
|
+
const balance = {
|
|
3340
|
+
credits: record.credits,
|
|
3341
|
+
totalSpent: record.totalSpent || 0,
|
|
3342
|
+
totalAllocated: record.credits + (record.totalSpent || 0),
|
|
3343
|
+
...(record.spendingLimit ? { spendingLimit: record.spendingLimit } : {}),
|
|
3344
|
+
};
|
|
3345
|
+
// ── Health score (simplified from handleKeyHealth) ──
|
|
3346
|
+
const velocity = this.creditLedger.getSpendingVelocity(actualKey, record.credits, 24);
|
|
3347
|
+
let balanceScore = 100;
|
|
3348
|
+
if (velocity.creditsPerHour > 0) {
|
|
3349
|
+
const hoursLeft = velocity.estimatedHoursRemaining ?? Infinity;
|
|
3350
|
+
if (hoursLeft <= 1)
|
|
3351
|
+
balanceScore = 0;
|
|
3352
|
+
else if (hoursLeft <= 6)
|
|
3353
|
+
balanceScore = 20;
|
|
3354
|
+
else if (hoursLeft <= 24)
|
|
3355
|
+
balanceScore = 40;
|
|
3356
|
+
else if (hoursLeft <= 72)
|
|
3357
|
+
balanceScore = 60;
|
|
3358
|
+
else if (hoursLeft <= 168)
|
|
3359
|
+
balanceScore = 80;
|
|
3360
|
+
}
|
|
3361
|
+
else if (record.credits <= 0) {
|
|
3362
|
+
balanceScore = 0;
|
|
3363
|
+
}
|
|
3364
|
+
// Quota component
|
|
3365
|
+
let quotaScore = 100;
|
|
3366
|
+
const quotaConfig = record.quota || this.config.globalQuota;
|
|
3367
|
+
if (quotaConfig) {
|
|
3368
|
+
this.gate.quotaTracker.resetIfNeeded(record);
|
|
3369
|
+
let maxUtil = 0;
|
|
3370
|
+
if (quotaConfig.dailyCallLimit && quotaConfig.dailyCallLimit > 0)
|
|
3371
|
+
maxUtil = Math.max(maxUtil, record.quotaDailyCalls / quotaConfig.dailyCallLimit);
|
|
3372
|
+
if (quotaConfig.monthlyCallLimit && quotaConfig.monthlyCallLimit > 0)
|
|
3373
|
+
maxUtil = Math.max(maxUtil, record.quotaMonthlyCalls / quotaConfig.monthlyCallLimit);
|
|
3374
|
+
if (quotaConfig.dailyCreditLimit && quotaConfig.dailyCreditLimit > 0)
|
|
3375
|
+
maxUtil = Math.max(maxUtil, record.quotaDailyCredits / quotaConfig.dailyCreditLimit);
|
|
3376
|
+
if (quotaConfig.monthlyCreditLimit && quotaConfig.monthlyCreditLimit > 0)
|
|
3377
|
+
maxUtil = Math.max(maxUtil, record.quotaMonthlyCredits / quotaConfig.monthlyCreditLimit);
|
|
3378
|
+
quotaScore = Math.max(0, Math.round((1 - maxUtil) * 100));
|
|
3379
|
+
}
|
|
3380
|
+
// Rate limit component
|
|
3381
|
+
const rateStatus = this.gate.rateLimiter.getStatus(actualKey);
|
|
3382
|
+
let rateLimitScore = 100;
|
|
3383
|
+
if (rateStatus.limit > 0) {
|
|
3384
|
+
const utilization = rateStatus.used / rateStatus.limit;
|
|
3385
|
+
rateLimitScore = Math.max(0, Math.round((1 - utilization) * 100));
|
|
3386
|
+
}
|
|
3387
|
+
// Error rate component
|
|
3388
|
+
const keyUsage = this.gate.meter.getKeyUsage(actualKey);
|
|
3389
|
+
let errorScore = 100;
|
|
3390
|
+
if (keyUsage.totalCalls > 0) {
|
|
3391
|
+
const errorRate = keyUsage.totalDenied / keyUsage.totalCalls;
|
|
3392
|
+
errorScore = Math.max(0, Math.round((1 - errorRate) * 100));
|
|
3393
|
+
}
|
|
3394
|
+
const healthScore = Math.round(balanceScore * 0.30 + quotaScore * 0.25 + rateLimitScore * 0.20 + errorScore * 0.25);
|
|
3395
|
+
let healthStatus = 'healthy';
|
|
3396
|
+
if (healthScore < 25)
|
|
3397
|
+
healthStatus = 'critical';
|
|
3398
|
+
else if (healthScore < 50)
|
|
3399
|
+
healthStatus = 'warning';
|
|
3400
|
+
else if (healthScore < 75)
|
|
3401
|
+
healthStatus = 'caution';
|
|
3402
|
+
else if (healthScore < 90)
|
|
3403
|
+
healthStatus = 'good';
|
|
3404
|
+
// ── Velocity ──
|
|
3405
|
+
const velocitySummary = {
|
|
3406
|
+
creditsPerHour: velocity.creditsPerHour,
|
|
3407
|
+
creditsPerDay: velocity.creditsPerDay,
|
|
3408
|
+
callsPerHour: velocity.callsPerHour,
|
|
3409
|
+
callsPerDay: velocity.callsPerDay,
|
|
3410
|
+
estimatedDepletionDate: velocity.estimatedDepletionDate || null,
|
|
3411
|
+
};
|
|
3412
|
+
// ── Rate limits ──
|
|
3413
|
+
const rateLimits = {
|
|
3414
|
+
global: {
|
|
3415
|
+
limit: rateStatus.limit,
|
|
3416
|
+
used: rateStatus.used,
|
|
3417
|
+
remaining: rateStatus.remaining,
|
|
3418
|
+
resetInMs: rateStatus.resetInMs,
|
|
3419
|
+
},
|
|
3420
|
+
};
|
|
3421
|
+
// ── Quotas ──
|
|
3422
|
+
let quotas;
|
|
3423
|
+
if (quotaConfig) {
|
|
3424
|
+
this.gate.quotaTracker.resetIfNeeded(record);
|
|
3425
|
+
quotas = {
|
|
3426
|
+
source: record.quota ? 'per-key' : 'global',
|
|
3427
|
+
daily: {
|
|
3428
|
+
callsUsed: record.quotaDailyCalls,
|
|
3429
|
+
callsLimit: quotaConfig.dailyCallLimit || 0,
|
|
3430
|
+
creditsUsed: record.quotaDailyCredits,
|
|
3431
|
+
creditsLimit: quotaConfig.dailyCreditLimit || 0,
|
|
3432
|
+
},
|
|
3433
|
+
monthly: {
|
|
3434
|
+
callsUsed: record.quotaMonthlyCalls,
|
|
3435
|
+
callsLimit: quotaConfig.monthlyCallLimit || 0,
|
|
3436
|
+
creditsUsed: record.quotaMonthlyCredits,
|
|
3437
|
+
creditsLimit: quotaConfig.monthlyCreditLimit || 0,
|
|
3438
|
+
},
|
|
3439
|
+
};
|
|
3440
|
+
}
|
|
3441
|
+
// ── Usage summary ──
|
|
3442
|
+
const usage = {
|
|
3443
|
+
totalCalls: keyUsage.totalCalls || 0,
|
|
3444
|
+
totalAllowed: keyUsage.totalAllowed || 0,
|
|
3445
|
+
totalDenied: keyUsage.totalDenied || 0,
|
|
3446
|
+
totalCredits: keyUsage.totalCreditsSpent || 0,
|
|
3447
|
+
};
|
|
3448
|
+
// ── Recent activity (last 10 events) ──
|
|
3449
|
+
const maskedForAudit = (0, audit_1.maskKeyForAudit)(actualKey);
|
|
3450
|
+
const auditResult = this.audit.query({ limit: 100 });
|
|
3451
|
+
const recentActivity = auditResult.events
|
|
3452
|
+
.filter(e => {
|
|
3453
|
+
for (const field of ['key', 'keyMasked', 'sourceKey', 'destKey']) {
|
|
3454
|
+
const val = e.metadata?.[field];
|
|
3455
|
+
if (val && typeof val === 'string' && val === maskedForAudit)
|
|
3456
|
+
return true;
|
|
3457
|
+
}
|
|
3458
|
+
if (e.actor === maskedForAudit)
|
|
3459
|
+
return true;
|
|
3460
|
+
return false;
|
|
3461
|
+
})
|
|
3462
|
+
.slice(0, 10)
|
|
3463
|
+
.map(e => ({
|
|
3464
|
+
timestamp: e.timestamp,
|
|
3465
|
+
event: e.type,
|
|
3466
|
+
...(e.metadata?.tool ? { tool: e.metadata.tool } : {}),
|
|
3467
|
+
...(e.metadata?.credits ? { credits: e.metadata.credits } : {}),
|
|
3468
|
+
}));
|
|
3469
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
3470
|
+
res.end(JSON.stringify({
|
|
3471
|
+
...metadata,
|
|
3472
|
+
balance,
|
|
3473
|
+
health: { score: healthScore, status: healthStatus },
|
|
3474
|
+
velocity: velocitySummary,
|
|
3475
|
+
rateLimits,
|
|
3476
|
+
...(quotas ? { quotas } : {}),
|
|
3477
|
+
usage,
|
|
3478
|
+
recentActivity: recentActivity.length > 0 ? recentActivity : [],
|
|
3479
|
+
}));
|
|
3480
|
+
}
|
|
3285
3481
|
// ─── /keys/auto-topup — Configure auto-topup ────────────────────────────────
|
|
3286
3482
|
async handleSetAutoTopup(req, res) {
|
|
3287
3483
|
if (req.method !== 'POST') {
|
|
@@ -6815,6 +7011,73 @@ class PayGateServer {
|
|
|
6815
7011
|
res.end(JSON.stringify({ count: sorted.length, requests: sorted }, null, 2));
|
|
6816
7012
|
}
|
|
6817
7013
|
}
|
|
7014
|
+
// ─── /tools/available — Per-key tool availability with pricing ──────────────
|
|
7015
|
+
handleToolAvailability(req, res) {
|
|
7016
|
+
if (!this.checkAdmin(req, res))
|
|
7017
|
+
return;
|
|
7018
|
+
const urlParts = req.url?.split('?') || [];
|
|
7019
|
+
const params = new URLSearchParams(urlParts[1] || '');
|
|
7020
|
+
const keyParam = params.get('key');
|
|
7021
|
+
if (!keyParam) {
|
|
7022
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
7023
|
+
res.end(JSON.stringify({ error: 'Missing required parameter: key' }));
|
|
7024
|
+
return;
|
|
7025
|
+
}
|
|
7026
|
+
// Resolve alias
|
|
7027
|
+
const keyRecord = this.gate.store.resolveKeyRaw(keyParam);
|
|
7028
|
+
if (!keyRecord) {
|
|
7029
|
+
const isExpired = this.gate.store.isExpired(keyParam);
|
|
7030
|
+
const reason = isExpired ? 'api_key_expired' : 'invalid_api_key';
|
|
7031
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
7032
|
+
res.end(JSON.stringify({ error: reason, tools: [] }));
|
|
7033
|
+
return;
|
|
7034
|
+
}
|
|
7035
|
+
// Get all discovered tools with pricing
|
|
7036
|
+
const allToolPricing = this.registry.getFullPricing().tools;
|
|
7037
|
+
// Build per-tool availability
|
|
7038
|
+
const effectiveAllowed = keyRecord.allowedTools || [];
|
|
7039
|
+
const effectiveDenied = keyRecord.deniedTools || [];
|
|
7040
|
+
const tools = allToolPricing.map(toolInfo => {
|
|
7041
|
+
const toolName = toolInfo.name;
|
|
7042
|
+
// ACL check
|
|
7043
|
+
let accessible = true;
|
|
7044
|
+
let denyReason;
|
|
7045
|
+
if (effectiveDenied.includes(toolName)) {
|
|
7046
|
+
accessible = false;
|
|
7047
|
+
denyReason = 'denied_by_acl';
|
|
7048
|
+
}
|
|
7049
|
+
else if (effectiveAllowed.length > 0 && !effectiveAllowed.includes(toolName)) {
|
|
7050
|
+
accessible = false;
|
|
7051
|
+
denyReason = 'not_in_allowed_list';
|
|
7052
|
+
}
|
|
7053
|
+
// Affordability
|
|
7054
|
+
const creditsRequired = toolInfo.creditsPerCall;
|
|
7055
|
+
const canAfford = keyRecord.credits >= creditsRequired;
|
|
7056
|
+
// Per-tool rate limit status
|
|
7057
|
+
const perToolRateLimit = toolInfo.rateLimitPerMin > 0
|
|
7058
|
+
? this.gate.rateLimiter.getStatus(`${keyRecord.key}:tool:${toolName}`, toolInfo.rateLimitPerMin)
|
|
7059
|
+
: null;
|
|
7060
|
+
return {
|
|
7061
|
+
tool: toolName,
|
|
7062
|
+
accessible,
|
|
7063
|
+
...(denyReason ? { denyReason } : {}),
|
|
7064
|
+
creditsPerCall: creditsRequired,
|
|
7065
|
+
canAfford,
|
|
7066
|
+
...(perToolRateLimit ? { rateLimit: perToolRateLimit } : {}),
|
|
7067
|
+
};
|
|
7068
|
+
});
|
|
7069
|
+
// Global rate limit
|
|
7070
|
+
const globalRateLimit = this.gate.rateLimiter.getStatus(keyRecord.key);
|
|
7071
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
7072
|
+
res.end(JSON.stringify({
|
|
7073
|
+
key: keyRecord.key.slice(0, 7) + '...' + keyRecord.key.slice(-4),
|
|
7074
|
+
creditsAvailable: keyRecord.credits,
|
|
7075
|
+
totalTools: tools.length,
|
|
7076
|
+
accessibleTools: tools.filter(t => t.accessible).length,
|
|
7077
|
+
...(globalRateLimit.limit > 0 ? { globalRateLimit } : {}),
|
|
7078
|
+
tools,
|
|
7079
|
+
}));
|
|
7080
|
+
}
|
|
6818
7081
|
/** Calculate percentile from an array of numbers. */
|
|
6819
7082
|
percentile(values, p) {
|
|
6820
7083
|
if (values.length === 0)
|