@nekzus/liop 2.6.1 → 2.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/README.md +85 -38
  2. package/dist/bin/agent.js +27 -894
  3. package/dist/bridge.d.ts +4 -3
  4. package/dist/bridge.js +1 -4
  5. package/dist/chunk-2XHSALII.js +3 -0
  6. package/dist/chunk-2ZYX736J.js +2 -0
  7. package/dist/chunk-3WRKTVOF.js +2 -0
  8. package/dist/chunk-6KFKO6ZX.js +2 -0
  9. package/dist/chunk-6VK2XALI.js +2 -0
  10. package/dist/chunk-AYDF3IFZ.js +2 -0
  11. package/dist/chunk-CTTTLCGL.js +31 -0
  12. package/dist/chunk-ECRS6N33.js +4 -0
  13. package/dist/chunk-FDJMH4CB.js +2 -0
  14. package/dist/chunk-FTF6AMF6.js +0 -1
  15. package/dist/chunk-GWVSCZUS.js +2 -0
  16. package/dist/chunk-LEXMMIQI.js +15 -0
  17. package/dist/chunk-LM26HOJ6.js +31 -0
  18. package/dist/chunk-MQ5SU26J.js +2 -0
  19. package/dist/chunk-N35BC62H.js +2 -0
  20. package/dist/chunk-P6IJ3VBD.js +3 -0
  21. package/dist/chunk-TKLRW52V.js +2 -0
  22. package/dist/chunk-V35CXJQN.js +2 -0
  23. package/dist/chunk-WP5ER7RH.js +3 -0
  24. package/dist/chunk-XLVEDKM6.js +2 -0
  25. package/dist/chunk-Y675R34Z.js +2 -0
  26. package/dist/chunk-YD2AF2M6.js +46 -0
  27. package/dist/chunk-YLCNV764.js +3 -0
  28. package/dist/chunk-ZZMSFCJP.js +3 -0
  29. package/dist/client.d.ts +5 -4
  30. package/dist/client.js +1 -11
  31. package/dist/gateway.d.ts +6 -5
  32. package/dist/gateway.js +1 -11
  33. package/dist/{hybrid-DWe1bMoV.d.ts → hybrid-BZpCiUaV.d.ts} +4 -4
  34. package/dist/{index-BuEQtBHD.d.ts → index-BYDtRGue.d.ts} +8 -4
  35. package/dist/{index-CdW8j8Bn.d.ts → index-ByzjpRCk.d.ts} +30 -2
  36. package/dist/index.d.ts +57 -10
  37. package/dist/index.js +4 -317
  38. package/dist/kyber-PULV7PQX.js +2 -0
  39. package/dist/mesh.d.ts +1 -1
  40. package/dist/mesh.js +1 -5
  41. package/dist/{node-B0b2vUFS.d.ts → node-Djzm6UYd.d.ts} +8 -0
  42. package/dist/protocol/liop_core.proto +22 -6
  43. package/dist/server.d.ts +4 -3
  44. package/dist/server.js +1 -8
  45. package/dist/{types-CzRSOlOo.d.ts → types-29zv9X1V.d.ts} +3 -0
  46. package/dist/types.d.ts +2 -1
  47. package/dist/types.js +1 -2
  48. package/dist/verifier-DqZ9MQ9l.d.ts +82 -0
  49. package/dist/verifier-J4NQYZX5.js +2 -0
  50. package/dist/workers/logic-execution.d.ts +7 -0
  51. package/dist/workers/logic-execution.js +1 -271
  52. package/dist/workers/zk-verifier.d.ts +9 -0
  53. package/dist/workers/zk-verifier.js +1 -173
  54. package/dist/zk-DzYNWBgo.d.ts +104 -0
  55. package/package.json +7 -2
  56. package/dist/bin/agent.js.map +0 -1
  57. package/dist/bridge.js.map +0 -1
  58. package/dist/chunk-2KSO3W4A.js +0 -77
  59. package/dist/chunk-2KSO3W4A.js.map +0 -1
  60. package/dist/chunk-2STZHJ24.js +0 -114
  61. package/dist/chunk-2STZHJ24.js.map +0 -1
  62. package/dist/chunk-3QH44NKZ.js +0 -115
  63. package/dist/chunk-3QH44NKZ.js.map +0 -1
  64. package/dist/chunk-5SWHE62F.js +0 -3683
  65. package/dist/chunk-5SWHE62F.js.map +0 -1
  66. package/dist/chunk-7BSCUYUE.js +0 -421
  67. package/dist/chunk-7BSCUYUE.js.map +0 -1
  68. package/dist/chunk-7PITYWOV.js +0 -68
  69. package/dist/chunk-7PITYWOV.js.map +0 -1
  70. package/dist/chunk-EJ426URA.js +0 -818
  71. package/dist/chunk-EJ426URA.js.map +0 -1
  72. package/dist/chunk-FCFTYOAS.js +0 -793
  73. package/dist/chunk-FCFTYOAS.js.map +0 -1
  74. package/dist/chunk-FTF6AMF6.js.map +0 -1
  75. package/dist/chunk-IK7CGWAZ.js +0 -24
  76. package/dist/chunk-IK7CGWAZ.js.map +0 -1
  77. package/dist/chunk-K3DGJLUY.js +0 -106
  78. package/dist/chunk-K3DGJLUY.js.map +0 -1
  79. package/dist/chunk-PAICJCKJ.js +0 -332
  80. package/dist/chunk-PAICJCKJ.js.map +0 -1
  81. package/dist/chunk-PUDETPL4.js +0 -279
  82. package/dist/chunk-PUDETPL4.js.map +0 -1
  83. package/dist/chunk-UJFWPRDM.js +0 -206884
  84. package/dist/chunk-UJFWPRDM.js.map +0 -1
  85. package/dist/chunk-VRB5HUYU.js +0 -1492
  86. package/dist/chunk-VRB5HUYU.js.map +0 -1
  87. package/dist/chunk-VRDZ5IKE.js +0 -558
  88. package/dist/chunk-VRDZ5IKE.js.map +0 -1
  89. package/dist/chunk-WPTOJYN5.js +0 -181
  90. package/dist/chunk-WPTOJYN5.js.map +0 -1
  91. package/dist/chunk-XRDN232O.js +0 -877
  92. package/dist/chunk-XRDN232O.js.map +0 -1
  93. package/dist/chunk-Y62GORQF.js +0 -51
  94. package/dist/chunk-Y62GORQF.js.map +0 -1
  95. package/dist/chunk-ZP3VVXOI.js +0 -66
  96. package/dist/chunk-ZP3VVXOI.js.map +0 -1
  97. package/dist/client.js.map +0 -1
  98. package/dist/gateway.js.map +0 -1
  99. package/dist/index.js.map +0 -1
  100. package/dist/kyber-CGOA7Q6A.js +0 -3
  101. package/dist/kyber-CGOA7Q6A.js.map +0 -1
  102. package/dist/mesh.js.map +0 -1
  103. package/dist/server.js.map +0 -1
  104. package/dist/types.js.map +0 -1
  105. package/dist/verifier-COnid_dg.d.ts +0 -31
  106. package/dist/verifier-XFU7ZB2W.js +0 -6
  107. package/dist/verifier-XFU7ZB2W.js.map +0 -1
  108. package/dist/workers/logic-execution.js.map +0 -1
  109. package/dist/workers/zk-verifier.js.map +0 -1
@@ -1,818 +0,0 @@
1
- import { LiopMcpRouter } from './chunk-VRB5HUYU.js';
2
- import { LIOP_SCOPES } from './chunk-WPTOJYN5.js';
3
- import { meshPeersConnected, manifestCacheSize, protocolMetrics } from './chunk-PAICJCKJ.js';
4
- import { log } from './chunk-K3DGJLUY.js';
5
- import * as http from 'http';
6
- import * as http2 from 'http2';
7
- import * as net from 'net';
8
-
9
- // src/security/prm.ts
10
- function buildProtectedResourceMetadata(issuer, audience) {
11
- return {
12
- resource: audience,
13
- authorization_servers: [issuer],
14
- scopes_supported: LIOP_SCOPES,
15
- bearer_methods_supported: ["header"],
16
- resource_documentation: "https://github.com/nekzus/liop"
17
- };
18
- }
19
-
20
- // src/gateway/grpc-web.ts
21
- var GRPC_WEB_CONSTANTS = {
22
- FLAG_DATA: 0,
23
- FLAG_TRAILERS: 128,
24
- HEADER_LENGTH: 5,
25
- CONTENT_TYPE_PREFIX: "application/grpc-web",
26
- STATUS_OK: 0,
27
- STATUS_CANCELLED: 1,
28
- STATUS_UNKNOWN: 2,
29
- STATUS_INVALID_ARGUMENT: 3,
30
- STATUS_DEADLINE_EXCEEDED: 4,
31
- STATUS_NOT_FOUND: 5,
32
- STATUS_PERMISSION_DENIED: 7,
33
- STATUS_UNAUTHENTICATED: 16,
34
- STATUS_INTERNAL: 13
35
- };
36
- function isGrpcWebRequest(contentType) {
37
- if (!contentType) return false;
38
- const lower = contentType.toLowerCase();
39
- return lower.startsWith("application/grpc-web") || lower.startsWith("application/grpc-web+proto") || lower.startsWith("application/grpc-web+json");
40
- }
41
- function encodeDataFrame(payload) {
42
- const buf = Buffer.isBuffer(payload) ? payload : Buffer.from(payload);
43
- const header = Buffer.alloc(GRPC_WEB_CONSTANTS.HEADER_LENGTH);
44
- header.writeUInt8(GRPC_WEB_CONSTANTS.FLAG_DATA, 0);
45
- header.writeUInt32BE(buf.length, 1);
46
- return Buffer.concat([header, buf]);
47
- }
48
- function encodeTrailerFrame(status = GRPC_WEB_CONSTANTS.STATUS_OK, message = "", customTrailers = {}) {
49
- const lines = [
50
- `grpc-status:${status}`,
51
- `grpc-message:${encodeURIComponent(message)}`
52
- ];
53
- for (const [key, value] of Object.entries(customTrailers)) {
54
- lines.push(`${key}:${value}`);
55
- }
56
- const trailerText = `${lines.join("\r\n")}\r
57
- `;
58
- const trailerBuf = Buffer.from(trailerText, "utf-8");
59
- const header = Buffer.alloc(GRPC_WEB_CONSTANTS.HEADER_LENGTH);
60
- header.writeUInt8(GRPC_WEB_CONSTANTS.FLAG_TRAILERS, 0);
61
- header.writeUInt32BE(trailerBuf.length, 1);
62
- return Buffer.concat([header, trailerBuf]);
63
- }
64
- function decodeFrames(buffer) {
65
- const frames = [];
66
- let offset = 0;
67
- while (offset + GRPC_WEB_CONSTANTS.HEADER_LENGTH <= buffer.length) {
68
- const flag = buffer.readUInt8(offset);
69
- const length = buffer.readUInt32BE(offset + 1);
70
- const payloadStart = offset + GRPC_WEB_CONSTANTS.HEADER_LENGTH;
71
- const payloadEnd = payloadStart + length;
72
- if (payloadEnd > buffer.length) {
73
- break;
74
- }
75
- const payload = buffer.subarray(payloadStart, payloadEnd);
76
- frames.push({
77
- isTrailer: (flag & GRPC_WEB_CONSTANTS.FLAG_TRAILERS) === GRPC_WEB_CONSTANTS.FLAG_TRAILERS,
78
- payload: Buffer.from(payload)
79
- });
80
- offset = payloadEnd;
81
- }
82
- return frames;
83
- }
84
- async function dispatchGrpcWebRequest(req, res, handler) {
85
- const chunks = [];
86
- req.on("data", (chunk) => {
87
- chunks.push(chunk);
88
- });
89
- req.on("end", async () => {
90
- try {
91
- const fullBody = Buffer.concat(chunks);
92
- const frames = decodeFrames(fullBody);
93
- const dataPayload = frames.find((f) => !f.isTrailer)?.payload ?? Buffer.alloc(0);
94
- const requestPath = req.url || "/";
95
- log.info(
96
- `[gRPC-Web] Handling request for ${requestPath} (${dataPayload.length} bytes)`
97
- );
98
- const result = await handler(requestPath, dataPayload);
99
- res.statusCode = 200;
100
- res.setHeader("content-type", "application/grpc-web+proto");
101
- res.setHeader("access-control-allow-origin", "*");
102
- res.setHeader(
103
- "access-control-expose-headers",
104
- "grpc-status, grpc-message, content-type"
105
- );
106
- if (result.data && result.data.length > 0) {
107
- res.write(encodeDataFrame(result.data));
108
- }
109
- res.write(encodeTrailerFrame(result.status, result.message || ""));
110
- res.end();
111
- } catch (error) {
112
- log.error(`[gRPC-Web] Dispatch error: ${error}`);
113
- res.statusCode = 200;
114
- res.setHeader("content-type", "application/grpc-web+proto");
115
- res.write(
116
- encodeTrailerFrame(
117
- GRPC_WEB_CONSTANTS.STATUS_INTERNAL,
118
- error.message || "Internal server error"
119
- )
120
- );
121
- res.end();
122
- }
123
- });
124
- }
125
-
126
- // src/gateway/interceptor.ts
127
- var DEFAULT_TIMEOUT_MS = 2500;
128
- async function executeGatewayInterceptor(request, context, options) {
129
- const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
130
- const failMode = options.failMode ?? "closed";
131
- const signal = AbortSignal.timeout(timeoutMs);
132
- const frozenRequest = Object.freeze(structuredClone(request));
133
- let onAbortListener;
134
- const abortPromise = new Promise((_, reject) => {
135
- onAbortListener = () => reject(signal.reason);
136
- signal.addEventListener("abort", onAbortListener, { once: true });
137
- });
138
- try {
139
- const result = await Promise.race([
140
- Promise.resolve(
141
- options.interceptor(frozenRequest, { ...context, signal })
142
- ),
143
- abortPromise
144
- ]);
145
- return result;
146
- } catch (error) {
147
- const errorMessage = error instanceof Error ? error.message : String(error);
148
- if (failMode === "open") {
149
- log.warn(
150
- `[LIOP-Gateway] Interceptor error (fail-open, allowing request): ${errorMessage}`
151
- );
152
- return {
153
- allowed: true,
154
- metadata: { interceptorError: errorMessage, failMode: "open" }
155
- };
156
- }
157
- log.error(
158
- `[LIOP-Gateway] Interceptor error (fail-closed, rejecting request): ${errorMessage}`
159
- );
160
- return {
161
- allowed: false,
162
- reason: `Interceptor unavailable: ${errorMessage}`,
163
- errorCode: -32098,
164
- metadata: { interceptorError: errorMessage, failMode: "closed" }
165
- };
166
- } finally {
167
- if (onAbortListener) {
168
- signal.removeEventListener("abort", onAbortListener);
169
- }
170
- }
171
- }
172
-
173
- // src/gateway/rate-limiter.ts
174
- var InMemoryRateLimiter = class {
175
- windowMs;
176
- maxRequests;
177
- records = /* @__PURE__ */ new Map();
178
- cleanupTimer;
179
- constructor(options = {}) {
180
- this.windowMs = options.windowMs ?? 6e4;
181
- this.maxRequests = options.maxRequests ?? 120;
182
- this.cleanupTimer = setInterval(() => this.cleanup(), 3e4);
183
- if (typeof this.cleanupTimer.unref === "function") {
184
- this.cleanupTimer.unref();
185
- }
186
- }
187
- /**
188
- * Evaluates whether a request from the given key (IP, token, or clientId) is permitted.
189
- *
190
- * @param key Identifier for the client (e.g., remote IP or OAuth client ID)
191
- * @returns Status containing allowed boolean, remaining quota, and time until reset
192
- */
193
- check(key) {
194
- const now = Date.now();
195
- let record = this.records.get(key);
196
- if (!record || now >= record.resetTime) {
197
- record = {
198
- count: 1,
199
- resetTime: now + this.windowMs
200
- };
201
- this.records.set(key, record);
202
- return {
203
- allowed: true,
204
- remaining: this.maxRequests - 1,
205
- limit: this.maxRequests,
206
- resetMs: this.windowMs
207
- };
208
- }
209
- if (record.count >= this.maxRequests) {
210
- return {
211
- allowed: false,
212
- remaining: 0,
213
- limit: this.maxRequests,
214
- resetMs: Math.max(0, record.resetTime - now)
215
- };
216
- }
217
- record.count++;
218
- return {
219
- allowed: true,
220
- remaining: this.maxRequests - record.count,
221
- limit: this.maxRequests,
222
- resetMs: Math.max(0, record.resetTime - now)
223
- };
224
- }
225
- /**
226
- * Explicitly resets the rate limit bucket for a given key.
227
- */
228
- reset(key) {
229
- this.records.delete(key);
230
- }
231
- /**
232
- * Cleans up expired rate-limiting buckets to prevent memory leaks.
233
- */
234
- cleanup() {
235
- const now = Date.now();
236
- for (const [key, record] of this.records.entries()) {
237
- if (now >= record.resetTime) {
238
- this.records.delete(key);
239
- }
240
- }
241
- }
242
- /**
243
- * Destroys internal timers and clears records.
244
- */
245
- close() {
246
- if (this.cleanupTimer) {
247
- clearInterval(this.cleanupTimer);
248
- this.cleanupTimer = void 0;
249
- }
250
- this.records.clear();
251
- }
252
- };
253
-
254
- // src/gateway/hybrid.ts
255
- var LiopHybridGateway = class {
256
- constructor(liopServer, meshNode = null, rpcPort = 50051, rateLimiterOptions, interceptorOptions) {
257
- this.liopServer = liopServer;
258
- this.meshNode = meshNode;
259
- this.jwtValidator = this.liopServer.jwtValidator;
260
- this.oauthProvider = this.liopServer.oauthProvider;
261
- this.rateLimiter = new InMemoryRateLimiter(rateLimiterOptions);
262
- this.interceptorOptions = interceptorOptions;
263
- this.router = new LiopMcpRouter(this.liopServer, this.meshNode, rpcPort);
264
- this.h2Server = http2.createServer();
265
- this.setupH2Routes();
266
- this.h1Server = http.createServer();
267
- this.setupH1Routes();
268
- this.netServer = net.createServer((socket) => {
269
- socket.once("data", (buffer) => {
270
- const isHttp2 = buffer.toString().startsWith("PRI * HTTP/2.0");
271
- log.info(
272
- `[LIOP-Gateway] Incoming L4 Connection. Protocol: ${isHttp2 ? "HTTP/2 (gRPC)" : "HTTP/1.1 (MCP)"}`
273
- );
274
- if (isHttp2) {
275
- this.h2Server.emit("connection", socket);
276
- } else {
277
- this.h1Server.emit("connection", socket);
278
- }
279
- socket.unshift(buffer);
280
- });
281
- socket.on(
282
- "error",
283
- (err) => log.error(`[LIOP-Gateway] NetServer Socket Error: ${err.message}`)
284
- );
285
- });
286
- this.h1Server.on(
287
- "error",
288
- (err) => log.error(`[LIOP-Gateway] H1 Server Error: ${err.message}`)
289
- );
290
- this.h2Server.on(
291
- "error",
292
- (err) => log.error(`[LIOP-Gateway] H2 Server Error: ${err.message}`)
293
- );
294
- log.info("[LIOP-Gateway] Hybrid adapter initialized.");
295
- }
296
- liopServer;
297
- meshNode;
298
- netServer;
299
- h2Server;
300
- h1Server;
301
- router;
302
- jwtValidator;
303
- // biome-ignore lint/suspicious/noExplicitAny: oidc-provider is loaded in Phase C
304
- oauthProvider;
305
- rateLimiter;
306
- interceptorOptions;
307
- isDraining = false;
308
- activeRequests = 0;
309
- setupH2Routes() {
310
- this.h2Server.on("stream", (stream, headers) => {
311
- const contentType = headers["content-type"];
312
- const path = headers[":path"];
313
- if (contentType === "application/grpc") {
314
- this.handleGrpcStream(stream);
315
- } else if (path === "/mcp") {
316
- this.handleMcpH2Stream(stream, headers);
317
- }
318
- });
319
- }
320
- setupH1Routes() {
321
- this.h1Server.on("request", async (req, res) => {
322
- this.activeRequests += 1;
323
- const onDone = () => {
324
- res.removeListener("finish", onDone);
325
- res.removeListener("close", onDone);
326
- this.activeRequests = Math.max(0, this.activeRequests - 1);
327
- };
328
- res.on("finish", onDone);
329
- res.on("close", onDone);
330
- const url = req.url || "";
331
- const method = req.method;
332
- if (method === "GET" && url === "/metrics") {
333
- if (this.meshNode) {
334
- meshPeersConnected.set({}, this.meshNode.getPeers().length);
335
- }
336
- if (this.router) {
337
- manifestCacheSize.set({}, this.router.getManifestCacheSize());
338
- }
339
- res.writeHead(200, {
340
- "Content-Type": "text/plain; version=0.0.4; charset=utf-8"
341
- });
342
- res.end(protocolMetrics.exportPrometheusText());
343
- return;
344
- }
345
- if (method === "GET" && url === "/healthz") {
346
- if (this.isDraining) {
347
- res.writeHead(503, { "Content-Type": "application/json" });
348
- res.end(
349
- JSON.stringify({
350
- status: "draining",
351
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
352
- })
353
- );
354
- return;
355
- }
356
- res.writeHead(200, { "Content-Type": "application/json" });
357
- res.end(
358
- JSON.stringify({
359
- status: "ok",
360
- uptime: Math.floor(process.uptime()),
361
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
362
- })
363
- );
364
- return;
365
- }
366
- if (method === "GET" && url === "/readyz") {
367
- if (this.isDraining) {
368
- res.writeHead(503, { "Content-Type": "application/json" });
369
- res.end(
370
- JSON.stringify({
371
- status: "draining",
372
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
373
- })
374
- );
375
- return;
376
- }
377
- const meshStarted = this.meshNode ? this.meshNode.isStarted() : true;
378
- if (!meshStarted) {
379
- res.writeHead(503, { "Content-Type": "application/json" });
380
- res.end(
381
- JSON.stringify({
382
- status: "unready",
383
- meshStarted: false,
384
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
385
- })
386
- );
387
- return;
388
- }
389
- res.writeHead(200, { "Content-Type": "application/json" });
390
- res.end(
391
- JSON.stringify({
392
- status: "ready",
393
- meshStarted: true,
394
- peers: this.meshNode ? this.meshNode.getPeers().length : 0,
395
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
396
- })
397
- );
398
- return;
399
- }
400
- if (this.isDraining && url === "/mcp") {
401
- res.writeHead(503, { "Content-Type": "application/json" });
402
- res.end(
403
- JSON.stringify({
404
- jsonrpc: "2.0",
405
- error: {
406
- code: -32e3,
407
- message: "Gateway is draining connections for shutdown"
408
- }
409
- })
410
- );
411
- return;
412
- }
413
- const contentType = req.headers["content-type"];
414
- if (isGrpcWebRequest(contentType)) {
415
- await dispatchGrpcWebRequest(req, res, async (reqPath, payload) => {
416
- log.info(
417
- `[LIOP-Gateway] gRPC-Web fallback call: ${reqPath} (${payload.length} bytes)`
418
- );
419
- return {
420
- status: GRPC_WEB_CONSTANTS.STATUS_OK,
421
- message: "OK",
422
- data: Buffer.from("")
423
- };
424
- });
425
- return;
426
- }
427
- if (url.startsWith("/oidc") && this.oauthProvider) {
428
- const callback = typeof this.oauthProvider.callback === "function" ? this.oauthProvider.callback() : this.oauthProvider;
429
- const originalUrl = req.url;
430
- req.url = (originalUrl || "").slice(5) || "/";
431
- try {
432
- return callback(req, res);
433
- } finally {
434
- req.url = originalUrl;
435
- }
436
- }
437
- if (method === "GET" && url === "/.well-known/oauth-protected-resource") {
438
- if (this.jwtValidator) {
439
- const prm = buildProtectedResourceMetadata(
440
- this.jwtValidator.getIssuer(),
441
- this.jwtValidator.getAudience()
442
- );
443
- res.writeHead(200, { "Content-Type": "application/json" });
444
- res.end(JSON.stringify(prm));
445
- return;
446
- }
447
- res.writeHead(404);
448
- res.end("Not Found");
449
- return;
450
- }
451
- if (method === "GET" && (url === "/" || url === "/mcp" || url === "/health")) {
452
- if (url === "/health" && req.headers.accept?.includes("application/json")) {
453
- const meshInfo = this.meshNode ? {
454
- peerId: this.meshNode.getPeerId()?.toString() || "",
455
- multiaddrs: this.meshNode.getMultiaddrs().map((m) => m.toString())
456
- } : null;
457
- const issuer = this.jwtValidator?.getIssuer();
458
- const baseUrl = issuer ? issuer.endsWith("/oidc") ? issuer : `${issuer}/oidc` : "";
459
- const authInfoResponse = this.jwtValidator && issuer ? {
460
- issuer,
461
- jwks_uri: `${baseUrl}/jwks`,
462
- ...this.oauthProvider ? {
463
- token_endpoint: `${baseUrl}/token`
464
- } : {}
465
- } : void 0;
466
- const topologyInfo = {
467
- tier: this.meshNode?.isPrivateNetwork?.() ? 1 : 2,
468
- prmEndpoint: this.jwtValidator ? "/.well-known/oauth-protected-resource" : void 0,
469
- transportCapabilities: ["http-mcp", "grpc-lio", "p2p-dht"]
470
- };
471
- res.writeHead(200, { "Content-Type": "application/json" });
472
- res.end(
473
- JSON.stringify({
474
- status: "healthy",
475
- node: this.liopServer.getServerInfo(),
476
- mesh: meshInfo,
477
- tools: this.liopServer.listTools().map((t) => t.name),
478
- auth: authInfoResponse,
479
- topology: topologyInfo,
480
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
481
- })
482
- );
483
- return;
484
- }
485
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
486
- res.end(`
487
- <body style="background:#0f172a;color:#f8fafc;font-family:sans-serif;display:flex;flex-direction:column;align-items:center;justify-content:center;height:100vh;margin:0">
488
- <div style="background:#1e293b;padding:40px;border-radius:16px;border:1px solid #38bdf8;text-align:center;box-shadow:0 20px 25px -5px rgba(0,0,0,0.1)">
489
- <h1 style="color:#38bdf8;margin-top:0">LIOP Protocol Transformer</h1>
490
- <p style="opacity:0.8;font-weight:600">L4/L7 Transcoding: JSON-RPC &harr; gRPC</p>
491
- <p style="opacity:0.6;font-size:14px">Active Protections: Kyber768 + AES-256-GCM + ZK-Proof Ready</p>
492
- <div style="background:#0f172a;padding:15px;border-radius:8px;margin-top:20px;border:1px dashed #334155">
493
- <code style="color:#10b981">Endpoint: http://localhost:3000/mcp</code>
494
- </div>
495
- </div>
496
- </body>
497
- `);
498
- return;
499
- }
500
- if (url === "/mcp" && method === "POST") {
501
- let authInfo = null;
502
- if (this.jwtValidator) {
503
- const authHeader = req.headers.authorization;
504
- if (!authHeader?.startsWith("Bearer ")) {
505
- res.writeHead(401, {
506
- "WWW-Authenticate": 'Bearer error="invalid_token", error_description="Missing or malformed Authorization header"',
507
- "Content-Type": "application/json"
508
- });
509
- res.end(JSON.stringify({ error: "Unauthorized" }));
510
- return;
511
- }
512
- try {
513
- authInfo = await this.jwtValidator.validate(authHeader.slice(7));
514
- } catch (e) {
515
- res.writeHead(401, {
516
- "WWW-Authenticate": `Bearer error="invalid_token", error_description="${e.message}"`,
517
- "Content-Type": "application/json"
518
- });
519
- res.end(JSON.stringify({ error: "Invalid token" }));
520
- return;
521
- }
522
- }
523
- let body = "";
524
- req.on("data", (chunk) => body += chunk.toString());
525
- req.on("end", async () => {
526
- const clientIp = req.socket.remoteAddress || "127.0.0.1";
527
- const rateLimitKey = authInfo?.clientId ? `client:${authInfo.clientId}` : `ip:${clientIp}`;
528
- const rateStatus = this.rateLimiter.check(rateLimitKey);
529
- if (!rateStatus.allowed) {
530
- res.writeHead(429, {
531
- "Content-Type": "application/json",
532
- "Retry-After": Math.ceil(rateStatus.resetMs / 1e3).toString()
533
- });
534
- res.end(
535
- JSON.stringify({
536
- jsonrpc: "2.0",
537
- id: null,
538
- error: {
539
- code: -32029,
540
- message: "Rate limit exceeded. Too many requests."
541
- }
542
- })
543
- );
544
- return;
545
- }
546
- try {
547
- const jsonRequest = JSON.parse(body);
548
- if (this.interceptorOptions?.interceptor) {
549
- const verdict = await executeGatewayInterceptor(
550
- jsonRequest,
551
- { clientIp, authInfo, protocol: "http1" },
552
- this.interceptorOptions
553
- );
554
- if (!verdict.allowed) {
555
- res.writeHead(403, { "Content-Type": "application/json" });
556
- res.end(
557
- JSON.stringify({
558
- jsonrpc: "2.0",
559
- id: jsonRequest.id ?? null,
560
- error: {
561
- code: verdict.errorCode ?? -32099,
562
- message: verdict.reason || "Forbidden: Rejected by Gateway Interceptor"
563
- }
564
- })
565
- );
566
- return;
567
- }
568
- }
569
- const mcpMethod = req.headers["mcp-method"];
570
- const mcpName = req.headers["mcp-name"];
571
- const mcpVersion = req.headers["mcp-protocol-version"];
572
- if (mcpMethod && mcpMethod !== jsonRequest.method) {
573
- res.writeHead(400, { "Content-Type": "application/json" });
574
- res.end(
575
- JSON.stringify({
576
- jsonrpc: "2.0",
577
- id: jsonRequest.id ?? null,
578
- error: {
579
- code: -32020,
580
- message: `HeaderMismatch: 'Mcp-Method' header (${mcpMethod}) does not match JSON-RPC method (${jsonRequest.method})`
581
- }
582
- })
583
- );
584
- return;
585
- }
586
- if (mcpName && jsonRequest.params?.name && mcpName !== jsonRequest.params.name) {
587
- res.writeHead(400, { "Content-Type": "application/json" });
588
- res.end(
589
- JSON.stringify({
590
- jsonrpc: "2.0",
591
- id: jsonRequest.id ?? null,
592
- error: {
593
- code: -32020,
594
- message: `HeaderMismatch: 'Mcp-Name' header (${mcpName}) does not match target name (${jsonRequest.params.name})`
595
- }
596
- })
597
- );
598
- return;
599
- }
600
- if (mcpVersion && jsonRequest.params) {
601
- if (!jsonRequest.params._meta) {
602
- jsonRequest.params._meta = {};
603
- }
604
- if (!jsonRequest.params._meta["io.modelcontextprotocol/protocolVersion"]) {
605
- jsonRequest.params._meta["io.modelcontextprotocol/protocolVersion"] = mcpVersion;
606
- }
607
- }
608
- const response = await this.router.dispatch(jsonRequest, authInfo);
609
- res.writeHead(200, { "Content-Type": "application/json" });
610
- res.end(JSON.stringify(response));
611
- } catch (e) {
612
- log.info(
613
- `[LIOP-Gateway] Error processing JSON-RPC payload: ${e.message}`
614
- );
615
- res.writeHead(400);
616
- res.end(
617
- JSON.stringify({
618
- jsonrpc: "2.0",
619
- error: { code: -32700, message: "Parse error" }
620
- })
621
- );
622
- }
623
- });
624
- } else {
625
- res.writeHead(404);
626
- res.end("Not Found");
627
- }
628
- });
629
- }
630
- handleGrpcStream(stream) {
631
- stream.on("data", (chunk) => {
632
- const data = chunk;
633
- if (data)
634
- log.info(
635
- `[LIOP-Gateway] Native gRPC Proxy passing ${data.length} bytes`
636
- );
637
- });
638
- stream.respond({ ":status": 200, "content-type": "application/grpc" });
639
- stream.end();
640
- }
641
- handleMcpH2Stream(stream, headers) {
642
- let body = "";
643
- stream.on("data", (chunk) => body += chunk.toString());
644
- stream.on("end", async () => {
645
- try {
646
- let authInfo = null;
647
- if (this.jwtValidator) {
648
- const authHeader = headers.authorization;
649
- if (!authHeader?.startsWith("Bearer ")) {
650
- stream.respond({
651
- ":status": 401,
652
- "www-authenticate": 'Bearer error="invalid_token", error_description="Missing or malformed Authorization header"',
653
- "content-type": "application/json"
654
- });
655
- stream.end(JSON.stringify({ error: "Unauthorized" }));
656
- return;
657
- }
658
- try {
659
- authInfo = await this.jwtValidator.validate(authHeader.slice(7));
660
- } catch (e) {
661
- stream.respond({
662
- ":status": 401,
663
- "www-authenticate": `Bearer error="invalid_token", error_description="${e.message}"`,
664
- "content-type": "application/json"
665
- });
666
- stream.end(JSON.stringify({ error: "Invalid token" }));
667
- return;
668
- }
669
- }
670
- const jsonRequest = JSON.parse(body);
671
- if (this.interceptorOptions?.interceptor) {
672
- const h2ClientIp = stream.session?.socket?.remoteAddress || "127.0.0.1";
673
- const verdict = await executeGatewayInterceptor(
674
- jsonRequest,
675
- { clientIp: h2ClientIp, authInfo, protocol: "http2" },
676
- this.interceptorOptions
677
- );
678
- if (!verdict.allowed) {
679
- stream.respond({
680
- ":status": 403,
681
- "content-type": "application/json"
682
- });
683
- stream.end(
684
- JSON.stringify({
685
- jsonrpc: "2.0",
686
- id: jsonRequest.id ?? null,
687
- error: {
688
- code: verdict.errorCode ?? -32099,
689
- message: verdict.reason || "Forbidden: Rejected by Gateway Interceptor"
690
- }
691
- })
692
- );
693
- return;
694
- }
695
- }
696
- const mcpMethod = headers["mcp-method"];
697
- const mcpName = headers["mcp-name"];
698
- const mcpVersion = headers["mcp-protocol-version"];
699
- if (mcpMethod && mcpMethod !== jsonRequest.method) {
700
- stream.respond({
701
- ":status": 400,
702
- "content-type": "application/json"
703
- });
704
- stream.end(
705
- JSON.stringify({
706
- jsonrpc: "2.0",
707
- id: jsonRequest.id ?? null,
708
- error: {
709
- code: -32020,
710
- message: `HeaderMismatch: 'Mcp-Method' header (${mcpMethod}) does not match JSON-RPC method (${jsonRequest.method})`
711
- }
712
- })
713
- );
714
- return;
715
- }
716
- if (mcpName && jsonRequest.params?.name && mcpName !== jsonRequest.params.name) {
717
- stream.respond({
718
- ":status": 400,
719
- "content-type": "application/json"
720
- });
721
- stream.end(
722
- JSON.stringify({
723
- jsonrpc: "2.0",
724
- id: jsonRequest.id ?? null,
725
- error: {
726
- code: -32020,
727
- message: `HeaderMismatch: 'Mcp-Name' header (${mcpName}) does not match target name (${jsonRequest.params.name})`
728
- }
729
- })
730
- );
731
- return;
732
- }
733
- if (mcpVersion && jsonRequest.params) {
734
- if (!jsonRequest.params._meta) {
735
- jsonRequest.params._meta = {};
736
- }
737
- if (!jsonRequest.params._meta["io.modelcontextprotocol/protocolVersion"]) {
738
- jsonRequest.params._meta["io.modelcontextprotocol/protocolVersion"] = mcpVersion;
739
- }
740
- }
741
- const response = await this.router.dispatch(jsonRequest, authInfo);
742
- if (response) {
743
- stream.respond({
744
- ":status": 200,
745
- "content-type": "application/json"
746
- });
747
- stream.end(JSON.stringify(response));
748
- } else stream.close();
749
- } catch (_e) {
750
- stream.respond({ ":status": 400 });
751
- stream.end();
752
- }
753
- });
754
- }
755
- async listen(port, host = "0.0.0.0") {
756
- if (this.meshNode) {
757
- await this.meshNode.start();
758
- const tools = this.liopServer.listTools();
759
- for (const tool of tools) {
760
- await this.meshNode.announceCapability(tool.name);
761
- log.info(
762
- `[LIOP-Gateway] \u{1F4E1} Announced local tool to Mesh: ${tool.name}`
763
- );
764
- }
765
- }
766
- return new Promise((resolve, reject) => {
767
- this.netServer.on("error", (err) => {
768
- if (err.code === "EADDRINUSE") {
769
- log.info(
770
- `[LIOP-Gateway] FATAL: Port ${port} is already in use by another process.`
771
- );
772
- } else {
773
- log.error(`[LIOP-Gateway] Binding Error: ${err.message}`);
774
- }
775
- reject(err);
776
- });
777
- this.netServer.listen(port, host, () => {
778
- const addr = this.netServer.address();
779
- const actualHost = typeof addr === "string" ? addr : addr?.address || host;
780
- const assignedPort = typeof addr === "string" ? port : addr?.port || port;
781
- log.info(
782
- `[LIOP-Gateway] \u2705 Transformer Mesh Gateway READY and listening on ${actualHost}:${assignedPort}`
783
- );
784
- resolve(assignedPort);
785
- });
786
- });
787
- }
788
- isDrained() {
789
- return this.isDraining;
790
- }
791
- async drain(timeoutMs = 5e3) {
792
- this.isDraining = true;
793
- log.info(
794
- `[LIOP-Gateway] Draining gateway connections (active: ${this.activeRequests}, timeout: ${timeoutMs}ms)...`
795
- );
796
- const start = Date.now();
797
- while (this.activeRequests > 0 && Date.now() - start < timeoutMs) {
798
- await new Promise((resolve) => setTimeout(resolve, 50));
799
- }
800
- await this.stop();
801
- }
802
- async stop() {
803
- if (this.meshNode) {
804
- await this.meshNode.stop();
805
- }
806
- this.rateLimiter.close();
807
- this.netServer.close();
808
- this.h2Server.close();
809
- this.h1Server.close();
810
- }
811
- getRouter() {
812
- return this.router;
813
- }
814
- };
815
-
816
- export { GRPC_WEB_CONSTANTS, InMemoryRateLimiter, LiopHybridGateway, buildProtectedResourceMetadata, decodeFrames, dispatchGrpcWebRequest, encodeDataFrame, encodeTrailerFrame, executeGatewayInterceptor, isGrpcWebRequest };
817
- //# sourceMappingURL=chunk-EJ426URA.js.map
818
- //# sourceMappingURL=chunk-EJ426URA.js.map