@nekzus/liop 1.3.0-alpha.1 → 2.0.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -31
- package/dist/bin/agent.d.ts +0 -1
- package/dist/bin/agent.js +5 -306
- package/dist/bin/agent.js.map +1 -0
- package/dist/{bridge/stream.d.ts → bridge.d.ts} +44 -3
- package/dist/bridge.js +2 -0
- package/dist/bridge.js.map +1 -0
- package/dist/chunk-5OAZNVIU.js +31 -0
- package/dist/chunk-5OAZNVIU.js.map +1 -0
- package/dist/chunk-62YQHKSS.js +3 -0
- package/dist/chunk-62YQHKSS.js.map +1 -0
- package/dist/chunk-7MAGL6ON.js +33 -0
- package/dist/chunk-7MAGL6ON.js.map +1 -0
- package/dist/chunk-ANFXJGMP.js +2 -0
- package/dist/chunk-ANFXJGMP.js.map +1 -0
- package/dist/chunk-DBXGYHKY.js +2 -0
- package/dist/chunk-DBXGYHKY.js.map +1 -0
- package/dist/chunk-HM77MWB6.js +2 -0
- package/dist/chunk-HM77MWB6.js.map +1 -0
- package/dist/chunk-HNDVAKEK.js +24 -0
- package/dist/chunk-HNDVAKEK.js.map +1 -0
- package/dist/chunk-HQZHZM6U.js +2 -0
- package/dist/chunk-HQZHZM6U.js.map +1 -0
- package/dist/chunk-JBMEAXYU.js +13 -0
- package/dist/chunk-JBMEAXYU.js.map +1 -0
- package/dist/chunk-P52IE4L6.js +2 -0
- package/dist/chunk-P52IE4L6.js.map +1 -0
- package/dist/chunk-PPCOS2NU.js +2 -0
- package/dist/chunk-PPCOS2NU.js.map +1 -0
- package/dist/chunk-RWRRBYG4.js +2 -0
- package/dist/chunk-RWRRBYG4.js.map +1 -0
- package/dist/chunk-S6RJHZV2.js +2 -0
- package/dist/chunk-S6RJHZV2.js.map +1 -0
- package/dist/chunk-UVTEJYHN.js +2 -0
- package/dist/chunk-UVTEJYHN.js.map +1 -0
- package/dist/client.d.ts +5 -0
- package/dist/client.js +2 -0
- package/dist/client.js.map +1 -0
- package/dist/{gateway/router.d.ts → gateway.d.ts} +37 -5
- package/dist/gateway.js +2 -0
- package/dist/gateway.js.map +1 -0
- package/dist/{client/index.d.ts → index-CyxNLlz7.d.ts} +24 -5
- package/dist/index.d.ts +313 -12
- package/dist/index.js +31 -12
- package/dist/index.js.map +1 -0
- package/dist/kyber-2WDOTUQX.js +2 -0
- package/dist/kyber-2WDOTUQX.js.map +1 -0
- package/dist/{mesh/node.d.ts → mesh.d.ts} +5 -3
- package/dist/mesh.js +2 -0
- package/dist/mesh.js.map +1 -0
- package/dist/server.d.ts +342 -0
- package/dist/server.js +2 -0
- package/dist/server.js.map +1 -0
- package/dist/types.d.ts +17 -14
- package/dist/types.js +2 -26
- package/dist/types.js.map +1 -0
- package/dist/{crypto/verifier.d.ts → verifier-DTCD9imJ.d.ts} +3 -1
- package/dist/verifier-RQRYXA4C.js +2 -0
- package/dist/verifier-RQRYXA4C.js.map +1 -0
- package/dist/workers/logic-execution.d.ts +9 -2
- package/dist/workers/logic-execution.js +2 -123
- package/dist/workers/logic-execution.js.map +1 -0
- package/dist/workers/zk-verifier.d.ts +4 -2
- package/dist/workers/zk-verifier.js +2 -98
- package/dist/workers/zk-verifier.js.map +1 -0
- package/package.json +32 -21
- package/dist/bridge/index.d.ts +0 -37
- package/dist/bridge/index.js +0 -249
- package/dist/bridge/stream.js +0 -210
- package/dist/client/index.js +0 -275
- package/dist/crypto/logic-image-id.d.ts +0 -3
- package/dist/crypto/logic-image-id.js +0 -27
- package/dist/crypto/verifier.js +0 -97
- package/dist/economy/estimator.d.ts +0 -53
- package/dist/economy/estimator.js +0 -69
- package/dist/economy/index.d.ts +0 -5
- package/dist/economy/index.js +0 -3
- package/dist/economy/otel.d.ts +0 -38
- package/dist/economy/otel.js +0 -100
- package/dist/economy/telemetry.d.ts +0 -77
- package/dist/economy/telemetry.js +0 -224
- package/dist/errors.d.ts +0 -14
- package/dist/errors.js +0 -19
- package/dist/gateway/hybrid.d.ts +0 -23
- package/dist/gateway/hybrid.js +0 -199
- package/dist/gateway/router.js +0 -1036
- package/dist/mesh/index.d.ts +0 -1
- package/dist/mesh/index.js +0 -1
- package/dist/mesh/node.js +0 -853
- package/dist/prompts/adapters.d.ts +0 -16
- package/dist/prompts/adapters.js +0 -55
- package/dist/rpc/client.d.ts +0 -22
- package/dist/rpc/client.js +0 -40
- package/dist/rpc/codec/lpm.d.ts +0 -20
- package/dist/rpc/codec/lpm.js +0 -36
- package/dist/rpc/crypto/aes.d.ts +0 -22
- package/dist/rpc/crypto/aes.js +0 -47
- package/dist/rpc/crypto/kyber.d.ts +0 -27
- package/dist/rpc/crypto/kyber.js +0 -70
- package/dist/rpc/proto.d.ts +0 -2
- package/dist/rpc/proto.js +0 -33
- package/dist/rpc/server.d.ts +0 -13
- package/dist/rpc/server.js +0 -50
- package/dist/rpc/tls.d.ts +0 -26
- package/dist/rpc/tls.js +0 -54
- package/dist/rpc/types.d.ts +0 -28
- package/dist/rpc/types.js +0 -5
- package/dist/sandbox/guardian.d.ts +0 -18
- package/dist/sandbox/guardian.js +0 -58
- package/dist/sandbox/wasi.d.ts +0 -36
- package/dist/sandbox/wasi.js +0 -209
- package/dist/security/guardian.d.ts +0 -22
- package/dist/security/guardian.js +0 -52
- package/dist/security/zk.d.ts +0 -37
- package/dist/security/zk.js +0 -76
- package/dist/server/index.d.ts +0 -189
- package/dist/server/index.js +0 -937
- package/dist/server/pii.d.ts +0 -40
- package/dist/server/pii.js +0 -266
- package/dist/utils/logger.d.ts +0 -21
- package/dist/utils/logger.js +0 -70
- package/dist/utils/mcpCompact.d.ts +0 -11
- package/dist/utils/mcpCompact.js +0 -29
package/README.md
CHANGED
|
@@ -49,16 +49,35 @@ This fundamentally solves the data privacy, bandwidth, and latency challenges of
|
|
|
49
49
|
## Installation
|
|
50
50
|
|
|
51
51
|
```bash
|
|
52
|
-
npm install @nekzus/liop
|
|
52
|
+
npm install @nekzus/liop@latest
|
|
53
53
|
```
|
|
54
54
|
|
|
55
55
|
> **Requirements:** Node.js ≥ 20.0. The SDK uses `node:crypto`, `node:vm`, and `piscina` (worker threads) internally.
|
|
56
56
|
|
|
57
|
+
### Zero-Bloat & Micro-Deployments (Opt-Out)
|
|
58
|
+
|
|
59
|
+
By default, the SDK provides out-of-the-box MCP backward compatibility (`LiopMcpBridge`) by declaring `@modelcontextprotocol/sdk` as an optional dependency (which is automatically resolved by standard installations of NPM, PNPM, or Yarn).
|
|
60
|
+
|
|
61
|
+
For constrained production environments (e.g., Docker, AWS Lambda, Edge/IoT) where every megabyte counts, you can perform a **pure, zero-bloat LIOP installation** by opting out of the optional dependencies:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# npm
|
|
65
|
+
npm install @nekzus/liop@latest --no-optional
|
|
66
|
+
|
|
67
|
+
# pnpm
|
|
68
|
+
pnpm add @nekzus/liop@latest --without optional
|
|
69
|
+
|
|
70
|
+
# yarn
|
|
71
|
+
yarn add @nekzus/liop@latest --ignore-optional
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The SDK uses dynamic `import()` statements under the hood to ensure that MCP translator modules are only loaded if they are actually instantiated, guaranteeing a lightweight memory footprint.
|
|
75
|
+
|
|
57
76
|
---
|
|
58
77
|
|
|
59
78
|
## LIOP Agent (CLI)
|
|
60
79
|
|
|
61
|
-
The SDK includes a zero-config agent (`liop
|
|
80
|
+
The SDK includes a zero-config agent CLI (`liop`) designed to bridge the Logic-Injection-on-Origin Protocol with local AI clients like **Claude Desktop**.
|
|
62
81
|
|
|
63
82
|
### Installation & Run
|
|
64
83
|
|
|
@@ -66,23 +85,28 @@ You can run the agent directly using `npx` (recommended) or install it globally:
|
|
|
66
85
|
|
|
67
86
|
```bash
|
|
68
87
|
# Run instantly
|
|
69
|
-
npx @nekzus/liop
|
|
88
|
+
npx @nekzus/liop@latest
|
|
70
89
|
|
|
71
90
|
# Or install globally
|
|
72
|
-
npm install -g @nekzus/liop
|
|
73
|
-
liop
|
|
91
|
+
npm install -g @nekzus/liop@latest
|
|
92
|
+
liop
|
|
74
93
|
```
|
|
75
94
|
|
|
76
95
|
### 🤖 Claude Desktop Configuration
|
|
77
96
|
|
|
78
|
-
To
|
|
97
|
+
To integrate LIOP into Claude Desktop, update your `claude_desktop_config.json` (typically found in `%APPDATA%\Claude\claude_desktop_config.json` on Windows):
|
|
79
98
|
|
|
80
99
|
```json
|
|
81
100
|
{
|
|
82
101
|
"mcpServers": {
|
|
83
|
-
"liop
|
|
102
|
+
"liop": {
|
|
84
103
|
"command": "npx",
|
|
85
|
-
"args": ["-y", "@nekzus/liop"]
|
|
104
|
+
"args": ["-y", "@nekzus/liop@latest"],
|
|
105
|
+
"env": {
|
|
106
|
+
"LIOP_NEXUS_URL": "http://your-nexus-host:3000",
|
|
107
|
+
"LIOP_LOG_LEVEL": "info",
|
|
108
|
+
"NODE_OPTIONS": "--use-system-ca"
|
|
109
|
+
}
|
|
86
110
|
}
|
|
87
111
|
}
|
|
88
112
|
}
|
|
@@ -95,7 +119,7 @@ The agent automatically manages your P2P identity:
|
|
|
95
119
|
- **Identity Path**: `~/.liop/identity.json`. This file contains your unique PeerID. Keep it safe if you want to maintain a consistent identity in the mesh.
|
|
96
120
|
- **Bootstrap Nodes**: By default, the agent connects to the **LIOP Alpha Nexus**. You can provide custom bootstrap addresses as CLI arguments:
|
|
97
121
|
```bash
|
|
98
|
-
npx @nekzus/liop /ip4/1.2.3.4/tcp/4001/p2p/PEER_ID
|
|
122
|
+
npx @nekzus/liop@latest /ip4/1.2.3.4/tcp/4001/p2p/PEER_ID
|
|
99
123
|
```
|
|
100
124
|
|
|
101
125
|
---
|
|
@@ -188,9 +212,28 @@ new LiopServer(
|
|
|
188
212
|
serverInfo: { name: string; version: string },
|
|
189
213
|
config?: {
|
|
190
214
|
capabilities?: Record<string, unknown>;
|
|
215
|
+
workerPool?: {
|
|
216
|
+
enabled?: boolean; // Enable OS-thread sandboxing (default: true)
|
|
217
|
+
maxThreads?: number; // Max worker threads (default: CPU count)
|
|
218
|
+
maxHeapMb?: number; // V8 heap limit per worker (default: 64, env: LIOP_WORKER_MAX_HEAP_MB)
|
|
219
|
+
};
|
|
191
220
|
security?: {
|
|
192
221
|
piiPatterns?: PiiRule[]; // Regex/validator rules for PII detection
|
|
193
222
|
forbiddenKeys?: string[]; // Keys stripped from outgoing responses
|
|
223
|
+
enableNerScanning?: boolean; // NLP entity detection via compromise (default: false)
|
|
224
|
+
rateLimit?: { // Sliding window rate limiter per tool
|
|
225
|
+
maxPerWindow?: number; // Max calls per window (default: 15)
|
|
226
|
+
windowMs?: number; // Window duration in ms (default: 60000)
|
|
227
|
+
};
|
|
228
|
+
globalRateLimit?: { // Cross-tool aggregate rate limiter
|
|
229
|
+
maxPerWindow?: number; // Max total calls per window (default: 40)
|
|
230
|
+
windowMs?: number; // Window duration in ms (default: 60000)
|
|
231
|
+
};
|
|
232
|
+
};
|
|
233
|
+
taxonomy?: { // Data domain classification
|
|
234
|
+
domain?: string; // e.g., "finance", "healthcare"
|
|
235
|
+
clearanceTier?: string; // e.g., "tier-0", "tier-1"
|
|
236
|
+
executionTypes?: string[]; // e.g., ["aggregation", "analytics"]
|
|
194
237
|
};
|
|
195
238
|
}
|
|
196
239
|
)
|
|
@@ -243,22 +286,32 @@ await bridge.connect();
|
|
|
243
286
|
### The Shield — Multi-Layer Defense
|
|
244
287
|
|
|
245
288
|
```
|
|
246
|
-
|
|
247
|
-
│ Layer 1: Guardian AST (Zero-Time Static Analysis)
|
|
248
|
-
│
|
|
249
|
-
│
|
|
250
|
-
|
|
251
|
-
│ Layer 2: WASI Sandbox (V8 Isolate)
|
|
252
|
-
│
|
|
253
|
-
|
|
254
|
-
│
|
|
255
|
-
|
|
256
|
-
│
|
|
257
|
-
|
|
258
|
-
│
|
|
259
|
-
|
|
260
|
-
│
|
|
261
|
-
|
|
289
|
+
┌───────────────────────────────────────────────────────────┐
|
|
290
|
+
│ Layer 1: Guardian AST (Zero-Time Static Analysis) │
|
|
291
|
+
│ 14-function WASI allowlist • 128 import cap • Blocks │
|
|
292
|
+
│ require, import(), fs, eval, fetch, __proto__ │
|
|
293
|
+
├───────────────────────────────────────────────────────────┤
|
|
294
|
+
│ Layer 2: WASI Sandbox (V8 Isolate) │
|
|
295
|
+
│ 25 poisoned globals (incl. Date, TypedArrays) • │
|
|
296
|
+
│ CPU Fuel limits • 5s timeout • maxHeapMb (64MB default) │
|
|
297
|
+
│ Object.freeze() on 6 core prototypes │
|
|
298
|
+
├───────────────────────────────────────────────────────────┤
|
|
299
|
+
│ Layer 3: Taint Analyzer (IFC — Static) │
|
|
300
|
+
│ Acorn AST 3-pass analysis blocks PII side-channels: │
|
|
301
|
+
│ charCodeAt, boolean inference, arithmetic derivation │
|
|
302
|
+
├───────────────────────────────────────────────────────────┤
|
|
303
|
+
│ Layer 4: PII Shield (Egress Filter) │
|
|
304
|
+
│ 4-stage pipeline: exact key → fuzzy key → pattern │
|
|
305
|
+
│ validators (Luhn, IBAN Mod-97) → NER (compromise) │
|
|
306
|
+
├───────────────────────────────────────────────────────────┤
|
|
307
|
+
│ Layer 5: Aggregation-First Policy │
|
|
308
|
+
│ Blocks raw row export • maxOutputRows (default: 10) • │
|
|
309
|
+
│ Conditional error: detailed (dev) vs opaque (production) │
|
|
310
|
+
├───────────────────────────────────────────────────────────┤
|
|
311
|
+
│ Layer 6: ZK-Receipt (Integrity Verification) │
|
|
312
|
+
│ SHA-256 ImageID + HMAC-SHA256 Seal (Kyber768-derived) │
|
|
313
|
+
│ Timing-safe verification • LiopMcpBridge auto-verifies │
|
|
314
|
+
└───────────────────────────────────────────────────────────┘
|
|
262
315
|
```
|
|
263
316
|
|
|
264
317
|
### PII Patterns
|
|
@@ -307,9 +360,10 @@ The following shows a complete Logic-Injection-on-Origin execution cycle (handle
|
|
|
307
360
|
2. LiopServer receives the payload via tools/call (JSON-RPC or direct)
|
|
308
361
|
3. Guardian AST inspects for sandbox escapes (zero-time heuristic analysis)
|
|
309
362
|
4. Code executes inside a V8 isolate with CPU fuel limits (no Node.js globals)
|
|
310
|
-
5.
|
|
311
|
-
6.
|
|
312
|
-
7.
|
|
363
|
+
5. Taint Analyzer blocks PII side-channel derivation (charCodeAt, boolean inference)
|
|
364
|
+
6. PII Shield scans output for forbidden data and keys
|
|
365
|
+
7. ZK-Receipt generated (SHA-256 ImageID + HMAC-SHA256 seal)
|
|
366
|
+
8. Result + receipt returned to the LLM (raw data never exposed)
|
|
313
367
|
```
|
|
314
368
|
|
|
315
369
|
### Data Dictionary & Zero-Shot Autonomy
|
|
@@ -390,11 +444,11 @@ await server.connectToMesh();
|
|
|
390
444
|
|
|
391
445
|
This package is continuously tested across multiple platforms and Node.js versions via CI/CD:
|
|
392
446
|
|
|
393
|
-
- **
|
|
447
|
+
- **285+ tests** spanning unit, integration, conformance, adversarial, and crossnet suites
|
|
394
448
|
- **Multi-OS matrix:** Ubuntu, Windows, macOS
|
|
395
|
-
- **Node.js versions:**
|
|
449
|
+
- **Node.js versions:** 20.x, 22.x
|
|
396
450
|
- **Code quality:** Enforced by [Biome.js](https://biomejs.dev/) (linting + formatting)
|
|
397
|
-
- **Security:** Verified defense-in-depth architecture — see [Security Architecture](https://nekzus-32.mintlify.app/typescript-sdk/security)
|
|
451
|
+
- **Security:** Verified 6-layer defense-in-depth architecture — see [Security Architecture](https://nekzus-32.mintlify.app/typescript-sdk/security)
|
|
398
452
|
|
|
399
453
|
> To run tests locally or contribute, clone the [repository](https://github.com/Nekzus/LIOP) and follow the [Contributing Guide](https://github.com/Nekzus/LIOP/blob/main/CONTRIBUTING.md).
|
|
400
454
|
|
package/dist/bin/agent.d.ts
CHANGED
package/dist/bin/agent.js
CHANGED
|
@@ -1,307 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import { MeshNode } from "../mesh/index.js";
|
|
8
|
-
import { LiopServer } from "../server/index.js";
|
|
9
|
-
import { log } from "../utils/logger.js";
|
|
10
|
-
/**
|
|
11
|
-
* Resolves a full libp2p multiaddr (with PeerID) from a LIOP node's
|
|
12
|
-
* HTTP health endpoint. This enables zero-config bootstrap — users
|
|
13
|
-
* only need to provide a URL, not a cryptographic PeerID.
|
|
14
|
-
*
|
|
15
|
-
* @param url - HTTP URL of a LIOP node's health endpoint (e.g. "http://host:3000")
|
|
16
|
-
* @returns Full multiaddr string with PeerID, or null if resolution fails
|
|
17
|
-
*/
|
|
18
|
-
async function resolveBootstrapFromUrl(url) {
|
|
19
|
-
try {
|
|
20
|
-
const healthUrl = url.endsWith("/health") ? url : `${url}/health`;
|
|
21
|
-
const response = await fetch(healthUrl, {
|
|
22
|
-
headers: { Accept: "application/json" },
|
|
23
|
-
signal: AbortSignal.timeout(10000), // Increased to 10s
|
|
24
|
-
});
|
|
25
|
-
if (!response.ok)
|
|
26
|
-
return null;
|
|
27
|
-
const data = await response.json();
|
|
28
|
-
if (!data.mesh?.multiaddrs?.length || !data.mesh?.peerId)
|
|
29
|
-
return null;
|
|
30
|
-
// Find TCP multiaddr (prefer non-websocket for stability)
|
|
31
|
-
const tcpAddr = data.mesh.multiaddrs.find((a) => a.includes("/tcp/") &&
|
|
32
|
-
!a.includes("/ws") &&
|
|
33
|
-
!a.includes("/ip4/127.0.0.1/"));
|
|
34
|
-
if (!tcpAddr)
|
|
35
|
-
return null;
|
|
36
|
-
// Rewrite internal Docker IP using industrial mapper if available
|
|
37
|
-
let resolved = industrialAddressMapper(tcpAddr);
|
|
38
|
-
if (!resolved || resolved === tcpAddr) {
|
|
39
|
-
const urlHost = new URL(url).hostname;
|
|
40
|
-
resolved = tcpAddr.replace(/\/ip4\/[^/]+/, `/ip4/${urlHost}`);
|
|
41
|
-
}
|
|
42
|
-
if (!resolved)
|
|
43
|
-
return null;
|
|
44
|
-
resolved += resolved.includes("/p2p/") ? "" : `/p2p/${data.mesh.peerId}`;
|
|
45
|
-
return resolved;
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Normalizes a bootstrap multiaddr string.
|
|
53
|
-
* If the address contains a Docker bridge IP (172.16-31.x.x) or Loopback (127.0.0.1),
|
|
54
|
-
* rewrites it to the host accessible via LIOP_NEXUS_URL (e.g. WSL2 IP).
|
|
55
|
-
* This is critical when WSL2 mirror-mode networking is broken.
|
|
56
|
-
*/
|
|
57
|
-
function normalizeBootstrap(addr) {
|
|
58
|
-
const trimmed = addr.trim();
|
|
59
|
-
// Remap Docker bridge IPs and ANY external physical IPs to 127.0.0.1
|
|
60
|
-
// because Test-NetConnection confirmed 127.0.0.1 is the only reliable path to Docker ports.
|
|
61
|
-
const dockerIpRegex = /\/ip4\/172\.(1[6-9]|2[0-9]|3[0-1])\.[0-9]{1,3}\.[0-9]{1,3}/;
|
|
62
|
-
const loopbackRegex = /\/ip4\/127\.0\.0\.1/;
|
|
63
|
-
const physicalIpRegex = /\/ip4\/192\.168\.[0-9]{1,3}\.[0-9]{1,3}/;
|
|
64
|
-
if (dockerIpRegex.test(trimmed) ||
|
|
65
|
-
loopbackRegex.test(trimmed) ||
|
|
66
|
-
physicalIpRegex.test(trimmed)) {
|
|
67
|
-
const targetIp = "127.0.0.1";
|
|
68
|
-
const normalized = trimmed
|
|
69
|
-
.replace(dockerIpRegex, `/ip4/${targetIp}`)
|
|
70
|
-
.replace(loopbackRegex, `/ip4/${targetIp}`)
|
|
71
|
-
.replace(physicalIpRegex, `/ip4/${targetIp}`);
|
|
72
|
-
if (normalized !== trimmed) {
|
|
73
|
-
log.info(`[LIOP-Agent] 🔄 Local Routing Hack → Forced 127.0.0.1: ${normalized}`);
|
|
74
|
-
}
|
|
75
|
-
return normalized;
|
|
76
|
-
}
|
|
77
|
-
return trimmed;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* industrialAddressMapper
|
|
81
|
-
*
|
|
82
|
-
* Mapea IPs internas de Docker a puertos industriales mapeados en el Host.
|
|
83
|
-
* Nexus (172.20.0.10) -> 13001
|
|
84
|
-
* Vault (172.20.0.11) -> 13003
|
|
85
|
-
* Bank (172.20.0.12) -> 13004
|
|
86
|
-
* Oracle(172.20.0.13) -> 13005
|
|
87
|
-
*/
|
|
88
|
-
function industrialAddressMapper(addr) {
|
|
89
|
-
if (addr.includes("/ip4/172.20.0.10"))
|
|
90
|
-
return addr.replace(/\/ip4\/172\.20\.0\.10\/tcp\/[0-9]+/, "/ip4/127.0.0.1/tcp/13001");
|
|
91
|
-
if (addr.includes("/ip4/172.20.0.11"))
|
|
92
|
-
return addr.replace(/\/ip4\/172\.20\.0\.11\/tcp\/[0-9]+/, "/ip4/127.0.0.1/tcp/13003");
|
|
93
|
-
if (addr.includes("/ip4/172.20.0.12"))
|
|
94
|
-
return addr.replace(/\/ip4\/172\.20\.0\.12\/tcp\/[0-9]+/, "/ip4/127.0.0.1/tcp/13004");
|
|
95
|
-
if (addr.includes("/ip4/172.20.0.13"))
|
|
96
|
-
return addr.replace(/\/ip4\/172\.20\.0\.13\/tcp\/[0-9]+/, "/ip4/127.0.0.1/tcp/13005");
|
|
97
|
-
// Drop container-internal loopbacks to prevent the Host Agent from dialing itself or conflicting ports
|
|
98
|
-
if (addr.includes("/ip4/127.0.0.1/tcp/4000") ||
|
|
99
|
-
addr.includes("/ip4/127.0.0.1/tcp/3000")) {
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
return addr;
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* LIOP Agent (Zero-Config CLI)
|
|
106
|
-
*
|
|
107
|
-
* Secure Logic-on-Origin gateway for Claude Desktop.
|
|
108
|
-
* Communicates via STDIO / JSON-RPC.
|
|
109
|
-
*
|
|
110
|
-
* All tool discovery is DYNAMIC via the /liop/manifest/1.0.0 protocol.
|
|
111
|
-
* No hardcoded tools, PeerIDs, or port mappings.
|
|
112
|
-
*/
|
|
113
|
-
async function main() {
|
|
114
|
-
const buildTime = new Date().toISOString();
|
|
115
|
-
log.info(`[LIOP-Agent] 🚀 Version 1.2.0-alpha.9 | Build: ${buildTime}`);
|
|
116
|
-
const liopDir = path.join(os.homedir(), ".liop");
|
|
117
|
-
const identityPath = path.join(liopDir, "identity.json");
|
|
118
|
-
if (!fs.existsSync(liopDir)) {
|
|
119
|
-
fs.mkdirSync(liopDir, { recursive: true });
|
|
120
|
-
}
|
|
121
|
-
// 1. Determine Bootstrap Nodes (Zero-Config Discovery)
|
|
122
|
-
let bootstrapNodes = [];
|
|
123
|
-
// Command line arguments take precedence
|
|
124
|
-
const args = process.argv.slice(2);
|
|
125
|
-
if (args.length > 0) {
|
|
126
|
-
bootstrapNodes = args.filter((a) => a.startsWith("/"));
|
|
127
|
-
}
|
|
128
|
-
// Priority 1: Physical Beacons (Industrial Pattern) - DETERMINISTIC & INSTANT
|
|
129
|
-
if (bootstrapNodes.length === 0) {
|
|
130
|
-
const searchDirs = [];
|
|
131
|
-
// Priority 1.1: Explicit file from environment variable
|
|
132
|
-
if (process.env.LIOP_BOOTSTRAP_FILE) {
|
|
133
|
-
const filePath = path.resolve(process.env.LIOP_BOOTSTRAP_FILE);
|
|
134
|
-
if (fs.existsSync(filePath)) {
|
|
135
|
-
const addr = fs.readFileSync(filePath, "utf8").trim();
|
|
136
|
-
if (addr)
|
|
137
|
-
bootstrapNodes.push(normalizeBootstrap(addr));
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
// Priority 1.2: Traditional locations (Scan for all *.multiaddr)
|
|
141
|
-
searchDirs.push(process.cwd(), path.join(process.cwd(), "tests/infra/nexus-data"), liopDir, path.join(path
|
|
142
|
-
.dirname(new URL(import.meta.url).pathname)
|
|
143
|
-
.replace(/^\/([A-Z]:)/, "$1"), "../../tests/infra/nexus-data"));
|
|
144
|
-
for (const dir of searchDirs) {
|
|
145
|
-
try {
|
|
146
|
-
if (fs.existsSync(dir)) {
|
|
147
|
-
const files = fs.readdirSync(dir);
|
|
148
|
-
const multiaddrFiles = files.filter((f) => f.endsWith(".multiaddr"));
|
|
149
|
-
for (const file of multiaddrFiles) {
|
|
150
|
-
const filePath = path.join(dir, file);
|
|
151
|
-
const addr = fs.readFileSync(filePath, "utf8").trim();
|
|
152
|
-
if (addr) {
|
|
153
|
-
const normalized = normalizeBootstrap(addr);
|
|
154
|
-
if (!bootstrapNodes.includes(normalized)) {
|
|
155
|
-
bootstrapNodes.push(normalized);
|
|
156
|
-
log.info(`[LIOP-Agent] ✅ Loaded beacon: ${file} from ${dir}`);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
// If we found any beacons in this directory, we consider discovery successful for this layer
|
|
161
|
-
if (bootstrapNodes.length > 0)
|
|
162
|
-
break;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
catch (_e) {
|
|
166
|
-
/* ignore */
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
// Priority 2: Auto-Discovery via NEXUS URL (Aggressive Parallel Discovery)
|
|
171
|
-
if (process.env.LIOP_NEXUS_URL) {
|
|
172
|
-
const nexusUrl = process.env.LIOP_NEXUS_URL;
|
|
173
|
-
log.info(`[LIOP-Agent] 🌐 Running parallel discovery from: ${nexusUrl} (Sources Found: ${bootstrapNodes.length})`);
|
|
174
|
-
const resolved = await resolveBootstrapFromUrl(nexusUrl);
|
|
175
|
-
if (resolved) {
|
|
176
|
-
const normalized = normalizeBootstrap(resolved);
|
|
177
|
-
if (!bootstrapNodes.includes(normalized)) {
|
|
178
|
-
bootstrapNodes.push(normalized);
|
|
179
|
-
log.info(`[LIOP-Agent] ✅ Added bootstrap from URL discovery: ${normalized}`);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
// Priority 3: Environment variable (direct multiaddr)
|
|
184
|
-
if (bootstrapNodes.length === 0 && process.env.LIOP_BOOTSTRAP) {
|
|
185
|
-
bootstrapNodes.push(process.env.LIOP_BOOTSTRAP.trim());
|
|
186
|
-
}
|
|
187
|
-
// Final fallback: local Nexus bootstrap for demo environments.
|
|
188
|
-
// Avoid injecting stale static peer IDs when discovery already found valid peers.
|
|
189
|
-
if (bootstrapNodes.length === 0) {
|
|
190
|
-
bootstrapNodes.push("/ip4/127.0.0.1/tcp/13001/p2p/12D3KooWD8FUFdnLQzzLFNdicsaTknM5cpD7os9sK9NWVSVABJMD");
|
|
191
|
-
}
|
|
192
|
-
// Sanitize/validate all candidate multiaddrs so malformed PeerIDs don't crash startup.
|
|
193
|
-
bootstrapNodes = bootstrapNodes.filter((addr) => {
|
|
194
|
-
try {
|
|
195
|
-
multiaddr(addr);
|
|
196
|
-
return true;
|
|
197
|
-
}
|
|
198
|
-
catch {
|
|
199
|
-
log.warn(`[LIOP-Agent] Ignoring invalid bootstrap multiaddr: ${addr}`);
|
|
200
|
-
return false;
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
// If no bootstrap nodes found, the agent operates in standalone mode.
|
|
204
|
-
// It will only serve local tools until peers are discovered.
|
|
205
|
-
if (bootstrapNodes.length === 0) {
|
|
206
|
-
log.info("[LIOP-Agent] No bootstrap nodes configured. Operating in standalone mode.");
|
|
207
|
-
log.info("[LIOP-Agent] Pass a multiaddr as argument or create 'nexus.multiaddr' file.");
|
|
208
|
-
}
|
|
209
|
-
// Initialize local server node (lightweight, no tools registered locally)
|
|
210
|
-
const liopServer = new LiopServer({
|
|
211
|
-
name: "@nekzus/liop-agent",
|
|
212
|
-
version: "1.0.0",
|
|
213
|
-
});
|
|
214
|
-
// Enable Zero-Shot Autonomy (Industrial Prompt Injection)
|
|
215
|
-
liopServer.enableZeroShotAutonomy();
|
|
216
|
-
// 2. Mesh Node Configuration
|
|
217
|
-
const meshNode = new MeshNode({
|
|
218
|
-
identityPath: identityPath,
|
|
219
|
-
bootstrapNodes: bootstrapNodes,
|
|
220
|
-
addressMapper: industrialAddressMapper,
|
|
221
|
-
});
|
|
222
|
-
// Start P2P Mesh
|
|
223
|
-
await meshNode.start();
|
|
224
|
-
// 3. Initialize the Dynamic Router
|
|
225
|
-
// No hardcoded tools — all discovery happens via liop:manifest protocol
|
|
226
|
-
const router = new LiopMcpRouter(liopServer, meshNode);
|
|
227
|
-
// Proactive Notification to Claude Desktop when tools/resources are discovered dynamically
|
|
228
|
-
router.onToolsChanged = () => {
|
|
229
|
-
process.stdout.write(`{"jsonrpc":"2.0","method":"notifications/tools/list_changed"}\n`);
|
|
230
|
-
process.stdout.write(`{"jsonrpc":"2.0","method":"notifications/resources/list_changed"}\n`);
|
|
231
|
-
};
|
|
232
|
-
// Initial warming period (2s) then Adaptive Background Discovery
|
|
233
|
-
// Polls DHT for new nodes and triggers onToolsChanged when topology shifts.
|
|
234
|
-
// Uses exponential backoff to reduce polling load on stable meshes.
|
|
235
|
-
setTimeout(() => {
|
|
236
|
-
// biome-ignore lint/suspicious/noExplicitAny: access internal for telemetry
|
|
237
|
-
const rtSize = meshNode.getRoutingTableSize?.() || 0;
|
|
238
|
-
log.info(`[LIOP-Agent] Warm-up complete. Routing Table size: ${rtSize}`);
|
|
239
|
-
router.refreshManifestCache(true).catch(() => { });
|
|
240
|
-
}, 2000);
|
|
241
|
-
const POLL_BASE_MS = 10_000;
|
|
242
|
-
const POLL_MAX_MS = 120_000;
|
|
243
|
-
let pollIntervalMs = POLL_BASE_MS;
|
|
244
|
-
const scheduleAdaptivePoll = () => {
|
|
245
|
-
setTimeout(async () => {
|
|
246
|
-
const prevSize = router.getCacheSize();
|
|
247
|
-
await router.refreshManifestCache(true).catch(() => { });
|
|
248
|
-
const newSize = router.getCacheSize();
|
|
249
|
-
if (newSize !== prevSize) {
|
|
250
|
-
// Topology changed — reset to aggressive polling
|
|
251
|
-
pollIntervalMs = POLL_BASE_MS;
|
|
252
|
-
log.info(`[LIOP-Agent] Topology change detected (${prevSize} → ${newSize}). Resetting poll to ${POLL_BASE_MS / 1000}s.`);
|
|
253
|
-
}
|
|
254
|
-
else {
|
|
255
|
-
// Stable — relax polling interval (factor 1.5)
|
|
256
|
-
pollIntervalMs = Math.min(Math.round(pollIntervalMs * 1.5), POLL_MAX_MS);
|
|
257
|
-
}
|
|
258
|
-
scheduleAdaptivePoll();
|
|
259
|
-
}, pollIntervalMs);
|
|
260
|
-
};
|
|
261
|
-
scheduleAdaptivePoll();
|
|
262
|
-
// 4. STDIO Transport — Buffered Line Reader
|
|
263
|
-
// Uses readline to guarantee complete JSON-RPC messages before parsing.
|
|
264
|
-
// Raw stdin.on("data") can fragment large payloads across multiple chunks.
|
|
265
|
-
const readline = await import("node:readline");
|
|
266
|
-
const rl = readline.createInterface({
|
|
267
|
-
input: process.stdin,
|
|
268
|
-
terminal: false,
|
|
269
|
-
});
|
|
270
|
-
process.stdout.on("error", (err) => {
|
|
271
|
-
if (err.code === "EPIPE") {
|
|
272
|
-
process.exit(0); // Graceful exit when Claude Desktop disconnects
|
|
273
|
-
}
|
|
274
|
-
});
|
|
275
|
-
rl.on("line", async (line) => {
|
|
276
|
-
const trimmed = line.trim();
|
|
277
|
-
if (!trimmed)
|
|
278
|
-
return;
|
|
279
|
-
try {
|
|
280
|
-
const request = JSON.parse(trimmed);
|
|
281
|
-
if (request.method) {
|
|
282
|
-
const response = await router.dispatch(request);
|
|
283
|
-
if (response) {
|
|
284
|
-
process.stdout.write(`${JSON.stringify(response)}\n`);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
catch (_err) {
|
|
289
|
-
// Silent catch for binary noise or malformed lines
|
|
290
|
-
}
|
|
291
|
-
});
|
|
292
|
-
rl.on("close", () => {
|
|
293
|
-
process.exit(0);
|
|
294
|
-
});
|
|
295
|
-
// Status directed only to stderr
|
|
296
|
-
log.info(`[LIOP-Agent] Guarding Claude Desktop via STDIO.`);
|
|
297
|
-
log.info(`[LIOP-Agent] P2P Mesh: Joined (${bootstrapNodes.length} bootstraps)`);
|
|
298
|
-
log.info("[LIOP-Agent] Tool discovery: Dynamic via /liop/manifest/1.0.0");
|
|
299
|
-
process.on("SIGINT", async () => {
|
|
300
|
-
await meshNode.stop();
|
|
301
|
-
process.exit(0);
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
main().catch((err) => {
|
|
305
|
-
log.error(`[LIOP-Agent] Fatal Error: ${err.message}`);
|
|
306
|
-
process.exit(1);
|
|
307
|
-
});
|
|
2
|
+
import {f}from'../chunk-5OAZNVIU.js';import {g}from'../chunk-7MAGL6ON.js';import'../chunk-UVTEJYHN.js';import'../chunk-ANFXJGMP.js';import'../chunk-DBXGYHKY.js';import'../chunk-HM77MWB6.js';import'../chunk-RWRRBYG4.js';import {a as a$1}from'../chunk-PPCOS2NU.js';import {a}from'../chunk-S6RJHZV2.js';import*as l from'fs';import*as _ from'os';import*as d from'path';import {multiaddr}from'@multiformats/multiaddr';async function x(t){try{let c=t.endsWith("/health")?t:`${t}/health`,u=await fetch(c,{headers:{Accept:"application/json"},signal:AbortSignal.timeout(1e4)});if(!u.ok)return null;let e=await u.json();if(!e.mesh?.multiaddrs?.length||!e.mesh?.peerId)return null;let p=e.mesh.multiaddrs.find(a=>a.includes("/tcp/")&&!a.includes("/ws")&&!a.includes("/ip4/127.0.0.1/"));if(!p)return null;let i=process.env.NODE_ENV==="development"||process.env.NODE_ENV==="test"?N(p):p;if(!i||i===p){let a=new URL(t).hostname;i=p.replace(/\/ip4\/[^/]+/,`/ip4/${a}`);}return i?(i+=i.includes("/p2p/")?"":`/p2p/${e.mesh.peerId}`,i):null}catch{return null}}function P(t){let c=t.trim(),u=/\/ip4\/172\.(1[6-9]|2[0-9]|3[0-1])\.[0-9]{1,3}\.[0-9]{1,3}/,e=/\/ip4\/127\.0\.0\.1/,p=/\/ip4\/192\.168\.[0-9]{1,3}\.[0-9]{1,3}/;if(u.test(c)||e.test(c)||p.test(c)){let f="127.0.0.1",i=c.replace(u,`/ip4/${f}`).replace(e,`/ip4/${f}`).replace(p,`/ip4/${f}`);return i!==c&&a.info(`[LIOP-Agent] \u{1F504} Local Routing Hack \u2192 Forced 127.0.0.1: ${i}`),i}return c}function N(t){return t.includes("/ip4/172.20.0.10")?t.replace(/\/ip4\/172\.20\.0\.10\/tcp\/[0-9]+/,"/ip4/127.0.0.1/tcp/13001"):t.includes("/ip4/172.20.0.11")?t.replace(/\/ip4\/172\.20\.0\.11\/tcp\/[0-9]+/,"/ip4/127.0.0.1/tcp/13003"):t.includes("/ip4/172.20.0.12")?t.replace(/\/ip4\/172\.20\.0\.12\/tcp\/[0-9]+/,"/ip4/127.0.0.1/tcp/13004"):t.includes("/ip4/172.20.0.13")?t.replace(/\/ip4\/172\.20\.0\.13\/tcp\/[0-9]+/,"/ip4/127.0.0.1/tcp/13005"):t.includes("/ip4/127.0.0.1/tcp/4000")||t.includes("/ip4/127.0.0.1/tcp/3000")?null:t}async function E(){if((process.platform==="win32"||process.platform==="darwin")&&!process.execArgv.includes("--use-system-ca")&&!(process.env.NODE_OPTIONS??"").includes("--use-system-ca")){let{spawn:s}=await import('child_process'),n=s(process.execPath,["--use-system-ca",...process.argv.slice(1)],{stdio:"inherit",env:process.env});n.on("exit",r=>process.exit(r??1)),n.on("error",()=>process.exit(1)),await new Promise(()=>{});return}let t=new Date().toISOString();a.info(`[LIOP-Agent] \u{1F680} Version 1.2.0-alpha.9 | Build: ${t}`);let c=d.join(_.homedir(),".liop"),u=d.join(c,"identity.json");l.existsSync(c)||l.mkdirSync(c,{recursive:true});let e=[],p=process.argv.slice(2);if(p.length>0&&(e=p.filter(s=>s.startsWith("/"))),e.length===0){let s=[];if(process.env.LIOP_BOOTSTRAP_FILE){a.warn("LIOP_BOOTSTRAP_FILE is deprecated and will be removed in the next major version. Use LIOP_NEXUS_URL for Auto-Discovery instead.");let n=d.resolve(process.env.LIOP_BOOTSTRAP_FILE);if(l.existsSync(n)){let r=l.readFileSync(n,"utf8").trim();r&&e.push(P(r));}}s.push(process.cwd(),d.join(process.cwd(),"tests/infra/nexus-data"),c,d.join(d.dirname(new URL(import.meta.url).pathname).replace(/^\/([A-Z]:)/,"$1"),"../../tests/infra/nexus-data"));for(let n of s)try{if(l.existsSync(n)){let h=l.readdirSync(n).filter(g=>g.endsWith(".multiaddr"));for(let g of h){let T=d.join(n,g),I=l.readFileSync(T,"utf8").trim();if(I){let S=P(I);e.includes(S)||(e.push(S),a.info(`[LIOP-Agent] \u2705 Loaded beacon: ${g} from ${n}`));}}if(e.length>0)break}}catch{}}if(process.env.LIOP_NEXUS_URL){let s=process.env.LIOP_NEXUS_URL;a.info(`[LIOP-Agent] \u{1F310} Running parallel discovery from: ${s} (Sources Found: ${e.length})`);let n=await x(s);if(n){let r=P(n);e.includes(r)||(e.push(r),a.info(`[LIOP-Agent] \u2705 Added bootstrap from URL discovery: ${r}`));}}e.length===0&&process.env.LIOP_BOOTSTRAP&&e.push(process.env.LIOP_BOOTSTRAP.trim()),e.length===0&&e.push("/ip4/127.0.0.1/tcp/13001/p2p/12D3KooWD8FUFdnLQzzLFNdicsaTknM5cpD7os9sK9NWVSVABJMD"),e=e.filter(s=>{try{return multiaddr(s),!0}catch{return a.warn(`[LIOP-Agent] Ignoring invalid bootstrap multiaddr: ${s}`),false}}),e.length===0&&(a.info("[LIOP-Agent] No bootstrap nodes configured. Operating in standalone mode."),a.info("[LIOP-Agent] Pass a multiaddr as argument or create 'nexus.multiaddr' file."));let f$1=new f({name:"@nekzus/liop",version:"1.0.0"});f$1.enableZeroShotAutonomy();let i=new a$1({identityPath:u,bootstrapNodes:e,addressMapper:process.env.NODE_ENV==="development"||process.env.NODE_ENV==="test"?N:void 0});await i.start();let a$2=new g(f$1,i);a$2.onToolsChanged=()=>{process.stdout.write(`{"jsonrpc":"2.0","method":"notifications/tools/list_changed"}
|
|
3
|
+
`),process.stdout.write(`{"jsonrpc":"2.0","method":"notifications/resources/list_changed"}
|
|
4
|
+
`);},setTimeout(()=>{let s=i.getRoutingTableSize?.()||0;a.info(`[LIOP-Agent] Warm-up complete. Routing Table size: ${s}`),a$2.refreshManifestCache(true).catch(()=>{});},2e3);let O=1e4,R=12e4,m=O,v=()=>{setTimeout(async()=>{let s=a$2.getCacheSize();await a$2.refreshManifestCache(true).catch(()=>{});let n=a$2.getCacheSize();n!==s?(m=O,a.info(`[LIOP-Agent] Topology change detected (${s} \u2192 ${n}). Resetting poll to ${O/1e3}s.`)):m=Math.min(Math.round(m*1.5),R),v();},m);};v();let L=(await import('readline')).createInterface({input:process.stdin,terminal:false});process.stdout.on("error",s=>{s.code==="EPIPE"&&process.exit(0);}),L.on("line",async s=>{let n=s.trim();if(n)try{let r=JSON.parse(n);if(r.method){let h=await a$2.dispatch(r);h&&process.stdout.write(`${JSON.stringify(h)}
|
|
5
|
+
`);}}catch{}}),L.on("close",()=>{process.exit(0);}),a.info("[LIOP-Agent] Guarding Claude Desktop via STDIO."),a.info(`[LIOP-Agent] P2P Mesh: Joined (${e.length} bootstraps)`),a.info("[LIOP-Agent] Tool discovery: Dynamic via /liop/manifest/1.0.0"),process.on("SIGINT",async()=>{await i.stop(),process.exit(0);});}E().catch(t=>{a.error(`[LIOP-Agent] Fatal Error: ${t.message}`),process.exit(1);});//# sourceMappingURL=agent.js.map
|
|
6
|
+
//# sourceMappingURL=agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/bin/agent.ts"],"names":["resolveBootstrapFromUrl","url","healthUrl","response","data","tcpAddr","resolved","industrialAddressMapper","urlHost","normalizeBootstrap","addr","trimmed","dockerIpRegex","loopbackRegex","physicalIpRegex","targetIp","normalized","log","main","spawn","child","code","buildTime","liopDir","identityPath","bootstrapNodes","args","a","searchDirs","filePath","dir","multiaddrFiles","f","file","nexusUrl","multiaddr","liopServer","LiopServer","meshNode","MeshNode","router","LiopMcpRouter","rtSize","POLL_BASE_MS","POLL_MAX_MS","pollIntervalMs","scheduleAdaptivePoll","prevSize","newSize","rl","err","line","request"],"mappings":"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","file":"agent.js","sourcesContent":["#!/usr/bin/env node\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { multiaddr } from \"@multiformats/multiaddr\";\nimport { LiopMcpRouter } from \"../gateway/router.js\";\nimport { MeshNode } from \"../mesh/index.js\";\nimport { LiopServer } from \"../server/index.js\";\nimport type { McpRequest } from \"../types.js\";\nimport { log } from \"../utils/logger.js\";\n\n/**\n * Resolves a full libp2p multiaddr (with PeerID) from a LIOP node's\n * HTTP health endpoint. This enables zero-config bootstrap — users\n * only need to provide a URL, not a cryptographic PeerID.\n *\n * @param url - HTTP URL of a LIOP node's health endpoint (e.g. \"http://host:3000\")\n * @returns Full multiaddr string with PeerID, or null if resolution fails\n */\nasync function resolveBootstrapFromUrl(url: string): Promise<string | null> {\n\ttry {\n\t\tconst healthUrl = url.endsWith(\"/health\") ? url : `${url}/health`;\n\t\tconst response = await fetch(healthUrl, {\n\t\t\theaders: { Accept: \"application/json\" },\n\t\t\tsignal: AbortSignal.timeout(10000), // Increased to 10s\n\t\t});\n\t\tif (!response.ok) return null;\n\n\t\tconst data = await response.json();\n\t\tif (!data.mesh?.multiaddrs?.length || !data.mesh?.peerId) return null;\n\n\t\t// Find TCP multiaddr (prefer non-websocket for stability)\n\t\tconst tcpAddr = data.mesh.multiaddrs.find(\n\t\t\t(a: string) =>\n\t\t\t\ta.includes(\"/tcp/\") &&\n\t\t\t\t!a.includes(\"/ws\") &&\n\t\t\t\t!a.includes(\"/ip4/127.0.0.1/\"),\n\t\t);\n\t\tif (!tcpAddr) return null;\n\n\t\t// Rewrite internal Docker IP using development mapper if available\n\t\tconst isDevMode =\n\t\t\tprocess.env.NODE_ENV === \"development\" || process.env.NODE_ENV === \"test\";\n\t\tlet resolved = isDevMode ? industrialAddressMapper(tcpAddr) : tcpAddr;\n\t\tif (!resolved || resolved === tcpAddr) {\n\t\t\tconst urlHost = new URL(url).hostname;\n\t\t\tresolved = tcpAddr.replace(/\\/ip4\\/[^/]+/, `/ip4/${urlHost}`);\n\t\t}\n\n\t\tif (!resolved) return null;\n\n\t\tresolved += resolved.includes(\"/p2p/\") ? \"\" : `/p2p/${data.mesh.peerId}`;\n\n\t\treturn resolved;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Normalizes a bootstrap multiaddr string.\n * If the address contains a Docker bridge IP (172.16-31.x.x) or Loopback (127.0.0.1),\n * rewrites it to the host accessible via LIOP_NEXUS_URL (e.g. WSL2 IP).\n * This is critical when WSL2 mirror-mode networking is broken.\n */\nfunction normalizeBootstrap(addr: string): string {\n\tconst trimmed = addr.trim();\n\t// Remap Docker bridge IPs and ANY external physical IPs to 127.0.0.1\n\t// because Test-NetConnection confirmed 127.0.0.1 is the only reliable path to Docker ports.\n\tconst dockerIpRegex =\n\t\t/\\/ip4\\/172\\.(1[6-9]|2[0-9]|3[0-1])\\.[0-9]{1,3}\\.[0-9]{1,3}/;\n\tconst loopbackRegex = /\\/ip4\\/127\\.0\\.0\\.1/;\n\tconst physicalIpRegex = /\\/ip4\\/192\\.168\\.[0-9]{1,3}\\.[0-9]{1,3}/;\n\n\tif (\n\t\tdockerIpRegex.test(trimmed) ||\n\t\tloopbackRegex.test(trimmed) ||\n\t\tphysicalIpRegex.test(trimmed)\n\t) {\n\t\tconst targetIp = \"127.0.0.1\";\n\t\tconst normalized = trimmed\n\t\t\t.replace(dockerIpRegex, `/ip4/${targetIp}`)\n\t\t\t.replace(loopbackRegex, `/ip4/${targetIp}`)\n\t\t\t.replace(physicalIpRegex, `/ip4/${targetIp}`);\n\n\t\tif (normalized !== trimmed) {\n\t\t\tlog.info(\n\t\t\t\t`[LIOP-Agent] 🔄 Local Routing Hack → Forced 127.0.0.1: ${normalized}`,\n\t\t\t);\n\t\t}\n\t\treturn normalized;\n\t}\n\n\treturn trimmed;\n}\n\n/**\n * industrialAddressMapper (DEVELOPMENT ONLY)\n *\n * Maps Docker-internal IPs to host-published ports for local demo environments.\n * This function is ONLY active when NODE_ENV is \"development\" or \"test\".\n * In production, it is completely bypassed.\n *\n * Nexus (172.20.0.10) -> 13001\n * Vault (172.20.0.11) -> 13003\n * Bank (172.20.0.12) -> 13004\n * Oracle(172.20.0.13) -> 13005\n */\nfunction industrialAddressMapper(addr: string): string | null {\n\tif (addr.includes(\"/ip4/172.20.0.10\"))\n\t\treturn addr.replace(\n\t\t\t/\\/ip4\\/172\\.20\\.0\\.10\\/tcp\\/[0-9]+/,\n\t\t\t\"/ip4/127.0.0.1/tcp/13001\",\n\t\t);\n\tif (addr.includes(\"/ip4/172.20.0.11\"))\n\t\treturn addr.replace(\n\t\t\t/\\/ip4\\/172\\.20\\.0\\.11\\/tcp\\/[0-9]+/,\n\t\t\t\"/ip4/127.0.0.1/tcp/13003\",\n\t\t);\n\tif (addr.includes(\"/ip4/172.20.0.12\"))\n\t\treturn addr.replace(\n\t\t\t/\\/ip4\\/172\\.20\\.0\\.12\\/tcp\\/[0-9]+/,\n\t\t\t\"/ip4/127.0.0.1/tcp/13004\",\n\t\t);\n\tif (addr.includes(\"/ip4/172.20.0.13\"))\n\t\treturn addr.replace(\n\t\t\t/\\/ip4\\/172\\.20\\.0\\.13\\/tcp\\/[0-9]+/,\n\t\t\t\"/ip4/127.0.0.1/tcp/13005\",\n\t\t);\n\n\t// Drop container-internal loopbacks to prevent the Host Agent from dialing itself or conflicting ports\n\tif (\n\t\taddr.includes(\"/ip4/127.0.0.1/tcp/4000\") ||\n\t\taddr.includes(\"/ip4/127.0.0.1/tcp/3000\")\n\t) {\n\t\treturn null;\n\t}\n\n\treturn addr;\n}\n\n/**\n * LIOP Agent (Zero-Config CLI)\n *\n * Secure Logic-on-Origin gateway for Claude Desktop.\n * Communicates via STDIO / JSON-RPC.\n *\n * All tool discovery is DYNAMIC via the /liop/manifest/1.0.0 protocol.\n * No hardcoded tools, PeerIDs, or port mappings.\n */\nasync function main() {\n\t// Auto-Relaunch: Ensure system CA certificates are loaded for TLS compatibility.\n\t// Corporate proxies (Cloudflare WARP, Zscaler) inject custom root CAs into the\n\t// OS certificate store. Node.js ignores these by default, causing UNABLE_TO_VERIFY_LEAF_SIGNATURE.\n\t// Pattern: if --use-system-ca is not active, re-spawn with the flag transparently.\n\t// stdio: \"inherit\" ensures Claude Desktop's JSON-RPC pipe is passed through cleanly.\n\tif (\n\t\t(process.platform === \"win32\" || process.platform === \"darwin\") &&\n\t\t!process.execArgv.includes(\"--use-system-ca\") &&\n\t\t!(process.env.NODE_OPTIONS ?? \"\").includes(\"--use-system-ca\")\n\t) {\n\t\tconst { spawn } = await import(\"node:child_process\");\n\t\tconst child = spawn(\n\t\t\tprocess.execPath,\n\t\t\t[\"--use-system-ca\", ...process.argv.slice(1)],\n\t\t\t{ stdio: \"inherit\", env: process.env },\n\t\t);\n\t\tchild.on(\"exit\", (code) => process.exit(code ?? 1));\n\t\tchild.on(\"error\", () => process.exit(1));\n\t\t// Block parent — child handles all I/O from here\n\t\tawait new Promise(() => {});\n\t\treturn;\n\t}\n\n\tconst buildTime = new Date().toISOString();\n\tlog.info(`[LIOP-Agent] 🚀 Version 1.2.0-alpha.9 | Build: ${buildTime}`);\n\n\tconst liopDir = path.join(os.homedir(), \".liop\");\n\tconst identityPath = path.join(liopDir, \"identity.json\");\n\n\tif (!fs.existsSync(liopDir)) {\n\t\tfs.mkdirSync(liopDir, { recursive: true });\n\t}\n\n\t// 1. Determine Bootstrap Nodes (Zero-Config Discovery)\n\tlet bootstrapNodes: string[] = [];\n\n\t// Command line arguments take precedence\n\tconst args = process.argv.slice(2);\n\tif (args.length > 0) {\n\t\tbootstrapNodes = args.filter((a) => a.startsWith(\"/\"));\n\t}\n\n\t// Priority 1: Physical Beacons (Industrial Pattern) - DETERMINISTIC & INSTANT\n\tif (bootstrapNodes.length === 0) {\n\t\tconst searchDirs = [];\n\n\t\t// Priority 1.1: Explicit file from environment variable\n\t\tif (process.env.LIOP_BOOTSTRAP_FILE) {\n\t\t\tlog.warn(\n\t\t\t\t\"LIOP_BOOTSTRAP_FILE is deprecated and will be removed in the next major version. \" +\n\t\t\t\t\t\"Use LIOP_NEXUS_URL for Auto-Discovery instead.\",\n\t\t\t);\n\t\t\tconst filePath = path.resolve(process.env.LIOP_BOOTSTRAP_FILE);\n\t\t\tif (fs.existsSync(filePath)) {\n\t\t\t\tconst addr = fs.readFileSync(filePath, \"utf8\").trim();\n\t\t\t\tif (addr) bootstrapNodes.push(normalizeBootstrap(addr));\n\t\t\t}\n\t\t}\n\n\t\t// Priority 1.2: Traditional locations (Scan for all *.multiaddr)\n\t\tsearchDirs.push(\n\t\t\tprocess.cwd(),\n\t\t\tpath.join(process.cwd(), \"tests/infra/nexus-data\"),\n\t\t\tliopDir,\n\t\t\tpath.join(\n\t\t\t\tpath\n\t\t\t\t\t.dirname(new URL(import.meta.url).pathname)\n\t\t\t\t\t.replace(/^\\/([A-Z]:)/, \"$1\"),\n\t\t\t\t\"../../tests/infra/nexus-data\",\n\t\t\t),\n\t\t);\n\n\t\tfor (const dir of searchDirs) {\n\t\t\ttry {\n\t\t\t\tif (fs.existsSync(dir)) {\n\t\t\t\t\tconst files = fs.readdirSync(dir);\n\t\t\t\t\tconst multiaddrFiles = files.filter((f) => f.endsWith(\".multiaddr\"));\n\n\t\t\t\t\tfor (const file of multiaddrFiles) {\n\t\t\t\t\t\tconst filePath = path.join(dir, file);\n\t\t\t\t\t\tconst addr = fs.readFileSync(filePath, \"utf8\").trim();\n\t\t\t\t\t\tif (addr) {\n\t\t\t\t\t\t\tconst normalized = normalizeBootstrap(addr);\n\t\t\t\t\t\t\tif (!bootstrapNodes.includes(normalized)) {\n\t\t\t\t\t\t\t\tbootstrapNodes.push(normalized);\n\t\t\t\t\t\t\t\tlog.info(`[LIOP-Agent] ✅ Loaded beacon: ${file} from ${dir}`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// If we found any beacons in this directory, we consider discovery successful for this layer\n\t\t\t\t\tif (bootstrapNodes.length > 0) break;\n\t\t\t\t}\n\t\t\t} catch (_e) {\n\t\t\t\t/* ignore */\n\t\t\t}\n\t\t}\n\t}\n\n\t// Priority 2: Auto-Discovery via NEXUS URL (Aggressive Parallel Discovery)\n\tif (process.env.LIOP_NEXUS_URL) {\n\t\tconst nexusUrl = process.env.LIOP_NEXUS_URL;\n\t\tlog.info(\n\t\t\t`[LIOP-Agent] 🌐 Running parallel discovery from: ${nexusUrl} (Sources Found: ${bootstrapNodes.length})`,\n\t\t);\n\n\t\tconst resolved = await resolveBootstrapFromUrl(nexusUrl);\n\t\tif (resolved) {\n\t\t\tconst normalized = normalizeBootstrap(resolved);\n\t\t\tif (!bootstrapNodes.includes(normalized)) {\n\t\t\t\tbootstrapNodes.push(normalized);\n\t\t\t\tlog.info(\n\t\t\t\t\t`[LIOP-Agent] ✅ Added bootstrap from URL discovery: ${normalized}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Priority 3: Environment variable (direct multiaddr)\n\tif (bootstrapNodes.length === 0 && process.env.LIOP_BOOTSTRAP) {\n\t\tbootstrapNodes.push(process.env.LIOP_BOOTSTRAP.trim());\n\t}\n\n\t// Final fallback: local Nexus bootstrap for demo environments.\n\t// Avoid injecting stale static peer IDs when discovery already found valid peers.\n\tif (bootstrapNodes.length === 0) {\n\t\tbootstrapNodes.push(\n\t\t\t\"/ip4/127.0.0.1/tcp/13001/p2p/12D3KooWD8FUFdnLQzzLFNdicsaTknM5cpD7os9sK9NWVSVABJMD\",\n\t\t);\n\t}\n\n\t// Sanitize/validate all candidate multiaddrs so malformed PeerIDs don't crash startup.\n\tbootstrapNodes = bootstrapNodes.filter((addr) => {\n\t\ttry {\n\t\t\tmultiaddr(addr);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\tlog.warn(`[LIOP-Agent] Ignoring invalid bootstrap multiaddr: ${addr}`);\n\t\t\treturn false;\n\t\t}\n\t});\n\n\t// If no bootstrap nodes found, the agent operates in standalone mode.\n\t// It will only serve local tools until peers are discovered.\n\tif (bootstrapNodes.length === 0) {\n\t\tlog.info(\n\t\t\t\"[LIOP-Agent] No bootstrap nodes configured. Operating in standalone mode.\",\n\t\t);\n\t\tlog.info(\n\t\t\t\"[LIOP-Agent] Pass a multiaddr as argument or create 'nexus.multiaddr' file.\",\n\t\t);\n\t}\n\n\t// Initialize local server node (lightweight, no tools registered locally)\n\tconst liopServer = new LiopServer({\n\t\tname: \"@nekzus/liop\",\n\t\tversion: \"1.0.0\",\n\t});\n\n\t// Enable Zero-Shot Autonomy (Industrial Prompt Injection)\n\tliopServer.enableZeroShotAutonomy();\n\n\t// 2. Mesh Node Configuration\n\tconst meshNode = new MeshNode({\n\t\tidentityPath: identityPath,\n\t\tbootstrapNodes: bootstrapNodes,\n\t\taddressMapper:\n\t\t\tprocess.env.NODE_ENV === \"development\" || process.env.NODE_ENV === \"test\"\n\t\t\t\t? industrialAddressMapper\n\t\t\t\t: undefined,\n\t});\n\n\t// Start P2P Mesh\n\tawait meshNode.start();\n\n\t// 3. Initialize the Dynamic Router\n\t// No hardcoded tools — all discovery happens via liop:manifest protocol\n\tconst router = new LiopMcpRouter(liopServer, meshNode);\n\n\t// Proactive Notification to Claude Desktop when tools/resources are discovered dynamically\n\trouter.onToolsChanged = () => {\n\t\tprocess.stdout.write(\n\t\t\t`{\"jsonrpc\":\"2.0\",\"method\":\"notifications/tools/list_changed\"}\\n`,\n\t\t);\n\t\tprocess.stdout.write(\n\t\t\t`{\"jsonrpc\":\"2.0\",\"method\":\"notifications/resources/list_changed\"}\\n`,\n\t\t);\n\t};\n\n\t// Initial warming period (2s) then Adaptive Background Discovery\n\t// Polls DHT for new nodes and triggers onToolsChanged when topology shifts.\n\t// Uses exponential backoff to reduce polling load on stable meshes.\n\tsetTimeout(() => {\n\t\t// biome-ignore lint/suspicious/noExplicitAny: access internal for telemetry\n\t\tconst rtSize = (meshNode as any).getRoutingTableSize?.() || 0;\n\t\tlog.info(`[LIOP-Agent] Warm-up complete. Routing Table size: ${rtSize}`);\n\t\trouter.refreshManifestCache(true).catch(() => {});\n\t}, 2000);\n\n\tconst POLL_BASE_MS = 10_000;\n\tconst POLL_MAX_MS = 120_000;\n\tlet pollIntervalMs = POLL_BASE_MS;\n\n\tconst scheduleAdaptivePoll = () => {\n\t\tsetTimeout(async () => {\n\t\t\tconst prevSize = router.getCacheSize();\n\t\t\tawait router.refreshManifestCache(true).catch(() => {});\n\t\t\tconst newSize = router.getCacheSize();\n\n\t\t\tif (newSize !== prevSize) {\n\t\t\t\t// Topology changed — reset to aggressive polling\n\t\t\t\tpollIntervalMs = POLL_BASE_MS;\n\t\t\t\tlog.info(\n\t\t\t\t\t`[LIOP-Agent] Topology change detected (${prevSize} → ${newSize}). Resetting poll to ${POLL_BASE_MS / 1000}s.`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t// Stable — relax polling interval (factor 1.5)\n\t\t\t\tpollIntervalMs = Math.min(\n\t\t\t\t\tMath.round(pollIntervalMs * 1.5),\n\t\t\t\t\tPOLL_MAX_MS,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tscheduleAdaptivePoll();\n\t\t}, pollIntervalMs);\n\t};\n\n\tscheduleAdaptivePoll();\n\n\t// 4. STDIO Transport — Buffered Line Reader\n\t// Uses readline to guarantee complete JSON-RPC messages before parsing.\n\t// Raw stdin.on(\"data\") can fragment large payloads across multiple chunks.\n\tconst readline = await import(\"node:readline\");\n\tconst rl = readline.createInterface({\n\t\tinput: process.stdin,\n\t\tterminal: false,\n\t});\n\n\tprocess.stdout.on(\"error\", (err: Error & { code?: string }) => {\n\t\tif (err.code === \"EPIPE\") {\n\t\t\tprocess.exit(0); // Graceful exit when Claude Desktop disconnects\n\t\t}\n\t});\n\n\trl.on(\"line\", async (line) => {\n\t\tconst trimmed = line.trim();\n\t\tif (!trimmed) return;\n\n\t\ttry {\n\t\t\tconst request = JSON.parse(trimmed) as McpRequest;\n\t\t\tif (request.method) {\n\t\t\t\tconst response = await router.dispatch(request);\n\t\t\t\tif (response) {\n\t\t\t\t\tprocess.stdout.write(`${JSON.stringify(response)}\\n`);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (_err) {\n\t\t\t// Silent catch for binary noise or malformed lines\n\t\t}\n\t});\n\n\trl.on(\"close\", () => {\n\t\tprocess.exit(0);\n\t});\n\n\t// Status directed only to stderr\n\tlog.info(`[LIOP-Agent] Guarding Claude Desktop via STDIO.`);\n\tlog.info(\n\t\t`[LIOP-Agent] P2P Mesh: Joined (${bootstrapNodes.length} bootstraps)`,\n\t);\n\tlog.info(\"[LIOP-Agent] Tool discovery: Dynamic via /liop/manifest/1.0.0\");\n\n\tprocess.on(\"SIGINT\", async () => {\n\t\tawait meshNode.stop();\n\t\tprocess.exit(0);\n\t});\n}\n\nmain().catch((err) => {\n\tlog.error(`[LIOP-Agent] Fatal Error: ${err.message}`);\n\tprocess.exit(1);\n});\n"]}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { LiopServer, LiopServerOptions } from './server.js';
|
|
3
|
+
import 'zod';
|
|
4
|
+
import './mesh.js';
|
|
5
|
+
import './types.js';
|
|
6
|
+
|
|
2
7
|
/**
|
|
3
8
|
* Configuration options for LiopStreamBridge.
|
|
4
9
|
*/
|
|
5
|
-
|
|
10
|
+
interface LiopStreamBridgeOptions {
|
|
6
11
|
/** Port to listen on (default: 3000) */
|
|
7
12
|
port?: number;
|
|
8
13
|
/** Max concurrent sessions per IP (default: 5) */
|
|
@@ -24,7 +29,7 @@ export interface LiopStreamBridgeOptions {
|
|
|
24
29
|
* - Per-IP rate limiting on session creation
|
|
25
30
|
* - Automatic eviction of idle sessions (TTL)
|
|
26
31
|
*/
|
|
27
|
-
|
|
32
|
+
declare class LiopStreamBridge {
|
|
28
33
|
private options;
|
|
29
34
|
private app;
|
|
30
35
|
private httpServer;
|
|
@@ -60,3 +65,39 @@ export declare class LiopStreamBridge {
|
|
|
60
65
|
*/
|
|
61
66
|
stop(): Promise<void>;
|
|
62
67
|
}
|
|
68
|
+
|
|
69
|
+
interface LiopBridgeOptions {
|
|
70
|
+
publishToMesh?: boolean;
|
|
71
|
+
meshIdentity?: string;
|
|
72
|
+
serverInfo?: {
|
|
73
|
+
name: string;
|
|
74
|
+
version: string;
|
|
75
|
+
};
|
|
76
|
+
security?: LiopServerOptions["security"];
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* LIOP MCP Bridge
|
|
80
|
+
* A bi-directional bridge that allows legacy MCP servers to join the LIOP mesh,
|
|
81
|
+
* or exposes a LIOP server as an MCP-compatible stdio process for tools like Claude Desktop.
|
|
82
|
+
*/
|
|
83
|
+
declare class LiopMcpBridge {
|
|
84
|
+
private options;
|
|
85
|
+
private liopServer;
|
|
86
|
+
private legacyMcpServer;
|
|
87
|
+
constructor(source: LiopServer | McpServer | any, options?: LiopBridgeOptions);
|
|
88
|
+
/**
|
|
89
|
+
* Handles an incoming standard MCP JSON-RPC 2.0 payload.
|
|
90
|
+
* Pipes it to the underlying server (LIOP or Legacy MCP).
|
|
91
|
+
*/
|
|
92
|
+
handleJsonRpcRequest(payload: Record<string, unknown>): Promise<unknown>;
|
|
93
|
+
private handleLiopToMcp;
|
|
94
|
+
private successResponse;
|
|
95
|
+
private errorResponse;
|
|
96
|
+
private verifyZkReceipt;
|
|
97
|
+
/**
|
|
98
|
+
* Connects the bridge via stdio or Mesh depending on mode.
|
|
99
|
+
*/
|
|
100
|
+
connect(): Promise<void>;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export { type LiopBridgeOptions, LiopMcpBridge, LiopStreamBridge, type LiopStreamBridgeOptions };
|
package/dist/bridge.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"bridge.js"}
|