@agentwares/agentguard 0.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/dist/cli.js ADDED
@@ -0,0 +1,4179 @@
1
+ #!/usr/bin/env node
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __commonJS = (cb, mod) => function __require() {
9
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+
28
+ // ../../node_modules/.pnpm/standardwebhooks@1.0.0/node_modules/standardwebhooks/dist/timing_safe_equal.js
29
+ var require_timing_safe_equal = __commonJS({
30
+ "../../node_modules/.pnpm/standardwebhooks@1.0.0/node_modules/standardwebhooks/dist/timing_safe_equal.js"(exports) {
31
+ "use strict";
32
+ Object.defineProperty(exports, "__esModule", { value: true });
33
+ exports.timingSafeEqual = void 0;
34
+ function assert(expr, msg = "") {
35
+ if (!expr) {
36
+ throw new Error(msg);
37
+ }
38
+ }
39
+ function timingSafeEqual(a, b) {
40
+ if (a.byteLength !== b.byteLength) {
41
+ return false;
42
+ }
43
+ if (!(a instanceof DataView)) {
44
+ a = new DataView(ArrayBuffer.isView(a) ? a.buffer : a);
45
+ }
46
+ if (!(b instanceof DataView)) {
47
+ b = new DataView(ArrayBuffer.isView(b) ? b.buffer : b);
48
+ }
49
+ assert(a instanceof DataView);
50
+ assert(b instanceof DataView);
51
+ const length = a.byteLength;
52
+ let out = 0;
53
+ let i = -1;
54
+ while (++i < length) {
55
+ out |= a.getUint8(i) ^ b.getUint8(i);
56
+ }
57
+ return out === 0;
58
+ }
59
+ exports.timingSafeEqual = timingSafeEqual;
60
+ }
61
+ });
62
+
63
+ // ../../node_modules/.pnpm/@stablelib+base64@1.0.1/node_modules/@stablelib/base64/lib/base64.js
64
+ var require_base64 = __commonJS({
65
+ "../../node_modules/.pnpm/@stablelib+base64@1.0.1/node_modules/@stablelib/base64/lib/base64.js"(exports) {
66
+ "use strict";
67
+ var __extends = exports && exports.__extends || /* @__PURE__ */ (function() {
68
+ var extendStatics = function(d, b) {
69
+ extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function(d2, b2) {
70
+ d2.__proto__ = b2;
71
+ } || function(d2, b2) {
72
+ for (var p in b2) if (b2.hasOwnProperty(p)) d2[p] = b2[p];
73
+ };
74
+ return extendStatics(d, b);
75
+ };
76
+ return function(d, b) {
77
+ extendStatics(d, b);
78
+ function __() {
79
+ this.constructor = d;
80
+ }
81
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
82
+ };
83
+ })();
84
+ Object.defineProperty(exports, "__esModule", { value: true });
85
+ var INVALID_BYTE = 256;
86
+ var Coder = (
87
+ /** @class */
88
+ (function() {
89
+ function Coder2(_paddingCharacter) {
90
+ if (_paddingCharacter === void 0) {
91
+ _paddingCharacter = "=";
92
+ }
93
+ this._paddingCharacter = _paddingCharacter;
94
+ }
95
+ Coder2.prototype.encodedLength = function(length) {
96
+ if (!this._paddingCharacter) {
97
+ return (length * 8 + 5) / 6 | 0;
98
+ }
99
+ return (length + 2) / 3 * 4 | 0;
100
+ };
101
+ Coder2.prototype.encode = function(data) {
102
+ var out = "";
103
+ var i = 0;
104
+ for (; i < data.length - 2; i += 3) {
105
+ var c = data[i] << 16 | data[i + 1] << 8 | data[i + 2];
106
+ out += this._encodeByte(c >>> 3 * 6 & 63);
107
+ out += this._encodeByte(c >>> 2 * 6 & 63);
108
+ out += this._encodeByte(c >>> 1 * 6 & 63);
109
+ out += this._encodeByte(c >>> 0 * 6 & 63);
110
+ }
111
+ var left = data.length - i;
112
+ if (left > 0) {
113
+ var c = data[i] << 16 | (left === 2 ? data[i + 1] << 8 : 0);
114
+ out += this._encodeByte(c >>> 3 * 6 & 63);
115
+ out += this._encodeByte(c >>> 2 * 6 & 63);
116
+ if (left === 2) {
117
+ out += this._encodeByte(c >>> 1 * 6 & 63);
118
+ } else {
119
+ out += this._paddingCharacter || "";
120
+ }
121
+ out += this._paddingCharacter || "";
122
+ }
123
+ return out;
124
+ };
125
+ Coder2.prototype.maxDecodedLength = function(length) {
126
+ if (!this._paddingCharacter) {
127
+ return (length * 6 + 7) / 8 | 0;
128
+ }
129
+ return length / 4 * 3 | 0;
130
+ };
131
+ Coder2.prototype.decodedLength = function(s) {
132
+ return this.maxDecodedLength(s.length - this._getPaddingLength(s));
133
+ };
134
+ Coder2.prototype.decode = function(s) {
135
+ if (s.length === 0) {
136
+ return new Uint8Array(0);
137
+ }
138
+ var paddingLength = this._getPaddingLength(s);
139
+ var length = s.length - paddingLength;
140
+ var out = new Uint8Array(this.maxDecodedLength(length));
141
+ var op = 0;
142
+ var i = 0;
143
+ var haveBad = 0;
144
+ var v0 = 0, v1 = 0, v2 = 0, v3 = 0;
145
+ for (; i < length - 4; i += 4) {
146
+ v0 = this._decodeChar(s.charCodeAt(i + 0));
147
+ v1 = this._decodeChar(s.charCodeAt(i + 1));
148
+ v2 = this._decodeChar(s.charCodeAt(i + 2));
149
+ v3 = this._decodeChar(s.charCodeAt(i + 3));
150
+ out[op++] = v0 << 2 | v1 >>> 4;
151
+ out[op++] = v1 << 4 | v2 >>> 2;
152
+ out[op++] = v2 << 6 | v3;
153
+ haveBad |= v0 & INVALID_BYTE;
154
+ haveBad |= v1 & INVALID_BYTE;
155
+ haveBad |= v2 & INVALID_BYTE;
156
+ haveBad |= v3 & INVALID_BYTE;
157
+ }
158
+ if (i < length - 1) {
159
+ v0 = this._decodeChar(s.charCodeAt(i));
160
+ v1 = this._decodeChar(s.charCodeAt(i + 1));
161
+ out[op++] = v0 << 2 | v1 >>> 4;
162
+ haveBad |= v0 & INVALID_BYTE;
163
+ haveBad |= v1 & INVALID_BYTE;
164
+ }
165
+ if (i < length - 2) {
166
+ v2 = this._decodeChar(s.charCodeAt(i + 2));
167
+ out[op++] = v1 << 4 | v2 >>> 2;
168
+ haveBad |= v2 & INVALID_BYTE;
169
+ }
170
+ if (i < length - 3) {
171
+ v3 = this._decodeChar(s.charCodeAt(i + 3));
172
+ out[op++] = v2 << 6 | v3;
173
+ haveBad |= v3 & INVALID_BYTE;
174
+ }
175
+ if (haveBad !== 0) {
176
+ throw new Error("Base64Coder: incorrect characters for decoding");
177
+ }
178
+ return out;
179
+ };
180
+ Coder2.prototype._encodeByte = function(b) {
181
+ var result = b;
182
+ result += 65;
183
+ result += 25 - b >>> 8 & 0 - 65 - 26 + 97;
184
+ result += 51 - b >>> 8 & 26 - 97 - 52 + 48;
185
+ result += 61 - b >>> 8 & 52 - 48 - 62 + 43;
186
+ result += 62 - b >>> 8 & 62 - 43 - 63 + 47;
187
+ return String.fromCharCode(result);
188
+ };
189
+ Coder2.prototype._decodeChar = function(c) {
190
+ var result = INVALID_BYTE;
191
+ result += (42 - c & c - 44) >>> 8 & -INVALID_BYTE + c - 43 + 62;
192
+ result += (46 - c & c - 48) >>> 8 & -INVALID_BYTE + c - 47 + 63;
193
+ result += (47 - c & c - 58) >>> 8 & -INVALID_BYTE + c - 48 + 52;
194
+ result += (64 - c & c - 91) >>> 8 & -INVALID_BYTE + c - 65 + 0;
195
+ result += (96 - c & c - 123) >>> 8 & -INVALID_BYTE + c - 97 + 26;
196
+ return result;
197
+ };
198
+ Coder2.prototype._getPaddingLength = function(s) {
199
+ var paddingLength = 0;
200
+ if (this._paddingCharacter) {
201
+ for (var i = s.length - 1; i >= 0; i--) {
202
+ if (s[i] !== this._paddingCharacter) {
203
+ break;
204
+ }
205
+ paddingLength++;
206
+ }
207
+ if (s.length < 4 || paddingLength > 2) {
208
+ throw new Error("Base64Coder: incorrect padding");
209
+ }
210
+ }
211
+ return paddingLength;
212
+ };
213
+ return Coder2;
214
+ })()
215
+ );
216
+ exports.Coder = Coder;
217
+ var stdCoder = new Coder();
218
+ function encode(data) {
219
+ return stdCoder.encode(data);
220
+ }
221
+ exports.encode = encode;
222
+ function decode(s) {
223
+ return stdCoder.decode(s);
224
+ }
225
+ exports.decode = decode;
226
+ var URLSafeCoder = (
227
+ /** @class */
228
+ (function(_super) {
229
+ __extends(URLSafeCoder2, _super);
230
+ function URLSafeCoder2() {
231
+ return _super !== null && _super.apply(this, arguments) || this;
232
+ }
233
+ URLSafeCoder2.prototype._encodeByte = function(b) {
234
+ var result = b;
235
+ result += 65;
236
+ result += 25 - b >>> 8 & 0 - 65 - 26 + 97;
237
+ result += 51 - b >>> 8 & 26 - 97 - 52 + 48;
238
+ result += 61 - b >>> 8 & 52 - 48 - 62 + 45;
239
+ result += 62 - b >>> 8 & 62 - 45 - 63 + 95;
240
+ return String.fromCharCode(result);
241
+ };
242
+ URLSafeCoder2.prototype._decodeChar = function(c) {
243
+ var result = INVALID_BYTE;
244
+ result += (44 - c & c - 46) >>> 8 & -INVALID_BYTE + c - 45 + 62;
245
+ result += (94 - c & c - 96) >>> 8 & -INVALID_BYTE + c - 95 + 63;
246
+ result += (47 - c & c - 58) >>> 8 & -INVALID_BYTE + c - 48 + 52;
247
+ result += (64 - c & c - 91) >>> 8 & -INVALID_BYTE + c - 65 + 0;
248
+ result += (96 - c & c - 123) >>> 8 & -INVALID_BYTE + c - 97 + 26;
249
+ return result;
250
+ };
251
+ return URLSafeCoder2;
252
+ })(Coder)
253
+ );
254
+ exports.URLSafeCoder = URLSafeCoder;
255
+ var urlSafeCoder = new URLSafeCoder();
256
+ function encodeURLSafe(data) {
257
+ return urlSafeCoder.encode(data);
258
+ }
259
+ exports.encodeURLSafe = encodeURLSafe;
260
+ function decodeURLSafe(s) {
261
+ return urlSafeCoder.decode(s);
262
+ }
263
+ exports.decodeURLSafe = decodeURLSafe;
264
+ exports.encodedLength = function(length) {
265
+ return stdCoder.encodedLength(length);
266
+ };
267
+ exports.maxDecodedLength = function(length) {
268
+ return stdCoder.maxDecodedLength(length);
269
+ };
270
+ exports.decodedLength = function(s) {
271
+ return stdCoder.decodedLength(s);
272
+ };
273
+ }
274
+ });
275
+
276
+ // ../../node_modules/.pnpm/fast-sha256@1.3.0/node_modules/fast-sha256/sha256.js
277
+ var require_sha256 = __commonJS({
278
+ "../../node_modules/.pnpm/fast-sha256@1.3.0/node_modules/fast-sha256/sha256.js"(exports, module) {
279
+ "use strict";
280
+ (function(root, factory) {
281
+ var exports2 = {};
282
+ factory(exports2);
283
+ var sha256 = exports2["default"];
284
+ for (var k in exports2) {
285
+ sha256[k] = exports2[k];
286
+ }
287
+ if (typeof module === "object" && typeof module.exports === "object") {
288
+ module.exports = sha256;
289
+ } else if (typeof define === "function" && define.amd) {
290
+ define(function() {
291
+ return sha256;
292
+ });
293
+ } else {
294
+ root.sha256 = sha256;
295
+ }
296
+ })(exports, function(exports2) {
297
+ "use strict";
298
+ exports2.__esModule = true;
299
+ exports2.digestLength = 32;
300
+ exports2.blockSize = 64;
301
+ var K = new Uint32Array([
302
+ 1116352408,
303
+ 1899447441,
304
+ 3049323471,
305
+ 3921009573,
306
+ 961987163,
307
+ 1508970993,
308
+ 2453635748,
309
+ 2870763221,
310
+ 3624381080,
311
+ 310598401,
312
+ 607225278,
313
+ 1426881987,
314
+ 1925078388,
315
+ 2162078206,
316
+ 2614888103,
317
+ 3248222580,
318
+ 3835390401,
319
+ 4022224774,
320
+ 264347078,
321
+ 604807628,
322
+ 770255983,
323
+ 1249150122,
324
+ 1555081692,
325
+ 1996064986,
326
+ 2554220882,
327
+ 2821834349,
328
+ 2952996808,
329
+ 3210313671,
330
+ 3336571891,
331
+ 3584528711,
332
+ 113926993,
333
+ 338241895,
334
+ 666307205,
335
+ 773529912,
336
+ 1294757372,
337
+ 1396182291,
338
+ 1695183700,
339
+ 1986661051,
340
+ 2177026350,
341
+ 2456956037,
342
+ 2730485921,
343
+ 2820302411,
344
+ 3259730800,
345
+ 3345764771,
346
+ 3516065817,
347
+ 3600352804,
348
+ 4094571909,
349
+ 275423344,
350
+ 430227734,
351
+ 506948616,
352
+ 659060556,
353
+ 883997877,
354
+ 958139571,
355
+ 1322822218,
356
+ 1537002063,
357
+ 1747873779,
358
+ 1955562222,
359
+ 2024104815,
360
+ 2227730452,
361
+ 2361852424,
362
+ 2428436474,
363
+ 2756734187,
364
+ 3204031479,
365
+ 3329325298
366
+ ]);
367
+ function hashBlocks(w, v, p, pos, len) {
368
+ var a, b, c, d, e, f, g, h, u, i, j, t1, t2;
369
+ while (len >= 64) {
370
+ a = v[0];
371
+ b = v[1];
372
+ c = v[2];
373
+ d = v[3];
374
+ e = v[4];
375
+ f = v[5];
376
+ g = v[6];
377
+ h = v[7];
378
+ for (i = 0; i < 16; i++) {
379
+ j = pos + i * 4;
380
+ w[i] = (p[j] & 255) << 24 | (p[j + 1] & 255) << 16 | (p[j + 2] & 255) << 8 | p[j + 3] & 255;
381
+ }
382
+ for (i = 16; i < 64; i++) {
383
+ u = w[i - 2];
384
+ t1 = (u >>> 17 | u << 32 - 17) ^ (u >>> 19 | u << 32 - 19) ^ u >>> 10;
385
+ u = w[i - 15];
386
+ t2 = (u >>> 7 | u << 32 - 7) ^ (u >>> 18 | u << 32 - 18) ^ u >>> 3;
387
+ w[i] = (t1 + w[i - 7] | 0) + (t2 + w[i - 16] | 0);
388
+ }
389
+ for (i = 0; i < 64; i++) {
390
+ t1 = (((e >>> 6 | e << 32 - 6) ^ (e >>> 11 | e << 32 - 11) ^ (e >>> 25 | e << 32 - 25)) + (e & f ^ ~e & g) | 0) + (h + (K[i] + w[i] | 0) | 0) | 0;
391
+ t2 = ((a >>> 2 | a << 32 - 2) ^ (a >>> 13 | a << 32 - 13) ^ (a >>> 22 | a << 32 - 22)) + (a & b ^ a & c ^ b & c) | 0;
392
+ h = g;
393
+ g = f;
394
+ f = e;
395
+ e = d + t1 | 0;
396
+ d = c;
397
+ c = b;
398
+ b = a;
399
+ a = t1 + t2 | 0;
400
+ }
401
+ v[0] += a;
402
+ v[1] += b;
403
+ v[2] += c;
404
+ v[3] += d;
405
+ v[4] += e;
406
+ v[5] += f;
407
+ v[6] += g;
408
+ v[7] += h;
409
+ pos += 64;
410
+ len -= 64;
411
+ }
412
+ return pos;
413
+ }
414
+ var Hash = (
415
+ /** @class */
416
+ (function() {
417
+ function Hash2() {
418
+ this.digestLength = exports2.digestLength;
419
+ this.blockSize = exports2.blockSize;
420
+ this.state = new Int32Array(8);
421
+ this.temp = new Int32Array(64);
422
+ this.buffer = new Uint8Array(128);
423
+ this.bufferLength = 0;
424
+ this.bytesHashed = 0;
425
+ this.finished = false;
426
+ this.reset();
427
+ }
428
+ Hash2.prototype.reset = function() {
429
+ this.state[0] = 1779033703;
430
+ this.state[1] = 3144134277;
431
+ this.state[2] = 1013904242;
432
+ this.state[3] = 2773480762;
433
+ this.state[4] = 1359893119;
434
+ this.state[5] = 2600822924;
435
+ this.state[6] = 528734635;
436
+ this.state[7] = 1541459225;
437
+ this.bufferLength = 0;
438
+ this.bytesHashed = 0;
439
+ this.finished = false;
440
+ return this;
441
+ };
442
+ Hash2.prototype.clean = function() {
443
+ for (var i = 0; i < this.buffer.length; i++) {
444
+ this.buffer[i] = 0;
445
+ }
446
+ for (var i = 0; i < this.temp.length; i++) {
447
+ this.temp[i] = 0;
448
+ }
449
+ this.reset();
450
+ };
451
+ Hash2.prototype.update = function(data, dataLength) {
452
+ if (dataLength === void 0) {
453
+ dataLength = data.length;
454
+ }
455
+ if (this.finished) {
456
+ throw new Error("SHA256: can't update because hash was finished.");
457
+ }
458
+ var dataPos = 0;
459
+ this.bytesHashed += dataLength;
460
+ if (this.bufferLength > 0) {
461
+ while (this.bufferLength < 64 && dataLength > 0) {
462
+ this.buffer[this.bufferLength++] = data[dataPos++];
463
+ dataLength--;
464
+ }
465
+ if (this.bufferLength === 64) {
466
+ hashBlocks(this.temp, this.state, this.buffer, 0, 64);
467
+ this.bufferLength = 0;
468
+ }
469
+ }
470
+ if (dataLength >= 64) {
471
+ dataPos = hashBlocks(this.temp, this.state, data, dataPos, dataLength);
472
+ dataLength %= 64;
473
+ }
474
+ while (dataLength > 0) {
475
+ this.buffer[this.bufferLength++] = data[dataPos++];
476
+ dataLength--;
477
+ }
478
+ return this;
479
+ };
480
+ Hash2.prototype.finish = function(out) {
481
+ if (!this.finished) {
482
+ var bytesHashed = this.bytesHashed;
483
+ var left = this.bufferLength;
484
+ var bitLenHi = bytesHashed / 536870912 | 0;
485
+ var bitLenLo = bytesHashed << 3;
486
+ var padLength = bytesHashed % 64 < 56 ? 64 : 128;
487
+ this.buffer[left] = 128;
488
+ for (var i = left + 1; i < padLength - 8; i++) {
489
+ this.buffer[i] = 0;
490
+ }
491
+ this.buffer[padLength - 8] = bitLenHi >>> 24 & 255;
492
+ this.buffer[padLength - 7] = bitLenHi >>> 16 & 255;
493
+ this.buffer[padLength - 6] = bitLenHi >>> 8 & 255;
494
+ this.buffer[padLength - 5] = bitLenHi >>> 0 & 255;
495
+ this.buffer[padLength - 4] = bitLenLo >>> 24 & 255;
496
+ this.buffer[padLength - 3] = bitLenLo >>> 16 & 255;
497
+ this.buffer[padLength - 2] = bitLenLo >>> 8 & 255;
498
+ this.buffer[padLength - 1] = bitLenLo >>> 0 & 255;
499
+ hashBlocks(this.temp, this.state, this.buffer, 0, padLength);
500
+ this.finished = true;
501
+ }
502
+ for (var i = 0; i < 8; i++) {
503
+ out[i * 4 + 0] = this.state[i] >>> 24 & 255;
504
+ out[i * 4 + 1] = this.state[i] >>> 16 & 255;
505
+ out[i * 4 + 2] = this.state[i] >>> 8 & 255;
506
+ out[i * 4 + 3] = this.state[i] >>> 0 & 255;
507
+ }
508
+ return this;
509
+ };
510
+ Hash2.prototype.digest = function() {
511
+ var out = new Uint8Array(this.digestLength);
512
+ this.finish(out);
513
+ return out;
514
+ };
515
+ Hash2.prototype._saveState = function(out) {
516
+ for (var i = 0; i < this.state.length; i++) {
517
+ out[i] = this.state[i];
518
+ }
519
+ };
520
+ Hash2.prototype._restoreState = function(from, bytesHashed) {
521
+ for (var i = 0; i < this.state.length; i++) {
522
+ this.state[i] = from[i];
523
+ }
524
+ this.bytesHashed = bytesHashed;
525
+ this.finished = false;
526
+ this.bufferLength = 0;
527
+ };
528
+ return Hash2;
529
+ })()
530
+ );
531
+ exports2.Hash = Hash;
532
+ var HMAC = (
533
+ /** @class */
534
+ (function() {
535
+ function HMAC2(key) {
536
+ this.inner = new Hash();
537
+ this.outer = new Hash();
538
+ this.blockSize = this.inner.blockSize;
539
+ this.digestLength = this.inner.digestLength;
540
+ var pad = new Uint8Array(this.blockSize);
541
+ if (key.length > this.blockSize) {
542
+ new Hash().update(key).finish(pad).clean();
543
+ } else {
544
+ for (var i = 0; i < key.length; i++) {
545
+ pad[i] = key[i];
546
+ }
547
+ }
548
+ for (var i = 0; i < pad.length; i++) {
549
+ pad[i] ^= 54;
550
+ }
551
+ this.inner.update(pad);
552
+ for (var i = 0; i < pad.length; i++) {
553
+ pad[i] ^= 54 ^ 92;
554
+ }
555
+ this.outer.update(pad);
556
+ this.istate = new Uint32Array(8);
557
+ this.ostate = new Uint32Array(8);
558
+ this.inner._saveState(this.istate);
559
+ this.outer._saveState(this.ostate);
560
+ for (var i = 0; i < pad.length; i++) {
561
+ pad[i] = 0;
562
+ }
563
+ }
564
+ HMAC2.prototype.reset = function() {
565
+ this.inner._restoreState(this.istate, this.inner.blockSize);
566
+ this.outer._restoreState(this.ostate, this.outer.blockSize);
567
+ return this;
568
+ };
569
+ HMAC2.prototype.clean = function() {
570
+ for (var i = 0; i < this.istate.length; i++) {
571
+ this.ostate[i] = this.istate[i] = 0;
572
+ }
573
+ this.inner.clean();
574
+ this.outer.clean();
575
+ };
576
+ HMAC2.prototype.update = function(data) {
577
+ this.inner.update(data);
578
+ return this;
579
+ };
580
+ HMAC2.prototype.finish = function(out) {
581
+ if (this.outer.finished) {
582
+ this.outer.finish(out);
583
+ } else {
584
+ this.inner.finish(out);
585
+ this.outer.update(out, this.digestLength).finish(out);
586
+ }
587
+ return this;
588
+ };
589
+ HMAC2.prototype.digest = function() {
590
+ var out = new Uint8Array(this.digestLength);
591
+ this.finish(out);
592
+ return out;
593
+ };
594
+ return HMAC2;
595
+ })()
596
+ );
597
+ exports2.HMAC = HMAC;
598
+ function hash(data) {
599
+ var h = new Hash().update(data);
600
+ var digest = h.digest();
601
+ h.clean();
602
+ return digest;
603
+ }
604
+ exports2.hash = hash;
605
+ exports2["default"] = hash;
606
+ function hmac(key, data) {
607
+ var h = new HMAC(key).update(data);
608
+ var digest = h.digest();
609
+ h.clean();
610
+ return digest;
611
+ }
612
+ exports2.hmac = hmac;
613
+ function fillBuffer(buffer, hmac2, info, counter) {
614
+ var num = counter[0];
615
+ if (num === 0) {
616
+ throw new Error("hkdf: cannot expand more");
617
+ }
618
+ hmac2.reset();
619
+ if (num > 1) {
620
+ hmac2.update(buffer);
621
+ }
622
+ if (info) {
623
+ hmac2.update(info);
624
+ }
625
+ hmac2.update(counter);
626
+ hmac2.finish(buffer);
627
+ counter[0]++;
628
+ }
629
+ var hkdfSalt = new Uint8Array(exports2.digestLength);
630
+ function hkdf(key, salt, info, length) {
631
+ if (salt === void 0) {
632
+ salt = hkdfSalt;
633
+ }
634
+ if (length === void 0) {
635
+ length = 32;
636
+ }
637
+ var counter = new Uint8Array([1]);
638
+ var okm = hmac(salt, key);
639
+ var hmac_ = new HMAC(okm);
640
+ var buffer = new Uint8Array(hmac_.digestLength);
641
+ var bufpos = buffer.length;
642
+ var out = new Uint8Array(length);
643
+ for (var i = 0; i < length; i++) {
644
+ if (bufpos === buffer.length) {
645
+ fillBuffer(buffer, hmac_, info, counter);
646
+ bufpos = 0;
647
+ }
648
+ out[i] = buffer[bufpos++];
649
+ }
650
+ hmac_.clean();
651
+ buffer.fill(0);
652
+ counter.fill(0);
653
+ return out;
654
+ }
655
+ exports2.hkdf = hkdf;
656
+ function pbkdf2(password, salt, iterations, dkLen) {
657
+ var prf = new HMAC(password);
658
+ var len = prf.digestLength;
659
+ var ctr = new Uint8Array(4);
660
+ var t = new Uint8Array(len);
661
+ var u = new Uint8Array(len);
662
+ var dk = new Uint8Array(dkLen);
663
+ for (var i = 0; i * len < dkLen; i++) {
664
+ var c = i + 1;
665
+ ctr[0] = c >>> 24 & 255;
666
+ ctr[1] = c >>> 16 & 255;
667
+ ctr[2] = c >>> 8 & 255;
668
+ ctr[3] = c >>> 0 & 255;
669
+ prf.reset();
670
+ prf.update(salt);
671
+ prf.update(ctr);
672
+ prf.finish(u);
673
+ for (var j = 0; j < len; j++) {
674
+ t[j] = u[j];
675
+ }
676
+ for (var j = 2; j <= iterations; j++) {
677
+ prf.reset();
678
+ prf.update(u).finish(u);
679
+ for (var k = 0; k < len; k++) {
680
+ t[k] ^= u[k];
681
+ }
682
+ }
683
+ for (var j = 0; j < len && i * len + j < dkLen; j++) {
684
+ dk[i * len + j] = t[j];
685
+ }
686
+ }
687
+ for (var i = 0; i < len; i++) {
688
+ t[i] = u[i] = 0;
689
+ }
690
+ for (var i = 0; i < 4; i++) {
691
+ ctr[i] = 0;
692
+ }
693
+ prf.clean();
694
+ return dk;
695
+ }
696
+ exports2.pbkdf2 = pbkdf2;
697
+ });
698
+ }
699
+ });
700
+
701
+ // ../../node_modules/.pnpm/standardwebhooks@1.0.0/node_modules/standardwebhooks/dist/index.js
702
+ var require_dist = __commonJS({
703
+ "../../node_modules/.pnpm/standardwebhooks@1.0.0/node_modules/standardwebhooks/dist/index.js"(exports) {
704
+ "use strict";
705
+ Object.defineProperty(exports, "__esModule", { value: true });
706
+ exports.Webhook = exports.WebhookVerificationError = void 0;
707
+ var timing_safe_equal_1 = require_timing_safe_equal();
708
+ var base64 = require_base64();
709
+ var sha256 = require_sha256();
710
+ var WEBHOOK_TOLERANCE_IN_SECONDS = 5 * 60;
711
+ var ExtendableError = class _ExtendableError extends Error {
712
+ constructor(message) {
713
+ super(message);
714
+ Object.setPrototypeOf(this, _ExtendableError.prototype);
715
+ this.name = "ExtendableError";
716
+ this.stack = new Error(message).stack;
717
+ }
718
+ };
719
+ var WebhookVerificationError = class _WebhookVerificationError extends ExtendableError {
720
+ constructor(message) {
721
+ super(message);
722
+ Object.setPrototypeOf(this, _WebhookVerificationError.prototype);
723
+ this.name = "WebhookVerificationError";
724
+ }
725
+ };
726
+ exports.WebhookVerificationError = WebhookVerificationError;
727
+ var Webhook2 = class _Webhook {
728
+ constructor(secret, options) {
729
+ if (!secret) {
730
+ throw new Error("Secret can't be empty.");
731
+ }
732
+ if ((options === null || options === void 0 ? void 0 : options.format) === "raw") {
733
+ if (secret instanceof Uint8Array) {
734
+ this.key = secret;
735
+ } else {
736
+ this.key = Uint8Array.from(secret, (c) => c.charCodeAt(0));
737
+ }
738
+ } else {
739
+ if (typeof secret !== "string") {
740
+ throw new Error("Expected secret to be of type string");
741
+ }
742
+ if (secret.startsWith(_Webhook.prefix)) {
743
+ secret = secret.substring(_Webhook.prefix.length);
744
+ }
745
+ this.key = base64.decode(secret);
746
+ }
747
+ }
748
+ verify(payload, headers_) {
749
+ const headers = {};
750
+ for (const key of Object.keys(headers_)) {
751
+ headers[key.toLowerCase()] = headers_[key];
752
+ }
753
+ const msgId = headers["webhook-id"];
754
+ const msgSignature = headers["webhook-signature"];
755
+ const msgTimestamp = headers["webhook-timestamp"];
756
+ if (!msgSignature || !msgId || !msgTimestamp) {
757
+ throw new WebhookVerificationError("Missing required headers");
758
+ }
759
+ const timestamp = this.verifyTimestamp(msgTimestamp);
760
+ const computedSignature = this.sign(msgId, timestamp, payload);
761
+ const expectedSignature = computedSignature.split(",")[1];
762
+ const passedSignatures = msgSignature.split(" ");
763
+ const encoder2 = new globalThis.TextEncoder();
764
+ for (const versionedSignature of passedSignatures) {
765
+ const [version3, signature] = versionedSignature.split(",");
766
+ if (version3 !== "v1") {
767
+ continue;
768
+ }
769
+ if ((0, timing_safe_equal_1.timingSafeEqual)(encoder2.encode(signature), encoder2.encode(expectedSignature))) {
770
+ return JSON.parse(payload.toString());
771
+ }
772
+ }
773
+ throw new WebhookVerificationError("No matching signature found");
774
+ }
775
+ sign(msgId, timestamp, payload) {
776
+ if (typeof payload === "string") {
777
+ } else if (payload.constructor.name === "Buffer") {
778
+ payload = payload.toString();
779
+ } else {
780
+ throw new Error("Expected payload to be of type string or Buffer.");
781
+ }
782
+ const encoder2 = new TextEncoder();
783
+ const timestampNumber = Math.floor(timestamp.getTime() / 1e3);
784
+ const toSign = encoder2.encode(`${msgId}.${timestampNumber}.${payload}`);
785
+ const expectedSignature = base64.encode(sha256.hmac(this.key, toSign));
786
+ return `v1,${expectedSignature}`;
787
+ }
788
+ verifyTimestamp(timestampHeader) {
789
+ const now = Math.floor(Date.now() / 1e3);
790
+ const timestamp = parseInt(timestampHeader, 10);
791
+ if (isNaN(timestamp)) {
792
+ throw new WebhookVerificationError("Invalid Signature Headers");
793
+ }
794
+ if (now - timestamp > WEBHOOK_TOLERANCE_IN_SECONDS) {
795
+ throw new WebhookVerificationError("Message timestamp too old");
796
+ }
797
+ if (timestamp > now + WEBHOOK_TOLERANCE_IN_SECONDS) {
798
+ throw new WebhookVerificationError("Message timestamp too new");
799
+ }
800
+ return new Date(timestamp * 1e3);
801
+ }
802
+ };
803
+ exports.Webhook = Webhook2;
804
+ Webhook2.prefix = "whsec_";
805
+ }
806
+ });
807
+
808
+ // src/cli.ts
809
+ import { readFileSync as readFileSync4 } from "fs";
810
+
811
+ // src/args.ts
812
+ var VALUE_FLAGS = /* @__PURE__ */ new Set([
813
+ "config",
814
+ "client",
815
+ "mode",
816
+ "port",
817
+ "host",
818
+ "agent",
819
+ "run-id",
820
+ "run",
821
+ "base",
822
+ "head",
823
+ "paths",
824
+ "format",
825
+ "by",
826
+ "note",
827
+ "allow",
828
+ "deny",
829
+ "writes",
830
+ "spend",
831
+ "tool-calls",
832
+ "deletes",
833
+ "emails",
834
+ "reason",
835
+ "url",
836
+ "timeout",
837
+ "out",
838
+ "upstream"
839
+ ]);
840
+ function parseArgs(argv) {
841
+ const out = { command: void 0, positionals: [], flags: {} };
842
+ const push = (key, value) => {
843
+ const existing = out.flags[key];
844
+ if (existing === void 0) out.flags[key] = value;
845
+ else if (Array.isArray(existing)) existing.push(String(value));
846
+ else out.flags[key] = [String(existing), String(value)];
847
+ };
848
+ for (let i = 0; i < argv.length; i += 1) {
849
+ const arg = argv[i];
850
+ if (arg === "--") {
851
+ out.positionals.push(...argv.slice(i + 1));
852
+ break;
853
+ }
854
+ if (arg.startsWith("--")) {
855
+ const body = arg.slice(2);
856
+ const eq = body.indexOf("=");
857
+ if (eq >= 0) {
858
+ push(body.slice(0, eq), body.slice(eq + 1));
859
+ continue;
860
+ }
861
+ if (body.startsWith("no-")) {
862
+ push(body.slice(3), false);
863
+ continue;
864
+ }
865
+ const next = argv[i + 1];
866
+ if (VALUE_FLAGS.has(body) && next !== void 0 && !next.startsWith("--")) {
867
+ push(body, next);
868
+ i += 1;
869
+ } else push(body, true);
870
+ continue;
871
+ }
872
+ if (arg.startsWith("-") && arg.length === 2) {
873
+ const short = {
874
+ c: "config",
875
+ y: "yes",
876
+ h: "help",
877
+ v: "version",
878
+ j: "json",
879
+ a: "all"
880
+ };
881
+ const key = short[arg[1]] ?? arg[1];
882
+ const next = argv[i + 1];
883
+ if (VALUE_FLAGS.has(key) && next !== void 0 && !next.startsWith("-")) {
884
+ push(key, next);
885
+ i += 1;
886
+ } else push(key, true);
887
+ continue;
888
+ }
889
+ if (out.command === void 0) out.command = arg;
890
+ else out.positionals.push(arg);
891
+ }
892
+ return out;
893
+ }
894
+ function flagString(args, name) {
895
+ const v = args.flags[name];
896
+ if (Array.isArray(v)) return v[v.length - 1];
897
+ return typeof v === "string" ? v : void 0;
898
+ }
899
+ function flagList(args, name) {
900
+ const v = args.flags[name];
901
+ if (Array.isArray(v)) return v.flatMap((s) => s.split(","));
902
+ return typeof v === "string" ? v.split(",") : [];
903
+ }
904
+ function flagBool(args, name) {
905
+ const v = args.flags[name];
906
+ if (typeof v === "boolean") return v;
907
+ if (typeof v === "string") return v !== "false" && v !== "0";
908
+ return void 0;
909
+ }
910
+ function flagNumber(args, name) {
911
+ const s = flagString(args, name);
912
+ if (s === void 0) return void 0;
913
+ const n = Number(s);
914
+ return Number.isFinite(n) ? n : void 0;
915
+ }
916
+
917
+ // src/context.ts
918
+ import { existsSync as existsSync2 } from "fs";
919
+ import { resolve } from "path";
920
+ import { Guard } from "@agentwares/agentguard-core";
921
+ import {
922
+ FileApprovalStore,
923
+ FileAuditSink,
924
+ FileKillSwitch as FileKillSwitch2,
925
+ FileStateStore,
926
+ loadPolicyFile
927
+ } from "@agentwares/agentguard-core/node";
928
+ import { existsSync as exists, readFileSync } from "fs";
929
+
930
+ // ../../packages/mcp-kit/dist/chunk-AO7AV7MI.js
931
+ import { completable } from "@modelcontextprotocol/sdk/server/completable.js";
932
+ import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
933
+ import {
934
+ CreateMessageResultSchema,
935
+ ElicitResultSchema,
936
+ SubscribeRequestSchema,
937
+ UnsubscribeRequestSchema
938
+ } from "@modelcontextprotocol/sdk/types.js";
939
+ import { z } from "zod";
940
+
941
+ // ../../packages/x402/dist/index.js
942
+ var X402_HEADER = "X-Payment-Requirements";
943
+ var X402_REQUIRED_HEADER = "PAYMENT-REQUIRED";
944
+ function toAmount(priceUsd) {
945
+ if (!Number.isFinite(priceUsd) || priceUsd < 0)
946
+ throw new Error("priceUsd must be a non-negative number");
947
+ return priceUsd.toFixed(6).replace(/0+$/, "").replace(/\.$/, ".0");
948
+ }
949
+ function buildPaymentRequired(opts) {
950
+ const amountUsd = toAmount(opts.priceUsd);
951
+ const accepts = [
952
+ {
953
+ rail: "x402",
954
+ amountUsd,
955
+ resource: opts.resource,
956
+ description: opts.description,
957
+ network: "eip155:8453",
958
+ asset: "USDC",
959
+ payTo: opts.payTo,
960
+ maxTimeoutSeconds: 300
961
+ },
962
+ {
963
+ rail: "mpp",
964
+ amountUsd,
965
+ resource: opts.resource,
966
+ description: opts.description,
967
+ currency: "usd",
968
+ payTo: opts.payTo,
969
+ maxTimeoutSeconds: 300
970
+ }
971
+ ];
972
+ const live = opts.live === true;
973
+ const body = {
974
+ code: "PAYMENT_REQUIRED",
975
+ cause: live ? `This call costs $${amountUsd}. No valid payment or credit was presented.` : `This call costs $${amountUsd}. Pay-per-call (x402 / Stripe MPP) is coming soon; use an API key with prepaid credits.`,
976
+ fix: live ? "Retry with a PAYMENT header satisfying one of `accepts`, or send an API key with credit balance." : `Create an API key${opts.signupUrl ? ` at ${opts.signupUrl}` : ""} and retry with Authorization: Bearer <key>. Try the free sample mode first.`,
977
+ retryable: true,
978
+ priceUsd: opts.priceUsd,
979
+ accepts,
980
+ comingSoon: !live,
981
+ alternatives: ["api_key_credits", "sample_mode"]
982
+ };
983
+ const encoded = JSON.stringify({ x402Version: 2, accepts, resource: opts.resource });
984
+ const headers = {
985
+ "Content-Type": "application/json",
986
+ "WWW-Authenticate": `Payment realm="agentwares", methods="x402 mpp", price="${amountUsd} USD"`,
987
+ [X402_HEADER]: encoded,
988
+ [X402_REQUIRED_HEADER]: btoa(encoded),
989
+ "Cache-Control": "no-store"
990
+ };
991
+ return { status: 402, headers, body };
992
+ }
993
+ var StubVerifier = class {
994
+ async verify(request, requirement) {
995
+ const hasHeader = request.headers.has("PAYMENT") || request.headers.has("X-PAYMENT");
996
+ return {
997
+ ok: false,
998
+ rail: requirement.rail,
999
+ reason: hasHeader ? "payments are not live yet (phase 2: wallet). Use an API key with credits." : "no payment header present"
1000
+ };
1001
+ }
1002
+ };
1003
+
1004
+ // ../../packages/mcp-kit/dist/chunk-AO7AV7MI.js
1005
+ import { z as z2 } from "zod";
1006
+ import { z as z3 } from "zod";
1007
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
1008
+ import {
1009
+ CallToolRequestSchema,
1010
+ ErrorCode,
1011
+ ListToolsRequestSchema,
1012
+ McpError
1013
+ } from "@modelcontextprotocol/sdk/types.js";
1014
+ import { z as z4 } from "zod";
1015
+ import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
1016
+ var ERROR_CODES = [
1017
+ "INVALID_INPUT",
1018
+ "NOT_FOUND",
1019
+ "UNAUTHORIZED",
1020
+ "PAYMENT_REQUIRED",
1021
+ "RATE_LIMITED",
1022
+ "UPSTREAM_ERROR",
1023
+ "INTERNAL"
1024
+ ];
1025
+ var DEFAULT_RETRYABLE = {
1026
+ INVALID_INPUT: false,
1027
+ NOT_FOUND: false,
1028
+ UNAUTHORIZED: false,
1029
+ PAYMENT_REQUIRED: true,
1030
+ RATE_LIMITED: true,
1031
+ UPSTREAM_ERROR: true,
1032
+ INTERNAL: false
1033
+ };
1034
+ var DEFAULT_HTTP_STATUS = {
1035
+ INVALID_INPUT: 400,
1036
+ NOT_FOUND: 404,
1037
+ UNAUTHORIZED: 401,
1038
+ PAYMENT_REQUIRED: 402,
1039
+ RATE_LIMITED: 429,
1040
+ UPSTREAM_ERROR: 502,
1041
+ INTERNAL: 500
1042
+ };
1043
+ var INTERNAL_FIX = "retry; if it persists, report the request id";
1044
+ function isStandardCode(code) {
1045
+ return ERROR_CODES.includes(code);
1046
+ }
1047
+ var McpToolError = class extends Error {
1048
+ code;
1049
+ cause;
1050
+ fix;
1051
+ retryable;
1052
+ details;
1053
+ httpStatus;
1054
+ extra;
1055
+ constructor(init) {
1056
+ super(`${init.code}: ${init.cause}`);
1057
+ this.name = "McpToolError";
1058
+ this.code = init.code;
1059
+ this.cause = init.cause;
1060
+ this.fix = init.fix;
1061
+ this.retryable = init.retryable ?? (isStandardCode(init.code) ? DEFAULT_RETRYABLE[init.code] : false);
1062
+ this.details = init.details;
1063
+ this.httpStatus = init.httpStatus ?? (isStandardCode(init.code) ? DEFAULT_HTTP_STATUS[init.code] : 500);
1064
+ this.extra = init.extra;
1065
+ }
1066
+ /** The body agents see: `{ code, cause, fix, retryable, details?, ...extra }`. */
1067
+ toJSON() {
1068
+ const body = {
1069
+ ...this.extra,
1070
+ code: this.code,
1071
+ cause: this.cause,
1072
+ fix: this.fix,
1073
+ retryable: this.retryable
1074
+ };
1075
+ if (this.details !== void 0) body.details = this.details;
1076
+ return body;
1077
+ }
1078
+ };
1079
+ function toolError(codeOrInit, cause, fix, extra) {
1080
+ if (typeof codeOrInit === "object") return new McpToolError(codeOrInit);
1081
+ return new McpToolError({ code: codeOrInit, cause: cause ?? "", fix: fix ?? "", ...extra });
1082
+ }
1083
+ function describeUnknown(err) {
1084
+ if (err instanceof Error) return err.message || err.name;
1085
+ if (typeof err === "string") return err;
1086
+ try {
1087
+ return JSON.stringify(err);
1088
+ } catch {
1089
+ return String(err);
1090
+ }
1091
+ }
1092
+ function normalizeError(err) {
1093
+ if (err instanceof McpToolError) return err;
1094
+ return new McpToolError({
1095
+ code: "INTERNAL",
1096
+ cause: describeUnknown(err),
1097
+ fix: INTERNAL_FIX,
1098
+ retryable: false
1099
+ });
1100
+ }
1101
+ function errorResult(err, opts = {}) {
1102
+ const error = normalizeError(err);
1103
+ const body = error.toJSON();
1104
+ const meta = { httpStatus: error.httpStatus };
1105
+ if (opts.requestId !== void 0) meta.requestId = opts.requestId;
1106
+ Object.assign(meta, opts.meta);
1107
+ return {
1108
+ isError: true,
1109
+ content: [{ type: "text", text: JSON.stringify(body) }],
1110
+ structuredContent: body,
1111
+ _meta: meta
1112
+ };
1113
+ }
1114
+ var TOOL_NAME_PATTERN = /^[a-z][a-z0-9]*(_[a-z0-9]+)+$/;
1115
+ var MIN_NAMESPACE_LENGTH = 3;
1116
+ var MIN_DESCRIPTION_LENGTH = 20;
1117
+ function isValidToolName(name) {
1118
+ if (!TOOL_NAME_PATTERN.test(name)) return false;
1119
+ const namespace = name.split("_")[0] ?? "";
1120
+ return namespace.length >= MIN_NAMESPACE_LENGTH;
1121
+ }
1122
+ function assertToolName(name) {
1123
+ if (!isValidToolName(name)) {
1124
+ throw new Error(
1125
+ `tool name must be namespaced: <namespace>_<verb>_<object>, got ${JSON.stringify(name)}`
1126
+ );
1127
+ }
1128
+ }
1129
+ function isPlainObject(value) {
1130
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
1131
+ const proto = Object.getPrototypeOf(value);
1132
+ return proto === Object.prototype || proto === null;
1133
+ }
1134
+ function isCallToolResult(value) {
1135
+ if (!isPlainObject(value) || !Array.isArray(value.content)) return false;
1136
+ return value.content.every(
1137
+ (block) => isPlainObject(block) && typeof block.type === "string"
1138
+ );
1139
+ }
1140
+ function toCallToolResult(value) {
1141
+ if (isCallToolResult(value)) return value;
1142
+ const text = typeof value === "string" ? value : JSON.stringify(value ?? null, null, 2);
1143
+ const result = { content: [{ type: "text", text }] };
1144
+ if (isPlainObject(value)) result.structuredContent = value;
1145
+ return result;
1146
+ }
1147
+ function issuePath(issue) {
1148
+ return issue.path.map(String).join(".") || "(root)";
1149
+ }
1150
+ function invalidInputError(issues) {
1151
+ const fields = [...new Set(issues.map(issuePath))];
1152
+ const summary = issues.map((issue) => `${issuePath(issue)}: ${issue.message}`).join("; ");
1153
+ return toolError({
1154
+ code: "INVALID_INPUT",
1155
+ cause: `invalid input for ${fields.join(", ")} \u2014 ${summary}`,
1156
+ fix: `fix ${fields.join(", ")} to match the tool's inputSchema (see details.issues), then retry`,
1157
+ retryable: false,
1158
+ details: {
1159
+ fields,
1160
+ issues: issues.map((issue) => ({
1161
+ path: issue.path.map(String),
1162
+ code: issue.code,
1163
+ message: issue.message
1164
+ }))
1165
+ }
1166
+ });
1167
+ }
1168
+ function defineTool(opts) {
1169
+ assertToolName(opts.name);
1170
+ if (typeof opts.description !== "string" || opts.description.trim().length < MIN_DESCRIPTION_LENGTH) {
1171
+ throw new Error(
1172
+ `tool ${opts.name}: description must be at least ${MIN_DESCRIPTION_LENGTH} characters \u2014 say what it does, when to use it and what it returns`
1173
+ );
1174
+ }
1175
+ const { name, title, description, input, output, annotations } = opts;
1176
+ const handler = opts.handler.bind(opts);
1177
+ const tool = {
1178
+ name,
1179
+ title,
1180
+ description,
1181
+ input,
1182
+ output,
1183
+ annotations,
1184
+ handler,
1185
+ async invoke(rawInput, ctx) {
1186
+ const fail = (err) => {
1187
+ const result = errorResult(err, { requestId: ctx.requestId });
1188
+ if (output) delete result.structuredContent;
1189
+ return result;
1190
+ };
1191
+ const parsed = input.safeParse(rawInput ?? {});
1192
+ if (!parsed.success) return fail(invalidInputError(parsed.error.issues));
1193
+ try {
1194
+ const value = await handler(parsed.data, ctx);
1195
+ if (output && !isCallToolResult(value)) {
1196
+ const checked = output.safeParse(value);
1197
+ if (!checked.success) {
1198
+ throw toolError({
1199
+ code: "INTERNAL",
1200
+ cause: `tool ${name} returned a value that does not match its output schema \u2014 ${checked.error.issues.map((issue) => `${issuePath(issue)}: ${issue.message}`).join("; ")}`,
1201
+ fix: "this is a bug in the tool, not in your call; report it with the request id",
1202
+ retryable: false,
1203
+ details: { issues: checked.error.issues }
1204
+ });
1205
+ }
1206
+ return toCallToolResult(checked.data);
1207
+ }
1208
+ return toCallToolResult(value);
1209
+ } catch (err) {
1210
+ return fail(err);
1211
+ }
1212
+ }
1213
+ };
1214
+ return tool;
1215
+ }
1216
+ var PNG_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==";
1217
+ var WAV_BASE64 = "UklGRiQAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQAAAAA=";
1218
+ var STATIC_TEXT_URI = "test://static-text";
1219
+ var STATIC_TEXT = "This is a static text resource for testing.";
1220
+ var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
1221
+ function embeddedResource() {
1222
+ return {
1223
+ type: "resource",
1224
+ resource: { uri: STATIC_TEXT_URI, mimeType: "text/plain", text: STATIC_TEXT }
1225
+ };
1226
+ }
1227
+ function requireExtra(ctx) {
1228
+ if (!ctx.extra) {
1229
+ throw toolError("INTERNAL", "no client connection in this context", "call this tool over MCP");
1230
+ }
1231
+ return ctx.extra;
1232
+ }
1233
+ async function elicit(ctx, message, requestedSchema) {
1234
+ const result = await requireExtra(ctx).sendRequest(
1235
+ { method: "elicitation/create", params: { message, requestedSchema } },
1236
+ ElicitResultSchema
1237
+ );
1238
+ return `Elicitation completed: action=${result.action}, content=${JSON.stringify(result.content ?? {})}`;
1239
+ }
1240
+ var conformanceTools = [
1241
+ defineTool({
1242
+ name: "test_simple_text",
1243
+ description: "Conformance fixture: returns a fixed simple text response.",
1244
+ input: z.object({}),
1245
+ handler: () => "This is a simple text response for testing."
1246
+ }),
1247
+ defineTool({
1248
+ name: "test_error_handling",
1249
+ description: "Conformance fixture: always returns an error result (isError: true).",
1250
+ input: z.object({}),
1251
+ handler: () => {
1252
+ throw toolError({
1253
+ code: "UPSTREAM_ERROR",
1254
+ cause: "This tool intentionally returns an error for testing",
1255
+ fix: "nothing to fix; this fixture always fails",
1256
+ retryable: false
1257
+ });
1258
+ }
1259
+ }),
1260
+ defineTool({
1261
+ name: "test_image_content",
1262
+ description: "Conformance fixture: returns a 1x1 PNG image content block.",
1263
+ input: z.object({}),
1264
+ handler: () => ({ content: [{ type: "image", data: PNG_BASE64, mimeType: "image/png" }] })
1265
+ }),
1266
+ defineTool({
1267
+ name: "test_audio_content",
1268
+ description: "Conformance fixture: returns an empty WAV audio content block.",
1269
+ input: z.object({}),
1270
+ handler: () => ({ content: [{ type: "audio", data: WAV_BASE64, mimeType: "audio/wav" }] })
1271
+ }),
1272
+ defineTool({
1273
+ name: "test_embedded_resource",
1274
+ description: "Conformance fixture: returns an embedded text resource content block.",
1275
+ input: z.object({}),
1276
+ handler: () => ({ content: [embeddedResource()] })
1277
+ }),
1278
+ defineTool({
1279
+ name: "test_multiple_content_types",
1280
+ description: "Conformance fixture: returns text, image and resource content blocks together.",
1281
+ input: z.object({}),
1282
+ handler: () => ({
1283
+ content: [
1284
+ { type: "text", text: "Mixed content response" },
1285
+ { type: "image", data: PNG_BASE64, mimeType: "image/png" },
1286
+ embeddedResource()
1287
+ ]
1288
+ })
1289
+ }),
1290
+ defineTool({
1291
+ name: "test_tool_with_logging",
1292
+ description: "Conformance fixture: emits three info log notifications while running.",
1293
+ input: z.object({}),
1294
+ handler: async (_input, ctx) => {
1295
+ await ctx.log?.("info", "Tool execution started");
1296
+ await sleep(50);
1297
+ await ctx.log?.("info", "Tool processing data");
1298
+ await sleep(50);
1299
+ await ctx.log?.("info", "Tool execution completed");
1300
+ return "Tool execution completed with logging";
1301
+ }
1302
+ }),
1303
+ defineTool({
1304
+ name: "test_tool_with_progress",
1305
+ description: "Conformance fixture: reports progress 0/100, 50/100, 100/100 while running.",
1306
+ input: z.object({}),
1307
+ handler: async (_input, ctx) => {
1308
+ await ctx.progress?.(0, 100, "starting");
1309
+ await sleep(50);
1310
+ await ctx.progress?.(50, 100, "halfway");
1311
+ await sleep(50);
1312
+ await ctx.progress?.(100, 100, "done");
1313
+ return "Tool execution completed with progress";
1314
+ }
1315
+ }),
1316
+ defineTool({
1317
+ name: "test_sampling",
1318
+ description: "Conformance fixture: asks the client to sample a completion and returns it.",
1319
+ input: z.object({ prompt: z.string().describe("Prompt to send to the client's model") }),
1320
+ handler: async ({ prompt }, ctx) => {
1321
+ const result = await requireExtra(ctx).sendRequest(
1322
+ {
1323
+ method: "sampling/createMessage",
1324
+ params: {
1325
+ messages: [{ role: "user", content: { type: "text", text: prompt } }],
1326
+ maxTokens: 100
1327
+ }
1328
+ },
1329
+ CreateMessageResultSchema
1330
+ );
1331
+ const content = Array.isArray(result.content) ? result.content[0] : result.content;
1332
+ const text = content && content.type === "text" ? content.text : JSON.stringify(content);
1333
+ return `Sampling completed (${result.model}): ${text}`;
1334
+ }
1335
+ }),
1336
+ defineTool({
1337
+ name: "test_elicitation",
1338
+ description: "Conformance fixture: asks the client for user input and reports the outcome.",
1339
+ input: z.object({ message: z.string().describe("Message shown to the user") }),
1340
+ handler: ({ message }, ctx) => elicit(ctx, message, {
1341
+ type: "object",
1342
+ properties: {
1343
+ name: { type: "string", title: "Name", description: "Your name" },
1344
+ email: { type: "string", title: "Email", format: "email" }
1345
+ },
1346
+ required: ["name"]
1347
+ })
1348
+ }),
1349
+ defineTool({
1350
+ name: "test_elicitation_sep1034_defaults",
1351
+ description: "Conformance fixture: elicitation schema with defaults for every primitive type.",
1352
+ input: z.object({}),
1353
+ handler: (_input, ctx) => elicit(ctx, "Please confirm your details", {
1354
+ type: "object",
1355
+ properties: {
1356
+ name: { type: "string", title: "Name", default: "John Doe" },
1357
+ age: { type: "integer", title: "Age", default: 30 },
1358
+ score: { type: "number", title: "Score", default: 95.5 },
1359
+ status: {
1360
+ type: "string",
1361
+ title: "Status",
1362
+ enum: ["active", "inactive", "pending"],
1363
+ default: "active"
1364
+ },
1365
+ verified: { type: "boolean", title: "Verified", default: true }
1366
+ }
1367
+ })
1368
+ }),
1369
+ defineTool({
1370
+ name: "test_elicitation_sep1330_enums",
1371
+ description: "Conformance fixture: elicitation schema with titled, untitled and multi-select enums.",
1372
+ input: z.object({}),
1373
+ handler: (_input, ctx) => elicit(ctx, "Pick some options", {
1374
+ type: "object",
1375
+ properties: {
1376
+ untitledSingle: {
1377
+ type: "string",
1378
+ title: "Untitled single",
1379
+ enum: ["red", "green", "blue"]
1380
+ },
1381
+ titledSingle: {
1382
+ type: "string",
1383
+ title: "Titled single",
1384
+ oneOf: [
1385
+ { const: "red", title: "Red" },
1386
+ { const: "green", title: "Green" },
1387
+ { const: "blue", title: "Blue" }
1388
+ ]
1389
+ },
1390
+ legacyEnum: {
1391
+ type: "string",
1392
+ title: "Legacy titled",
1393
+ enum: ["red", "green", "blue"],
1394
+ enumNames: ["Red", "Green", "Blue"]
1395
+ },
1396
+ untitledMulti: {
1397
+ type: "array",
1398
+ title: "Untitled multi",
1399
+ items: { type: "string", enum: ["red", "green", "blue"] }
1400
+ },
1401
+ titledMulti: {
1402
+ type: "array",
1403
+ title: "Titled multi",
1404
+ items: {
1405
+ anyOf: [
1406
+ { const: "red", title: "Red" },
1407
+ { const: "green", title: "Green" },
1408
+ { const: "blue", title: "Blue" }
1409
+ ]
1410
+ }
1411
+ }
1412
+ }
1413
+ })
1414
+ }),
1415
+ defineTool({
1416
+ name: "json_schema_2020_12_tool",
1417
+ description: "Conformance fixture: input schema with a $defs reference to an address object.",
1418
+ input: z.object({
1419
+ name: z.string(),
1420
+ address: z.object({ street: z.string(), city: z.string() }).meta({ id: "address" })
1421
+ }),
1422
+ handler: (input) => input
1423
+ })
1424
+ ];
1425
+ function paymentRequiredError(body, reason) {
1426
+ const { code, cause, fix, retryable, ...rest } = body;
1427
+ return toolError({
1428
+ code,
1429
+ cause,
1430
+ fix,
1431
+ retryable,
1432
+ httpStatus: 402,
1433
+ details: reason !== void 0 ? { reason } : void 0,
1434
+ extra: rest
1435
+ });
1436
+ }
1437
+ function formatUsd(priceUsd) {
1438
+ return priceUsd.toFixed(6).replace(/0+$/, "").replace(/\.$/, ".00").replace(/\.(\d)$/, ".$10");
1439
+ }
1440
+ function withPayment(tool, opts) {
1441
+ if (!Number.isFinite(opts.priceUsd) || opts.priceUsd < 0) {
1442
+ throw new Error(`withPayment(${tool.name}): priceUsd must be a non-negative number`);
1443
+ }
1444
+ const sampleField = opts.sampleField ?? "sample";
1445
+ if (sampleField in tool.input.shape) {
1446
+ throw new Error(
1447
+ `withPayment(${tool.name}): input already has a field named ${JSON.stringify(sampleField)}; pass sampleField to rename it`
1448
+ );
1449
+ }
1450
+ const verifier = opts.verifier ?? new StubVerifier();
1451
+ const input = tool.input.extend({
1452
+ [sampleField]: z2.boolean().default(false).describe(
1453
+ "Set true to return an example response at no charge (sample mode). Default false."
1454
+ )
1455
+ });
1456
+ const description = `${tool.description.trim()} Paid: $${formatUsd(opts.priceUsd)} per call; without credit you get a PAYMENT_REQUIRED result. Set ${sampleField}=true for a free example response.`;
1457
+ return defineTool({
1458
+ name: tool.name,
1459
+ title: tool.title,
1460
+ description,
1461
+ input,
1462
+ output: tool.output,
1463
+ annotations: tool.annotations,
1464
+ async handler(raw, ctx) {
1465
+ const record = { ...raw };
1466
+ const sample = record[sampleField] === true;
1467
+ delete record[sampleField];
1468
+ if (sample) return tool.handler(record, { ...ctx, sample: true });
1469
+ let reason;
1470
+ if (opts.authorize) {
1471
+ const auth = await opts.authorize(ctx);
1472
+ if (auth.ok) return tool.handler(record, { ...ctx, userId: auth.userId });
1473
+ reason = auth.reason;
1474
+ }
1475
+ const required = buildPaymentRequired({
1476
+ resource: tool.name,
1477
+ priceUsd: opts.priceUsd,
1478
+ live: opts.live,
1479
+ description: opts.description,
1480
+ payTo: opts.payTo,
1481
+ signupUrl: opts.signupUrl
1482
+ });
1483
+ const requirement = required.body.accepts[0];
1484
+ if (requirement) {
1485
+ const verified = await verifier.verify(
1486
+ new Request(`https://mcp.local/${tool.name}`, { headers: ctx.headers }),
1487
+ requirement
1488
+ );
1489
+ if (verified.ok) return tool.handler(record, ctx);
1490
+ reason = reason ?? verified.reason;
1491
+ }
1492
+ throw paymentRequiredError(required.body, reason);
1493
+ }
1494
+ });
1495
+ }
1496
+ var exampleEcho = defineTool({
1497
+ name: "example_echo",
1498
+ title: "Echo",
1499
+ description: "Echo a message back unchanged. Use it to check the connection and to see the result shape mcp-kit tools return (JSON text plus structuredContent).",
1500
+ input: z3.object({ message: z3.string().describe("Text to echo back") }),
1501
+ output: z3.object({ message: z3.string() }),
1502
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
1503
+ handler: ({ message }) => ({ message })
1504
+ });
1505
+ var exampleAdd = defineTool({
1506
+ name: "example_add",
1507
+ title: "Add",
1508
+ description: "Add two numbers and return their sum as { sum }. Demonstrates typed input and output schemas; send non-numbers to see an INVALID_INPUT error result.",
1509
+ input: z3.object({
1510
+ a: z3.number().describe("First addend"),
1511
+ b: z3.number().describe("Second addend")
1512
+ }),
1513
+ output: z3.object({ sum: z3.number() }),
1514
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
1515
+ handler: ({ a, b }) => ({ sum: a + b })
1516
+ });
1517
+ var examplePaidLookup = withPayment(
1518
+ defineTool({
1519
+ name: "example_paid_lookup",
1520
+ title: "Paid lookup",
1521
+ description: "Look up a record by query and return { query, result, sample } (fake data). Demonstrates a paid tool: without an entitlement the call returns a PAYMENT_REQUIRED result with x402 and MPP payment options.",
1522
+ input: z3.object({ query: z3.string().min(1).describe("What to look up") }),
1523
+ output: z3.object({ query: z3.string(), result: z3.string(), sample: z3.boolean() }),
1524
+ annotations: { readOnlyHint: true, idempotentHint: true },
1525
+ handler: ({ query }, ctx) => ({
1526
+ query,
1527
+ result: ctx.sample ? `SAMPLE: example record for "${query}"` : `record for "${query}"`,
1528
+ sample: ctx.sample === true
1529
+ })
1530
+ }),
1531
+ { priceUsd: 0.05, description: "One record lookup" }
1532
+ );
1533
+ function isZodObject(schema) {
1534
+ return schema instanceof z4.ZodObject;
1535
+ }
1536
+ function contextFromExtra(extra) {
1537
+ const headers = new Headers();
1538
+ const raw = extra.requestInfo?.headers;
1539
+ if (raw) {
1540
+ for (const [key, value] of Object.entries(raw)) {
1541
+ if (Array.isArray(value)) for (const item of value) headers.append(key, item);
1542
+ else if (typeof value === "string") headers.append(key, value);
1543
+ }
1544
+ }
1545
+ const progressToken = extra._meta?.progressToken;
1546
+ return {
1547
+ headers,
1548
+ requestId: String(extra.requestId),
1549
+ signal: extra.signal,
1550
+ sessionId: extra.sessionId,
1551
+ meta: extra._meta,
1552
+ extra,
1553
+ log: async (level, data) => {
1554
+ await extra.sendNotification({ method: "notifications/message", params: { level, data } });
1555
+ },
1556
+ progress: async (progress, total, message) => {
1557
+ if (progressToken === void 0) return;
1558
+ await extra.sendNotification({
1559
+ method: "notifications/progress",
1560
+ params: { progressToken, progress, total, message }
1561
+ });
1562
+ }
1563
+ };
1564
+ }
1565
+ function createMcpServer(opts) {
1566
+ const byName = /* @__PURE__ */ new Map();
1567
+ for (const tool of opts.tools) {
1568
+ if (byName.has(tool.name)) throw new Error(`duplicate tool name: ${tool.name}`);
1569
+ byName.set(tool.name, tool);
1570
+ }
1571
+ const server = new McpServer(
1572
+ { name: opts.name, version: opts.version, title: opts.title, websiteUrl: opts.websiteUrl },
1573
+ { instructions: opts.instructions, capabilities: { logging: {}, ...opts.capabilities } }
1574
+ );
1575
+ const registered = /* @__PURE__ */ new Map();
1576
+ for (const tool of byName.values()) {
1577
+ const config = {
1578
+ title: tool.title,
1579
+ description: tool.description,
1580
+ inputSchema: tool.input,
1581
+ annotations: tool.annotations
1582
+ };
1583
+ if (isZodObject(tool.output)) config.outputSchema = tool.output;
1584
+ registered.set(
1585
+ tool.name,
1586
+ server.registerTool(tool.name, config, () => {
1587
+ throw new Error("unreachable: mcp-kit dispatches tools/call itself");
1588
+ })
1589
+ );
1590
+ }
1591
+ if (byName.size > 0) {
1592
+ server.server.setRequestHandler(ListToolsRequestSchema, async () => {
1593
+ const enabled = [...byName.values()].filter(
1594
+ (tool) => registered.get(tool.name)?.enabled !== false
1595
+ );
1596
+ return {
1597
+ tools: listToolManifest(enabled).map((entry) => ({
1598
+ ...entry,
1599
+ inputSchema: entry.inputSchema,
1600
+ outputSchema: entry.outputSchema
1601
+ }))
1602
+ };
1603
+ });
1604
+ server.server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
1605
+ const name = request.params.name;
1606
+ const tool = byName.get(name);
1607
+ const entry = registered.get(name);
1608
+ if (!tool || !entry) throw new McpError(ErrorCode.InvalidParams, `Tool ${name} not found`);
1609
+ if (!entry.enabled) throw new McpError(ErrorCode.InvalidParams, `Tool ${name} disabled`);
1610
+ return tool.invoke(request.params.arguments ?? {}, contextFromExtra(extra));
1611
+ });
1612
+ }
1613
+ return server;
1614
+ }
1615
+ function toJsonSchema(schema, io = "input") {
1616
+ const json2 = z4.toJSONSchema(schema, { io, target: "draft-2020-12" });
1617
+ if (isZodObject(schema) && schema._zod.def.catchall === void 0 && json2.additionalProperties === void 0) {
1618
+ json2.additionalProperties = false;
1619
+ }
1620
+ return json2;
1621
+ }
1622
+ function listToolManifest(tools) {
1623
+ return tools.map((tool) => {
1624
+ const entry = {
1625
+ name: tool.name,
1626
+ description: tool.description,
1627
+ inputSchema: toJsonSchema(tool.input, "input")
1628
+ };
1629
+ if (tool.title !== void 0) entry.title = tool.title;
1630
+ if (isZodObject(tool.output)) entry.outputSchema = toJsonSchema(tool.output, "output");
1631
+ if (tool.annotations !== void 0) entry.annotations = tool.annotations;
1632
+ return entry;
1633
+ });
1634
+ }
1635
+ var DEFAULT_CORS = {
1636
+ origin: "*",
1637
+ methods: "GET, POST, DELETE, OPTIONS",
1638
+ headers: "Content-Type, Accept, Authorization, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID, PAYMENT, X-PAYMENT",
1639
+ exposeHeaders: "Mcp-Session-Id, Mcp-Protocol-Version, WWW-Authenticate, X-Payment-Requirements, PAYMENT-REQUIRED",
1640
+ maxAge: 86400
1641
+ };
1642
+ var DEFAULT_SESSION_TTL_MS = 30 * 6e4;
1643
+ function corsHeaders(cors) {
1644
+ if (!cors) return {};
1645
+ const c = { ...DEFAULT_CORS, ...typeof cors === "object" ? cors : {} };
1646
+ return {
1647
+ "Access-Control-Allow-Origin": c.origin,
1648
+ "Access-Control-Allow-Methods": c.methods,
1649
+ "Access-Control-Allow-Headers": c.headers,
1650
+ "Access-Control-Expose-Headers": c.exposeHeaders,
1651
+ "Access-Control-Max-Age": String(c.maxAge)
1652
+ };
1653
+ }
1654
+ function jsonResponse(body, status, headers = {}) {
1655
+ return new Response(JSON.stringify(body), {
1656
+ status,
1657
+ headers: { "Content-Type": "application/json", "Cache-Control": "no-store", ...headers }
1658
+ });
1659
+ }
1660
+ function methodNotAllowed(method, allow, cors) {
1661
+ return jsonResponse(
1662
+ {
1663
+ code: "METHOD_NOT_ALLOWED",
1664
+ cause: `${method} is not supported here without a session`,
1665
+ fix: "send JSON-RPC over POST with `Accept: application/json, text/event-stream` (start with `initialize`)",
1666
+ retryable: false
1667
+ },
1668
+ 405,
1669
+ { Allow: allow, ...cors }
1670
+ );
1671
+ }
1672
+ function splitOptions(opts, transport) {
1673
+ if (typeof opts === "function") return { factory: opts, transport };
1674
+ const {
1675
+ cors,
1676
+ jsonResponse: jsonResponse2,
1677
+ sessions,
1678
+ allowedHosts,
1679
+ allowedOrigins,
1680
+ enableDnsRebindingProtection,
1681
+ ...server
1682
+ } = opts;
1683
+ return {
1684
+ factory: () => createMcpServer(server),
1685
+ transport: {
1686
+ cors,
1687
+ jsonResponse: jsonResponse2,
1688
+ sessions,
1689
+ allowedHosts,
1690
+ allowedOrigins,
1691
+ enableDnsRebindingProtection,
1692
+ ...transport
1693
+ }
1694
+ };
1695
+ }
1696
+ function withCors(response, cors) {
1697
+ const keys = Object.keys(cors);
1698
+ if (keys.length === 0) return response;
1699
+ const headers = new Headers(response.headers);
1700
+ for (const key of keys) headers.set(key, cors[key] ?? "");
1701
+ return new Response(response.body, {
1702
+ status: response.status,
1703
+ statusText: response.statusText,
1704
+ headers
1705
+ });
1706
+ }
1707
+ function errorResponse(err, cors) {
1708
+ const error = normalizeError(err);
1709
+ return jsonResponse(error.toJSON(), error.httpStatus, cors);
1710
+ }
1711
+ function createStatelessHandler({ factory, cors, transportBase }) {
1712
+ return async (request) => {
1713
+ if (request.method !== "POST") return methodNotAllowed(request.method, "POST, OPTIONS", cors);
1714
+ const server = factory();
1715
+ const transport = new WebStandardStreamableHTTPServerTransport({
1716
+ ...transportBase,
1717
+ sessionIdGenerator: void 0
1718
+ });
1719
+ let closed = false;
1720
+ const cleanup = async () => {
1721
+ if (closed) return;
1722
+ closed = true;
1723
+ try {
1724
+ await server.close();
1725
+ } catch {
1726
+ }
1727
+ };
1728
+ try {
1729
+ await server.connect(transport);
1730
+ const response = withCors(await transport.handleRequest(request), cors);
1731
+ if (!response.body) {
1732
+ await cleanup();
1733
+ return response;
1734
+ }
1735
+ request.signal?.addEventListener("abort", () => void cleanup(), { once: true });
1736
+ const body = response.body.pipeThrough(
1737
+ new TransformStream({ flush: () => cleanup() })
1738
+ );
1739
+ return new Response(body, {
1740
+ status: response.status,
1741
+ statusText: response.statusText,
1742
+ headers: response.headers
1743
+ });
1744
+ } catch (err) {
1745
+ await cleanup();
1746
+ return errorResponse(err, cors);
1747
+ }
1748
+ };
1749
+ }
1750
+ function createStatefulHandler({ factory, cors, transportBase }, options) {
1751
+ const ttlMs = options.ttlMs ?? DEFAULT_SESSION_TTL_MS;
1752
+ const sessions = /* @__PURE__ */ new Map();
1753
+ const drop = (id) => {
1754
+ const session = sessions.get(id);
1755
+ if (!session) return;
1756
+ sessions.delete(id);
1757
+ session.server.close().catch(() => void 0);
1758
+ };
1759
+ const sweep = () => {
1760
+ const now = Date.now();
1761
+ for (const [id, session] of sessions) if (now - session.lastSeen > ttlMs) drop(id);
1762
+ };
1763
+ return async (request) => {
1764
+ const sessionId = request.headers.get("mcp-session-id");
1765
+ if (sessionId) {
1766
+ const session = sessions.get(sessionId);
1767
+ if (!session) {
1768
+ return jsonResponse(
1769
+ {
1770
+ code: "NOT_FOUND",
1771
+ cause: "unknown or expired Mcp-Session-Id",
1772
+ fix: "send `initialize` again without an Mcp-Session-Id header and use the id from the response",
1773
+ retryable: true
1774
+ },
1775
+ 404,
1776
+ cors
1777
+ );
1778
+ }
1779
+ session.lastSeen = Date.now();
1780
+ try {
1781
+ return withCors(await session.transport.handleRequest(request), cors);
1782
+ } catch (err) {
1783
+ return errorResponse(err, cors);
1784
+ }
1785
+ }
1786
+ if (request.method !== "POST") return methodNotAllowed(request.method, "POST, OPTIONS", cors);
1787
+ sweep();
1788
+ const server = factory();
1789
+ const transport = new WebStandardStreamableHTTPServerTransport({
1790
+ ...transportBase,
1791
+ sessionIdGenerator: () => crypto.randomUUID(),
1792
+ onsessioninitialized: (id) => {
1793
+ sessions.set(id, { server, transport, lastSeen: Date.now() });
1794
+ },
1795
+ onsessionclosed: (id) => {
1796
+ sessions.delete(id);
1797
+ }
1798
+ });
1799
+ try {
1800
+ await server.connect(transport);
1801
+ const previousOnClose = transport.onclose;
1802
+ transport.onclose = () => {
1803
+ previousOnClose?.();
1804
+ if (transport.sessionId) sessions.delete(transport.sessionId);
1805
+ };
1806
+ const response = withCors(await transport.handleRequest(request), cors);
1807
+ if (!transport.sessionId) server.close().catch(() => void 0);
1808
+ return response;
1809
+ } catch (err) {
1810
+ server.close().catch(() => void 0);
1811
+ return errorResponse(err, cors);
1812
+ }
1813
+ };
1814
+ }
1815
+ function createHttpHandler(opts, transportOptions = {}) {
1816
+ const { factory, transport: t } = splitOptions(opts, transportOptions);
1817
+ const cors = corsHeaders(t.cors);
1818
+ const sessions = t.sessions ? typeof t.sessions === "object" ? t.sessions : {} : void 0;
1819
+ const context = {
1820
+ factory,
1821
+ cors,
1822
+ transportBase: {
1823
+ enableJsonResponse: t.jsonResponse ?? sessions === void 0,
1824
+ allowedHosts: t.allowedHosts,
1825
+ allowedOrigins: t.allowedOrigins,
1826
+ enableDnsRebindingProtection: t.enableDnsRebindingProtection
1827
+ }
1828
+ };
1829
+ const handler = sessions ? createStatefulHandler(context, sessions) : createStatelessHandler(context);
1830
+ const allow = sessions ? "GET, POST, DELETE, OPTIONS" : "POST, OPTIONS";
1831
+ return async (request) => {
1832
+ if (request.method === "OPTIONS") {
1833
+ return new Response(null, { status: 204, headers: { Allow: allow, ...cors } });
1834
+ }
1835
+ return handler(request);
1836
+ };
1837
+ }
1838
+ function localhostHosts(port) {
1839
+ const hosts = ["localhost", "127.0.0.1", "[::1]"];
1840
+ return [...hosts, ...hosts.map((h) => `${h}:${port}`)];
1841
+ }
1842
+
1843
+ // ../../packages/mcp-kit/dist/index.js
1844
+ import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
1845
+ async function serveStdio(serverOrOpts) {
1846
+ const server = serverOrOpts instanceof McpServer2 ? serverOrOpts : createMcpServer(serverOrOpts);
1847
+ const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
1848
+ const transport = new StdioServerTransport();
1849
+ await server.connect(transport);
1850
+ await new Promise((resolve5) => {
1851
+ const previous = transport.onclose;
1852
+ transport.onclose = () => {
1853
+ previous?.();
1854
+ resolve5();
1855
+ };
1856
+ });
1857
+ }
1858
+
1859
+ // src/proxy/http.ts
1860
+ import { writeFileSync, unlinkSync, existsSync, mkdirSync } from "fs";
1861
+ import { createServer } from "http";
1862
+ import { join } from "path";
1863
+ import { FileKillSwitch } from "@agentwares/agentguard-core/node";
1864
+
1865
+ // src/proxy/server.ts
1866
+ import { McpServer as McpServer3 } from "@modelcontextprotocol/sdk/server/mcp.js";
1867
+ import {
1868
+ CallToolRequestSchema as CallToolRequestSchema2,
1869
+ CompleteRequestSchema,
1870
+ GetPromptRequestSchema,
1871
+ ListPromptsRequestSchema,
1872
+ ListResourceTemplatesRequestSchema,
1873
+ ListResourcesRequestSchema,
1874
+ ListToolsRequestSchema as ListToolsRequestSchema2,
1875
+ ReadResourceRequestSchema,
1876
+ SetLevelRequestSchema,
1877
+ SubscribeRequestSchema as SubscribeRequestSchema2,
1878
+ UnsubscribeRequestSchema as UnsubscribeRequestSchema2
1879
+ } from "@modelcontextprotocol/sdk/types.js";
1880
+ import {
1881
+ keyFromHeaders,
1882
+ resolveAgent
1883
+ } from "@agentwares/agentguard-core";
1884
+ var PROXY_VERSION = "0.1.0";
1885
+ function headerValue(extra, name) {
1886
+ const raw = extra.requestInfo?.headers?.[name] ?? extra.requestInfo?.headers?.[name.toLowerCase()];
1887
+ if (Array.isArray(raw)) return raw[0];
1888
+ return typeof raw === "string" ? raw : void 0;
1889
+ }
1890
+ function headersOf(extra) {
1891
+ const out = {};
1892
+ for (const [k, v] of Object.entries(extra.requestInfo?.headers ?? {}))
1893
+ out[k.toLowerCase()] = Array.isArray(v) ? v[0] : v;
1894
+ return out;
1895
+ }
1896
+ function runIdFor(extra, meta, fallback) {
1897
+ const header = headerValue(extra, "x-run-id");
1898
+ if (header && header.trim()) return header.trim().slice(0, 128);
1899
+ const fromMeta = meta?.runId ?? meta?.["agentguard/runId"] ?? meta?.run_id;
1900
+ if (typeof fromMeta === "string" && fromMeta.trim()) return fromMeta.trim().slice(0, 128);
1901
+ if (extra.sessionId) return `sess_${extra.sessionId.slice(0, 12)}`;
1902
+ return fallback;
1903
+ }
1904
+ function toCallToolResult2(result, tool) {
1905
+ const meta = {
1906
+ agentguard: {
1907
+ class: result.decision.classification.class,
1908
+ verb: result.decision.classification.verb,
1909
+ mode: result.decision.mode,
1910
+ outcome: result.outcome,
1911
+ dryRun: result.faked,
1912
+ seq: result.entry.seq,
1913
+ run_id: result.entry.run_id
1914
+ }
1915
+ };
1916
+ const hasOutputSchema = Boolean(tool?.tool.outputSchema);
1917
+ if (result.error) {
1918
+ const upstream2 = result.value;
1919
+ if (upstream2 && upstream2.content) {
1920
+ return { ...upstream2, isError: true, _meta: { ...upstream2._meta ?? {}, ...meta } };
1921
+ }
1922
+ const body = result.error;
1923
+ return {
1924
+ isError: true,
1925
+ content: [{ type: "text", text: JSON.stringify(body) }],
1926
+ ...hasOutputSchema ? {} : { structuredContent: body },
1927
+ _meta: { ...meta, httpStatus: httpStatusFor(body.code) }
1928
+ };
1929
+ }
1930
+ if (result.faked) {
1931
+ const value = result.value;
1932
+ const isObject = value !== null && typeof value === "object" && !Array.isArray(value);
1933
+ return {
1934
+ content: [
1935
+ { type: "text", text: typeof value === "string" ? value : JSON.stringify(value, null, 2) }
1936
+ ],
1937
+ ...isObject ? { structuredContent: value } : {},
1938
+ _meta: meta
1939
+ };
1940
+ }
1941
+ const upstream = result.value;
1942
+ return { ...upstream, _meta: { ...upstream._meta ?? {}, ...meta } };
1943
+ }
1944
+ function httpStatusFor(code) {
1945
+ switch (code) {
1946
+ case "KILLED":
1947
+ case "TOOL_DENIED":
1948
+ case "APPROVAL_DENIED":
1949
+ return 403;
1950
+ case "APPROVAL_REQUIRED":
1951
+ return 202;
1952
+ case "CAP_EXCEEDED":
1953
+ case "LOOP_DETECTED":
1954
+ return 429;
1955
+ case "UNKNOWN_TOOL":
1956
+ return 404;
1957
+ case "UPSTREAM_ERROR":
1958
+ return 502;
1959
+ default:
1960
+ return 500;
1961
+ }
1962
+ }
1963
+ function annotateTool(t, mode) {
1964
+ return {
1965
+ ...t.tool,
1966
+ name: t.name,
1967
+ _meta: {
1968
+ ...t.tool._meta ?? {},
1969
+ agentguard: {
1970
+ upstream: t.upstream,
1971
+ class: t.classification.class,
1972
+ verb: t.classification.verb,
1973
+ mode
1974
+ }
1975
+ }
1976
+ };
1977
+ }
1978
+ function killedError(state) {
1979
+ return Object.assign(
1980
+ new Error(`agentguard kill switch is on (${state.source ?? "?"}: ${state.reason ?? ""})`),
1981
+ { code: -32e3 }
1982
+ );
1983
+ }
1984
+ function createDownstreamServer(runtime, opts = {}) {
1985
+ const server = new McpServer3(
1986
+ { name: "agentguard", version: PROXY_VERSION, title: "agentguard policy proxy" },
1987
+ {
1988
+ capabilities: {
1989
+ tools: { listChanged: true },
1990
+ resources: { subscribe: true, listChanged: true },
1991
+ prompts: { listChanged: true },
1992
+ logging: {},
1993
+ completions: {}
1994
+ },
1995
+ instructions: `Tool calls pass through agentguard (${runtime.policy.mode} mode). Errors carry { code, cause, fix, retryable }; follow "fix". CAP_EXCEEDED, LOOP_DETECTED and KILLED are not retryable: stop and report to the user. APPROVAL_REQUIRED: relay the approval command to the user, then retry the identical call once.`
1996
+ }
1997
+ );
1998
+ const session = {
1999
+ id: crypto.randomUUID(),
2000
+ server,
2001
+ lastActive: Date.now(),
2002
+ inflight: /* @__PURE__ */ new Map()
2003
+ };
2004
+ runtime.attach(session);
2005
+ const previousOnClose = server.server.onclose;
2006
+ server.server.onclose = () => {
2007
+ previousOnClose?.();
2008
+ runtime.detach(session);
2009
+ };
2010
+ const touch = () => {
2011
+ session.lastActive = Date.now();
2012
+ };
2013
+ const agentFor = async (extra) => {
2014
+ const key = keyFromHeaders(headersOf(extra));
2015
+ if (!key) return { agent: void 0 };
2016
+ const resolved = await resolveAgent(runtime.policy, { key });
2017
+ if (resolved.error) {
2018
+ return {
2019
+ agent: null,
2020
+ error: {
2021
+ code: "TOOL_DENIED",
2022
+ cause: "the agent key in Authorization / X-Agentguard-Key is not in agentguard.yaml",
2023
+ fix: "use a key created with `agentguard key create <agent>`, or omit the key to use the global policy",
2024
+ retryable: false
2025
+ }
2026
+ };
2027
+ }
2028
+ return { agent: resolved.agent };
2029
+ };
2030
+ const low = server.server;
2031
+ low.setRequestHandler(ListToolsRequestSchema2, async (_req, extra) => {
2032
+ touch();
2033
+ const { agent } = await agentFor(extra);
2034
+ const scope = agent === void 0 ? runtime.agent : agent;
2035
+ return {
2036
+ tools: runtime.listTools(scope).map((t) => annotateTool(t, scope?.mode ?? runtime.policy.mode))
2037
+ };
2038
+ });
2039
+ low.setRequestHandler(CallToolRequestSchema2, async (req, extra) => {
2040
+ touch();
2041
+ const { agent, error } = await agentFor(extra);
2042
+ const runId = runIdFor(extra, req.params._meta, opts.defaultRunId ?? runtime.defaultRunId);
2043
+ if (error) {
2044
+ return {
2045
+ isError: true,
2046
+ content: [{ type: "text", text: JSON.stringify(error) }],
2047
+ structuredContent: error,
2048
+ _meta: { httpStatus: 401 }
2049
+ };
2050
+ }
2051
+ const progressToken = req.params._meta?.progressToken;
2052
+ const { result, tool } = await runtime.callTool(req.params.name, req.params.arguments ?? {}, {
2053
+ runId,
2054
+ agent,
2055
+ sessionId: extra.sessionId ?? session.id,
2056
+ session,
2057
+ requestId: extra.requestId,
2058
+ onprogress: progressToken === void 0 ? void 0 : (p) => {
2059
+ void extra.sendNotification({
2060
+ method: "notifications/progress",
2061
+ params: { progressToken, ...p }
2062
+ });
2063
+ }
2064
+ });
2065
+ return toCallToolResult2(result, tool);
2066
+ });
2067
+ const guardRead = async () => {
2068
+ const state = await runtime.guard.killState();
2069
+ if (state.killed) throw killedError(state);
2070
+ };
2071
+ low.setRequestHandler(ListResourcesRequestSchema, async () => {
2072
+ touch();
2073
+ return runtime.listResources();
2074
+ });
2075
+ low.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
2076
+ touch();
2077
+ return runtime.listResourceTemplates();
2078
+ });
2079
+ low.setRequestHandler(ReadResourceRequestSchema, async (req) => {
2080
+ touch();
2081
+ await guardRead();
2082
+ return runtime.readResource(req.params.uri);
2083
+ });
2084
+ low.setRequestHandler(SubscribeRequestSchema2, async (req) => {
2085
+ await runtime.subscribe(req.params.uri, true);
2086
+ return {};
2087
+ });
2088
+ low.setRequestHandler(UnsubscribeRequestSchema2, async (req) => {
2089
+ await runtime.subscribe(req.params.uri, false);
2090
+ return {};
2091
+ });
2092
+ low.setRequestHandler(ListPromptsRequestSchema, async () => {
2093
+ touch();
2094
+ return runtime.listPrompts();
2095
+ });
2096
+ low.setRequestHandler(GetPromptRequestSchema, async (req) => {
2097
+ touch();
2098
+ await guardRead();
2099
+ return runtime.getPrompt(req.params.name, req.params.arguments);
2100
+ });
2101
+ low.setRequestHandler(CompleteRequestSchema, async (req) => {
2102
+ touch();
2103
+ return runtime.complete(req.params);
2104
+ });
2105
+ low.setRequestHandler(SetLevelRequestSchema, async (req) => {
2106
+ await runtime.setLevel(req.params.level);
2107
+ return {};
2108
+ });
2109
+ return server;
2110
+ }
2111
+
2112
+ // src/proxy/http.ts
2113
+ function registrationPath(stateDir) {
2114
+ return join(stateDir, "http.json");
2115
+ }
2116
+ function json(res, status, body) {
2117
+ res.writeHead(status, { "Content-Type": "application/json", "Cache-Control": "no-store" });
2118
+ res.end(JSON.stringify(body));
2119
+ }
2120
+ function html(res, status, body) {
2121
+ res.writeHead(status, {
2122
+ "Content-Type": "text/html; charset=utf-8",
2123
+ "Cache-Control": "no-store"
2124
+ });
2125
+ res.end(
2126
+ `<!doctype html><meta name="viewport" content="width=device-width"><body style="font:16px system-ui;padding:2rem;max-width:40rem">${body}</body>`
2127
+ );
2128
+ }
2129
+ async function readBody(req) {
2130
+ if (req.method === "GET" || req.method === "HEAD") return void 0;
2131
+ const chunks = [];
2132
+ for await (const chunk of req) chunks.push(chunk);
2133
+ return new Uint8Array(Buffer.concat(chunks));
2134
+ }
2135
+ function isLoopback(host) {
2136
+ return host === "127.0.0.1" || host === "localhost" || host === "::1" || host === "[::1]";
2137
+ }
2138
+ async function startHttpProxy(opts) {
2139
+ const { runtime } = opts;
2140
+ const host = opts.host ?? "127.0.0.1";
2141
+ const path = opts.path ?? "/mcp";
2142
+ const token = opts.token ?? crypto.randomUUID().replace(/-/g, "");
2143
+ const kill = new FileKillSwitch(runtime.loaded.killPath);
2144
+ const holder = {};
2145
+ const server = createServer((req, res) => {
2146
+ void (async () => {
2147
+ const url2 = new URL(req.url ?? "/", `http://${req.headers.host ?? host}`);
2148
+ const authorized = () => {
2149
+ const q = url2.searchParams.get("token");
2150
+ const h = req.headers.authorization?.replace(/^Bearer\s+/i, "");
2151
+ return q === token || h === token;
2152
+ };
2153
+ if (url2.pathname === path && holder.mcp) {
2154
+ const headers = new Headers();
2155
+ for (const [k, v] of Object.entries(req.headers)) {
2156
+ if (Array.isArray(v)) for (const item of v) headers.append(k, item);
2157
+ else if (typeof v === "string") headers.set(k, v);
2158
+ }
2159
+ const controller = new AbortController();
2160
+ res.on("close", () => {
2161
+ if (!res.writableFinished) controller.abort();
2162
+ });
2163
+ const response = await holder.mcp(
2164
+ new Request(url2, {
2165
+ method: req.method ?? "GET",
2166
+ headers,
2167
+ body: await readBody(req),
2168
+ signal: controller.signal
2169
+ })
2170
+ );
2171
+ const out = {};
2172
+ response.headers.forEach((v, k) => {
2173
+ out[k] = v;
2174
+ });
2175
+ res.writeHead(response.status, out);
2176
+ if (!response.body) return res.end();
2177
+ const reader = response.body.getReader();
2178
+ try {
2179
+ for (; ; ) {
2180
+ const { done, value } = await reader.read();
2181
+ if (done) break;
2182
+ res.write(value);
2183
+ }
2184
+ } catch {
2185
+ }
2186
+ return res.end();
2187
+ }
2188
+ if (url2.pathname === "/health") {
2189
+ const killed = await runtime.guard.killState();
2190
+ return json(res, 200, {
2191
+ ok: true,
2192
+ name: "agentguard",
2193
+ version: PROXY_VERSION,
2194
+ mode: runtime.policy.mode,
2195
+ killed: killed.killed,
2196
+ upstreams: runtime.upstreamStatus(),
2197
+ tools: runtime.listTools().length,
2198
+ mcp: `${url2.origin}${path}`
2199
+ });
2200
+ }
2201
+ if (url2.pathname === "/status") {
2202
+ if (!authorized()) return json(res, 401, unauthorized());
2203
+ const runId = url2.searchParams.get("run") ?? runtime.defaultRunId;
2204
+ return json(res, 200, { runId, ...await runtime.guard.status(runId) });
2205
+ }
2206
+ if (url2.pathname === "/kill" && req.method === "POST") {
2207
+ if (!authorized()) return json(res, 401, unauthorized());
2208
+ const body = await readBody(req);
2209
+ let reason = "killed via HTTP";
2210
+ try {
2211
+ const parsed = JSON.parse(Buffer.from(body ?? []).toString("utf8") || "{}");
2212
+ if (parsed.reason) reason = String(parsed.reason).slice(0, 200);
2213
+ } catch {
2214
+ }
2215
+ kill.kill(reason);
2216
+ await runtime.guard.halt(reason);
2217
+ return json(res, 200, { killed: true, reason });
2218
+ }
2219
+ if (url2.pathname === "/resume" && req.method === "POST") {
2220
+ if (!authorized()) return json(res, 401, unauthorized());
2221
+ kill.resume();
2222
+ await runtime.guard.resume();
2223
+ return json(res, 200, { killed: false });
2224
+ }
2225
+ const decision = /^\/(approve|deny)\/([A-Za-z0-9_-]+)$/.exec(url2.pathname);
2226
+ if (decision) {
2227
+ if (!authorized()) return json(res, 401, unauthorized());
2228
+ const [, action, id] = decision;
2229
+ const record = await runtime.guard.decide(
2230
+ id,
2231
+ action === "approve" ? "approved" : "denied",
2232
+ "http"
2233
+ );
2234
+ if (!record)
2235
+ return json(res, 404, {
2236
+ code: "NOT_FOUND",
2237
+ cause: `no approval ${id}`,
2238
+ fix: "list pending approvals with `agentguard approvals`",
2239
+ retryable: false
2240
+ });
2241
+ if (req.headers.accept?.includes("text/html")) {
2242
+ return html(
2243
+ res,
2244
+ 200,
2245
+ `<h1>${record.status === "approved" ? "Approved" : record.status === "denied" ? "Denied" : record.status}</h1><p><code>${record.tool}</code> \u2014 approval <code>${record.id}</code>.</p><p>The agent may now retry the call once.</p>`
2246
+ );
2247
+ }
2248
+ return json(res, 200, record);
2249
+ }
2250
+ if (url2.pathname === "/approvals") {
2251
+ if (!authorized()) return json(res, 401, unauthorized());
2252
+ return json(res, 200, {
2253
+ approvals: await runtime.guard.approvals.list(
2254
+ url2.searchParams.get("all") ? void 0 : "pending"
2255
+ )
2256
+ });
2257
+ }
2258
+ return json(res, 404, {
2259
+ code: "NOT_FOUND",
2260
+ cause: `no route for ${url2.pathname}`,
2261
+ fix: `POST JSON-RPC to ${path}; GET /health`,
2262
+ retryable: false
2263
+ });
2264
+ })().catch((err) => {
2265
+ if (!res.headersSent)
2266
+ json(res, 500, {
2267
+ code: "INTERNAL",
2268
+ cause: err instanceof Error ? err.message : String(err),
2269
+ fix: "retry; if it persists, report the request",
2270
+ retryable: false
2271
+ });
2272
+ else res.end();
2273
+ });
2274
+ });
2275
+ await new Promise((resolve5, reject) => {
2276
+ server.once("error", reject);
2277
+ server.listen(opts.port ?? 8788, host, () => resolve5());
2278
+ });
2279
+ const address = server.address();
2280
+ const port = typeof address === "object" && address ? address.port : opts.port ?? 8788;
2281
+ holder.mcp = createHttpHandler(() => createDownstreamServer(runtime), {
2282
+ sessions: true,
2283
+ jsonResponse: false,
2284
+ cors: true,
2285
+ allowedHosts: isLoopback(host) ? localhostHosts(port) : void 0,
2286
+ enableDnsRebindingProtection: isLoopback(host)
2287
+ });
2288
+ const baseUrl = `http://${host}:${port}`;
2289
+ const url = `${baseUrl}${path}`;
2290
+ const regFile = registrationPath(runtime.loaded.stateDir);
2291
+ if (opts.registerFile !== false) {
2292
+ mkdirSync(runtime.loaded.stateDir, { recursive: true });
2293
+ const reg = {
2294
+ url,
2295
+ baseUrl,
2296
+ token,
2297
+ pid: process.pid,
2298
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
2299
+ };
2300
+ writeFileSync(regFile, JSON.stringify(reg, null, 2));
2301
+ }
2302
+ return {
2303
+ url,
2304
+ port,
2305
+ token,
2306
+ close: () => new Promise((resolve5, reject) => {
2307
+ if (opts.registerFile !== false && existsSync(regFile)) {
2308
+ try {
2309
+ unlinkSync(regFile);
2310
+ } catch {
2311
+ }
2312
+ }
2313
+ server.closeAllConnections();
2314
+ server.close((err) => err ? reject(err) : resolve5());
2315
+ })
2316
+ };
2317
+ }
2318
+ function unauthorized() {
2319
+ return {
2320
+ code: "UNAUTHORIZED",
2321
+ cause: "missing or wrong control token",
2322
+ fix: "pass ?token=<token> or Authorization: Bearer <token> from <state>/http.json",
2323
+ retryable: false
2324
+ };
2325
+ }
2326
+
2327
+ // ../../node_modules/.pnpm/postal-mime@2.7.5/node_modules/postal-mime/src/decode-strings.js
2328
+ var textEncoder = new TextEncoder();
2329
+ var base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2330
+ var base64Lookup = new Uint8Array(256);
2331
+ for (let i = 0; i < base64Chars.length; i++) {
2332
+ base64Lookup[base64Chars.charCodeAt(i)] = i;
2333
+ }
2334
+ var charsetAliases = /* @__PURE__ */ new Map([
2335
+ ["iso-8859-8-i", "iso-8859-8"],
2336
+ ["iso-8859-8-e", "iso-8859-8"]
2337
+ ]);
2338
+ function getDecoder(charset) {
2339
+ charset = (charset || "utf8").trim().toLowerCase();
2340
+ charset = charsetAliases.get(charset) || charset;
2341
+ let decoder;
2342
+ try {
2343
+ decoder = new TextDecoder(charset);
2344
+ } catch (err) {
2345
+ decoder = new TextDecoder("windows-1252");
2346
+ }
2347
+ return decoder;
2348
+ }
2349
+
2350
+ // ../../node_modules/.pnpm/postal-mime@2.7.5/node_modules/postal-mime/src/mime-node.js
2351
+ var defaultDecoder = getDecoder();
2352
+
2353
+ // ../../node_modules/.pnpm/postal-mime@2.7.5/node_modules/postal-mime/src/postal-mime.js
2354
+ var MAX_HEADERS_SIZE = 2 * 1024 * 1024;
2355
+
2356
+ // ../../node_modules/.pnpm/resend@6.25.0/node_modules/resend/dist/index.mjs
2357
+ var import_standardwebhooks = __toESM(require_dist(), 1);
2358
+ var version = "6.25.0";
2359
+ var defaultUserAgent = `resend-node:${version}`;
2360
+
2361
+ // ../../packages/notify/dist/index.js
2362
+ var palette = {
2363
+ bg: "#f4f5f7",
2364
+ card: "#ffffff",
2365
+ text: "#111827",
2366
+ muted: "#6b7280",
2367
+ border: "#e5e7eb",
2368
+ link: "#2563eb",
2369
+ codeBg: "#f3f4f6",
2370
+ red: "#b91c1c",
2371
+ redBg: "#fef2f2",
2372
+ green: "#15803d",
2373
+ greenBg: "#f0fdf4",
2374
+ amber: "#b45309",
2375
+ amberBg: "#fffbeb",
2376
+ gray: "#374151",
2377
+ grayBg: "#f3f4f6"
2378
+ };
2379
+ var fontStack = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif";
2380
+ var baseText = `font-family:${fontStack};font-size:15px;line-height:1.55;color:${palette.text};`;
2381
+ var USER_AGENT = "agentwares-notify/0.1";
2382
+ function errorMessage(e) {
2383
+ if (e instanceof Error) return e.message;
2384
+ return typeof e === "string" ? e : "unknown error";
2385
+ }
2386
+ async function readBody2(res) {
2387
+ try {
2388
+ return (await res.text()).slice(0, 500);
2389
+ } catch {
2390
+ return "";
2391
+ }
2392
+ }
2393
+ function defaultFetch() {
2394
+ return typeof fetch === "function" ? (input, init) => fetch(input, init) : void 0;
2395
+ }
2396
+ async function postJson(url, body, headers, fetchImpl) {
2397
+ if (!fetchImpl) return { error: "fetch is not available" };
2398
+ try {
2399
+ const res = await fetchImpl(url, {
2400
+ method: "POST",
2401
+ headers: { "Content-Type": "application/json", "User-Agent": USER_AGENT, ...headers },
2402
+ body: typeof body === "string" ? body : JSON.stringify(body)
2403
+ });
2404
+ return { res, text: await readBody2(res) };
2405
+ } catch (e) {
2406
+ return { error: errorMessage(e) };
2407
+ }
2408
+ }
2409
+ async function sendSlack(webhookUrl, message, opts = {}) {
2410
+ const r = await postJson(webhookUrl, message, {}, opts.fetch ?? defaultFetch());
2411
+ if ("error" in r) return { ok: false, error: r.error };
2412
+ return r.res.ok ? { ok: true, status: r.res.status } : { ok: false, status: r.res.status, error: r.text || r.res.statusText || "slack error" };
2413
+ }
2414
+ var SIGNATURE_HEADER = "X-Agentwares-Signature";
2415
+ var encoder = new TextEncoder();
2416
+ function toHex(bytes) {
2417
+ return Array.from(new Uint8Array(bytes), (b) => b.toString(16).padStart(2, "0")).join("");
2418
+ }
2419
+ async function hmacSha256Hex(secret, body) {
2420
+ const key = await crypto.subtle.importKey(
2421
+ "raw",
2422
+ encoder.encode(secret),
2423
+ { name: "HMAC", hash: "SHA-256" },
2424
+ false,
2425
+ ["sign"]
2426
+ );
2427
+ return toHex(await crypto.subtle.sign("HMAC", key, encoder.encode(body)));
2428
+ }
2429
+ async function sendWebhook(url, payload, opts = {}) {
2430
+ const body = JSON.stringify(payload);
2431
+ const headers = { ...opts.headers ?? {} };
2432
+ if (opts.secret) headers[SIGNATURE_HEADER] = `sha256=${await hmacSha256Hex(opts.secret, body)}`;
2433
+ const r = await postJson(url, body, headers, opts.fetch ?? defaultFetch());
2434
+ if ("error" in r) return { ok: false, error: r.error };
2435
+ return r.res.ok ? { ok: true, status: r.res.status } : { ok: false, status: r.res.status, error: r.text || r.res.statusText || "webhook error" };
2436
+ }
2437
+
2438
+ // src/notify.ts
2439
+ function approvalSlackMessage(event, url) {
2440
+ const a = event.approval;
2441
+ const argsText = JSON.stringify(a.args ?? {}, null, 2).slice(0, 1500);
2442
+ const text = `[agentguard] approval needed: ${a.tool} (run ${a.run_id}) \u2014 run \`agentguard approve ${a.id}\`${url ? ` or open ${url}` : ""}`;
2443
+ const blocks = [
2444
+ { type: "header", text: { type: "plain_text", text: `Approval needed: ${a.tool}` } },
2445
+ {
2446
+ type: "section",
2447
+ text: {
2448
+ type: "mrkdwn",
2449
+ text: `*run* \`${a.run_id}\`${a.agent ? ` \xB7 *agent* \`${a.agent}\`` : ""}${a.upstream ? ` \xB7 *upstream* \`${a.upstream}\`` : ""}
2450
+ \`\`\`${argsText}\`\`\``
2451
+ }
2452
+ },
2453
+ {
2454
+ type: "context",
2455
+ elements: [
2456
+ {
2457
+ type: "mrkdwn",
2458
+ text: `Approve: \`agentguard approve ${a.id}\` \xB7 Deny: \`agentguard deny ${a.id}\` \xB7 expires ${a.expires_at}`
2459
+ }
2460
+ ]
2461
+ }
2462
+ ];
2463
+ if (url) {
2464
+ blocks.push({
2465
+ type: "actions",
2466
+ elements: [
2467
+ { type: "button", text: { type: "plain_text", text: "Approve" }, style: "primary", url },
2468
+ {
2469
+ type: "button",
2470
+ text: { type: "plain_text", text: "Deny" },
2471
+ style: "danger",
2472
+ url: url.replace("/approve/", "/deny/")
2473
+ }
2474
+ ]
2475
+ });
2476
+ }
2477
+ return { text, blocks };
2478
+ }
2479
+ function haltSlackMessage(event) {
2480
+ const cause = event.error?.cause ?? "";
2481
+ const text = `[agentguard] ${event.type}: ${event.tool} (run ${event.runId}) \u2014 ${cause}`;
2482
+ return {
2483
+ text,
2484
+ blocks: [
2485
+ {
2486
+ type: "section",
2487
+ text: {
2488
+ type: "mrkdwn",
2489
+ text: `*${event.type}* on \`${event.tool}\` (run \`${event.runId}\`)
2490
+ ${cause}`
2491
+ }
2492
+ },
2493
+ {
2494
+ type: "context",
2495
+ elements: [
2496
+ {
2497
+ type: "mrkdwn",
2498
+ text: `\`agentguard report --run ${event.runId}\` for the full picture${event.type === "KILLED" ? "" : " \xB7 `agentguard kill` to stop everything"}`
2499
+ }
2500
+ ]
2501
+ }
2502
+ ]
2503
+ };
2504
+ }
2505
+ function createAlerter(opts) {
2506
+ const { policy } = opts;
2507
+ const log = opts.log ?? (() => void 0);
2508
+ const channel = { fetch: opts.fetch };
2509
+ return async (event) => {
2510
+ if (event.type === "APPROVAL_DECIDED") return;
2511
+ if (!policy.alerts.on.includes(event.type)) return;
2512
+ const slack = event.type === "APPROVAL_REQUIRED" ? policy.approval.notify.slack || policy.alerts.slack : policy.alerts.slack;
2513
+ const webhook = event.type === "APPROVAL_REQUIRED" ? policy.approval.notify.webhook || policy.alerts.webhook : policy.alerts.webhook;
2514
+ const url = event.approval ? opts.approvalUrl?.(event.approval) : void 0;
2515
+ const message = event.type === "APPROVAL_REQUIRED" && event.approval ? approvalSlackMessage(event, url) : haltSlackMessage(event);
2516
+ const jobs = [];
2517
+ if (slack) {
2518
+ jobs.push(
2519
+ sendSlack(slack, message, channel).then((r) => {
2520
+ if (!r.ok) log(`slack alert failed: ${r.error ?? r.status}`);
2521
+ })
2522
+ );
2523
+ }
2524
+ if (webhook) {
2525
+ jobs.push(
2526
+ sendWebhook(
2527
+ webhook,
2528
+ {
2529
+ event: event.type,
2530
+ product: "agentguard",
2531
+ subject: message.text,
2532
+ summary: event.error?.cause ?? "",
2533
+ occurredAt: event.at,
2534
+ data: {
2535
+ runId: event.runId,
2536
+ tool: event.tool,
2537
+ error: event.error,
2538
+ approval: event.approval,
2539
+ approveUrl: url
2540
+ }
2541
+ },
2542
+ channel
2543
+ ).then((r) => {
2544
+ if (!r.ok) log(`webhook alert failed: ${r.error ?? r.status}`);
2545
+ })
2546
+ );
2547
+ }
2548
+ await Promise.all(jobs);
2549
+ };
2550
+ }
2551
+
2552
+ // src/context.ts
2553
+ import { homedir } from "os";
2554
+ var DEFAULT_POLICY_FILE = "agentguard.yaml";
2555
+ function homeFrom(io) {
2556
+ return io.env.HOME ?? io.env.USERPROFILE ?? homedir();
2557
+ }
2558
+ function defaultIo() {
2559
+ return {
2560
+ out: (line) => process.stdout.write(line + "\n"),
2561
+ err: (line) => process.stderr.write(line + "\n"),
2562
+ cwd: process.cwd(),
2563
+ env: process.env,
2564
+ tty: process.stdout.isTTY === true
2565
+ };
2566
+ }
2567
+ function policyPathFrom(args, io) {
2568
+ return resolve(
2569
+ io.cwd,
2570
+ flagString(args, "config") ?? io.env.AGENTGUARD_CONFIG ?? DEFAULT_POLICY_FILE
2571
+ );
2572
+ }
2573
+ function loadPolicyFor(args, io, opts = {}) {
2574
+ return loadPolicyFile(policyPathFrom(args, io), {
2575
+ env: io.env,
2576
+ allowMissingEnv: opts.allowMissingEnv
2577
+ });
2578
+ }
2579
+ function createFileGuard(loaded, opts = {}) {
2580
+ const kill = new FileKillSwitch2(loaded.killPath);
2581
+ const audit = new FileAuditSink(loaded.auditPath);
2582
+ const approvals = new FileApprovalStore(loaded.stateDir);
2583
+ const alerter = opts.alerts === false ? void 0 : createAlerter({ policy: loaded.policy, approvalUrl: opts.approvalUrl, log: opts.log });
2584
+ const guard = new Guard({
2585
+ policy: loaded.policy,
2586
+ state: new FileStateStore(loaded.stateDir),
2587
+ audit,
2588
+ kill,
2589
+ approvals,
2590
+ env: opts.env ?? process.env,
2591
+ approvalUrl: opts.approvalUrl,
2592
+ onEvent: async (event) => {
2593
+ await opts.onEvent?.(event);
2594
+ await alerter?.(event);
2595
+ }
2596
+ });
2597
+ return { guard, kill, audit, approvals, loaded };
2598
+ }
2599
+ function readRegistration(loaded) {
2600
+ const file = registrationPath(loaded.stateDir);
2601
+ if (!exists(file)) return void 0;
2602
+ try {
2603
+ const reg = JSON.parse(readFileSync(file, "utf8"));
2604
+ return reg;
2605
+ } catch {
2606
+ return void 0;
2607
+ }
2608
+ }
2609
+ function describeError(err) {
2610
+ if (err && typeof err === "object" && "code" in err && "cause" in err) {
2611
+ const e = err;
2612
+ return `${e.code}: ${e.cause}${e.fix ? `
2613
+ fix: ${e.fix}` : ""}`;
2614
+ }
2615
+ return err instanceof Error ? err.message : String(err);
2616
+ }
2617
+
2618
+ // src/commands/control.ts
2619
+ async function pokeHttp(io, reg, path, body) {
2620
+ if (!reg) return;
2621
+ try {
2622
+ const res = await fetch(`${reg.baseUrl}${path}`, {
2623
+ method: "POST",
2624
+ headers: { Authorization: `Bearer ${reg.token}`, "Content-Type": "application/json" },
2625
+ body: body === void 0 ? void 0 : JSON.stringify(body),
2626
+ signal: AbortSignal.timeout(2e3)
2627
+ });
2628
+ if (!res.ok)
2629
+ io.err(
2630
+ `running proxy at ${reg.baseUrl} answered ${res.status} (the file switch still applies)`
2631
+ );
2632
+ } catch {
2633
+ io.err(
2634
+ `no proxy answering at ${reg.baseUrl} (stale ${reg.pid}?) \u2014 the file switch still applies`
2635
+ );
2636
+ }
2637
+ }
2638
+ async function killCommand(args, io) {
2639
+ const loaded = loadPolicyFor(args, io, { allowMissingEnv: true });
2640
+ const { kill } = createFileGuard(loaded, { alerts: false, env: io.env });
2641
+ const reason = args.positionals.join(" ") || flagString(args, "reason") || `agentguard kill by ${io.env.USER ?? "operator"}`;
2642
+ kill.kill(reason);
2643
+ await pokeHttp(io, readRegistration(loaded), "/kill", { reason });
2644
+ io.out(
2645
+ `KILLED: ${kill.path} written \u2014 every tool call through this policy now returns KILLED. Clear with: agentguard resume`
2646
+ );
2647
+ return 0;
2648
+ }
2649
+ async function resumeCommand(args, io) {
2650
+ const loaded = loadPolicyFor(args, io, { allowMissingEnv: true });
2651
+ const { kill } = createFileGuard(loaded, { alerts: false, env: io.env });
2652
+ kill.resume();
2653
+ await pokeHttp(io, readRegistration(loaded), "/resume");
2654
+ io.out(`resumed: ${kill.path} removed`);
2655
+ if (io.env[loaded.policy.kill.env])
2656
+ io.err(
2657
+ `note: ${loaded.policy.kill.env} is set in this shell's environment; proxies started from it stay killed`
2658
+ );
2659
+ return 0;
2660
+ }
2661
+ async function decideCommand(decision, args, io) {
2662
+ const id = args.positionals[0];
2663
+ if (!id) {
2664
+ io.err(
2665
+ `usage: agentguard ${decision === "approved" ? "approve" : "deny"} <approval-id> [--by name] [--note text]`
2666
+ );
2667
+ return 1;
2668
+ }
2669
+ const loaded = loadPolicyFor(args, io, { allowMissingEnv: true });
2670
+ const { guard } = createFileGuard(loaded, { alerts: false, env: io.env });
2671
+ const record = await guard.decide(
2672
+ id,
2673
+ decision,
2674
+ flagString(args, "by") ?? io.env.USER ?? "cli",
2675
+ flagString(args, "note")
2676
+ );
2677
+ if (!record) {
2678
+ io.err(`no approval ${id} \u2014 pending ones: agentguard approvals`);
2679
+ return 1;
2680
+ }
2681
+ io.out(
2682
+ `${record.status}: ${record.tool} (${record.id}, run ${record.run_id})${record.status === "approved" ? " \u2014 the agent may now retry the identical call once" : ""}`
2683
+ );
2684
+ return 0;
2685
+ }
2686
+ async function approvalsCommand(args, io) {
2687
+ const loaded = loadPolicyFor(args, io, { allowMissingEnv: true });
2688
+ const { guard } = createFileGuard(loaded, { alerts: false, env: io.env });
2689
+ const list = await guard.approvals.list(flagBool(args, "all") ? void 0 : "pending");
2690
+ if (flagBool(args, "json")) {
2691
+ io.out(JSON.stringify(list, null, 2));
2692
+ return 0;
2693
+ }
2694
+ if (list.length === 0) {
2695
+ io.out(flagBool(args, "all") ? "no approvals recorded" : "no pending approvals");
2696
+ return 0;
2697
+ }
2698
+ for (const r of list) {
2699
+ io.out(
2700
+ `${r.id} ${r.status.padEnd(9)} ${r.tool} run ${r.run_id}${r.agent ? ` agent ${r.agent}` : ""} expires ${r.expires_at}`
2701
+ );
2702
+ io.out(` args: ${JSON.stringify(r.args).slice(0, 300)}`);
2703
+ if (r.status === "pending")
2704
+ io.out(` \u2192 agentguard approve ${r.id} | agentguard deny ${r.id}`);
2705
+ }
2706
+ return 0;
2707
+ }
2708
+
2709
+ // src/commands/init.ts
2710
+ import { existsSync as existsSync4, writeFileSync as writeFileSync3 } from "fs";
2711
+ import { relative, resolve as resolve3 } from "path";
2712
+ import YAML from "yaml";
2713
+
2714
+ // src/configs.ts
2715
+ import { copyFileSync, existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
2716
+ import { homedir as homedir2, platform } from "os";
2717
+ import { join as join2, resolve as resolve2 } from "path";
2718
+ function candidateConfigs(cwd = process.cwd(), home = homedir2(), env = process.env) {
2719
+ const desktop = platform() === "darwin" ? join2(home, "Library", "Application Support", "Claude", "claude_desktop_config.json") : platform() === "win32" ? join2(
2720
+ env.APPDATA ?? join2(home, "AppData", "Roaming"),
2721
+ "Claude",
2722
+ "claude_desktop_config.json"
2723
+ ) : join2(
2724
+ env.XDG_CONFIG_HOME ?? join2(home, ".config"),
2725
+ "Claude",
2726
+ "claude_desktop_config.json"
2727
+ );
2728
+ return [
2729
+ {
2730
+ kind: "claude-code-project",
2731
+ label: "Claude Code (project .mcp.json)",
2732
+ path: join2(cwd, ".mcp.json"),
2733
+ serversKey: "mcpServers"
2734
+ },
2735
+ {
2736
+ kind: "cursor-project",
2737
+ label: "Cursor (project)",
2738
+ path: join2(cwd, ".cursor", "mcp.json"),
2739
+ serversKey: "mcpServers"
2740
+ },
2741
+ {
2742
+ kind: "vscode",
2743
+ label: "VS Code (project)",
2744
+ path: join2(cwd, ".vscode", "mcp.json"),
2745
+ serversKey: "servers"
2746
+ },
2747
+ { kind: "generic", label: "mcp.json", path: join2(cwd, "mcp.json"), serversKey: "mcpServers" },
2748
+ { kind: "claude-desktop", label: "Claude Desktop", path: desktop, serversKey: "mcpServers" },
2749
+ {
2750
+ kind: "cursor-user",
2751
+ label: "Cursor (user)",
2752
+ path: join2(home, ".cursor", "mcp.json"),
2753
+ serversKey: "mcpServers"
2754
+ },
2755
+ {
2756
+ kind: "windsurf",
2757
+ label: "Windsurf",
2758
+ path: join2(home, ".codeium", "windsurf", "mcp_config.json"),
2759
+ serversKey: "mcpServers"
2760
+ },
2761
+ {
2762
+ kind: "claude-code-user",
2763
+ label: "Claude Code (user ~/.claude.json)",
2764
+ path: join2(home, ".claude.json"),
2765
+ serversKey: "mcpServers"
2766
+ }
2767
+ ];
2768
+ }
2769
+ function detectClientConfigs(cwd, home, env) {
2770
+ return candidateConfigs(cwd, home, env).filter((c) => existsSync3(c.path));
2771
+ }
2772
+ function describeConfigPath(path) {
2773
+ const abs = resolve2(path);
2774
+ const known = candidateConfigs().find((c) => c.path === abs);
2775
+ if (known) return known;
2776
+ let serversKey = "mcpServers";
2777
+ try {
2778
+ const parsed = JSON.parse(readFileSync2(abs, "utf8"));
2779
+ if (parsed.servers && !parsed.mcpServers) serversKey = "servers";
2780
+ } catch {
2781
+ }
2782
+ return {
2783
+ kind: abs.includes(".vscode") ? "vscode" : "generic",
2784
+ label: abs,
2785
+ path: abs,
2786
+ serversKey
2787
+ };
2788
+ }
2789
+ function readServers(file) {
2790
+ const raw = JSON.parse(readFileSync2(file.path, "utf8"));
2791
+ const servers = raw[file.serversKey];
2792
+ if (!servers || typeof servers !== "object") return {};
2793
+ return servers;
2794
+ }
2795
+ var PROXY_SERVER_NAME = "agentguard";
2796
+ function isProxyEntry(entry) {
2797
+ return (entry.args ?? []).some((a) => a === "agentguard" || /(^|\/)agentguard(\.js)?$/.test(a)) && (entry.args ?? []).includes("proxy");
2798
+ }
2799
+ function serversToUpstreams(servers) {
2800
+ const upstreams = [];
2801
+ const skipped = [];
2802
+ for (const [rawName, entry] of Object.entries(servers)) {
2803
+ if (isProxyEntry(entry)) {
2804
+ skipped.push({ name: rawName, reason: "already the agentguard proxy" });
2805
+ continue;
2806
+ }
2807
+ const name = rawName.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/^[^a-zA-Z0-9]+/, "") || "upstream";
2808
+ if (entry.url) {
2809
+ upstreams.push({
2810
+ name,
2811
+ url: entry.url,
2812
+ headers: entry.headers ?? {},
2813
+ args: [],
2814
+ env: {},
2815
+ prefix: false,
2816
+ timeout_ms: 6e4
2817
+ });
2818
+ } else if (entry.command) {
2819
+ upstreams.push({
2820
+ name,
2821
+ command: entry.command,
2822
+ args: entry.args ?? [],
2823
+ env: entry.env ?? {},
2824
+ cwd: entry.cwd,
2825
+ headers: {},
2826
+ prefix: false,
2827
+ timeout_ms: 6e4
2828
+ });
2829
+ } else {
2830
+ skipped.push({ name: rawName, reason: "no `command` or `url`" });
2831
+ }
2832
+ }
2833
+ return { upstreams, skipped };
2834
+ }
2835
+ function backupPath(file) {
2836
+ return `${file.path}.agentguard-backup`;
2837
+ }
2838
+ function proxyEntry(file, policyPath, opts = {}) {
2839
+ const args = ["-y", "agentguard", "proxy", "--config", policyPath];
2840
+ if (opts.agent) args.push("--agent", opts.agent);
2841
+ const entry = { command: "npx", args };
2842
+ if (file.serversKey === "servers") entry.type = "stdio";
2843
+ return entry;
2844
+ }
2845
+ function rewriteClientConfig(file, policyPath, opts = {}) {
2846
+ const raw = JSON.parse(readFileSync2(file.path, "utf8"));
2847
+ const servers = raw[file.serversKey] ?? {};
2848
+ const replaced = Object.keys(servers).filter((n) => !isProxyEntry(servers[n]));
2849
+ const backup = backupPath(file);
2850
+ if (!existsSync3(backup)) copyFileSync(file.path, backup);
2851
+ raw[file.serversKey] = { [PROXY_SERVER_NAME]: proxyEntry(file, policyPath, opts) };
2852
+ writeFileSync2(file.path, JSON.stringify(raw, null, 2) + "\n");
2853
+ return { backup, replaced };
2854
+ }
2855
+ function restoreClientConfig(file) {
2856
+ const backup = backupPath(file);
2857
+ if (!existsSync3(backup)) return false;
2858
+ copyFileSync(backup, file.path);
2859
+ return true;
2860
+ }
2861
+
2862
+ // src/commands/tools.ts
2863
+ import {
2864
+ Guard as Guard2,
2865
+ classifyTool as classifyTool2,
2866
+ parsePolicy
2867
+ } from "@agentwares/agentguard-core";
2868
+
2869
+ // src/proxy/runtime.ts
2870
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2871
+ import {
2872
+ getDefaultEnvironment,
2873
+ StdioClientTransport
2874
+ } from "@modelcontextprotocol/sdk/client/stdio.js";
2875
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
2876
+ import {
2877
+ CreateMessageRequestSchema,
2878
+ ElicitRequestSchema,
2879
+ LoggingMessageNotificationSchema,
2880
+ PromptListChangedNotificationSchema,
2881
+ ResourceListChangedNotificationSchema,
2882
+ ResourceUpdatedNotificationSchema,
2883
+ ToolListChangedNotificationSchema
2884
+ } from "@modelcontextprotocol/sdk/types.js";
2885
+ import {
2886
+ GuardError,
2887
+ classifyTool,
2888
+ upstreamHeaders
2889
+ } from "@agentwares/agentguard-core";
2890
+ var ProxyRuntime = class {
2891
+ loaded;
2892
+ guard;
2893
+ agent;
2894
+ defaultRunId;
2895
+ sessions = /* @__PURE__ */ new Set();
2896
+ connections = /* @__PURE__ */ new Map();
2897
+ exposed = [];
2898
+ resourceOwner = /* @__PURE__ */ new Map();
2899
+ templateOwner = /* @__PURE__ */ new Map();
2900
+ promptOwner = /* @__PURE__ */ new Map();
2901
+ log;
2902
+ connectTimeoutMs;
2903
+ testClients;
2904
+ /** stop relaying upstream stderr once we are tearing down — shutdown noise is not the user's problem */
2905
+ closing = false;
2906
+ constructor(opts) {
2907
+ this.loaded = opts.loaded;
2908
+ this.guard = opts.guard;
2909
+ this.agent = opts.agent;
2910
+ this.defaultRunId = opts.defaultRunId;
2911
+ this.log = opts.log ?? ((line) => process.stderr.write(`[agentguard] ${line}
2912
+ `));
2913
+ this.connectTimeoutMs = opts.connectTimeoutMs ?? 15e3;
2914
+ this.testClients = opts.clients ?? {};
2915
+ }
2916
+ get policy() {
2917
+ return this.loaded.policy;
2918
+ }
2919
+ async start() {
2920
+ await Promise.all(this.policy.upstreams.map((u) => this.connect(u)));
2921
+ for (const [name, client] of Object.entries(this.testClients)) {
2922
+ if (this.connections.has(name)) continue;
2923
+ const upstream = {
2924
+ name,
2925
+ command: "in-memory",
2926
+ args: [],
2927
+ env: {},
2928
+ headers: {},
2929
+ prefix: false,
2930
+ timeout_ms: 6e4
2931
+ };
2932
+ const conn = {
2933
+ upstream,
2934
+ client,
2935
+ tools: [],
2936
+ capabilities: client.getServerCapabilities(),
2937
+ connected: true
2938
+ };
2939
+ this.connections.set(name, conn);
2940
+ this.wire(conn);
2941
+ await this.refreshTools(name);
2942
+ }
2943
+ this.rebuildExposed();
2944
+ const status = this.upstreamStatus();
2945
+ this.log(
2946
+ `connected ${status.filter((s) => s.connected).length}/${status.length} upstreams, ${this.exposed.length} tools, mode ${this.policy.mode}`
2947
+ );
2948
+ }
2949
+ async connect(upstream) {
2950
+ const client = new Client(
2951
+ { name: "agentguard", version: "0.1.0" },
2952
+ { capabilities: { sampling: {}, elicitation: {} } }
2953
+ );
2954
+ const conn = {
2955
+ upstream,
2956
+ client,
2957
+ tools: [],
2958
+ capabilities: void 0,
2959
+ connected: false
2960
+ };
2961
+ this.connections.set(upstream.name, conn);
2962
+ try {
2963
+ const transport = upstream.url ? new StreamableHTTPClientTransport(new URL(upstream.url), {
2964
+ requestInit: { headers: upstreamHeaders(upstream) }
2965
+ }) : new StdioClientTransport({
2966
+ command: upstream.command,
2967
+ args: upstream.args,
2968
+ env: { ...getDefaultEnvironment(), ...upstream.env },
2969
+ cwd: upstream.cwd,
2970
+ stderr: "pipe"
2971
+ });
2972
+ if (transport instanceof StdioClientTransport) {
2973
+ transport.stderr?.on("data", (chunk) => {
2974
+ if (this.closing) return;
2975
+ for (const line of chunk.toString("utf8").split("\n"))
2976
+ if (line.trim()) this.log(`${upstream.name}: ${line}`);
2977
+ });
2978
+ }
2979
+ await withTimeout(
2980
+ client.connect(transport),
2981
+ this.connectTimeoutMs,
2982
+ `connect to upstream "${upstream.name}"`
2983
+ );
2984
+ conn.capabilities = client.getServerCapabilities();
2985
+ conn.connected = true;
2986
+ this.wire(conn);
2987
+ await this.refreshTools(upstream.name);
2988
+ } catch (err) {
2989
+ conn.connected = false;
2990
+ conn.error = err instanceof Error ? err.message : String(err);
2991
+ this.log(`upstream "${upstream.name}" unavailable: ${conn.error}`);
2992
+ }
2993
+ }
2994
+ wire(conn) {
2995
+ const { client, upstream } = conn;
2996
+ client.onclose = () => {
2997
+ conn.connected = false;
2998
+ conn.error = "connection closed";
2999
+ if (!this.closing) this.log(`upstream "${upstream.name}" closed`);
3000
+ };
3001
+ client.setNotificationHandler(LoggingMessageNotificationSchema, async (n) => {
3002
+ await this.broadcast(upstream.name, n.method, n.params);
3003
+ });
3004
+ client.setNotificationHandler(ResourceUpdatedNotificationSchema, async (n) => {
3005
+ await this.broadcast(upstream.name, n.method, n.params);
3006
+ });
3007
+ client.setNotificationHandler(ResourceListChangedNotificationSchema, async () => {
3008
+ this.resourceOwner.clear();
3009
+ await this.broadcast(upstream.name, "notifications/resources/list_changed", void 0);
3010
+ });
3011
+ client.setNotificationHandler(PromptListChangedNotificationSchema, async () => {
3012
+ this.promptOwner.clear();
3013
+ await this.broadcast(upstream.name, "notifications/prompts/list_changed", void 0);
3014
+ });
3015
+ client.setNotificationHandler(ToolListChangedNotificationSchema, async () => {
3016
+ await this.refreshTools(upstream.name);
3017
+ this.rebuildExposed();
3018
+ for (const s of this.sessions) s.server.server.sendToolListChanged().catch(() => void 0);
3019
+ });
3020
+ try {
3021
+ client.setRequestHandler(CreateMessageRequestSchema, async (req) => {
3022
+ const session = this.pickSession(upstream.name);
3023
+ if (!session) throw new Error("no downstream client is connected to relay sampling to");
3024
+ const related = session.inflight.get(upstream.name)?.at(-1);
3025
+ return session.server.server.createMessage(req.params, { relatedRequestId: related });
3026
+ });
3027
+ client.setRequestHandler(ElicitRequestSchema, async (req) => {
3028
+ const session = this.pickSession(upstream.name);
3029
+ if (!session) throw new Error("no downstream client is connected to relay elicitation to");
3030
+ const related = session.inflight.get(upstream.name)?.at(-1);
3031
+ return session.server.server.elicitInput(req.params, { relatedRequestId: related });
3032
+ });
3033
+ } catch {
3034
+ }
3035
+ }
3036
+ pickSession(upstream) {
3037
+ let best;
3038
+ for (const s of this.sessions) {
3039
+ if ((s.inflight.get(upstream)?.length ?? 0) > 0) return s;
3040
+ if (!best || s.lastActive > best.lastActive) best = s;
3041
+ }
3042
+ return best;
3043
+ }
3044
+ async broadcast(upstream, method, params) {
3045
+ for (const s of this.sessions) {
3046
+ const related = s.inflight.get(upstream)?.at(-1);
3047
+ try {
3048
+ await s.server.server.notification(
3049
+ { method, params },
3050
+ related !== void 0 ? { relatedRequestId: related } : void 0
3051
+ );
3052
+ } catch {
3053
+ }
3054
+ }
3055
+ }
3056
+ async refreshTools(name) {
3057
+ const conn = this.connections.get(name);
3058
+ if (!conn?.connected) return;
3059
+ if (conn.capabilities && !conn.capabilities.tools) {
3060
+ conn.tools = [];
3061
+ return;
3062
+ }
3063
+ try {
3064
+ const tools = [];
3065
+ let cursor;
3066
+ do {
3067
+ const page = await conn.client.listTools(cursor ? { cursor } : void 0, {
3068
+ timeout: conn.upstream.timeout_ms
3069
+ });
3070
+ tools.push(...page.tools);
3071
+ cursor = page.nextCursor;
3072
+ } while (cursor);
3073
+ conn.tools = tools;
3074
+ } catch (err) {
3075
+ this.log(
3076
+ `tools/list failed for "${name}": ${err instanceof Error ? err.message : String(err)}`
3077
+ );
3078
+ conn.tools = [];
3079
+ }
3080
+ }
3081
+ rebuildExposed() {
3082
+ const taken = /* @__PURE__ */ new Set();
3083
+ const out = [];
3084
+ const order = [...this.policy.upstreams.map((u) => u.name), ...Object.keys(this.testClients)];
3085
+ for (const name of order) {
3086
+ const conn = this.connections.get(name);
3087
+ if (!conn) continue;
3088
+ const prefix = conn.upstream.prefix === true ? name : typeof conn.upstream.prefix === "string" ? conn.upstream.prefix : void 0;
3089
+ for (const tool of conn.tools) {
3090
+ let exposedName = prefix ? `${prefix}__${tool.name}` : tool.name;
3091
+ if (taken.has(exposedName)) exposedName = `${name}__${tool.name}`;
3092
+ if (taken.has(exposedName)) {
3093
+ this.log(`skipping duplicate tool ${exposedName} from ${name}`);
3094
+ continue;
3095
+ }
3096
+ taken.add(exposedName);
3097
+ out.push({
3098
+ name: exposedName,
3099
+ upstream: name,
3100
+ original: tool.name,
3101
+ tool,
3102
+ classification: classifyTool(
3103
+ {
3104
+ name: exposedName,
3105
+ description: tool.description,
3106
+ annotations: tool.annotations,
3107
+ inputSchema: tool.inputSchema,
3108
+ outputSchema: tool.outputSchema
3109
+ },
3110
+ this.policy
3111
+ )
3112
+ });
3113
+ }
3114
+ }
3115
+ this.exposed = out;
3116
+ }
3117
+ listTools(agent) {
3118
+ if (agent?.upstreams) return this.exposed.filter((t) => agent.upstreams.includes(t.upstream));
3119
+ return this.exposed;
3120
+ }
3121
+ findTool(name) {
3122
+ return this.exposed.find((t) => t.name === name);
3123
+ }
3124
+ upstreamStatus() {
3125
+ return [...this.connections.values()].map((c) => ({
3126
+ name: c.upstream.name,
3127
+ connected: c.connected,
3128
+ tools: c.tools.length,
3129
+ transport: c.upstream.url ? "http" : "stdio",
3130
+ error: c.error
3131
+ }));
3132
+ }
3133
+ attach(session) {
3134
+ this.sessions.add(session);
3135
+ }
3136
+ detach(session) {
3137
+ this.sessions.delete(session);
3138
+ }
3139
+ /** Run a tool call through the policy engine and, if allowed, the upstream. */
3140
+ async callTool(name, args, ctx) {
3141
+ const tool = this.findTool(name);
3142
+ const agent = ctx.agent === void 0 ? this.agent : ctx.agent;
3143
+ if (!tool) {
3144
+ const result2 = await this.guard.run(
3145
+ { tool: { name }, args, runId: ctx.runId, agent, sessionId: ctx.sessionId },
3146
+ async () => {
3147
+ throw new GuardError({
3148
+ code: "UNKNOWN_TOOL",
3149
+ cause: `no upstream exposes a tool named "${name}"`,
3150
+ fix: "call tools/list and use one of the listed names",
3151
+ retryable: false
3152
+ });
3153
+ }
3154
+ );
3155
+ return { result: result2 };
3156
+ }
3157
+ const conn = this.connections.get(tool.upstream);
3158
+ const track = () => {
3159
+ if (!ctx.session || ctx.requestId === void 0) return () => void 0;
3160
+ const list = ctx.session.inflight.get(tool.upstream) ?? [];
3161
+ list.push(ctx.requestId);
3162
+ ctx.session.inflight.set(tool.upstream, list);
3163
+ return () => {
3164
+ const l = ctx.session.inflight.get(tool.upstream) ?? [];
3165
+ const i = l.indexOf(ctx.requestId);
3166
+ if (i >= 0) l.splice(i, 1);
3167
+ };
3168
+ };
3169
+ const result = await this.guard.run(
3170
+ {
3171
+ tool: {
3172
+ name: tool.name,
3173
+ description: tool.tool.description,
3174
+ annotations: tool.tool.annotations,
3175
+ inputSchema: tool.tool.inputSchema,
3176
+ outputSchema: tool.tool.outputSchema
3177
+ },
3178
+ args,
3179
+ runId: ctx.runId,
3180
+ agent,
3181
+ sessionId: ctx.sessionId,
3182
+ upstream: tool.upstream
3183
+ },
3184
+ async (finalArgs) => {
3185
+ if (!conn?.connected) {
3186
+ throw new GuardError({
3187
+ code: "UPSTREAM_ERROR",
3188
+ cause: `upstream "${tool.upstream}" is not connected${conn?.error ? ` (${conn.error})` : ""}`,
3189
+ fix: "check the upstream command/url in agentguard.yaml and restart the proxy",
3190
+ retryable: true
3191
+ });
3192
+ }
3193
+ const untrack = track();
3194
+ try {
3195
+ return await conn.client.callTool(
3196
+ { name: tool.original, arguments: finalArgs ?? {} },
3197
+ void 0,
3198
+ {
3199
+ timeout: conn.upstream.timeout_ms,
3200
+ resetTimeoutOnProgress: true,
3201
+ onprogress: ctx.onprogress
3202
+ }
3203
+ );
3204
+ } finally {
3205
+ untrack();
3206
+ }
3207
+ }
3208
+ );
3209
+ return { result, tool };
3210
+ }
3211
+ connectedWith(capability) {
3212
+ return [...this.connections.values()].filter(
3213
+ (c) => c.connected && (c.capabilities?.[capability] || c.capabilities === void 0)
3214
+ );
3215
+ }
3216
+ async listResources() {
3217
+ const resources = [];
3218
+ for (const conn of this.connectedWith("resources")) {
3219
+ try {
3220
+ const page = await conn.client.listResources(void 0, {
3221
+ timeout: conn.upstream.timeout_ms
3222
+ });
3223
+ for (const r of page.resources) {
3224
+ this.resourceOwner.set(r.uri, conn.upstream.name);
3225
+ resources.push(r);
3226
+ }
3227
+ } catch {
3228
+ }
3229
+ }
3230
+ return { resources };
3231
+ }
3232
+ async listResourceTemplates() {
3233
+ const resourceTemplates = [];
3234
+ for (const conn of this.connectedWith("resources")) {
3235
+ try {
3236
+ const page = await conn.client.listResourceTemplates(void 0, {
3237
+ timeout: conn.upstream.timeout_ms
3238
+ });
3239
+ for (const t of page.resourceTemplates) {
3240
+ this.templateOwner.set(t.uriTemplate, conn.upstream.name);
3241
+ resourceTemplates.push(t);
3242
+ }
3243
+ } catch {
3244
+ }
3245
+ }
3246
+ return { resourceTemplates };
3247
+ }
3248
+ ownerForUri(uri) {
3249
+ const owner = this.resourceOwner.get(uri);
3250
+ if (owner && this.connections.get(owner)?.connected) return [this.connections.get(owner)];
3251
+ for (const [template, name] of this.templateOwner) {
3252
+ if (templateMatches(template, uri) && this.connections.get(name)?.connected)
3253
+ return [this.connections.get(name)];
3254
+ }
3255
+ return this.connectedWith("resources");
3256
+ }
3257
+ async readResource(uri) {
3258
+ let lastError;
3259
+ for (const conn of this.ownerForUri(uri)) {
3260
+ try {
3261
+ const result = await conn.client.readResource(
3262
+ { uri },
3263
+ { timeout: conn.upstream.timeout_ms }
3264
+ );
3265
+ this.resourceOwner.set(uri, conn.upstream.name);
3266
+ return result;
3267
+ } catch (err) {
3268
+ lastError = err;
3269
+ }
3270
+ }
3271
+ throw lastError ?? new Error(`no upstream serves resource ${uri}`);
3272
+ }
3273
+ async subscribe(uri, on) {
3274
+ for (const conn of this.ownerForUri(uri)) {
3275
+ try {
3276
+ if (on) await conn.client.subscribeResource({ uri });
3277
+ else await conn.client.unsubscribeResource({ uri });
3278
+ return;
3279
+ } catch {
3280
+ }
3281
+ }
3282
+ }
3283
+ async listPrompts() {
3284
+ const prompts = [];
3285
+ const taken = /* @__PURE__ */ new Set();
3286
+ this.promptOwner.clear();
3287
+ for (const conn of this.connectedWith("prompts")) {
3288
+ try {
3289
+ const page = await conn.client.listPrompts(void 0, {
3290
+ timeout: conn.upstream.timeout_ms
3291
+ });
3292
+ for (const p of page.prompts) {
3293
+ const name = taken.has(p.name) ? `${conn.upstream.name}__${p.name}` : p.name;
3294
+ taken.add(name);
3295
+ this.promptOwner.set(name, { upstream: conn.upstream.name, original: p.name });
3296
+ prompts.push({ ...p, name });
3297
+ }
3298
+ } catch {
3299
+ }
3300
+ }
3301
+ return { prompts };
3302
+ }
3303
+ async getPrompt(name, args) {
3304
+ if (this.promptOwner.size === 0) await this.listPrompts();
3305
+ const owner = this.promptOwner.get(name);
3306
+ const conns = owner ? [this.connections.get(owner.upstream)] : this.connectedWith("prompts");
3307
+ let lastError;
3308
+ for (const conn of conns) {
3309
+ try {
3310
+ return await conn.client.getPrompt(
3311
+ { name: owner?.original ?? name, arguments: args },
3312
+ { timeout: conn.upstream.timeout_ms }
3313
+ );
3314
+ } catch (err) {
3315
+ lastError = err;
3316
+ }
3317
+ }
3318
+ throw lastError ?? new Error(`no upstream serves prompt ${name}`);
3319
+ }
3320
+ async complete(params) {
3321
+ let conns;
3322
+ if (params.ref.type === "ref/prompt") {
3323
+ if (this.promptOwner.size === 0) await this.listPrompts();
3324
+ const owner = this.promptOwner.get(params.ref.name);
3325
+ conns = owner ? [this.connections.get(owner.upstream)] : this.connectedWith("completions");
3326
+ if (owner) params = { ...params, ref: { ...params.ref, name: owner.original } };
3327
+ } else {
3328
+ if (this.templateOwner.size === 0) await this.listResourceTemplates();
3329
+ conns = this.ownerForUri(params.ref.uri);
3330
+ }
3331
+ let lastError;
3332
+ for (const conn of conns) {
3333
+ try {
3334
+ return await conn.client.complete(params, { timeout: conn.upstream.timeout_ms });
3335
+ } catch (err) {
3336
+ lastError = err;
3337
+ }
3338
+ }
3339
+ throw lastError ?? new Error("no upstream supports completions");
3340
+ }
3341
+ async setLevel(level) {
3342
+ await Promise.all(
3343
+ this.connectedWith("logging").map(
3344
+ (c) => c.client.setLoggingLevel(level).catch(() => void 0)
3345
+ )
3346
+ );
3347
+ }
3348
+ async close() {
3349
+ this.closing = true;
3350
+ for (const conn of this.connections.values()) {
3351
+ try {
3352
+ await conn.client.close();
3353
+ } catch {
3354
+ }
3355
+ }
3356
+ this.connections.clear();
3357
+ }
3358
+ };
3359
+ function templateMatches(template, uri) {
3360
+ const re = new RegExp(
3361
+ "^" + template.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\{[^}]+\\\}/g, "[^/]+") + "$"
3362
+ );
3363
+ return re.test(uri);
3364
+ }
3365
+ async function withTimeout(promise, ms, what) {
3366
+ let timer;
3367
+ const timeout = new Promise((_, reject) => {
3368
+ timer = setTimeout(() => reject(new Error(`${what} timed out after ${ms}ms`)), ms);
3369
+ });
3370
+ try {
3371
+ return await Promise.race([promise, timeout]);
3372
+ } finally {
3373
+ if (timer) clearTimeout(timer);
3374
+ }
3375
+ }
3376
+
3377
+ // src/commands/tools.ts
3378
+ async function probeUpstreams(upstreams, opts = {}) {
3379
+ const policy = opts.policy ?? parsePolicy({ upstreams });
3380
+ const loaded = {
3381
+ policy: { ...policy, upstreams },
3382
+ path: "",
3383
+ baseDir: process.cwd(),
3384
+ stateDir: "",
3385
+ auditPath: "",
3386
+ killPath: ""
3387
+ };
3388
+ const guard = new Guard2({ policy: loaded.policy, env: opts.env ?? {} });
3389
+ const runtime = new ProxyRuntime({
3390
+ loaded,
3391
+ guard,
3392
+ defaultRunId: "probe",
3393
+ log: opts.log ?? (() => void 0),
3394
+ connectTimeoutMs: opts.timeoutMs ?? 15e3
3395
+ });
3396
+ try {
3397
+ await runtime.start();
3398
+ return {
3399
+ tools: runtime.listTools().map((t) => ({
3400
+ upstream: t.upstream,
3401
+ name: t.name,
3402
+ tool: t.tool,
3403
+ classification: classifyTool2(
3404
+ { name: t.name, annotations: t.tool.annotations },
3405
+ loaded.policy
3406
+ )
3407
+ })),
3408
+ upstreams: runtime.upstreamStatus()
3409
+ };
3410
+ } finally {
3411
+ await runtime.close();
3412
+ }
3413
+ }
3414
+ function formatToolTable(tools) {
3415
+ const width = Math.min(48, Math.max(12, ...tools.map((t) => t.name.length)));
3416
+ const lines = [
3417
+ `${"tool".padEnd(width)} ${"class".padEnd(8)}${"verb".padEnd(9)}${"upstream".padEnd(14)}why`
3418
+ ];
3419
+ for (const t of tools) {
3420
+ const c = t.classification;
3421
+ lines.push(
3422
+ `${t.name.padEnd(width)} ${c.class.padEnd(8)}${c.verb.padEnd(9)}${t.upstream.padEnd(14)}${c.source}: ${c.reason}`
3423
+ );
3424
+ }
3425
+ return lines;
3426
+ }
3427
+ async function toolsCommand(args, io) {
3428
+ const loaded = loadPolicyFor(args, io);
3429
+ const result = await probeUpstreams(loaded.policy.upstreams, {
3430
+ policy: loaded.policy,
3431
+ log: io.err,
3432
+ env: io.env
3433
+ });
3434
+ if (flagBool(args, "json")) {
3435
+ io.out(
3436
+ JSON.stringify(
3437
+ {
3438
+ upstreams: result.upstreams,
3439
+ tools: result.tools.map((t) => ({
3440
+ name: t.name,
3441
+ upstream: t.upstream,
3442
+ ...t.classification
3443
+ }))
3444
+ },
3445
+ null,
3446
+ 2
3447
+ )
3448
+ );
3449
+ return 0;
3450
+ }
3451
+ for (const u of result.upstreams)
3452
+ io.err(
3453
+ `${u.connected ? "ok " : "down"} ${u.name}: ${u.connected ? `${u.tools} tools` : u.error}`
3454
+ );
3455
+ for (const line of formatToolTable(result.tools)) io.out(line);
3456
+ const counts = result.tools.reduce(
3457
+ (acc, t) => ({ ...acc, [t.classification.class]: (acc[t.classification.class] ?? 0) + 1 }),
3458
+ {}
3459
+ );
3460
+ io.err(
3461
+ `
3462
+ ${result.tools.length} tools: ${Object.entries(counts).map(([k, v]) => `${v} ${k}`).join(", ")} \xB7 mode ${loaded.policy.mode}`
3463
+ );
3464
+ return result.upstreams.every((u) => u.connected) ? 0 : 2;
3465
+ }
3466
+
3467
+ // src/commands/init.ts
3468
+ function dump(value, indent = 0) {
3469
+ const text = YAML.stringify(value, { indent: 2, lineWidth: 100 }).trimEnd();
3470
+ const pad = " ".repeat(indent);
3471
+ return text.split("\n").map((l) => pad + l).join("\n");
3472
+ }
3473
+ function flow(list) {
3474
+ return list.length === 0 ? "[]" : `[${list.map((s) => JSON.stringify(s)).join(", ")}]`;
3475
+ }
3476
+ function renderStarterPolicy(opts) {
3477
+ const tools = opts.tools ?? [];
3478
+ const unknown = tools.filter((t) => t.classification.class === "unknown").map((t) => t.name);
3479
+ const destructive = tools.filter((t) => t.classification.destructive || t.classification.verb === "delete").map((t) => t.name);
3480
+ const spend = tools.filter((t) => t.classification.class === "spend").map((t) => t.name);
3481
+ const date = (opts.generatedAt ?? /* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3482
+ const counts = tools.reduce(
3483
+ (acc, t) => ({ ...acc, [t.classification.class]: (acc[t.classification.class] ?? 0) + 1 }),
3484
+ {}
3485
+ );
3486
+ const detected = tools.length === 0 ? ["# (no upstreams probed \u2014 run `agentguard tools` after fixing the upstreams above)"] : tools.map(
3487
+ (t) => `# ${t.name.padEnd(36)} ${t.classification.class.padEnd(8)} ${t.classification.verb.padEnd(8)} ${t.classification.source}: ${t.classification.reason}`
3488
+ );
3489
+ const probeNotes = (opts.probeErrors ?? []).map((e) => `# ! ${e}`);
3490
+ const upstreamsYaml = opts.upstreams.length ? dump({ upstreams: opts.upstreams.map(cleanUpstream) }) : 'upstreams: []\n # - name: crm\n # url: https://mcp.example.com/mcp\n # auth: ${CRM_TOKEN}\n # - name: files\n # command: npx\n # args: [-y, "@modelcontextprotocol/server-filesystem", "."]';
3491
+ return `# agentguard.yaml \u2014 generated by \`agentguard init\` on ${date}
3492
+ # Every knob is explained inline. Docs: https://github.com/agentwares/agentwares/tree/main/apps/agentguard-cli#policy
3493
+ version: 1
3494
+
3495
+ # dry-run: reads pass through; writes and spends are FAKED (plausible success, nothing executes)
3496
+ # and recorded. Run your agent once, then \`agentguard report\` and \`agentguard diff\`.
3497
+ # Flip to \`enforce\` when the diff looks right. Caps and the loop breaker apply in both modes.
3498
+ mode: ${opts.mode ?? "dry-run"}
3499
+
3500
+ # The servers your agent used to talk to directly. agentguard connects to them and holds their
3501
+ # credentials; your agent only talks to agentguard. Use \${ENV_VAR} for secrets.
3502
+ ${upstreamsYaml}
3503
+
3504
+ # Detected tools (${tools.length}${tools.length ? `: ${Object.entries(counts).map(([k, v]) => `${v} ${k}`).join(", ")}` : ""}):
3505
+ ${[...detected, ...probeNotes].join("\n")}
3506
+ # readOnlyHint annotations and verb heuristics classify most tools. List anything that is wrong here;
3507
+ # these patterns win over annotations. \`*\` matches anything.
3508
+ classify:
3509
+ read: []
3510
+ write: ${flow(unknown)}${unknown.length ? " # could not be classified \u2014 treated as writes; move to read: if they only read" : ""}
3511
+ spend: ${flow(spend)}
3512
+ unknown: write # a tool nobody can classify counts as a write (alternatives: read | block)
3513
+
3514
+ # Blast-radius caps. The call that would exceed a cap gets CAP_EXCEEDED with the remaining budget.
3515
+ # \`writes\` counts every write or spend; \`deletes\`/\`emails\` count the subsets matched by \`counters\`.
3516
+ caps:
3517
+ per_run:
3518
+ tool_calls: 400
3519
+ writes: 50
3520
+ deletes: 10
3521
+ emails: 5
3522
+ spend_usd: 25
3523
+ per_day:
3524
+ spend_usd: 200
3525
+
3526
+ # Dollar amounts. Declare how to read the amount from a spend tool's arguments; results reporting
3527
+ # cost_usd / amount_usd are picked up automatically. Unknown spend tools cost \`default_usd\`.
3528
+ spend:
3529
+ tools: {}
3530
+ # stripe_create_charge: { amount_arg: amount, divisor: 100, currency_arg: currency }
3531
+ # openai_*: { fixed_usd: 0.01 }
3532
+ default_usd: 0
3533
+
3534
+ # Semantic loop breaker: the same (tool, normalized arguments) ${3} times in the last 30 calls, or a
3535
+ # repeating A\u2192B\u2192A\u2192B cycle, returns LOOP_DETECTED. Timestamps, ids and whitespace are ignored.
3536
+ loop:
3537
+ window: 30
3538
+ max_repeats: 3
3539
+ max_cycle_len: 4
3540
+ max_read_repeats: 10
3541
+
3542
+ # Always fake these, even in enforce mode. Remove entries as you gain trust.
3543
+ dry_run:
3544
+ tools: []
3545
+ synthesize: true
3546
+
3547
+ # Destructive actions that need a human. The agent gets APPROVAL_REQUIRED with an id; you run
3548
+ # \`agentguard approve <id>\` (or click the link in Slack); the agent retries the identical call once.
3549
+ approval:
3550
+ tools: ${flow(destructive)}
3551
+ wait_s: 0 # > 0 holds the call open this long waiting for the decision
3552
+ ttl_s: 3600
3553
+ # notify:
3554
+ # slack: \${SLACK_WEBHOOK_URL}
3555
+
3556
+ # Kill switch: \`agentguard kill\` creates this file (or POST /kill on the HTTP proxy); every call
3557
+ # returns KILLED until \`agentguard resume\`. AGENTGUARD_KILL=1 in the environment does the same.
3558
+ kill:
3559
+ file: .agentguard/KILL
3560
+
3561
+ # Scoped agent keys for the HTTP proxy: \`agentguard key create deployer --allow 'crm_get_*'\`.
3562
+ # Only the key's hash is stored here; the agent sends the key as a Bearer token.
3563
+ agents: []
3564
+
3565
+ # Alerts on halts (Slack incoming webhook or any HTTPS endpoint; signed with X-Agentwares-Signature).
3566
+ # alerts:
3567
+ # slack: \${SLACK_WEBHOOK_URL}
3568
+ # on: [LOOP_DETECTED, CAP_EXCEEDED, KILLED, APPROVAL_REQUIRED]
3569
+
3570
+ # Hash-chained JSONL of every call; \`agentguard verify\` proves it was not edited.
3571
+ audit:
3572
+ path: .agentguard/audit.jsonl
3573
+ redact: true
3574
+ include_results: false
3575
+ `;
3576
+ }
3577
+ function cleanUpstream(u) {
3578
+ const out = { name: u.name };
3579
+ if (u.url) {
3580
+ out.url = u.url;
3581
+ if (Object.keys(u.headers).length) out.headers = u.headers;
3582
+ } else {
3583
+ out.command = u.command;
3584
+ if (u.args.length) out.args = u.args;
3585
+ if (Object.keys(u.env).length) out.env = u.env;
3586
+ if (u.cwd) out.cwd = u.cwd;
3587
+ }
3588
+ return out;
3589
+ }
3590
+ async function initCommand(args, io) {
3591
+ const policyPath = policyPathFrom(args, io);
3592
+ const explicit = flagString(args, "client");
3593
+ const configs = explicit ? [describeConfigPath(resolve3(io.cwd, explicit))] : detectClientConfigs(io.cwd, homeFrom(io), io.env);
3594
+ const rel = (p) => {
3595
+ const r = relative(io.cwd, p);
3596
+ return r.startsWith("..") ? p : r || ".";
3597
+ };
3598
+ if (flagBool(args, "undo")) {
3599
+ let restored = 0;
3600
+ for (const c of configs) {
3601
+ if (restoreClientConfig(c)) {
3602
+ io.out(`restored ${rel(c.path)} from backup`);
3603
+ restored += 1;
3604
+ }
3605
+ }
3606
+ if (restored === 0)
3607
+ io.err("nothing to undo (no *.agentguard-backup files next to your MCP configs)");
3608
+ return 0;
3609
+ }
3610
+ const projectLevel = configs.filter((c) => c.path.startsWith(io.cwd + "/"));
3611
+ const userLevel = configs.filter((c) => !projectLevel.includes(c));
3612
+ let chosen;
3613
+ if (explicit) chosen = configs;
3614
+ else if (flagBool(args, "all")) chosen = configs;
3615
+ else chosen = projectLevel.slice(0, 1);
3616
+ if (!explicit && !flagBool(args, "all")) {
3617
+ for (const c of userLevel)
3618
+ io.out(
3619
+ `found ${c.path} (${c.label}, user-level) \u2014 not touched; pass --client ${JSON.stringify(c.path)} to route it through agentguard`
3620
+ );
3621
+ }
3622
+ if (!chosen.every((c) => existsSync4(c.path))) {
3623
+ io.err(`no MCP config at ${chosen.map((c) => c.path).join(", ")}`);
3624
+ return 1;
3625
+ }
3626
+ const servers = {};
3627
+ for (const c of chosen) {
3628
+ const found = readServers(c);
3629
+ io.out(
3630
+ `found ${rel(c.path)} (${c.label}) \u2192 ${Object.keys(found).length} server${Object.keys(found).length === 1 ? "" : "s"}: ${Object.keys(found).join(", ") || "none"}`
3631
+ );
3632
+ for (const [name, entry] of Object.entries(found))
3633
+ if (!(name in servers)) servers[name] = entry;
3634
+ }
3635
+ if (chosen.length === 0)
3636
+ io.out(
3637
+ "found no MCP config (Claude Code .mcp.json, Cursor, VS Code, Claude Desktop, Windsurf) \u2014 writing a policy with no upstreams"
3638
+ );
3639
+ const { upstreams, skipped } = serversToUpstreams(servers);
3640
+ for (const s of skipped) io.err(`skipped ${s.name}: ${s.reason}`);
3641
+ if (skipped.some((s) => s.reason.includes("already")) && upstreams.length === 0 && existsSync4(policyPath)) {
3642
+ io.out(
3643
+ `already initialized: ${rel(policyPath)} exists and ${chosen.map((c) => rel(c.path)).join(", ")} already points at agentguard`
3644
+ );
3645
+ return 0;
3646
+ }
3647
+ let probed = [];
3648
+ const probeErrors = [];
3649
+ if (flagBool(args, "probe") !== false && upstreams.length > 0) {
3650
+ io.out(`probing ${upstreams.length} upstream${upstreams.length === 1 ? "" : "s"}\u2026`);
3651
+ const result = await probeUpstreams(upstreams, {
3652
+ log: () => void 0,
3653
+ env: io.env,
3654
+ timeoutMs: 2e4
3655
+ });
3656
+ probed = result.tools;
3657
+ for (const u of result.upstreams) {
3658
+ if (u.connected) io.out(` ${u.name}: ${u.tools} tools`);
3659
+ else {
3660
+ io.err(
3661
+ ` ${u.name}: could not connect (${u.error}) \u2014 the proxy will keep retrying at start; fix the entry in agentguard.yaml`
3662
+ );
3663
+ probeErrors.push(`${u.name}: ${u.error}`);
3664
+ }
3665
+ }
3666
+ }
3667
+ const mode = flagString(args, "mode") ?? "dry-run";
3668
+ if (existsSync4(policyPath) && !flagBool(args, "force")) {
3669
+ io.out(`kept ${rel(policyPath)} (exists; pass --force to regenerate)`);
3670
+ } else {
3671
+ writeFileSync3(policyPath, renderStarterPolicy({ upstreams, tools: probed, mode, probeErrors }));
3672
+ const counts = probed.reduce(
3673
+ (acc, t) => ({ ...acc, [t.classification.class]: (acc[t.classification.class] ?? 0) + 1 }),
3674
+ {}
3675
+ );
3676
+ io.out(
3677
+ `wrote ${rel(policyPath)} (mode: ${mode}, ${upstreams.length} upstream${upstreams.length === 1 ? "" : "s"}${probed.length ? `, ${probed.length} tools: ${Object.entries(counts).map(([k, v]) => `${v} ${k}`).join(", ")}` : ""})`
3678
+ );
3679
+ }
3680
+ if (flagBool(args, "rewrite") !== false) {
3681
+ for (const c of chosen) {
3682
+ const { backup, replaced } = rewriteClientConfig(c, policyPath, {
3683
+ agent: flagString(args, "agent")
3684
+ });
3685
+ io.out(
3686
+ `rewrote ${rel(c.path)} \u2192 ${replaced.length ? `${replaced.join(", ")} now go through` : "points at"} \`npx @agentwares/agentguard proxy\` (backup: ${rel(backup)})`
3687
+ );
3688
+ }
3689
+ }
3690
+ io.out("");
3691
+ io.out("Next:");
3692
+ io.out(
3693
+ ` 1. restart your MCP client so it re-reads ${chosen.map((c) => rel(c.path)).join(", ") || "its config"}`
3694
+ );
3695
+ io.out(
3696
+ ` 2. run your agent once \u2014 ${mode === "dry-run" ? "writes are faked; nothing executes upstream" : "enforce mode: caps, loop breaker and approvals are live"}`
3697
+ );
3698
+ io.out(" 3. agentguard report # what this run would have destroyed / spent");
3699
+ io.out(" agentguard diff # the record-by-record mutation diff");
3700
+ if (mode === "dry-run")
3701
+ io.out(` 4. set \`mode: enforce\` in ${rel(policyPath)} when it looks right`);
3702
+ io.out(" undo any time: agentguard init --undo");
3703
+ return 0;
3704
+ }
3705
+
3706
+ // src/commands/keys.ts
3707
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "fs";
3708
+ import YAML2, { isMap, isSeq, YAMLSeq } from "yaml";
3709
+ import { generateAgentKey, hashAgentKey } from "@agentwares/agentguard-core";
3710
+ async function keyCommand(args, io) {
3711
+ const [sub, name] = args.positionals;
3712
+ const path = policyPathFrom(args, io);
3713
+ const doc = YAML2.parseDocument(readFileSync3(path, "utf8"));
3714
+ let agents = doc.get("agents");
3715
+ if (!isSeq(agents)) {
3716
+ agents = new YAMLSeq();
3717
+ doc.set("agents", agents);
3718
+ }
3719
+ const seq = agents;
3720
+ const items = seq.items.filter(isMap);
3721
+ if (sub === "list" || sub === void 0) {
3722
+ if (items.length === 0)
3723
+ io.out(
3724
+ "no agents in agentguard.yaml \u2014 create one: agentguard key create <name> --allow 'crm_get_*'"
3725
+ );
3726
+ for (const a of items) {
3727
+ const allow2 = a.get("allow");
3728
+ io.out(
3729
+ `${String(a.get("name")).padEnd(20)} key: ${a.get("key_hash") ? "set" : "none (stdio --agent only)"} allow: ${isSeq(allow2) ? allow2.items.map(String).join(", ") : "all"}`
3730
+ );
3731
+ }
3732
+ return 0;
3733
+ }
3734
+ if (!name) {
3735
+ io.err(
3736
+ "usage: agentguard key create <agent> [--allow pattern]... [--deny pattern]... [--writes n] [--spend n] [--deletes n] [--emails n] [--tool-calls n] [--mode dry-run|enforce] [--upstream name]..."
3737
+ );
3738
+ return 1;
3739
+ }
3740
+ if (sub === "revoke") {
3741
+ const idx = items.findIndex((a) => a.get("name") === name);
3742
+ if (idx < 0) {
3743
+ io.err(`no agent "${name}"`);
3744
+ return 1;
3745
+ }
3746
+ seq.items.splice(seq.items.indexOf(items[idx]), 1);
3747
+ writeFileSync4(path, doc.toString());
3748
+ io.out(`revoked ${name}: removed from agentguard.yaml (running proxies pick it up on restart)`);
3749
+ return 0;
3750
+ }
3751
+ if (sub !== "create") {
3752
+ io.err(`unknown subcommand "${sub}" \u2014 use create, list or revoke`);
3753
+ return 1;
3754
+ }
3755
+ const key = generateAgentKey();
3756
+ const keyHash = await hashAgentKey(key);
3757
+ const existing = items.find((a) => a.get("name") === name);
3758
+ const entry = { name, key_hash: keyHash };
3759
+ const allow = flagList(args, "allow");
3760
+ const deny = flagList(args, "deny");
3761
+ if (allow.length) entry.allow = allow;
3762
+ if (deny.length) entry.deny = deny;
3763
+ const upstreams = flagList(args, "upstream");
3764
+ if (upstreams.length) entry.upstreams = upstreams;
3765
+ const mode = flagString(args, "mode");
3766
+ if (mode) entry.mode = mode;
3767
+ const perRun = {};
3768
+ for (const [flag, counter] of [
3769
+ ["writes", "writes"],
3770
+ ["spend", "spend_usd"],
3771
+ ["deletes", "deletes"],
3772
+ ["emails", "emails"],
3773
+ ["tool-calls", "tool_calls"]
3774
+ ]) {
3775
+ const n = flagNumber(args, flag);
3776
+ if (n !== void 0) perRun[counter] = n;
3777
+ }
3778
+ if (Object.keys(perRun).length) entry.caps = { per_run: perRun };
3779
+ if (existing) {
3780
+ for (const [k, v] of Object.entries(entry)) existing.set(k, v);
3781
+ } else {
3782
+ seq.add(doc.createNode(entry));
3783
+ }
3784
+ writeFileSync4(path, doc.toString());
3785
+ io.err(
3786
+ `${existing ? "rotated" : "created"} agent "${name}" in ${path}${allow.length ? ` (allow: ${allow.join(", ")})` : " (allow: all tools)"}`
3787
+ );
3788
+ io.err(
3789
+ "the key is shown once; give it to the agent as `Authorization: Bearer <key>` (HTTP) \u2014 over stdio use `agentguard proxy --agent " + name + "`"
3790
+ );
3791
+ io.out(key);
3792
+ return 0;
3793
+ }
3794
+
3795
+ // src/commands/permission-diff.ts
3796
+ import { execFileSync } from "child_process";
3797
+ import {
3798
+ diffPermissionFile,
3799
+ renderPermissionDiffMarkdown
3800
+ } from "@agentwares/agentguard-core";
3801
+ var DEFAULT_PERMISSION_PATHS = [
3802
+ "agentguard.yaml",
3803
+ "agentguard.yml",
3804
+ ".claude/settings.json",
3805
+ ".claude/settings.local.json",
3806
+ ".mcp.json",
3807
+ "mcp.json",
3808
+ ".cursor/mcp.json",
3809
+ ".vscode/mcp.json"
3810
+ ];
3811
+ var PERMISSION_FILE = /(^|\/)(agentguard[^/]*\.ya?ml|\.claude\/settings(\.local)?\.json|[^/]*mcp[^/]*\.json|claude_desktop_config\.json)$/;
3812
+ function git(cwd, argv) {
3813
+ try {
3814
+ return execFileSync("git", argv, {
3815
+ cwd,
3816
+ encoding: "utf8",
3817
+ stdio: ["ignore", "pipe", "ignore"]
3818
+ });
3819
+ } catch {
3820
+ return void 0;
3821
+ }
3822
+ }
3823
+ function showAt(cwd, ref, path) {
3824
+ return git(cwd, ["show", `${ref}:${path}`]);
3825
+ }
3826
+ function changedPermissionFiles(cwd, base, head) {
3827
+ const out = git(cwd, ["diff", "--name-only", base, head]) ?? "";
3828
+ return out.split("\n").map((l) => l.trim()).filter((l) => l && PERMISSION_FILE.test(l));
3829
+ }
3830
+ function resolveBase(cwd, explicit) {
3831
+ if (explicit) return explicit;
3832
+ for (const candidate of ["origin/main", "main", "origin/master", "master"]) {
3833
+ if (git(cwd, ["rev-parse", "--verify", "--quiet", candidate]) !== void 0) return candidate;
3834
+ }
3835
+ return "HEAD~1";
3836
+ }
3837
+ function runPermissionDiff(cwd, opts) {
3838
+ const base = resolveBase(cwd, opts.base);
3839
+ const head = opts.head ?? "HEAD";
3840
+ const files = [
3841
+ .../* @__PURE__ */ new Set([
3842
+ ...opts.paths ?? DEFAULT_PERMISSION_PATHS,
3843
+ ...changedPermissionFiles(cwd, base, head)
3844
+ ])
3845
+ ];
3846
+ const findings = [];
3847
+ const touched = [];
3848
+ for (const file of files) {
3849
+ const before = showAt(cwd, base, file);
3850
+ const after = showAt(cwd, head, file);
3851
+ if (before === void 0 && after === void 0) continue;
3852
+ if (before === after) continue;
3853
+ touched.push(file);
3854
+ findings.push(...diffPermissionFile(file, before, after));
3855
+ }
3856
+ return {
3857
+ base,
3858
+ head,
3859
+ files: touched,
3860
+ findings,
3861
+ markdown: renderPermissionDiffMarkdown(findings, { title: opts.title })
3862
+ };
3863
+ }
3864
+ async function permissionDiffCommand(args, io) {
3865
+ const paths = flagList(args, "paths");
3866
+ const result = runPermissionDiff(io.cwd, {
3867
+ base: flagString(args, "base"),
3868
+ head: flagString(args, "head"),
3869
+ paths: paths.length ? paths : void 0
3870
+ });
3871
+ const format = flagString(args, "format") ?? (flagBool(args, "json") ? "json" : "markdown");
3872
+ if (format === "json") io.out(JSON.stringify(result, null, 2));
3873
+ else io.out(result.markdown);
3874
+ const widen = result.findings.filter((f) => f.severity === "widen").length;
3875
+ if (flagBool(args, "fail-on-widen") && widen > 0) return 1;
3876
+ return 0;
3877
+ }
3878
+
3879
+ // src/commands/proxy.ts
3880
+ import { resolveAgent as resolveAgent2 } from "@agentwares/agentguard-core";
3881
+ function newRunId(now = /* @__PURE__ */ new Date()) {
3882
+ return `run_${now.toISOString().replace(/[-:.TZ]/g, "").slice(0, 14)}_${Math.random().toString(36).slice(2, 6)}`;
3883
+ }
3884
+ async function proxyCommand(args, io) {
3885
+ const loaded = loadPolicyFor(args, io);
3886
+ const log = (line) => io.err(`[agentguard] ${line}`);
3887
+ const modeOverride = flagString(args, "mode");
3888
+ if (modeOverride === "dry-run" || modeOverride === "enforce") loaded.policy.mode = modeOverride;
3889
+ const agentName = flagString(args, "agent");
3890
+ let agent = void 0;
3891
+ if (agentName) {
3892
+ const resolved = await resolveAgent2(loaded.policy, { name: agentName });
3893
+ if (resolved.error) {
3894
+ io.err(
3895
+ `unknown agent "${agentName}" \u2014 agents in agentguard.yaml: ${loaded.policy.agents.map((a) => a.name).join(", ") || "none"}`
3896
+ );
3897
+ return 1;
3898
+ }
3899
+ agent = resolved.agent;
3900
+ }
3901
+ const http = flagBool(args, "http") === true;
3902
+ let approvalBase;
3903
+ const fileGuard = createFileGuard(loaded, {
3904
+ log,
3905
+ env: io.env,
3906
+ approvalUrl: (r) => approvalBase ? `${approvalBase.baseUrl}/approve/${r.id}?token=${approvalBase.token}` : void 0,
3907
+ onEvent: (e) => {
3908
+ if (e.type === "APPROVAL_DECIDED") return;
3909
+ log(`${e.type} ${e.tool} (run ${e.runId})${e.error ? `: ${e.error.cause}` : ""}`);
3910
+ if (e.type === "APPROVAL_REQUIRED" && e.approval)
3911
+ log(`approve with: agentguard approve ${e.approval.id}`);
3912
+ }
3913
+ });
3914
+ const runtime = new ProxyRuntime({
3915
+ loaded,
3916
+ guard: fileGuard.guard,
3917
+ agent,
3918
+ defaultRunId: flagString(args, "run-id") ?? newRunId(),
3919
+ log
3920
+ });
3921
+ await runtime.start();
3922
+ const killed = await fileGuard.kill.check();
3923
+ if (killed.killed)
3924
+ log(
3925
+ `kill switch is ON (${killed.reason}); every call returns KILLED until \`agentguard resume\``
3926
+ );
3927
+ if (http) {
3928
+ const server2 = await startHttpProxy({
3929
+ runtime,
3930
+ port: flagNumber(args, "port") ?? 8788,
3931
+ host: flagString(args, "host") ?? "127.0.0.1",
3932
+ token: io.env.AGENTGUARD_HTTP_TOKEN
3933
+ });
3934
+ approvalBase = { baseUrl: server2.url.replace(/\/mcp$/, ""), token: server2.token };
3935
+ log(
3936
+ `Streamable HTTP at ${server2.url} \xB7 GET ${approvalBase.baseUrl}/health \xB7 control token in ${loaded.stateDir}/http.json`
3937
+ );
3938
+ log(
3939
+ `point your agent at ${server2.url} (send X-Run-Id per run; Authorization: Bearer <agent key> for scoped agents)`
3940
+ );
3941
+ const stop2 = async () => {
3942
+ await server2.close();
3943
+ await runtime.close();
3944
+ process.exit(0);
3945
+ };
3946
+ process.once("SIGINT", () => void stop2());
3947
+ process.once("SIGTERM", () => void stop2());
3948
+ await new Promise(() => void 0);
3949
+ }
3950
+ const server = createDownstreamServer(runtime);
3951
+ const stop = async () => {
3952
+ await runtime.close();
3953
+ };
3954
+ process.once("SIGINT", () => void stop().then(() => process.exit(0)));
3955
+ process.once("SIGTERM", () => void stop().then(() => process.exit(0)));
3956
+ await serveStdio(server);
3957
+ await stop();
3958
+ return 0;
3959
+ }
3960
+
3961
+ // src/commands/report.ts
3962
+ import { resolve as resolve4 } from "path";
3963
+ import {
3964
+ buildReport,
3965
+ latestRunId,
3966
+ listRuns,
3967
+ renderMutationDiff,
3968
+ renderReportMarkdown
3969
+ } from "@agentwares/agentguard-core";
3970
+ import { readAuditFile, verifyAuditFile } from "@agentwares/agentguard-core/node";
3971
+ function pickRun(args, entries) {
3972
+ if (flagBool(args, "all")) return void 0;
3973
+ return flagString(args, "run") ?? latestRunId(entries);
3974
+ }
3975
+ async function reportCommand(args, io) {
3976
+ const loaded = loadPolicyFor(args, io, { allowMissingEnv: true });
3977
+ const entries = readAuditFile(loaded.auditPath);
3978
+ const runId = pickRun(args, entries);
3979
+ const report = buildReport(entries, { runId });
3980
+ if (flagBool(args, "json")) {
3981
+ io.out(
3982
+ JSON.stringify(
3983
+ {
3984
+ ...report,
3985
+ mutations: report.mutations.map((m) => ({
3986
+ seq: m.seq,
3987
+ ts: m.ts,
3988
+ tool: m.tool,
3989
+ mutation: m.mutation
3990
+ }))
3991
+ },
3992
+ null,
3993
+ 2
3994
+ )
3995
+ );
3996
+ return 0;
3997
+ }
3998
+ io.out(renderReportMarkdown(report));
3999
+ const chain = await verifyAuditFile(loaded.auditPath);
4000
+ io.out(
4001
+ chain.ok ? `Audit chain: ${chain.entries} entries, verified (${loaded.auditPath}).` : `Audit chain BROKEN at entry ${chain.brokenAt}: ${chain.reason}`
4002
+ );
4003
+ if (runId && listRuns(entries).length > 1) io.out(`Other runs: agentguard report --all`);
4004
+ return 0;
4005
+ }
4006
+ async function diffCommand(args, io) {
4007
+ const loaded = loadPolicyFor(args, io, { allowMissingEnv: true });
4008
+ const entries = readAuditFile(loaded.auditPath);
4009
+ const runId = pickRun(args, entries);
4010
+ io.out(renderMutationDiff(entries, { runId }).trimEnd());
4011
+ return 0;
4012
+ }
4013
+ async function verifyCommand(args, io) {
4014
+ let path = args.positionals[0];
4015
+ if (!path) path = loadPolicyFor(args, io, { allowMissingEnv: true }).auditPath;
4016
+ path = resolve4(io.cwd, path);
4017
+ const result = await verifyAuditFile(path);
4018
+ if (flagBool(args, "json")) io.out(JSON.stringify({ path, ...result }));
4019
+ else if (result.ok)
4020
+ io.out(
4021
+ `ok: ${result.entries} entries, chain intact, head ${result.head?.slice(0, 16)}\u2026 (${path})`
4022
+ );
4023
+ else io.out(`BROKEN at entry ${result.brokenAt}: ${result.reason} (${path})`);
4024
+ return result.ok ? 0 : 1;
4025
+ }
4026
+ async function statusCommand(args, io) {
4027
+ const loaded = loadPolicyFor(args, io, { allowMissingEnv: true });
4028
+ const { guard, kill } = createFileGuard(loaded, { alerts: false, env: io.env });
4029
+ const entries = readAuditFile(loaded.auditPath);
4030
+ const runId = flagString(args, "run") ?? latestRunId(entries) ?? "(none yet)";
4031
+ const status = await guard.status(runId);
4032
+ const killed = kill.check();
4033
+ const reg = readRegistration(loaded);
4034
+ const pending = await guard.approvals.list("pending");
4035
+ if (flagBool(args, "json")) {
4036
+ io.out(
4037
+ JSON.stringify(
4038
+ {
4039
+ policy: loaded.path,
4040
+ mode: loaded.policy.mode,
4041
+ runId,
4042
+ ...status,
4043
+ killed,
4044
+ pendingApprovals: pending.length,
4045
+ http: reg ? { url: reg.url, pid: reg.pid } : void 0
4046
+ },
4047
+ null,
4048
+ 2
4049
+ )
4050
+ );
4051
+ return 0;
4052
+ }
4053
+ io.out(
4054
+ `policy ${loaded.path} (mode: ${loaded.policy.mode}, ${loaded.policy.upstreams.length} upstreams)`
4055
+ );
4056
+ io.out(
4057
+ `kill ${killed.killed ? `ON \u2014 ${killed.reason} (agentguard resume to clear)` : "off"}`
4058
+ );
4059
+ io.out(
4060
+ `http ${reg ? `${reg.url} (pid ${reg.pid})` : "not running (agentguard proxy --http)"}`
4061
+ );
4062
+ io.out(`audit ${entries.length} entries in ${loaded.auditPath}`);
4063
+ io.out(
4064
+ `pending ${pending.length} approval${pending.length === 1 ? "" : "s"}${pending.length ? ` \u2014 agentguard approvals` : ""}`
4065
+ );
4066
+ io.out(`run ${runId}`);
4067
+ const fmt = (scope) => Object.entries(status.caps[scope]).map(([k, limit]) => `${k} ${status.usage[scope][k] ?? 0}/${limit}`).join(" ") || "no caps";
4068
+ io.out(` per_run ${fmt("per_run")}`);
4069
+ io.out(` per_day ${fmt("per_day")}`);
4070
+ return 0;
4071
+ }
4072
+
4073
+ // src/cli.ts
4074
+ function version2() {
4075
+ try {
4076
+ return JSON.parse(readFileSync4(new URL("../package.json", import.meta.url), "utf8")).version;
4077
+ } catch {
4078
+ return "0.0.0";
4079
+ }
4080
+ }
4081
+ var HELP = `agentguard \u2014 MCP policy proxy for agents that touch production
4082
+
4083
+ npx @agentwares/agentguard init read your MCP config, write agentguard.yaml (dry-run), route every server through the proxy
4084
+ agentguard report [--run id|--all] what this run did / would have destroyed / spent, where it was halted
4085
+ agentguard diff [--run id] record-by-record mutation diff from a dry run
4086
+ agentguard verify [audit.jsonl] prove the hash-chained audit log was not edited
4087
+
4088
+ agentguard proxy [--http --port 8788] [--agent name] [--run-id id] [--mode dry-run|enforce]
4089
+ agentguard tools [--json] the tools your agent will see, with class and why
4090
+ agentguard status [--run id] counters vs caps, kill state, pending approvals
4091
+
4092
+ agentguard kill [reason] halt every run now (file + running HTTP proxy); agentguard resume
4093
+ agentguard approvals [--all] pending approvals; agentguard approve <id> | deny <id> [--note text]
4094
+ agentguard key create <agent> [--allow 'crm_get_*']... [--deny p] [--writes n] [--spend n] [--mode m]
4095
+ agentguard key list | revoke <agent>
4096
+ agentguard permission-diff [--base ref] [--head ref] [--paths a,b] [--format markdown|json] [--fail-on-widen]
4097
+
4098
+ Options: --config <agentguard.yaml> (or AGENTGUARD_CONFIG), --json, --help, --version
4099
+ Docs: https://github.com/agentwares/agentwares/tree/main/apps/agentguard-cli`;
4100
+ async function main(argv, io = defaultIo()) {
4101
+ const args = parseArgs(argv);
4102
+ if (args.flags.version) {
4103
+ io.out(version2());
4104
+ return 0;
4105
+ }
4106
+ if (args.command === void 0 && !args.flags.help && io.tty === false) {
4107
+ try {
4108
+ return await proxyCommand(args, io);
4109
+ } catch (err) {
4110
+ io.err(describeError(err));
4111
+ return 1;
4112
+ }
4113
+ }
4114
+ if (args.flags.help || args.command === void 0 || args.command === "help") {
4115
+ io.out(HELP);
4116
+ return args.command === void 0 && !args.flags.help ? 1 : 0;
4117
+ }
4118
+ try {
4119
+ switch (args.command) {
4120
+ case "init":
4121
+ return await initCommand(args, io);
4122
+ case "proxy":
4123
+ case "serve":
4124
+ return await proxyCommand(args, io);
4125
+ case "report":
4126
+ return await reportCommand(args, io);
4127
+ case "diff":
4128
+ return await diffCommand(args, io);
4129
+ case "verify":
4130
+ return await verifyCommand(args, io);
4131
+ case "status":
4132
+ return await statusCommand(args, io);
4133
+ case "tools":
4134
+ return await toolsCommand(args, io);
4135
+ case "kill":
4136
+ return await killCommand(args, io);
4137
+ case "resume":
4138
+ return await resumeCommand(args, io);
4139
+ case "approve":
4140
+ return await decideCommand("approved", args, io);
4141
+ case "deny":
4142
+ return await decideCommand("denied", args, io);
4143
+ case "approvals":
4144
+ return await approvalsCommand(args, io);
4145
+ case "key":
4146
+ case "keys":
4147
+ return await keyCommand(args, io);
4148
+ case "permission-diff":
4149
+ return await permissionDiffCommand(args, io);
4150
+ default:
4151
+ io.err(`unknown command "${args.command}"
4152
+ `);
4153
+ io.err(HELP);
4154
+ return 1;
4155
+ }
4156
+ } catch (err) {
4157
+ io.err(describeError(err));
4158
+ return 1;
4159
+ }
4160
+ }
4161
+ var isMain = process.argv[1] !== void 0 && /(^|\/)(cli\.[cm]?[jt]s|agentguard)$/.test(process.argv[1]);
4162
+ if (isMain) {
4163
+ main(process.argv.slice(2)).then(
4164
+ (code) => {
4165
+ if (code !== 0) process.exitCode = code;
4166
+ },
4167
+ (err) => {
4168
+ process.stderr.write(`${describeError(err)}
4169
+ `);
4170
+ process.exitCode = 1;
4171
+ }
4172
+ );
4173
+ }
4174
+ export {
4175
+ HELP,
4176
+ main,
4177
+ version2 as version
4178
+ };
4179
+ //# sourceMappingURL=cli.js.map