curatedmcp 2.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.
Files changed (97) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +175 -0
  3. package/dist/audit/catalog.d.ts +5 -0
  4. package/dist/audit/catalog.d.ts.map +1 -0
  5. package/dist/audit/catalog.js +69 -0
  6. package/dist/audit/index.d.ts +10 -0
  7. package/dist/audit/index.d.ts.map +1 -0
  8. package/dist/audit/index.js +32 -0
  9. package/dist/audit/report.d.ts +6 -0
  10. package/dist/audit/report.d.ts.map +1 -0
  11. package/dist/audit/report.js +79 -0
  12. package/dist/audit/risk.d.ts +3 -0
  13. package/dist/audit/risk.d.ts.map +1 -0
  14. package/dist/audit/risk.js +68 -0
  15. package/dist/audit/scanner.d.ts +8 -0
  16. package/dist/audit/scanner.d.ts.map +1 -0
  17. package/dist/audit/scanner.js +69 -0
  18. package/dist/audit/types.d.ts +29 -0
  19. package/dist/audit/types.d.ts.map +1 -0
  20. package/dist/audit/types.js +2 -0
  21. package/dist/auth.d.ts +23 -0
  22. package/dist/auth.d.ts.map +1 -0
  23. package/dist/auth.js +52 -0
  24. package/dist/cli/add.d.ts +18 -0
  25. package/dist/cli/add.d.ts.map +1 -0
  26. package/dist/cli/add.js +114 -0
  27. package/dist/cli/audit.d.ts +2 -0
  28. package/dist/cli/audit.d.ts.map +1 -0
  29. package/dist/cli/audit.js +58 -0
  30. package/dist/cli/guard.d.ts +2 -0
  31. package/dist/cli/guard.d.ts.map +1 -0
  32. package/dist/cli/guard.js +58 -0
  33. package/dist/cli/init.d.ts +2 -0
  34. package/dist/cli/init.d.ts.map +1 -0
  35. package/dist/cli/init.js +44 -0
  36. package/dist/cli/list.d.ts +5 -0
  37. package/dist/cli/list.d.ts.map +1 -0
  38. package/dist/cli/list.js +33 -0
  39. package/dist/cli/login.d.ts +6 -0
  40. package/dist/cli/login.d.ts.map +1 -0
  41. package/dist/cli/login.js +43 -0
  42. package/dist/cli/remove.d.ts +6 -0
  43. package/dist/cli/remove.d.ts.map +1 -0
  44. package/dist/cli/remove.js +15 -0
  45. package/dist/cli/sync.d.ts +2 -0
  46. package/dist/cli/sync.d.ts.map +1 -0
  47. package/dist/cli/sync.js +104 -0
  48. package/dist/cli.d.ts +10 -0
  49. package/dist/cli.d.ts.map +1 -0
  50. package/dist/cli.js +132 -0
  51. package/dist/guard/broker.d.ts +62 -0
  52. package/dist/guard/broker.d.ts.map +1 -0
  53. package/dist/guard/broker.js +147 -0
  54. package/dist/guard/dashboard.d.ts +14 -0
  55. package/dist/guard/dashboard.d.ts.map +1 -0
  56. package/dist/guard/dashboard.js +428 -0
  57. package/dist/guard/default-policy.json +33 -0
  58. package/dist/guard/index.d.ts +20 -0
  59. package/dist/guard/index.d.ts.map +1 -0
  60. package/dist/guard/index.js +61 -0
  61. package/dist/guard/logger.d.ts +30 -0
  62. package/dist/guard/logger.d.ts.map +1 -0
  63. package/dist/guard/logger.js +118 -0
  64. package/dist/guard/policy.d.ts +19 -0
  65. package/dist/guard/policy.d.ts.map +1 -0
  66. package/dist/guard/policy.js +108 -0
  67. package/dist/guard/proxy.d.ts +29 -0
  68. package/dist/guard/proxy.d.ts.map +1 -0
  69. package/dist/guard/proxy.js +109 -0
  70. package/dist/guard/types.d.ts +70 -0
  71. package/dist/guard/types.d.ts.map +1 -0
  72. package/dist/guard/types.js +2 -0
  73. package/dist/index.d.ts +3 -0
  74. package/dist/index.d.ts.map +1 -0
  75. package/dist/index.js +259 -0
  76. package/dist/proxy.d.ts +122 -0
  77. package/dist/proxy.d.ts.map +1 -0
  78. package/dist/proxy.js +165 -0
  79. package/dist/stack.d.ts +45 -0
  80. package/dist/stack.d.ts.map +1 -0
  81. package/dist/stack.js +93 -0
  82. package/dist/telemetry.d.ts +15 -0
  83. package/dist/telemetry.d.ts.map +1 -0
  84. package/dist/telemetry.js +71 -0
  85. package/dist/tools/get-details.d.ts +14 -0
  86. package/dist/tools/get-details.d.ts.map +1 -0
  87. package/dist/tools/get-details.js +27 -0
  88. package/dist/tools/install.d.ts +2 -0
  89. package/dist/tools/install.d.ts.map +1 -0
  90. package/dist/tools/install.js +74 -0
  91. package/dist/tools/list-categories.d.ts +2 -0
  92. package/dist/tools/list-categories.d.ts.map +1 -0
  93. package/dist/tools/list-categories.js +13 -0
  94. package/dist/tools/search.d.ts +16 -0
  95. package/dist/tools/search.d.ts.map +1 -0
  96. package/dist/tools/search.js +17 -0
  97. package/package.json +78 -0
@@ -0,0 +1,428 @@
1
+ export class Dashboard {
2
+ constructor(actionLogger, policyEngine, port = 4242) {
3
+ this.app = null;
4
+ this.actionLogger = actionLogger;
5
+ this.policyEngine = policyEngine;
6
+ this.port = port;
7
+ }
8
+ setupRoutes(express, app) {
9
+ this.app = app;
10
+ this.app.use(express.static("public"));
11
+ this.app.use(express.json());
12
+ this.app.get("/api/status", (req, res) => {
13
+ const data = this.getDashboardData();
14
+ res.json(data);
15
+ });
16
+ this.app.post("/api/approve/:requestId", (req, res) => {
17
+ const { requestId } = req.params;
18
+ const { approver } = req.body;
19
+ try {
20
+ this.actionLogger.approveAction(requestId, approver || "manual");
21
+ res.json({ success: true });
22
+ }
23
+ catch (error) {
24
+ res.status(400).json({ error: String(error) });
25
+ }
26
+ });
27
+ this.app.post("/api/reject/:requestId", (req, res) => {
28
+ const { requestId } = req.params;
29
+ const { reason } = req.body;
30
+ try {
31
+ this.actionLogger.rejectAction(requestId, reason || "Manual rejection");
32
+ res.json({ success: true });
33
+ }
34
+ catch (error) {
35
+ res.status(400).json({ error: String(error) });
36
+ }
37
+ });
38
+ this.app.get("/", (req, res) => {
39
+ res.send(this.renderHTML());
40
+ });
41
+ }
42
+ getDashboardData() {
43
+ const summary = this.actionLogger.getSummary();
44
+ const recentActions = this.actionLogger.getRecentActions(20);
45
+ const pendingApprovals = this.actionLogger.getPendingApprovals();
46
+ const activePolicies = this.policyEngine.listRules();
47
+ return {
48
+ summary: {
49
+ totalLogs: summary?.total || 0,
50
+ blockedCount: summary?.blocked || 0,
51
+ approvedCount: summary?.approved || 0,
52
+ rejectedCount: summary?.rejected || 0,
53
+ },
54
+ recentActions,
55
+ pendingApprovals,
56
+ activePolicies,
57
+ retentionMinutes: 1440, // 24h default
58
+ };
59
+ }
60
+ renderHTML() {
61
+ return `
62
+ <!DOCTYPE html>
63
+ <html lang="en">
64
+ <head>
65
+ <meta charset="UTF-8">
66
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
67
+ <title>Sentinel Dashboard</title>
68
+ <style>
69
+ * { margin: 0; padding: 0; box-sizing: border-box; }
70
+ body {
71
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
72
+ background: #f5f5f5;
73
+ color: #333;
74
+ }
75
+ .container { max-width: 1200px; margin: 0 auto; padding: 20px; }
76
+ header {
77
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
78
+ color: white;
79
+ padding: 30px 20px;
80
+ margin: -20px -20px 30px -20px;
81
+ border-radius: 8px 8px 0 0;
82
+ }
83
+ h1 { font-size: 28px; margin-bottom: 10px; }
84
+ .subtitle { opacity: 0.9; font-size: 14px; }
85
+
86
+ .tabs {
87
+ display: flex;
88
+ gap: 10px;
89
+ margin-bottom: 20px;
90
+ border-bottom: 2px solid #e0e0e0;
91
+ }
92
+ .tab {
93
+ padding: 12px 20px;
94
+ background: none;
95
+ border: none;
96
+ cursor: pointer;
97
+ font-size: 14px;
98
+ font-weight: 500;
99
+ border-bottom: 3px solid transparent;
100
+ color: #666;
101
+ transition: all 0.2s;
102
+ }
103
+ .tab.active {
104
+ color: #667eea;
105
+ border-bottom-color: #667eea;
106
+ }
107
+ .tab:hover {
108
+ color: #667eea;
109
+ }
110
+
111
+ .grid {
112
+ display: grid;
113
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
114
+ gap: 20px;
115
+ margin-bottom: 30px;
116
+ }
117
+ .card {
118
+ background: white;
119
+ padding: 20px;
120
+ border-radius: 8px;
121
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
122
+ }
123
+ .card-title {
124
+ font-size: 12px;
125
+ color: #999;
126
+ text-transform: uppercase;
127
+ margin-bottom: 10px;
128
+ }
129
+ .card-value {
130
+ font-size: 32px;
131
+ font-weight: bold;
132
+ color: #667eea;
133
+ }
134
+
135
+ .actions-table {
136
+ background: white;
137
+ border-radius: 8px;
138
+ overflow: hidden;
139
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
140
+ }
141
+ table {
142
+ width: 100%;
143
+ border-collapse: collapse;
144
+ }
145
+ th {
146
+ background: #f9f9f9;
147
+ padding: 12px;
148
+ text-align: left;
149
+ font-weight: 600;
150
+ font-size: 12px;
151
+ color: #666;
152
+ border-bottom: 1px solid #e0e0e0;
153
+ }
154
+ td {
155
+ padding: 12px;
156
+ border-bottom: 1px solid #e0e0e0;
157
+ }
158
+ tr:last-child td { border-bottom: none; }
159
+
160
+ .badge {
161
+ display: inline-block;
162
+ padding: 4px 8px;
163
+ border-radius: 4px;
164
+ font-size: 11px;
165
+ font-weight: 600;
166
+ text-transform: uppercase;
167
+ }
168
+ .badge.critical { background: #fee; color: #c33; }
169
+ .badge.warning { background: #fef3cd; color: #856404; }
170
+ .badge.info { background: #d1ecf1; color: #0c5460; }
171
+ .badge.block { background: #f8d7da; color: #721c24; }
172
+ .badge.allow { background: #d4edda; color: #155724; }
173
+
174
+ .modal {
175
+ display: none;
176
+ position: fixed;
177
+ top: 0;
178
+ left: 0;
179
+ right: 0;
180
+ bottom: 0;
181
+ background: rgba(0,0,0,0.5);
182
+ z-index: 1000;
183
+ }
184
+ .modal.active { display: flex; align-items: center; justify-content: center; }
185
+ .modal-content {
186
+ background: white;
187
+ padding: 30px;
188
+ border-radius: 8px;
189
+ max-width: 500px;
190
+ width: 90%;
191
+ }
192
+ .modal-actions {
193
+ display: flex;
194
+ gap: 10px;
195
+ margin-top: 20px;
196
+ justify-content: flex-end;
197
+ }
198
+ button {
199
+ padding: 8px 16px;
200
+ border: none;
201
+ border-radius: 4px;
202
+ cursor: pointer;
203
+ font-weight: 500;
204
+ transition: all 0.2s;
205
+ }
206
+ .btn-primary {
207
+ background: #667eea;
208
+ color: white;
209
+ }
210
+ .btn-primary:hover { background: #5568d3; }
211
+ .btn-danger {
212
+ background: #e74c3c;
213
+ color: white;
214
+ }
215
+ .btn-danger:hover { background: #c0392b; }
216
+ .btn-secondary {
217
+ background: #e0e0e0;
218
+ color: #333;
219
+ }
220
+ .btn-secondary:hover { background: #d0d0d0; }
221
+
222
+ .tab-content { display: none; }
223
+ .tab-content.active { display: block; }
224
+ </style>
225
+ </head>
226
+ <body>
227
+ <div class="container">
228
+ <header>
229
+ <h1>🛡️ Sentinel Dashboard</h1>
230
+ <p class="subtitle">Local-first MCP action firewall</p>
231
+ </header>
232
+
233
+ <div class="tabs">
234
+ <button class="tab active" data-tab="overview">Overview</button>
235
+ <button class="tab" data-tab="actions">Recent Actions</button>
236
+ <button class="tab" data-tab="approvals">Pending Approvals</button>
237
+ <button class="tab" data-tab="policies">Policies</button>
238
+ </div>
239
+
240
+ <div id="overview" class="tab-content active">
241
+ <div class="grid" id="summary-grid">
242
+ <div class="card">
243
+ <div class="card-title">Total Actions</div>
244
+ <div class="card-value">0</div>
245
+ </div>
246
+ <div class="card">
247
+ <div class="card-title">Blocked</div>
248
+ <div class="card-value">0</div>
249
+ </div>
250
+ <div class="card">
251
+ <div class="card-title">Approved</div>
252
+ <div class="card-value">0</div>
253
+ </div>
254
+ <div class="card">
255
+ <div class="card-title">Rejected</div>
256
+ <div class="card-value">0</div>
257
+ </div>
258
+ </div>
259
+ </div>
260
+
261
+ <div id="actions" class="tab-content">
262
+ <div class="actions-table">
263
+ <table>
264
+ <thead>
265
+ <tr>
266
+ <th>Tool</th>
267
+ <th>Server</th>
268
+ <th>Action</th>
269
+ <th>Severity</th>
270
+ <th>Time</th>
271
+ </tr>
272
+ </thead>
273
+ <tbody id="actions-body">
274
+ <tr><td colspan="5" style="text-align:center; color: #999;">No actions yet</td></tr>
275
+ </tbody>
276
+ </table>
277
+ </div>
278
+ </div>
279
+
280
+ <div id="approvals" class="tab-content">
281
+ <div class="actions-table">
282
+ <table>
283
+ <thead>
284
+ <tr>
285
+ <th>Tool</th>
286
+ <th>Server</th>
287
+ <th>Arguments</th>
288
+ <th>Actions</th>
289
+ </tr>
290
+ </thead>
291
+ <tbody id="approvals-body">
292
+ <tr><td colspan="4" style="text-align:center; color: #999;">No pending approvals</td></tr>
293
+ </tbody>
294
+ </table>
295
+ </div>
296
+ </div>
297
+
298
+ <div id="policies" class="tab-content">
299
+ <div class="actions-table">
300
+ <table>
301
+ <thead>
302
+ <tr>
303
+ <th>Name</th>
304
+ <th>Tool Pattern</th>
305
+ <th>Action</th>
306
+ <th>Status</th>
307
+ </tr>
308
+ </thead>
309
+ <tbody id="policies-body">
310
+ <tr><td colspan="4" style="text-align:center; color: #999;">No policies</td></tr>
311
+ </tbody>
312
+ </table>
313
+ </div>
314
+ </div>
315
+ </div>
316
+
317
+ <script>
318
+ // Tab switching
319
+ document.querySelectorAll('.tab').forEach(tab => {
320
+ tab.addEventListener('click', (e) => {
321
+ document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
322
+ document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
323
+ e.target.classList.add('active');
324
+ const tabId = e.target.getAttribute('data-tab');
325
+ document.getElementById(tabId).classList.add('active');
326
+ });
327
+ });
328
+
329
+ // Load dashboard data
330
+ async function loadData() {
331
+ try {
332
+ const response = await fetch('/api/status');
333
+ const data = await response.json();
334
+ updateUI(data);
335
+ } catch (error) {
336
+ console.error('Failed to load dashboard:', error);
337
+ }
338
+ }
339
+
340
+ function updateUI(data) {
341
+ // Update summary
342
+ const values = [data.summary.totalLogs, data.summary.blockedCount, data.summary.approvedCount, data.summary.rejectedCount];
343
+ document.querySelectorAll('.card-value').forEach((el, i) => {
344
+ el.textContent = values[i] || 0;
345
+ });
346
+
347
+ // Update actions table
348
+ const actionsBody = document.getElementById('actions-body');
349
+ if (data.recentActions.length > 0) {
350
+ actionsBody.innerHTML = data.recentActions.map(action => '
351
+ <tr>
352
+ <td>' + action.toolName + '</td>
353
+ <td>' + action.serverId + '</td>
354
+ <td><span class="badge badge-' + action.action.toLowerCase() + '">' + action.action + '</span></td>
355
+ <td><span class="badge badge-' + action.severity.toLowerCase() + '">' + action.severity + '</span></td>
356
+ <td>' + new Date(action.timestamp).toLocaleTimeString() + '</td>
357
+ </tr>
358
+ ').join('');
359
+ }
360
+
361
+ // Update pending approvals
362
+ const approvalsBody = document.getElementById('approvals-body');
363
+ if (data.pendingApprovals.length > 0) {
364
+ approvalsBody.innerHTML = data.pendingApprovals.map(approval => '
365
+ <tr>
366
+ <td>' + approval.toolName + '</td>
367
+ <td>' + approval.serverId + '</td>
368
+ <td style="font-size: 12px; color: #666;"><code>' + approval.arguments.substring(0, 50) + '...</code></td>
369
+ <td>
370
+ <button class="btn-primary" onclick="approveRequest(\'' + approval.requestId + '\')">Approve</button>
371
+ <button class="btn-danger" onclick="rejectRequest(\'' + approval.requestId + '\')">Reject</button>
372
+ </td>
373
+ </tr>
374
+ ').join('');
375
+ }
376
+
377
+ // Update policies
378
+ const policiesBody = document.getElementById('policies-body');
379
+ if (data.activePolicies.length > 0) {
380
+ policiesBody.innerHTML = data.activePolicies.map(policy => '
381
+ <tr>
382
+ <td>' + policy.name + '</td>
383
+ <td><code>' + policy.toolName + '</code></td>
384
+ <td><span class="badge badge-' + policy.action.toLowerCase() + '">' + policy.action + '</span></td>
385
+ <td>' + (policy.enabled ? '✓ Active' : '✗ Disabled') + '</td>
386
+ </tr>
387
+ ').join('');
388
+ }
389
+ }
390
+
391
+ async function approveRequest(requestId) {
392
+ try {
393
+ await fetch('/api/approve/' + requestId, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({approver: 'dashboard'}) });
394
+ loadData();
395
+ } catch (error) {
396
+ console.error('Approval failed:', error);
397
+ }
398
+ }
399
+
400
+ async function rejectRequest(requestId) {
401
+ try {
402
+ await fetch('/api/reject/' + requestId, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({reason: 'Rejected via dashboard'}) });
403
+ loadData();
404
+ } catch (error) {
405
+ console.error('Rejection failed:', error);
406
+ }
407
+ }
408
+
409
+ // Load data on startup and refresh every 5s
410
+ loadData();
411
+ setInterval(loadData, 5000);
412
+ </script>
413
+ </body>
414
+ </html>
415
+ `;
416
+ }
417
+ async start() {
418
+ const mod = await import("express");
419
+ const express = (mod.default ??
420
+ mod);
421
+ const app = express();
422
+ this.setupRoutes(express, app);
423
+ app.listen(this.port, () => {
424
+ console.log(`📊 Guard dashboard running at http://localhost:${this.port}`);
425
+ });
426
+ }
427
+ }
428
+ //# sourceMappingURL=dashboard.js.map
@@ -0,0 +1,33 @@
1
+ [
2
+ {
3
+ "id": "default-block-shell",
4
+ "name": "Block Shell Execution",
5
+ "serverName": "*",
6
+ "toolName": "*",
7
+ "action": "BLOCK",
8
+ "severity": "CRITICAL",
9
+ "enabled": true,
10
+ "createdAt": 1712428800000
11
+ },
12
+ {
13
+ "id": "default-block-rm",
14
+ "name": "Block File Deletion",
15
+ "serverName": "*",
16
+ "toolName": "*rm*",
17
+ "action": "BLOCK",
18
+ "severity": "CRITICAL",
19
+ "enabled": true,
20
+ "createdAt": 1712428800000
21
+ },
22
+ {
23
+ "id": "default-block-secrets",
24
+ "name": "Block Secret Access",
25
+ "serverName": "*",
26
+ "toolName": "*",
27
+ "argumentContains": ["SECRET", "PASSWORD", "TOKEN"],
28
+ "action": "REQUIRE_APPROVAL",
29
+ "severity": "CRITICAL",
30
+ "enabled": true,
31
+ "createdAt": 1712428800000
32
+ }
33
+ ]
@@ -0,0 +1,20 @@
1
+ export { PolicyEngine } from "./policy.js";
2
+ export { SentinelProxy } from "./proxy.js";
3
+ export { ActionLogger } from "./logger.js";
4
+ export interface GuardOptions {
5
+ command: string[];
6
+ policyPath?: string;
7
+ dbPath?: string;
8
+ port?: number;
9
+ dashboard?: boolean;
10
+ registryKey?: string;
11
+ registrySlug?: string;
12
+ registryUrl?: string;
13
+ }
14
+ /**
15
+ * Run the MCP action firewall in front of a downstream server.
16
+ * Local policy is always enforced; the cloud broker (team registry) is
17
+ * opt-in via flags or CURATED_REGISTRY_* env vars and fails open.
18
+ */
19
+ export declare function runGuard(opts: GuardOptions): Promise<void>;
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/guard/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3C,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAiBD;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAqChE"}
@@ -0,0 +1,61 @@
1
+ import path from "path";
2
+ import os from "os";
3
+ import fs from "fs";
4
+ import { SentinelProxy } from "./proxy.js";
5
+ import { PolicyEngine } from "./policy.js";
6
+ import { ActionLogger } from "./logger.js";
7
+ import { Dashboard } from "./dashboard.js";
8
+ import { CuratedBroker } from "./broker.js";
9
+ export { PolicyEngine } from "./policy.js";
10
+ export { SentinelProxy } from "./proxy.js";
11
+ export { ActionLogger } from "./logger.js";
12
+ const GUARD_DIR = path.join(os.homedir(), ".curatedmcp", "guard");
13
+ function ensureDir() {
14
+ if (!fs.existsSync(GUARD_DIR))
15
+ fs.mkdirSync(GUARD_DIR, { recursive: true });
16
+ }
17
+ function seedDefaultPolicy(policyPath) {
18
+ if (fs.existsSync(policyPath))
19
+ return;
20
+ try {
21
+ const here = path.dirname(new URL(import.meta.url).pathname);
22
+ const seed = path.join(here, "default-policy.json");
23
+ if (fs.existsSync(seed))
24
+ fs.copyFileSync(seed, policyPath);
25
+ }
26
+ catch {
27
+ // PolicyEngine seeds sane defaults in-memory if the file is absent.
28
+ }
29
+ }
30
+ /**
31
+ * Run the MCP action firewall in front of a downstream server.
32
+ * Local policy is always enforced; the cloud broker (team registry) is
33
+ * opt-in via flags or CURATED_REGISTRY_* env vars and fails open.
34
+ */
35
+ export async function runGuard(opts) {
36
+ ensureDir();
37
+ const policyPath = opts.policyPath ?? path.join(GUARD_DIR, "policy.json");
38
+ const dbPath = opts.dbPath ?? path.join(GUARD_DIR, "actions.db");
39
+ seedDefaultPolicy(policyPath);
40
+ const registryKey = opts.registryKey ?? process.env.CURATED_REGISTRY_KEY;
41
+ const registrySlug = opts.registrySlug ?? process.env.CURATED_REGISTRY_SLUG;
42
+ const broker = registryKey && registrySlug
43
+ ? new CuratedBroker({
44
+ registryUrl: opts.registryUrl ??
45
+ process.env.CURATED_REGISTRY_URL ??
46
+ "https://curatedmcp.com",
47
+ registryKey,
48
+ registrySlug,
49
+ })
50
+ : null;
51
+ if (!broker) {
52
+ console.log("ℹ️ Guard running in local-only mode. Pass --registry-key + --registry-slug (or set CURATED_REGISTRY_*) to enable team identity & audit.");
53
+ }
54
+ const proxy = new SentinelProxy(policyPath, dbPath, opts.command.join(" "), broker);
55
+ await proxy.start();
56
+ if (opts.dashboard) {
57
+ const dashboard = new Dashboard(new ActionLogger(dbPath), new PolicyEngine(policyPath), opts.port ?? 4242);
58
+ await dashboard.start();
59
+ }
60
+ }
61
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,30 @@
1
+ import { ActionLog, PendingApproval } from "./types.js";
2
+ /**
3
+ * ActionLogger - Simple JSON-based persistent logger
4
+ * Production versions can migrate to proper SQLite
5
+ */
6
+ export declare class ActionLogger {
7
+ private dbPath;
8
+ private logsFile;
9
+ private approvalsFile;
10
+ private logs;
11
+ private approvals;
12
+ constructor(dbPath: string);
13
+ private loadFromDisk;
14
+ private saveToDisk;
15
+ private initializeSchema;
16
+ logAction(log: ActionLog): void;
17
+ getPendingApprovals(): PendingApproval[];
18
+ approveAction(requestId: string, approvedBy: string): void;
19
+ rejectAction(requestId: string, reason: string): void;
20
+ getRecentActions(limit?: number): ActionLog[];
21
+ purgeExpired(retentionMinutes: number): number;
22
+ getSummary(): {
23
+ total: number;
24
+ blocked: number;
25
+ approved: number;
26
+ rejected: number;
27
+ };
28
+ close(): void;
29
+ }
30
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/guard/logger.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAExD;;;GAGG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,IAAI,CAAmB;IAC/B,OAAO,CAAC,SAAS,CAAyB;gBAE9B,MAAM,EAAE,MAAM;IAa1B,OAAO,CAAC,YAAY;IAiBpB,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,gBAAgB;IAIxB,SAAS,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI;IAQ/B,mBAAmB,IAAI,eAAe,EAAE;IAQxC,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAkB1D,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAiBrD,gBAAgB,CAAC,KAAK,GAAE,MAAW,GAAG,SAAS,EAAE;IAMjD,YAAY,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM;IAgB9C,UAAU;;;;;;IAaV,KAAK,IAAI,IAAI;CAGd"}