paygate-mcp 3.1.0 → 3.3.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 +116 -0
- package/dist/admin-keys.d.ts +89 -0
- package/dist/admin-keys.d.ts.map +1 -0
- package/dist/admin-keys.js +178 -0
- package/dist/admin-keys.js.map +1 -0
- package/dist/audit.d.ts +1 -1
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js.map +1 -1
- package/dist/gate.d.ts +8 -0
- package/dist/gate.d.ts.map +1 -1
- package/dist/gate.js +35 -0
- package/dist/gate.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/redis-sync.d.ts.map +1 -1
- package/dist/redis-sync.js +6 -0
- package/dist/redis-sync.js.map +1 -1
- package/dist/server.d.ts +9 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +272 -28
- package/dist/server.js.map +1 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +9 -0
- package/dist/store.js.map +1 -1
- package/dist/types.d.ts +13 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/webhook.d.ts +1 -1
- package/dist/webhook.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -61,6 +61,8 @@ Agent → PayGate (auth + billing) → Your MCP Server (stdio or HTTP)
|
|
|
61
61
|
- **Multi-Tenant Namespaces** — Isolate API keys and usage data by tenant with namespace-filtered admin endpoints, analytics, and usage export
|
|
62
62
|
- **Scoped Tokens** — Issue short-lived `pgt_` tokens scoped to specific tools with auto-expiry (max 24h), HMAC-SHA256 signed, zero server-side state
|
|
63
63
|
- **Token Revocation List** — Revoke scoped tokens before expiry with O(1) lookup, auto-cleanup, Redis cross-instance sync, and admin API
|
|
64
|
+
- **Usage-Based Auto-Topup** — Automatically add credits when balance drops below a threshold with configurable daily limits, audit trail, webhook events, and Redis sync
|
|
65
|
+
- **Admin API Key Management** — Multiple admin keys with role-based permissions (super_admin, admin, viewer), file persistence, audit trail, and safety guards
|
|
64
66
|
- **Refund on Failure** — Automatically refund credits when downstream tool calls fail
|
|
65
67
|
- **Webhook Events** — POST batched usage events to any URL for external billing/alerting
|
|
66
68
|
- **Config File Mode** — Load all settings from a JSON file (`--config`)
|
|
@@ -283,6 +285,10 @@ A real-time admin UI for managing keys, viewing usage, and monitoring tool calls
|
|
|
283
285
|
| `/keys/tags` | POST | `X-Admin-Key` | Set key tags/metadata (merge semantics) |
|
|
284
286
|
| `/keys/ip` | POST | `X-Admin-Key` | Set IP allowlist (CIDR + exact match) |
|
|
285
287
|
| `/keys/search` | POST | `X-Admin-Key` | Search keys by tag values |
|
|
288
|
+
| `/keys/auto-topup` | POST | `X-Admin-Key` | Configure or disable auto-topup for a key |
|
|
289
|
+
| `/admin/keys` | GET | `X-Admin-Key` (super_admin) | List all admin keys (masked) |
|
|
290
|
+
| `/admin/keys` | POST | `X-Admin-Key` (super_admin) | Create a new admin key with role |
|
|
291
|
+
| `/admin/keys/revoke` | POST | `X-Admin-Key` (super_admin) | Revoke an admin key |
|
|
286
292
|
| `/limits` | POST | `X-Admin-Key` | Set spending limit on a key |
|
|
287
293
|
| `/usage` | GET | `X-Admin-Key` | Export usage data (JSON or CSV) |
|
|
288
294
|
| `/status` | GET | `X-Admin-Key` | Full dashboard with usage stats |
|
|
@@ -1321,6 +1327,114 @@ tokens.revocationList.size; // 1
|
|
|
1321
1327
|
tokens.destroy();
|
|
1322
1328
|
```
|
|
1323
1329
|
|
|
1330
|
+
### Usage-Based Auto-Topup
|
|
1331
|
+
|
|
1332
|
+
Automatically refill credits when a key's balance drops below a threshold. Prevents service interruptions for high-value API consumers.
|
|
1333
|
+
|
|
1334
|
+
**Configure auto-topup (admin):**
|
|
1335
|
+
|
|
1336
|
+
```bash
|
|
1337
|
+
curl -X POST http://localhost:3402/keys/auto-topup \
|
|
1338
|
+
-H "X-Admin-Key: YOUR_ADMIN_KEY" \
|
|
1339
|
+
-H "Content-Type: application/json" \
|
|
1340
|
+
-d '{"key": "pg_abc123...", "threshold": 100, "amount": 500, "maxDaily": 10}'
|
|
1341
|
+
```
|
|
1342
|
+
|
|
1343
|
+
Returns:
|
|
1344
|
+
|
|
1345
|
+
```json
|
|
1346
|
+
{
|
|
1347
|
+
"autoTopup": { "threshold": 100, "amount": 500, "maxDaily": 10 },
|
|
1348
|
+
"message": "Auto-topup enabled: add 500 credits when balance drops below 100 (max 10/day)"
|
|
1349
|
+
}
|
|
1350
|
+
```
|
|
1351
|
+
|
|
1352
|
+
**Disable auto-topup:**
|
|
1353
|
+
|
|
1354
|
+
```bash
|
|
1355
|
+
curl -X POST http://localhost:3402/keys/auto-topup \
|
|
1356
|
+
-H "X-Admin-Key: YOUR_ADMIN_KEY" \
|
|
1357
|
+
-H "Content-Type: application/json" \
|
|
1358
|
+
-d '{"key": "pg_abc123...", "disable": true}'
|
|
1359
|
+
```
|
|
1360
|
+
|
|
1361
|
+
Auto-topup behavior:
|
|
1362
|
+
- **Post-deduction trigger** — After each tool call (or batch) deducts credits, the gate checks if credits fell below the threshold and automatically adds credits.
|
|
1363
|
+
- **Daily limits** — `maxDaily` caps how many auto-topups can occur per UTC day. Set to `0` for unlimited.
|
|
1364
|
+
- **Audit trail** — Every auto-topup is logged as a `key.auto_topped_up` audit event. Configuration changes are logged as `key.auto_topup_configured`.
|
|
1365
|
+
- **Webhook events** — Both `key.auto_topup_configured` and `key.auto_topped_up` events are sent via webhooks.
|
|
1366
|
+
- **Redis sync** — In multi-instance deployments, auto-topup credits are synced atomically via Redis.
|
|
1367
|
+
- **State persistence** — Auto-topup config and daily counters are persisted in the state file and Redis.
|
|
1368
|
+
|
|
1369
|
+
**Programmatic usage:**
|
|
1370
|
+
|
|
1371
|
+
```typescript
|
|
1372
|
+
import { Gate } from 'paygate-mcp';
|
|
1373
|
+
|
|
1374
|
+
const gate = new Gate(config, 'state.json');
|
|
1375
|
+
const record = gate.store.createKey('premium-client', 1000);
|
|
1376
|
+
|
|
1377
|
+
// Configure auto-topup
|
|
1378
|
+
record.autoTopup = { threshold: 100, amount: 500, maxDaily: 5 };
|
|
1379
|
+
gate.store.save();
|
|
1380
|
+
|
|
1381
|
+
// Hook for notifications
|
|
1382
|
+
gate.onAutoTopup = (apiKey, amount, newBalance) => {
|
|
1383
|
+
console.log(`Auto-topped up ${amount} credits → balance: ${newBalance}`);
|
|
1384
|
+
};
|
|
1385
|
+
|
|
1386
|
+
// Gate.evaluate() automatically triggers auto-topup after credit deduction
|
|
1387
|
+
const result = gate.evaluate(record.key, { name: 'expensive-tool' });
|
|
1388
|
+
```
|
|
1389
|
+
|
|
1390
|
+
### Admin API Key Management
|
|
1391
|
+
|
|
1392
|
+
Manage multiple admin keys with role-based permissions. The bootstrap admin key (from constructor or CLI) is always a `super_admin`.
|
|
1393
|
+
|
|
1394
|
+
**Roles:**
|
|
1395
|
+
| Role | Description |
|
|
1396
|
+
|------|-------------|
|
|
1397
|
+
| `super_admin` | Full access, including admin key management |
|
|
1398
|
+
| `admin` | All API key and system operations, but cannot manage admin keys |
|
|
1399
|
+
| `viewer` | Read-only access to status, usage, analytics, audit, etc. |
|
|
1400
|
+
|
|
1401
|
+
**Create an admin key (super_admin only):**
|
|
1402
|
+
|
|
1403
|
+
```bash
|
|
1404
|
+
curl -X POST http://localhost:3402/admin/keys \
|
|
1405
|
+
-H "X-Admin-Key: $ADMIN_KEY" \
|
|
1406
|
+
-H "Content-Type: application/json" \
|
|
1407
|
+
-d '{"name": "CI Bot", "role": "admin"}'
|
|
1408
|
+
# Returns: { "key": "ak_...", "name": "CI Bot", "role": "admin", "createdAt": "..." }
|
|
1409
|
+
```
|
|
1410
|
+
|
|
1411
|
+
**List admin keys (super_admin only):**
|
|
1412
|
+
|
|
1413
|
+
```bash
|
|
1414
|
+
curl http://localhost:3402/admin/keys \
|
|
1415
|
+
-H "X-Admin-Key: $ADMIN_KEY"
|
|
1416
|
+
# Returns masked keys with roles, status, and last used timestamps
|
|
1417
|
+
```
|
|
1418
|
+
|
|
1419
|
+
**Revoke an admin key (super_admin only):**
|
|
1420
|
+
|
|
1421
|
+
```bash
|
|
1422
|
+
curl -X POST http://localhost:3402/admin/keys/revoke \
|
|
1423
|
+
-H "X-Admin-Key: $ADMIN_KEY" \
|
|
1424
|
+
-H "Content-Type: application/json" \
|
|
1425
|
+
-d '{"key": "ak_..."}'
|
|
1426
|
+
```
|
|
1427
|
+
|
|
1428
|
+
**Behavior:**
|
|
1429
|
+
- The default role for `POST /admin/keys` is `admin` if not specified.
|
|
1430
|
+
- Cannot revoke your own admin key (safety guard).
|
|
1431
|
+
- Cannot revoke the last `super_admin` key (safety guard).
|
|
1432
|
+
- `viewer` keys can access all read-only endpoints (GET) but are denied write operations (POST).
|
|
1433
|
+
- `admin` keys can create/revoke/rotate API keys, manage teams, tokens, etc. but cannot manage admin keys.
|
|
1434
|
+
- Admin keys are persisted to a separate file (`*-admin.json`) alongside the state file.
|
|
1435
|
+
- All operations are logged in the audit trail (`admin_key.created`, `admin_key.revoked`).
|
|
1436
|
+
- Webhook events are fired for admin key lifecycle changes.
|
|
1437
|
+
|
|
1324
1438
|
### Horizontal Scaling (Redis)
|
|
1325
1439
|
|
|
1326
1440
|
Enable Redis-backed state for multi-process deployments. Multiple PayGate instances share API keys, credits, and usage data through Redis:
|
|
@@ -1566,6 +1680,8 @@ const result = await client.callTool('search', { query: 'hello' });
|
|
|
1566
1680
|
- [x] Multi-tenant namespaces — Isolate API keys and usage data by tenant with namespace-filtered endpoints
|
|
1567
1681
|
- [x] Scoped tokens — Short-lived `pgt_` tokens with tool ACL narrowing, HMAC-SHA256 signed, zero server-side state
|
|
1568
1682
|
- [x] Token revocation list — Revoke scoped tokens before expiry with O(1) lookup, auto-cleanup, Redis sync
|
|
1683
|
+
- [x] Usage-based auto-topup — Automatically refill credits when balance drops below threshold with daily limits
|
|
1684
|
+
- [x] Admin API key management — Multiple admin keys with role-based permissions (super_admin, admin, viewer)
|
|
1569
1685
|
|
|
1570
1686
|
## Requirements
|
|
1571
1687
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AdminKeyManager — Multiple admin keys with role-based permissions.
|
|
3
|
+
*
|
|
4
|
+
* Roles (hierarchical):
|
|
5
|
+
* - super_admin: Full access, including admin key management
|
|
6
|
+
* - admin: All API key and system operations, but cannot manage admin keys
|
|
7
|
+
* - viewer: Read-only access to status, usage, analytics, audit, etc.
|
|
8
|
+
*
|
|
9
|
+
* The bootstrap admin key (from constructor) is always a super_admin.
|
|
10
|
+
* Supports file-based persistence (separate file from API key state).
|
|
11
|
+
*/
|
|
12
|
+
export type AdminRole = 'super_admin' | 'admin' | 'viewer';
|
|
13
|
+
export interface AdminKeyRecord {
|
|
14
|
+
/** The admin API key (ak_ prefix, or legacy admin_ prefix for bootstrap) */
|
|
15
|
+
key: string;
|
|
16
|
+
/** Human-readable name */
|
|
17
|
+
name: string;
|
|
18
|
+
/** Permission level */
|
|
19
|
+
role: AdminRole;
|
|
20
|
+
/** ISO timestamp when the key was created */
|
|
21
|
+
createdAt: string;
|
|
22
|
+
/** Who created this key (masked key or 'bootstrap') */
|
|
23
|
+
createdBy: string;
|
|
24
|
+
/** Whether this key is active (can be revoked) */
|
|
25
|
+
active: boolean;
|
|
26
|
+
/** Last time this key was used for authentication (ISO timestamp) */
|
|
27
|
+
lastUsedAt: string | null;
|
|
28
|
+
}
|
|
29
|
+
/** Role hierarchy for permission checks (higher = more permissions) */
|
|
30
|
+
export declare const ROLE_HIERARCHY: Record<AdminRole, number>;
|
|
31
|
+
/** Valid role names */
|
|
32
|
+
export declare const VALID_ROLES: AdminRole[];
|
|
33
|
+
export declare class AdminKeyManager {
|
|
34
|
+
private keys;
|
|
35
|
+
private readonly filePath;
|
|
36
|
+
constructor(filePath?: string);
|
|
37
|
+
/** Load admin keys from file. */
|
|
38
|
+
private load;
|
|
39
|
+
/** Save admin keys to file. */
|
|
40
|
+
save(): void;
|
|
41
|
+
/**
|
|
42
|
+
* Bootstrap the manager with the initial admin key.
|
|
43
|
+
* This is the key passed to PayGateServer constructor (or auto-generated).
|
|
44
|
+
* It always gets super_admin role. Skips if the key is already known.
|
|
45
|
+
*/
|
|
46
|
+
bootstrap(key: string): void;
|
|
47
|
+
/**
|
|
48
|
+
* Validate an admin key. Returns the record if valid, null otherwise.
|
|
49
|
+
* Updates lastUsedAt on successful validation.
|
|
50
|
+
*/
|
|
51
|
+
validate(key: string): AdminKeyRecord | null;
|
|
52
|
+
/**
|
|
53
|
+
* Check if a key has at least the minimum required role.
|
|
54
|
+
*/
|
|
55
|
+
hasRole(key: string, minRole: AdminRole): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Create a new admin key with ak_ prefix.
|
|
58
|
+
*/
|
|
59
|
+
create(name: string, role: AdminRole, createdBy: string): AdminKeyRecord;
|
|
60
|
+
/**
|
|
61
|
+
* Revoke an admin key.
|
|
62
|
+
* Cannot revoke the last active super_admin key (safety check).
|
|
63
|
+
*/
|
|
64
|
+
revoke(key: string): {
|
|
65
|
+
success: boolean;
|
|
66
|
+
error?: string;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* List all admin keys.
|
|
70
|
+
*/
|
|
71
|
+
list(): AdminKeyRecord[];
|
|
72
|
+
/**
|
|
73
|
+
* Get a specific admin key record.
|
|
74
|
+
*/
|
|
75
|
+
get(key: string): AdminKeyRecord | undefined;
|
|
76
|
+
/**
|
|
77
|
+
* Number of active admin keys.
|
|
78
|
+
*/
|
|
79
|
+
get activeCount(): number;
|
|
80
|
+
/**
|
|
81
|
+
* Export state for persistence.
|
|
82
|
+
*/
|
|
83
|
+
toJSON(): AdminKeyRecord[];
|
|
84
|
+
/**
|
|
85
|
+
* Import state from array. Clears existing keys.
|
|
86
|
+
*/
|
|
87
|
+
fromJSON(records: AdminKeyRecord[]): void;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=admin-keys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-keys.d.ts","sourceRoot":"","sources":["../src/admin-keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE3D,MAAM,WAAW,cAAc;IAC7B,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAC;IACZ,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,IAAI,EAAE,SAAS,CAAC;IAChB,6CAA6C;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,MAAM,EAAE,OAAO,CAAC;IAChB,qEAAqE;IACrE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,uEAAuE;AACvE,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAIpD,CAAC;AAEF,uBAAuB;AACvB,eAAO,MAAM,WAAW,EAAE,SAAS,EAAuC,CAAC;AAI3E,qBAAa,eAAe;IAC1B,OAAO,CAAC,IAAI,CAA0C;IACtD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;gBAE7B,QAAQ,CAAC,EAAE,MAAM;IAO7B,iCAAiC;IACjC,OAAO,CAAC,IAAI;IAcZ,+BAA+B;IAC/B,IAAI,IAAI,IAAI;IAOZ;;;;OAIG;IACH,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAgB5B;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI;IAQ5C;;OAEG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,GAAG,OAAO;IAQjD;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,cAAc;IAgBxE;;;OAGG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAqBzD;;OAEG;IACH,IAAI,IAAI,cAAc,EAAE;IAIxB;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAI5C;;OAEG;IACH,IAAI,WAAW,IAAI,MAAM,CAExB;IAID;;OAEG;IACH,MAAM,IAAI,cAAc,EAAE;IAI1B;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,IAAI;CAM1C"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* AdminKeyManager — Multiple admin keys with role-based permissions.
|
|
4
|
+
*
|
|
5
|
+
* Roles (hierarchical):
|
|
6
|
+
* - super_admin: Full access, including admin key management
|
|
7
|
+
* - admin: All API key and system operations, but cannot manage admin keys
|
|
8
|
+
* - viewer: Read-only access to status, usage, analytics, audit, etc.
|
|
9
|
+
*
|
|
10
|
+
* The bootstrap admin key (from constructor) is always a super_admin.
|
|
11
|
+
* Supports file-based persistence (separate file from API key state).
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.AdminKeyManager = exports.VALID_ROLES = exports.ROLE_HIERARCHY = void 0;
|
|
15
|
+
const crypto_1 = require("crypto");
|
|
16
|
+
const fs_1 = require("fs");
|
|
17
|
+
/** Role hierarchy for permission checks (higher = more permissions) */
|
|
18
|
+
exports.ROLE_HIERARCHY = {
|
|
19
|
+
super_admin: 3,
|
|
20
|
+
admin: 2,
|
|
21
|
+
viewer: 1,
|
|
22
|
+
};
|
|
23
|
+
/** Valid role names */
|
|
24
|
+
exports.VALID_ROLES = ['super_admin', 'admin', 'viewer'];
|
|
25
|
+
// ─── AdminKeyManager ─────────────────────────────────────────────────────────
|
|
26
|
+
class AdminKeyManager {
|
|
27
|
+
keys = new Map();
|
|
28
|
+
filePath;
|
|
29
|
+
constructor(filePath) {
|
|
30
|
+
this.filePath = filePath || null;
|
|
31
|
+
if (this.filePath)
|
|
32
|
+
this.load();
|
|
33
|
+
}
|
|
34
|
+
// ─── Persistence ─────────────────────────────────────────────────────────
|
|
35
|
+
/** Load admin keys from file. */
|
|
36
|
+
load() {
|
|
37
|
+
if (!this.filePath || !(0, fs_1.existsSync)(this.filePath))
|
|
38
|
+
return;
|
|
39
|
+
try {
|
|
40
|
+
const data = JSON.parse((0, fs_1.readFileSync)(this.filePath, 'utf-8'));
|
|
41
|
+
if (Array.isArray(data)) {
|
|
42
|
+
for (const record of data) {
|
|
43
|
+
if (record && record.key) {
|
|
44
|
+
this.keys.set(record.key, record);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch { /* ignore corrupted file */ }
|
|
50
|
+
}
|
|
51
|
+
/** Save admin keys to file. */
|
|
52
|
+
save() {
|
|
53
|
+
if (!this.filePath)
|
|
54
|
+
return;
|
|
55
|
+
(0, fs_1.writeFileSync)(this.filePath, JSON.stringify(this.toJSON(), null, 2));
|
|
56
|
+
}
|
|
57
|
+
// ─── Bootstrap ───────────────────────────────────────────────────────────
|
|
58
|
+
/**
|
|
59
|
+
* Bootstrap the manager with the initial admin key.
|
|
60
|
+
* This is the key passed to PayGateServer constructor (or auto-generated).
|
|
61
|
+
* It always gets super_admin role. Skips if the key is already known.
|
|
62
|
+
*/
|
|
63
|
+
bootstrap(key) {
|
|
64
|
+
if (this.keys.has(key))
|
|
65
|
+
return;
|
|
66
|
+
this.keys.set(key, {
|
|
67
|
+
key,
|
|
68
|
+
name: 'Bootstrap Admin',
|
|
69
|
+
role: 'super_admin',
|
|
70
|
+
createdAt: new Date().toISOString(),
|
|
71
|
+
createdBy: 'bootstrap',
|
|
72
|
+
active: true,
|
|
73
|
+
lastUsedAt: null,
|
|
74
|
+
});
|
|
75
|
+
this.save();
|
|
76
|
+
}
|
|
77
|
+
// ─── Validation ──────────────────────────────────────────────────────────
|
|
78
|
+
/**
|
|
79
|
+
* Validate an admin key. Returns the record if valid, null otherwise.
|
|
80
|
+
* Updates lastUsedAt on successful validation.
|
|
81
|
+
*/
|
|
82
|
+
validate(key) {
|
|
83
|
+
if (!key)
|
|
84
|
+
return null;
|
|
85
|
+
const record = this.keys.get(key);
|
|
86
|
+
if (!record || !record.active)
|
|
87
|
+
return null;
|
|
88
|
+
record.lastUsedAt = new Date().toISOString();
|
|
89
|
+
return record;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Check if a key has at least the minimum required role.
|
|
93
|
+
*/
|
|
94
|
+
hasRole(key, minRole) {
|
|
95
|
+
const record = this.validate(key);
|
|
96
|
+
if (!record)
|
|
97
|
+
return false;
|
|
98
|
+
return exports.ROLE_HIERARCHY[record.role] >= exports.ROLE_HIERARCHY[minRole];
|
|
99
|
+
}
|
|
100
|
+
// ─── CRUD ────────────────────────────────────────────────────────────────
|
|
101
|
+
/**
|
|
102
|
+
* Create a new admin key with ak_ prefix.
|
|
103
|
+
*/
|
|
104
|
+
create(name, role, createdBy) {
|
|
105
|
+
const key = `ak_${(0, crypto_1.randomBytes)(16).toString('hex')}`;
|
|
106
|
+
const record = {
|
|
107
|
+
key,
|
|
108
|
+
name,
|
|
109
|
+
role,
|
|
110
|
+
createdAt: new Date().toISOString(),
|
|
111
|
+
createdBy,
|
|
112
|
+
active: true,
|
|
113
|
+
lastUsedAt: null,
|
|
114
|
+
};
|
|
115
|
+
this.keys.set(key, record);
|
|
116
|
+
this.save();
|
|
117
|
+
return record;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Revoke an admin key.
|
|
121
|
+
* Cannot revoke the last active super_admin key (safety check).
|
|
122
|
+
*/
|
|
123
|
+
revoke(key) {
|
|
124
|
+
const record = this.keys.get(key);
|
|
125
|
+
if (!record)
|
|
126
|
+
return { success: false, error: 'Admin key not found' };
|
|
127
|
+
if (!record.active)
|
|
128
|
+
return { success: false, error: 'Admin key already revoked' };
|
|
129
|
+
// Prevent revoking the last super_admin
|
|
130
|
+
if (record.role === 'super_admin') {
|
|
131
|
+
const activeSuperAdmins = Array.from(this.keys.values())
|
|
132
|
+
.filter(k => k.active && k.role === 'super_admin');
|
|
133
|
+
if (activeSuperAdmins.length <= 1) {
|
|
134
|
+
return { success: false, error: 'Cannot revoke the last super_admin key' };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
record.active = false;
|
|
138
|
+
this.save();
|
|
139
|
+
return { success: true };
|
|
140
|
+
}
|
|
141
|
+
// ─── Query ───────────────────────────────────────────────────────────────
|
|
142
|
+
/**
|
|
143
|
+
* List all admin keys.
|
|
144
|
+
*/
|
|
145
|
+
list() {
|
|
146
|
+
return Array.from(this.keys.values());
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Get a specific admin key record.
|
|
150
|
+
*/
|
|
151
|
+
get(key) {
|
|
152
|
+
return this.keys.get(key);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Number of active admin keys.
|
|
156
|
+
*/
|
|
157
|
+
get activeCount() {
|
|
158
|
+
return Array.from(this.keys.values()).filter(k => k.active).length;
|
|
159
|
+
}
|
|
160
|
+
// ─── Serialization ──────────────────────────────────────────────────────
|
|
161
|
+
/**
|
|
162
|
+
* Export state for persistence.
|
|
163
|
+
*/
|
|
164
|
+
toJSON() {
|
|
165
|
+
return Array.from(this.keys.values());
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Import state from array. Clears existing keys.
|
|
169
|
+
*/
|
|
170
|
+
fromJSON(records) {
|
|
171
|
+
this.keys.clear();
|
|
172
|
+
for (const record of records) {
|
|
173
|
+
this.keys.set(record.key, record);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
exports.AdminKeyManager = AdminKeyManager;
|
|
178
|
+
//# sourceMappingURL=admin-keys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-keys.js","sourceRoot":"","sources":["../src/admin-keys.ts"],"names":[],"mappings":"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"}
|
package/dist/audit.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* session lifecycle, and admin operations. Ring buffer with configurable
|
|
6
6
|
* max size and age-based retention. Zero external dependencies.
|
|
7
7
|
*/
|
|
8
|
-
export type AuditEventType = 'key.created' | 'key.revoked' | 'key.rotated' | 'key.topup' | 'key.acl_updated' | 'key.expiry_updated' | 'key.quota_updated' | 'key.tags_updated' | 'key.ip_updated' | 'key.limit_updated' | 'gate.allow' | 'gate.deny' | 'session.created' | 'session.destroyed' | 'oauth.client_registered' | 'oauth.token_issued' | 'oauth.token_revoked' | 'team.created' | 'team.updated' | 'team.deleted' | 'team.key_assigned' | 'team.key_removed' | 'admin.auth_failed' | 'admin.alerts_configured' | 'webhook.dead_letter_cleared' | 'token.created' | 'token.revoked' | 'billing.refund';
|
|
8
|
+
export type AuditEventType = 'key.created' | 'key.revoked' | 'key.rotated' | 'key.topup' | 'key.acl_updated' | 'key.expiry_updated' | 'key.quota_updated' | 'key.tags_updated' | 'key.ip_updated' | 'key.limit_updated' | 'gate.allow' | 'gate.deny' | 'session.created' | 'session.destroyed' | 'oauth.client_registered' | 'oauth.token_issued' | 'oauth.token_revoked' | 'team.created' | 'team.updated' | 'team.deleted' | 'team.key_assigned' | 'team.key_removed' | 'admin.auth_failed' | 'admin.alerts_configured' | 'webhook.dead_letter_cleared' | 'token.created' | 'token.revoked' | 'billing.refund' | 'key.auto_topup_configured' | 'key.auto_topped_up' | 'admin_key.created' | 'admin_key.revoked';
|
|
9
9
|
export interface AuditEvent {
|
|
10
10
|
/** Monotonically increasing ID */
|
|
11
11
|
id: number;
|
package/dist/audit.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,MAAM,cAAc,GAEtB,aAAa,GACb,aAAa,GACb,aAAa,GACb,WAAW,GACX,iBAAiB,GACjB,oBAAoB,GACpB,mBAAmB,GACnB,kBAAkB,GAClB,gBAAgB,GAChB,mBAAmB,GAEnB,YAAY,GACZ,WAAW,GAEX,iBAAiB,GACjB,mBAAmB,GAEnB,yBAAyB,GACzB,oBAAoB,GACpB,qBAAqB,GAErB,cAAc,GACd,cAAc,GACd,cAAc,GACd,mBAAmB,GACnB,kBAAkB,GAElB,mBAAmB,GACnB,yBAAyB,GAEzB,6BAA6B,GAE7B,eAAe,GACf,eAAe,GAEf,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"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"}
|
package/dist/audit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;AAoRH,0CAGC;AAxLD,MAAM,oBAAoB,GAAmB;IAC3C,SAAS,EAAE,MAAM;IACjB,WAAW,EAAE,GAAG,EAAE,UAAU;IAC5B,iBAAiB,EAAE,MAAM,EAAE,WAAW;CACvC,CAAC;AAEF,gFAAgF;AAEhF,MAAa,WAAW;IACd,MAAM,GAAiB,EAAE,CAAC;IAC1B,MAAM,GAAG,CAAC,CAAC;IACF,MAAM,CAAiB;IAChC,YAAY,GAA0C,IAAI,CAAC;IAEnE,YAAY,MAAgC;QAC1C,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,oBAAoB,EAAE,GAAG,MAAM,EAAE,CAAC;QAErD,gCAAgC;QAChC,IAAI,IAAI,CAAC,MAAM,CAAC,iBAAiB,GAAG,CAAC,EAAE,CAAC;YACtC,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;YAC9F,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC,CAAC,6BAA6B;QAC1D,CAAC;IACH,CAAC;IAED;;OAEG;IACH,GAAG,CAAC,IAAoB,EAAE,KAAa,EAAE,OAAe,EAAE,WAAoC,EAAE;QAC9F,MAAM,KAAK,GAAe;YACxB,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE;YACjB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,IAAI;YACJ,KAAK;YACL,OAAO;YACP,QAAQ;SACT,CAAC;QAEF,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAExB,sDAAsD;QACtD,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;YAC/C,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC1D,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAgB,EAAE;QACtB,IAAI,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC;QAE3B,oBAAoB;QACpB,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YACjC,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACvD,CAAC;QAED,oDAAoD;QACpD,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;YACZ,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;YACzC,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;QAC9E,CAAC;QAED,uBAAuB;QACvB,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;YACZ,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;YAC9C,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,IAAI,SAAS,CAAC,CAAC;QAChF,CAAC;QACD,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;YACZ,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;YAC9C,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,IAAI,SAAS,CAAC,CAAC;QAChF,CAAC;QAED,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC;QAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;QAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC;QAE1D,uEAAuE;QACvE,MAAM,QAAQ,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC;QAEpD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAChD,CAAC;IAED;;OAEG;IACH,KAAK;QAQH,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,UAAU,GAAG,GAAG,GAAG,SAAS,CAAC;QACnC,MAAM,SAAS,GAAG,GAAG,GAAG,UAAU,CAAC;QAEnC,MAAM,YAAY,GAA2B,EAAE,CAAC;QAChD,IAAI,cAAc,GAAG,CAAC,CAAC;QACvB,IAAI,aAAa,GAAG,CAAC,CAAC;QAEtB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAC5B,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;YACvD,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;YAC3C,IAAI,EAAE,IAAI,UAAU;gBAAE,cAAc,EAAE,CAAC;YACvC,IAAI,EAAE,IAAI,SAAS;gBAAE,aAAa,EAAE,CAAC;QACvC,CAAC;QAED,OAAO;YACL,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM;YAC/B,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;YACrE,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;YAC1F,YAAY;YACZ,cAAc;YACd,aAAa;SACd,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,SAAS;QACP,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAC1B,CAAC;IAED;;OAEG;IACH,SAAS,CAAC,IAAgB,EAAE;QAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,MAAM,EAAE,CAAC,CAAC;QAC9D,MAAM,MAAM,GAAG,iCAAiC,CAAC;QACjD,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CACjC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CACvG,CAAC;QACF,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IAED;;OAEG;IACH,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;IAC5B,CAAC;IAED;;OAEG;IACH,gBAAgB;QACd,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,CAAC;YAAE,OAAO,CAAC,CAAC;QAE3C,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,SAAS,CAAC,CAAC;QAClE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;QAClC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,IAAI,MAAM,CAAC,CAAC;QACjF,OAAO,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;IACrC,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IAClB,CAAC;IAED;;OAEG;IACH,OAAO;QACL,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACjC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAC3B,CAAC;IACH,CAAC;CACF;AAzKD,kCAyKC;AAED,gFAAgF;AAEhF,SAAgB,eAAe,CAAC,GAAW;IACzC,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,KAAK,CAAC;IAC1C,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACjD,CAAC"}
|
package/dist/gate.d.ts
CHANGED
|
@@ -34,6 +34,8 @@ export declare class Gate {
|
|
|
34
34
|
onUsageEvent?: (event: UsageEvent) => void;
|
|
35
35
|
/** Optional hook called after credits are deducted (for Redis sync). */
|
|
36
36
|
onCreditsDeducted?: (apiKey: string, amount: number) => void;
|
|
37
|
+
/** Optional hook called when auto-topup is triggered (for audit/webhook). */
|
|
38
|
+
onAutoTopup?: (apiKey: string, amount: number, newBalance: number) => void;
|
|
37
39
|
constructor(config: PayGateConfig, statePath?: string);
|
|
38
40
|
/**
|
|
39
41
|
* Evaluate a tool call request.
|
|
@@ -111,6 +113,12 @@ export declare class Gate {
|
|
|
111
113
|
refund(apiKey: string, toolName: string, credits: number): void;
|
|
112
114
|
/** Whether refund-on-failure is enabled */
|
|
113
115
|
get refundOnFailure(): boolean;
|
|
116
|
+
/**
|
|
117
|
+
* Check if auto-topup should be triggered for a key.
|
|
118
|
+
* Called after credit deduction. If credits are below the threshold
|
|
119
|
+
* and daily limits allow, credits are added automatically.
|
|
120
|
+
*/
|
|
121
|
+
checkAutoTopup(apiKey: string): boolean;
|
|
114
122
|
destroy(): void;
|
|
115
123
|
private recordEvent;
|
|
116
124
|
}
|
package/dist/gate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,YAAY,EAAe,aAAa,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC7I,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,qBAAa,IAAI;IACf,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;IACxC,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,mEAAmE;IACnE,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzF,uDAAuD;IACvD,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,iFAAiF;IACjF,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IAC3C,wEAAwE;IACxE,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"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"}
|
package/dist/gate.js
CHANGED
|
@@ -33,6 +33,8 @@ class Gate {
|
|
|
33
33
|
onUsageEvent;
|
|
34
34
|
/** Optional hook called after credits are deducted (for Redis sync). */
|
|
35
35
|
onCreditsDeducted;
|
|
36
|
+
/** Optional hook called when auto-topup is triggered (for audit/webhook). */
|
|
37
|
+
onAutoTopup;
|
|
36
38
|
constructor(config, statePath) {
|
|
37
39
|
this.config = config;
|
|
38
40
|
this.store = new store_1.KeyStore(statePath);
|
|
@@ -186,6 +188,8 @@ class Gate {
|
|
|
186
188
|
this.teamRecorder(apiKey, creditsRequired);
|
|
187
189
|
}
|
|
188
190
|
this.store.save();
|
|
191
|
+
// Auto-topup: if credits dropped below threshold, add credits
|
|
192
|
+
this.checkAutoTopup(apiKey);
|
|
189
193
|
const remaining = this.store.getKey(apiKey)?.credits ?? 0;
|
|
190
194
|
this.recordEvent(apiKey, keyRecord.name, toolName, creditsRequired, true, undefined, keyRecord.namespace);
|
|
191
195
|
return { allowed: true, creditsCharged: creditsRequired, remainingCredits: remaining };
|
|
@@ -442,6 +446,8 @@ class Gate {
|
|
|
442
446
|
this.teamRecorder(apiKey, totalCreditsNeeded);
|
|
443
447
|
}
|
|
444
448
|
this.store.save();
|
|
449
|
+
// Auto-topup: if credits dropped below threshold, add credits
|
|
450
|
+
this.checkAutoTopup(apiKey);
|
|
445
451
|
const remaining = this.store.getKey(apiKey)?.credits ?? 0;
|
|
446
452
|
// Record usage events for each call
|
|
447
453
|
for (let i = 0; i < calls.length; i++) {
|
|
@@ -581,6 +587,35 @@ class Gate {
|
|
|
581
587
|
get refundOnFailure() {
|
|
582
588
|
return this.config.refundOnFailure;
|
|
583
589
|
}
|
|
590
|
+
/**
|
|
591
|
+
* Check if auto-topup should be triggered for a key.
|
|
592
|
+
* Called after credit deduction. If credits are below the threshold
|
|
593
|
+
* and daily limits allow, credits are added automatically.
|
|
594
|
+
*/
|
|
595
|
+
checkAutoTopup(apiKey) {
|
|
596
|
+
const record = this.store.getKey(apiKey);
|
|
597
|
+
if (!record || !record.autoTopup)
|
|
598
|
+
return false;
|
|
599
|
+
const { threshold, amount, maxDaily } = record.autoTopup;
|
|
600
|
+
if (record.credits >= threshold)
|
|
601
|
+
return false;
|
|
602
|
+
// Reset daily counter if needed (UTC date boundary)
|
|
603
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
604
|
+
if (record.autoTopupLastResetDay !== today) {
|
|
605
|
+
record.autoTopupTodayCount = 0;
|
|
606
|
+
record.autoTopupLastResetDay = today;
|
|
607
|
+
}
|
|
608
|
+
// Check daily limit (0 = unlimited)
|
|
609
|
+
if (maxDaily > 0 && record.autoTopupTodayCount >= maxDaily)
|
|
610
|
+
return false;
|
|
611
|
+
// Perform auto-topup
|
|
612
|
+
record.credits += amount;
|
|
613
|
+
record.autoTopupTodayCount++;
|
|
614
|
+
this.store.save();
|
|
615
|
+
// Notify listeners
|
|
616
|
+
this.onAutoTopup?.(apiKey, amount, record.credits);
|
|
617
|
+
return true;
|
|
618
|
+
}
|
|
584
619
|
destroy() {
|
|
585
620
|
this.rateLimiter.destroy();
|
|
586
621
|
this.webhook?.destroy();
|