paygate-mcp 9.9.0 → 10.1.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 CHANGED
@@ -11,7 +11,7 @@ Monetize any MCP server with one command. Add API key auth, per-tool pricing, ra
11
11
  - [Quick Start](#quick-start)
12
12
  - [What It Does](#what-it-does)
13
13
  - [Usage](#usage) — Local stdio, remote HTTP, multi-server, client SDK
14
- - [API Reference](#api-reference) — All 178+ endpoints
14
+ - [API Reference](#api-reference) — All 196+ endpoints
15
15
  - [CLI Options](#cli-options)
16
16
  - [Deployment](#deployment) — Docker, docker-compose, systemd, PM2
17
17
  - [Load Testing](#load-testing) — k6 benchmarking for production
@@ -66,7 +66,7 @@ Agent → PayGate (auth + billing) → Your MCP Server (stdio or HTTP)
66
66
  - **SSE Streaming** — Full MCP Streamable HTTP transport (POST SSE, GET notifications, DELETE sessions)
67
67
  - **Audit Log** — Structured audit trail with retention policies, query API, CSV/JSON export
68
68
  - **Registry/Discovery** — Agent-discoverable pricing via `/.well-known/mcp-payment`, `/pricing`, and `/.well-known/mcp.json` identity card
69
- - **OpenAPI 3.1 + Interactive Docs** — Auto-generated spec at `/openapi.json`, Swagger UI at `/docs` — all 178+ endpoints documented
69
+ - **OpenAPI 3.1 + Interactive Docs** — Auto-generated spec at `/openapi.json`, Swagger UI at `/docs` — all 196+ endpoints documented
70
70
  - **Public Endpoint Rate Limiting** — Configurable per-IP rate limit (default 300/min) on `/health`, `/info`, `/pricing`, `/docs`, `/openapi.json`, `/.well-known/*`, `/robots.txt`, `/` — 429 with Retry-After header
71
71
  - **Robots.txt + HEAD Support** — Standard `/robots.txt` (allow public, disallow admin/keys), HEAD method on all public endpoints for uptime monitoring
72
72
  - **Prometheus Metrics** — `/metrics` endpoint with counters, gauges, and uptime in standard text format
@@ -174,6 +174,12 @@ Agent → PayGate (auth + billing) → Your MCP Server (stdio or HTTP)
174
174
  - **IP Access Control** — Fine-grained IP-based access control with CIDR notation support — global allow/deny lists, per-key IP binding, automatic blocking after configurable violation thresholds, X-Forwarded-For/X-Real-IP trusted proxy depth, IPv6-mapped IPv4 normalization, manage via `GET/POST/DELETE /admin/ip-access`
175
175
  - **Request Signing (HMAC-SHA256)** — Cryptographic request authentication with replay protection — `X-Signature: t=<ts>,n=<nonce>,s=<sig>` header, timestamp tolerance with nonce dedup, per-key signing secrets with rotation, timing-safe comparison, manage via `GET/POST/DELETE /admin/signing`
176
176
  - **Multi-Tenant Isolation** — Full tenant isolation for platform operators — per-tenant rate limits, credit pools, usage tracking, API key binding, tenant suspension/activation, cross-tenant reporting, configurable limits (10K tenants, 1K keys/tenant), manage via `GET/POST/DELETE /admin/tenants`
177
+ - **Request Tracing** — End-to-end structured tracing with span recording at gate, backend, and transform stages — trace/request ID lookup, timing breakdown (gateMs/backendMs/transformMs), configurable sample rate, retention limits, P95 latency tracking, JSON export, manage via `GET/POST/DELETE /admin/tracing`
178
+ - **Budget Policy Engine** — Burn rate monitoring with progressive throttling — daily/monthly budget enforcement, credits/minute burn rate tracking over configurable windows, three actions (alert/throttle/deny), per-namespace and per-key targeting, budget remaining forecast, automatic daily/monthly reset, manage via `GET/POST/DELETE /admin/budget-policies`
179
+ - **Tool Dependency Graph** — DAG-based workflow validation — register tool dependencies, enforce execution order, failure propagation (upstream failure blocks downstream), topological sort, cycle detection, per-workflow execution tracking, hard vs soft dependencies, group scoping, manage via `GET/POST/DELETE /admin/tool-deps`
180
+ - **Quota Management** — Granular daily/weekly/monthly hard caps per API key — per-tool or global quotas, calls or credits metric, burst allowance (temporary over-limit percentage), three overage actions (deny/warn/throttle), UTC-based period boundaries (daily midnight, weekly Monday, monthly 1st), automatic period rollover, manage via `GET/POST/DELETE /admin/quota-rules`
181
+ - **Webhook Replay (DLQ)** — Dead letter queue management for failed webhook deliveries — record failures with full request context (URL, headers, body, HMAC signature), replay individual or bulk failed deliveries, status tracking (pending → retrying → succeeded/exhausted), configurable max retries with timeout, purge by ID or status, age-based expiry, manage via `GET/POST/DELETE /admin/webhook-replay`
182
+ - **Config Profiles** — Named configuration presets with save/activate/rollback — profile inheritance chains (base → child merging), SHA-256 checksums, flat-key diffing for comparison (onlyInA/onlyInB/changed/unchanged), import/export as JSON with merge or replace mode, activation history, circular inheritance detection, manage via `GET/POST/DELETE /admin/config-profiles`
177
183
  - **Anomaly Detection** — `GET /admin/anomalies` identifies unusual patterns: keys with high denial rates, rapid credit depletion, low remaining credits, with severity ratings and detailed descriptions
178
184
  - **Usage Forecasting** — `GET /admin/forecast` predicts future credit consumption with per-key depletion estimates, calls remaining, at-risk key identification, system-wide consumption aggregates, and per-tool cost breakdown
179
185
  - **Compliance Report** — `GET /admin/compliance` generates compliance-ready report with key governance (expiry coverage), access control (ACL/IP/spending limit coverage), audit trail completeness, weighted overall score, and actionable recommendations
@@ -487,7 +493,7 @@ A real-time admin UI for managing keys, viewing usage, and monitoring tool calls
487
493
  | `/.well-known/mcp-payment` | GET | None | Server payment metadata (SEP-2007) |
488
494
  | `/.well-known/mcp.json` | GET | None | MCP Server Identity card (discovery) |
489
495
  | `/pricing` | GET | None | Full per-tool pricing breakdown |
490
- | `/openapi.json` | GET | None | OpenAPI 3.1 spec (all 178+ endpoints) |
496
+ | `/openapi.json` | GET | None | OpenAPI 3.1 spec (all 196+ endpoints) |
491
497
  | `/docs` | GET | None | Interactive API docs (Swagger UI) |
492
498
  | `/robots.txt` | GET | None | Crawler directives (allow public, disallow admin/keys) |
493
499
  | `/portal` | GET | None | Self-service API key portal (browser UI, auth via X-API-Key prompt) |
@@ -0,0 +1,130 @@
1
+ /**
2
+ * BudgetPolicyEngine — Spend governance and burn rate control.
3
+ *
4
+ * Protects against unexpected cost explosions from leaked tokens,
5
+ * runaway agents, or usage pattern changes. Monitors burn rates
6
+ * and enforces daily/monthly budgets with progressive throttling.
7
+ *
8
+ * Features:
9
+ * - Burn rate monitoring with configurable window and threshold
10
+ * - Progressive throttling (auto-reduce rate limits on overspend)
11
+ * - Daily and monthly budget enforcement
12
+ * - Budget remaining forecast via X-Budget-Remaining header
13
+ * - Per-namespace and per-group policy targeting
14
+ * - Stats: budget utilization, burn rate, throttle events
15
+ *
16
+ * Zero external dependencies.
17
+ */
18
+ export interface BudgetPolicy {
19
+ policyId: string;
20
+ name: string;
21
+ /** Target namespace. Null = global. */
22
+ targetNamespace?: string;
23
+ /** Target API key. Null = all keys in namespace. */
24
+ targetApiKey?: string;
25
+ /** Credits per minute threshold to trigger alert. Default 100. */
26
+ burnRateThreshold: number;
27
+ /** Window in seconds to measure burn rate. Default 60. */
28
+ burnRateWindowSec: number;
29
+ /** Daily budget in credits. 0 = unlimited. */
30
+ dailyBudget: number;
31
+ /** Monthly budget in credits. 0 = unlimited. */
32
+ monthlyBudget: number;
33
+ /** Action when burn rate exceeded. */
34
+ onBurnRateExceeded: 'alert' | 'throttle' | 'deny';
35
+ /** Throttle reduction percent. Default 50. */
36
+ throttleReductionPercent: number;
37
+ /** Throttle cooldown in seconds. Default 300 (5 min). */
38
+ throttleCooldownSec: number;
39
+ active: boolean;
40
+ createdAt: number;
41
+ updatedAt: number;
42
+ }
43
+ export interface BudgetState {
44
+ policyId: string;
45
+ /** Rolling window spend entries. */
46
+ windowSpend: {
47
+ time: number;
48
+ credits: number;
49
+ }[];
50
+ /** Is currently throttled? */
51
+ isThrottled: boolean;
52
+ /** Throttle expires at (epoch ms). */
53
+ throttledUntil: number;
54
+ /** Today's spend. */
55
+ dailySpent: number;
56
+ /** This month's spend. */
57
+ monthlySpent: number;
58
+ /** Day boundary (epoch ms). */
59
+ dailyResetAt: number;
60
+ /** Month boundary (epoch ms). */
61
+ monthlyResetAt: number;
62
+ /** Total burn rate alerts triggered. */
63
+ burnRateAlerts: number;
64
+ /** Total throttle events. */
65
+ throttleEvents: number;
66
+ }
67
+ export interface BudgetCheckResult {
68
+ allowed: boolean;
69
+ reason?: string;
70
+ burnRateExceeded: boolean;
71
+ isThrottled: boolean;
72
+ dailyRemaining?: number;
73
+ monthlyRemaining?: number;
74
+ currentBurnRate: number;
75
+ /** Estimated hours until budget exhaustion at current rate. */
76
+ hoursRemaining?: number;
77
+ }
78
+ export interface BudgetPolicyStats {
79
+ totalPolicies: number;
80
+ activePolicies: number;
81
+ totalBurnRateAlerts: number;
82
+ totalThrottleEvents: number;
83
+ policies: Array<{
84
+ policyId: string;
85
+ name: string;
86
+ dailyUtilization: number;
87
+ monthlyUtilization: number;
88
+ currentBurnRate: number;
89
+ isThrottled: boolean;
90
+ }>;
91
+ }
92
+ export type BudgetPolicyCreateParams = Omit<BudgetPolicy, 'policyId' | 'createdAt' | 'updatedAt'>;
93
+ export declare class BudgetPolicyEngine {
94
+ private policies;
95
+ private state;
96
+ /**
97
+ * Create a new budget policy.
98
+ */
99
+ createPolicy(params: BudgetPolicyCreateParams): BudgetPolicy;
100
+ /**
101
+ * Get a policy by ID.
102
+ */
103
+ getPolicy(policyId: string): BudgetPolicy | undefined;
104
+ /**
105
+ * List all policies.
106
+ */
107
+ listPolicies(): BudgetPolicy[];
108
+ /**
109
+ * Delete a policy.
110
+ */
111
+ deletePolicy(policyId: string): boolean;
112
+ /**
113
+ * Record spending and check against budget policies.
114
+ * Returns the most restrictive result from all applicable policies.
115
+ */
116
+ recordSpend(namespace: string | undefined, apiKey: string | undefined, credits: number): BudgetCheckResult;
117
+ /**
118
+ * Get stats across all policies.
119
+ */
120
+ stats(): BudgetPolicyStats;
121
+ /**
122
+ * Clear all policies and state.
123
+ */
124
+ clear(): void;
125
+ private findApplicable;
126
+ private checkPolicy;
127
+ private computeBurnRate;
128
+ private resetIfNeeded;
129
+ }
130
+ //# sourceMappingURL=budget-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budget-policy.d.ts","sourceRoot":"","sources":["../src/budget-policy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,326 @@
1
+ "use strict";
2
+ /**
3
+ * BudgetPolicyEngine — Spend governance and burn rate control.
4
+ *
5
+ * Protects against unexpected cost explosions from leaked tokens,
6
+ * runaway agents, or usage pattern changes. Monitors burn rates
7
+ * and enforces daily/monthly budgets with progressive throttling.
8
+ *
9
+ * Features:
10
+ * - Burn rate monitoring with configurable window and threshold
11
+ * - Progressive throttling (auto-reduce rate limits on overspend)
12
+ * - Daily and monthly budget enforcement
13
+ * - Budget remaining forecast via X-Budget-Remaining header
14
+ * - Per-namespace and per-group policy targeting
15
+ * - Stats: budget utilization, burn rate, throttle events
16
+ *
17
+ * Zero external dependencies.
18
+ */
19
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ var desc = Object.getOwnPropertyDescriptor(m, k);
22
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
23
+ desc = { enumerable: true, get: function() { return m[k]; } };
24
+ }
25
+ Object.defineProperty(o, k2, desc);
26
+ }) : (function(o, m, k, k2) {
27
+ if (k2 === undefined) k2 = k;
28
+ o[k2] = m[k];
29
+ }));
30
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
31
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
32
+ }) : function(o, v) {
33
+ o["default"] = v;
34
+ });
35
+ var __importStar = (this && this.__importStar) || (function () {
36
+ var ownKeys = function(o) {
37
+ ownKeys = Object.getOwnPropertyNames || function (o) {
38
+ var ar = [];
39
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
40
+ return ar;
41
+ };
42
+ return ownKeys(o);
43
+ };
44
+ return function (mod) {
45
+ if (mod && mod.__esModule) return mod;
46
+ var result = {};
47
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
48
+ __setModuleDefault(result, mod);
49
+ return result;
50
+ };
51
+ })();
52
+ Object.defineProperty(exports, "__esModule", { value: true });
53
+ exports.BudgetPolicyEngine = void 0;
54
+ const crypto = __importStar(require("crypto"));
55
+ // ─── Default values ─────────────────────────────────────────────────────────
56
+ function generatePolicyId() {
57
+ return 'bpol_' + crypto.randomBytes(8).toString('hex');
58
+ }
59
+ function nextDayStart() {
60
+ const d = new Date();
61
+ d.setUTCHours(0, 0, 0, 0);
62
+ d.setUTCDate(d.getUTCDate() + 1);
63
+ return d.getTime();
64
+ }
65
+ function nextMonthStart() {
66
+ const d = new Date();
67
+ d.setUTCDate(1);
68
+ d.setUTCHours(0, 0, 0, 0);
69
+ d.setUTCMonth(d.getUTCMonth() + 1);
70
+ return d.getTime();
71
+ }
72
+ // ─── BudgetPolicyEngine Class ───────────────────────────────────────────────
73
+ class BudgetPolicyEngine {
74
+ policies = new Map();
75
+ state = new Map();
76
+ /**
77
+ * Create a new budget policy.
78
+ */
79
+ createPolicy(params) {
80
+ const policyId = generatePolicyId();
81
+ const now = Date.now();
82
+ const policy = {
83
+ policyId,
84
+ name: params.name,
85
+ targetNamespace: params.targetNamespace,
86
+ targetApiKey: params.targetApiKey,
87
+ burnRateThreshold: params.burnRateThreshold ?? 100,
88
+ burnRateWindowSec: params.burnRateWindowSec ?? 60,
89
+ dailyBudget: params.dailyBudget ?? 0,
90
+ monthlyBudget: params.monthlyBudget ?? 0,
91
+ onBurnRateExceeded: params.onBurnRateExceeded ?? 'alert',
92
+ throttleReductionPercent: params.throttleReductionPercent ?? 50,
93
+ throttleCooldownSec: params.throttleCooldownSec ?? 300,
94
+ active: params.active ?? true,
95
+ createdAt: now,
96
+ updatedAt: now,
97
+ };
98
+ this.policies.set(policyId, policy);
99
+ this.state.set(policyId, {
100
+ policyId,
101
+ windowSpend: [],
102
+ isThrottled: false,
103
+ throttledUntil: 0,
104
+ dailySpent: 0,
105
+ monthlySpent: 0,
106
+ dailyResetAt: nextDayStart(),
107
+ monthlyResetAt: nextMonthStart(),
108
+ burnRateAlerts: 0,
109
+ throttleEvents: 0,
110
+ });
111
+ return { ...policy };
112
+ }
113
+ /**
114
+ * Get a policy by ID.
115
+ */
116
+ getPolicy(policyId) {
117
+ const p = this.policies.get(policyId);
118
+ return p ? { ...p } : undefined;
119
+ }
120
+ /**
121
+ * List all policies.
122
+ */
123
+ listPolicies() {
124
+ return Array.from(this.policies.values()).map(p => ({ ...p }));
125
+ }
126
+ /**
127
+ * Delete a policy.
128
+ */
129
+ deletePolicy(policyId) {
130
+ this.state.delete(policyId);
131
+ return this.policies.delete(policyId);
132
+ }
133
+ /**
134
+ * Record spending and check against budget policies.
135
+ * Returns the most restrictive result from all applicable policies.
136
+ */
137
+ recordSpend(namespace, apiKey, credits) {
138
+ const applicablePolicies = this.findApplicable(namespace, apiKey);
139
+ if (applicablePolicies.length === 0) {
140
+ return { allowed: true, burnRateExceeded: false, isThrottled: false, currentBurnRate: 0 };
141
+ }
142
+ let mostRestrictive = {
143
+ allowed: true,
144
+ burnRateExceeded: false,
145
+ isThrottled: false,
146
+ currentBurnRate: 0,
147
+ };
148
+ for (const policy of applicablePolicies) {
149
+ const result = this.checkPolicy(policy, credits);
150
+ if (!result.allowed) {
151
+ mostRestrictive = result;
152
+ break;
153
+ }
154
+ if (result.burnRateExceeded)
155
+ mostRestrictive.burnRateExceeded = true;
156
+ if (result.isThrottled)
157
+ mostRestrictive.isThrottled = true;
158
+ if (result.currentBurnRate > mostRestrictive.currentBurnRate) {
159
+ mostRestrictive.currentBurnRate = result.currentBurnRate;
160
+ }
161
+ if (result.dailyRemaining !== undefined) {
162
+ mostRestrictive.dailyRemaining = Math.min(mostRestrictive.dailyRemaining ?? Infinity, result.dailyRemaining);
163
+ }
164
+ if (result.monthlyRemaining !== undefined) {
165
+ mostRestrictive.monthlyRemaining = Math.min(mostRestrictive.monthlyRemaining ?? Infinity, result.monthlyRemaining);
166
+ }
167
+ if (result.hoursRemaining !== undefined) {
168
+ mostRestrictive.hoursRemaining = Math.min(mostRestrictive.hoursRemaining ?? Infinity, result.hoursRemaining);
169
+ }
170
+ }
171
+ return mostRestrictive;
172
+ }
173
+ /**
174
+ * Get stats across all policies.
175
+ */
176
+ stats() {
177
+ let totalAlerts = 0;
178
+ let totalThrottles = 0;
179
+ const policyStats = [];
180
+ for (const policy of this.policies.values()) {
181
+ const st = this.state.get(policy.policyId);
182
+ if (!st)
183
+ continue;
184
+ this.resetIfNeeded(st);
185
+ totalAlerts += st.burnRateAlerts;
186
+ totalThrottles += st.throttleEvents;
187
+ const burnRate = this.computeBurnRate(st, policy);
188
+ policyStats.push({
189
+ policyId: policy.policyId,
190
+ name: policy.name,
191
+ dailyUtilization: policy.dailyBudget > 0 ? Math.round((st.dailySpent / policy.dailyBudget) * 100) : 0,
192
+ monthlyUtilization: policy.monthlyBudget > 0 ? Math.round((st.monthlySpent / policy.monthlyBudget) * 100) : 0,
193
+ currentBurnRate: burnRate,
194
+ isThrottled: st.isThrottled && st.throttledUntil > Date.now(),
195
+ });
196
+ }
197
+ return {
198
+ totalPolicies: this.policies.size,
199
+ activePolicies: Array.from(this.policies.values()).filter(p => p.active).length,
200
+ totalBurnRateAlerts: totalAlerts,
201
+ totalThrottleEvents: totalThrottles,
202
+ policies: policyStats,
203
+ };
204
+ }
205
+ /**
206
+ * Clear all policies and state.
207
+ */
208
+ clear() {
209
+ this.policies.clear();
210
+ this.state.clear();
211
+ }
212
+ // ─── Private ───────────────────────────────────────────────────────────────
213
+ findApplicable(namespace, apiKey) {
214
+ const results = [];
215
+ for (const policy of this.policies.values()) {
216
+ if (!policy.active)
217
+ continue;
218
+ // Check targeting: most specific match wins
219
+ if (policy.targetApiKey && policy.targetApiKey !== apiKey)
220
+ continue;
221
+ if (policy.targetNamespace && policy.targetNamespace !== namespace)
222
+ continue;
223
+ results.push(policy);
224
+ }
225
+ return results;
226
+ }
227
+ checkPolicy(policy, credits) {
228
+ const st = this.state.get(policy.policyId);
229
+ this.resetIfNeeded(st);
230
+ const now = Date.now();
231
+ // Record spend
232
+ st.dailySpent += credits;
233
+ st.monthlySpent += credits;
234
+ st.windowSpend.push({ time: now, credits });
235
+ // Prune old window entries
236
+ const windowCutoff = now - (policy.burnRateWindowSec * 1000);
237
+ st.windowSpend = st.windowSpend.filter(e => e.time >= windowCutoff);
238
+ // Compute burn rate (credits/minute)
239
+ const burnRate = this.computeBurnRate(st, policy);
240
+ const burnRateExceeded = burnRate > policy.burnRateThreshold;
241
+ // Check daily budget
242
+ if (policy.dailyBudget > 0 && st.dailySpent > policy.dailyBudget) {
243
+ return {
244
+ allowed: false,
245
+ reason: 'daily-budget-exceeded',
246
+ burnRateExceeded,
247
+ isThrottled: false,
248
+ dailyRemaining: 0,
249
+ monthlyRemaining: policy.monthlyBudget > 0 ? Math.max(0, policy.monthlyBudget - st.monthlySpent) : undefined,
250
+ currentBurnRate: burnRate,
251
+ };
252
+ }
253
+ // Check monthly budget
254
+ if (policy.monthlyBudget > 0 && st.monthlySpent > policy.monthlyBudget) {
255
+ return {
256
+ allowed: false,
257
+ reason: 'monthly-budget-exceeded',
258
+ burnRateExceeded,
259
+ isThrottled: false,
260
+ dailyRemaining: policy.dailyBudget > 0 ? Math.max(0, policy.dailyBudget - st.dailySpent) : undefined,
261
+ monthlyRemaining: 0,
262
+ currentBurnRate: burnRate,
263
+ };
264
+ }
265
+ // Handle burn rate exceedance
266
+ if (burnRateExceeded) {
267
+ st.burnRateAlerts++;
268
+ if (policy.onBurnRateExceeded === 'deny') {
269
+ return {
270
+ allowed: false,
271
+ reason: 'burn-rate-exceeded',
272
+ burnRateExceeded: true,
273
+ isThrottled: false,
274
+ currentBurnRate: burnRate,
275
+ };
276
+ }
277
+ if (policy.onBurnRateExceeded === 'throttle' && !st.isThrottled) {
278
+ st.isThrottled = true;
279
+ st.throttledUntil = now + (policy.throttleCooldownSec * 1000);
280
+ st.throttleEvents++;
281
+ }
282
+ }
283
+ // Check if throttle expired
284
+ if (st.isThrottled && now > st.throttledUntil) {
285
+ st.isThrottled = false;
286
+ }
287
+ // Compute remaining
288
+ const dailyRemaining = policy.dailyBudget > 0 ? Math.max(0, policy.dailyBudget - st.dailySpent) : undefined;
289
+ const monthlyRemaining = policy.monthlyBudget > 0 ? Math.max(0, policy.monthlyBudget - st.monthlySpent) : undefined;
290
+ const remaining = dailyRemaining !== undefined ? dailyRemaining : monthlyRemaining;
291
+ const hoursRemaining = remaining !== undefined && burnRate > 0
292
+ ? Math.round((remaining / burnRate) * 60) / 60 // convert from minutes
293
+ : undefined;
294
+ return {
295
+ allowed: true,
296
+ burnRateExceeded,
297
+ isThrottled: st.isThrottled,
298
+ dailyRemaining,
299
+ monthlyRemaining,
300
+ currentBurnRate: burnRate,
301
+ hoursRemaining,
302
+ };
303
+ }
304
+ computeBurnRate(st, policy) {
305
+ const now = Date.now();
306
+ const windowCutoff = now - (policy.burnRateWindowSec * 1000);
307
+ const windowCredits = st.windowSpend
308
+ .filter(e => e.time >= windowCutoff)
309
+ .reduce((sum, e) => sum + e.credits, 0);
310
+ // Credits per minute
311
+ return (windowCredits / policy.burnRateWindowSec) * 60;
312
+ }
313
+ resetIfNeeded(st) {
314
+ const now = Date.now();
315
+ if (now >= st.dailyResetAt) {
316
+ st.dailySpent = 0;
317
+ st.dailyResetAt = nextDayStart();
318
+ }
319
+ if (now >= st.monthlyResetAt) {
320
+ st.monthlySpent = 0;
321
+ st.monthlyResetAt = nextMonthStart();
322
+ }
323
+ }
324
+ }
325
+ exports.BudgetPolicyEngine = BudgetPolicyEngine;
326
+ //# sourceMappingURL=budget-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budget-policy.js","sourceRoot":"","sources":["../src/budget-policy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * ConfigProfileManager — Named configuration presets.
3
+ *
4
+ * Store and switch between named configuration profiles for different
5
+ * environments (dev, staging, production) or use cases:
6
+ * - Save current config as a named profile
7
+ * - Switch between profiles at runtime
8
+ * - Compare profiles to see differences
9
+ * - Import/export profiles as JSON
10
+ * - Profile inheritance (extend a base profile)
11
+ * - Rollback to previous profile
12
+ *
13
+ * Zero external dependencies.
14
+ */
15
+ export interface ConfigProfile {
16
+ id: string;
17
+ name: string;
18
+ /** Optional description of what this profile is for. */
19
+ description?: string;
20
+ /** The configuration snapshot. */
21
+ config: Record<string, unknown>;
22
+ /** Profile this extends (inherits values from). */
23
+ extendsProfile?: string;
24
+ /** SHA-256 checksum of the config JSON. */
25
+ checksum: string;
26
+ /** Whether this is the currently active profile. */
27
+ active: boolean;
28
+ createdAt: number;
29
+ updatedAt: number;
30
+ }
31
+ export interface ProfileDiff {
32
+ /** Keys only in profile A. */
33
+ onlyInA: string[];
34
+ /** Keys only in profile B. */
35
+ onlyInB: string[];
36
+ /** Keys present in both but with different values. */
37
+ changed: Array<{
38
+ key: string;
39
+ valueA: unknown;
40
+ valueB: unknown;
41
+ }>;
42
+ /** Keys with identical values in both. */
43
+ unchanged: string[];
44
+ }
45
+ export interface ProfileStats {
46
+ totalProfiles: number;
47
+ activeProfile: string | null;
48
+ switchCount: number;
49
+ lastSwitchAt: number | null;
50
+ rollbackAvailable: boolean;
51
+ }
52
+ export interface ConfigProfileManagerConfig {
53
+ enabled: boolean;
54
+ maxProfiles: number;
55
+ /** Max config size in bytes. Default 1MB. */
56
+ maxConfigSize: number;
57
+ }
58
+ export declare class ConfigProfileManager {
59
+ private config;
60
+ private profiles;
61
+ private activeProfileId;
62
+ private previousProfileId;
63
+ private _switchCount;
64
+ private _lastSwitchAt;
65
+ private counter;
66
+ constructor(config?: Partial<ConfigProfileManagerConfig>);
67
+ /**
68
+ * Save a configuration as a named profile.
69
+ */
70
+ saveProfile(params: {
71
+ name: string;
72
+ config: Record<string, unknown>;
73
+ description?: string;
74
+ extendsProfile?: string;
75
+ activate?: boolean;
76
+ }): ConfigProfile;
77
+ /**
78
+ * Get a profile by ID.
79
+ */
80
+ getProfile(id: string): ConfigProfile | undefined;
81
+ /**
82
+ * Get a profile by name.
83
+ */
84
+ getProfileByName(name: string): ConfigProfile | undefined;
85
+ /**
86
+ * List all profiles.
87
+ */
88
+ listProfiles(): ConfigProfile[];
89
+ /**
90
+ * Delete a profile.
91
+ */
92
+ deleteProfile(id: string): boolean;
93
+ /**
94
+ * Activate a profile (makes it the current active profile).
95
+ * Returns the resolved configuration (with inheritance applied).
96
+ */
97
+ activateProfile(id: string): Record<string, unknown>;
98
+ /**
99
+ * Rollback to the previously active profile.
100
+ */
101
+ rollback(): Record<string, unknown> | null;
102
+ /**
103
+ * Get the currently active profile.
104
+ */
105
+ getActive(): ConfigProfile | null;
106
+ /**
107
+ * Resolve configuration for a profile, applying inheritance.
108
+ */
109
+ resolveConfig(id: string): Record<string, unknown>;
110
+ /**
111
+ * Compare two profiles and return the differences.
112
+ */
113
+ compare(idA: string, idB: string): ProfileDiff;
114
+ /**
115
+ * Export all profiles as JSON.
116
+ */
117
+ exportProfiles(): string;
118
+ /**
119
+ * Import profiles from JSON. Returns count imported.
120
+ */
121
+ importProfiles(json: string, mode?: 'merge' | 'replace'): number;
122
+ configure(updates: Partial<ConfigProfileManagerConfig>): ConfigProfileManagerConfig;
123
+ stats(): ProfileStats;
124
+ clear(): void;
125
+ }
126
+ //# sourceMappingURL=config-profiles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-profiles.d.ts","sourceRoot":"","sources":["../src/config-profiles.ts"],"names":[],"mappings":"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"}