@nekzus/liop-studio 1.0.0-alpha.2 → 1.0.0-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,53 +1,96 @@
1
- # @nekzus/liop-studio
1
+ <div align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/Nekzus/LIOP/main/docs/logo/dark.svg">
4
+ <img alt="Logic-Injection-on-Origin Protocol Logo" src="https://raw.githubusercontent.com/Nekzus/LIOP/main/docs/logo/light.svg" width="600">
5
+ </picture>
6
+
7
+ <h1>@nekzus/liop-studio</h1>
8
+ <p><strong>LIOP Sovereign Studio & Network Scanner</strong></p>
9
+ <p>Interactive developer studio, real-time telemetry console, and zero-trust capability scanner for the Logic-Injection-on-Origin Protocol (LIOP) and Model Context Protocol (MCP).</p>
10
+
11
+ <p align="center">
12
+ <a href="https://www.npmjs.com/package/@nekzus/liop-studio"><img src="https://img.shields.io/npm/v/@nekzus/liop-studio.svg" alt="npm version"></a>
13
+ <a href="https://github.com/Nekzus/LIOP/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-Apache_2.0-blue.svg" alt="License"></a>
14
+ <a href="https://nekzus-32.mintlify.app/typescript-sdk/playground"><img src="https://img.shields.io/badge/docs-mintlify-0D9373?style=flat" alt="Docs"></a>
15
+ <a href="https://deepwiki.com/Nekzus/LIOP"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
16
+ </p>
17
+ </div>
2
18
 
3
- [![npm version](https://img.shields.io/npm/v/@nekzus/liop-studio.svg)](https://www.npmjs.com/package/@nekzus/liop-studio)
4
- [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
5
- [![Documentation](https://img.shields.io/badge/docs-mintlify-4E46E5.svg)](https://nekzus-32.mintlify.app/)
19
+ ---
20
+
21
+ ## Official Documentation
22
+
23
+ Interactive guides and architectural deep dives for LIOP Studio and Playground are hosted on Mintlify:
24
+
25
+ | Documentation Section | Focus Area | Canonical Guide |
26
+ |---|---|---|
27
+ | **Studio & Playground** | Web console architecture, 5-tab output inspector, and execution flows | [Playground & Studio Guide](https://nekzus-32.mintlify.app/typescript-sdk/playground) |
28
+ | **Observability Runbook** | Prometheus metrics catalog and 26-panel Grafana master dashboard | [Observability Runbook](https://nekzus-32.mintlify.app/operations/observability-runbook) |
29
+ | **TypeScript SDK** | Core runtime integration, client negotiation, and server sandboxing | [SDK Overview](https://nekzus-32.mintlify.app/typescript-sdk/overview) |
30
+
31
+ ---
32
+
33
+ ## Overview
34
+
35
+ `@nekzus/liop-studio` provides both a full-featured web dashboard and a headless CLI scanner to interact with LIOP sovereign enclaves and standard MCP servers. It allows developers and security auditors to test Logic-Injection-on-Origin payloads, measure AST instruction fuel consumption, trace post-quantum cryptographic handshakes, and inspect ZK-Receipts in real time.
6
36
 
7
- > **LIOP Sovereign Studio & Mesh Scanner**: Developer studio, interactive testbed, and network scanner for the Logic-Injection-on-Origin Protocol (LIOP) and Model Context Protocol (MCP).
37
+ ### Key Capabilities
38
+
39
+ - **Interactive Web Console (`:16000`)**: Full-featured React 19 UI with real-time SSE execution streaming, CodeMirror 6 code editor, and live cluster health monitoring.
40
+ - **5-Tab Results Inspector**:
41
+ - `Output`: Archetype-specific formatted data and sanitized JSON structures.
42
+ - `Debug`: Granular step-by-step traces across AST validation, sandbox execution, and egress filtering.
43
+ - `Telemetry`: In-situ AST fuel consumption (quantized to 100 units), BPE token counting (`o200k_base`), and wire egress savings.
44
+ - `Export`: Auto-generated client integration snippets (TypeScript SDK, Python, cURL, gRPC).
45
+ - `History`: In-memory inspection of the last 20 execution runs.
46
+ - **Headless Network Scanner**: CLI command (`scan`) to perform non-interactive health probes and capability discovery over HTTP, gRPC, and P2P multiaddrs.
47
+ - **Dual Dynamic Themes**: High-contrast OLED Obsidian (`#000000`) and Slate Navy (`#0f172a`) modes backed by semantic CSS custom properties.
8
48
 
9
49
  ---
10
50
 
11
- ## 🚀 Quickstart
51
+ ## Quickstart
12
52
 
13
53
  ### 1. Launch Interactive Studio (Web UI)
54
+
55
+ Run the studio directly using `npx` (serves the web interface on `http://localhost:16000`):
56
+
14
57
  ```bash
15
- # Launch on default port (http://localhost:16000)
58
+ # Launch default web console on port 16000
16
59
  npx @nekzus/liop-studio
17
60
 
18
- # Launch targeting a specific MCP endpoint
19
- npx @nekzus/liop-studio --http "http://localhost:3000/mcp"
61
+ # Launch targeting a specific MCP HTTP endpoint
62
+ npx @nekzus/liop-studio --http "http://localhost:15000/mcp"
20
63
 
21
- # Launch targeting a native LIOP gRPC node
22
- npx @nekzus/liop-studio --grpc "127.0.0.1:50051"
64
+ # Launch targeting a native LIOP gRPC enclave
65
+ npx @nekzus/liop-studio --grpc "127.0.0.1:15011"
23
66
  ```
24
67
 
25
- ### 2. Run Headless Network & Capability Scan (CLI)
68
+ ### 2. Run Headless Capability Scan (CLI)
69
+
70
+ Perform instant health, discovery, and security scans from terminal scripts or CI pipelines:
71
+
26
72
  ```bash
27
- # Quick health and capabilities scan of an MCP or LIOP server
28
- npx @nekzus/liop-studio scan http://localhost:3000/mcp
73
+ # Quick health and capabilities scan of an HTTP MCP / LIOP gateway
74
+ npx @nekzus/liop-studio scan http://localhost:15000/mcp
29
75
 
30
- # Scan a native gRPC node
31
- npx @nekzus/liop-studio scan 127.0.0.1:50051
76
+ # Scan a native gRPC sovereign enclave
77
+ npx @nekzus/liop-studio scan 127.0.0.1:15011
32
78
 
33
79
  # Scan a P2P DHT multiaddr
34
- npx @nekzus/liop-studio scan /ip4/127.0.0.1/tcp/4000/p2p/12D3KooW...
80
+ npx @nekzus/liop-studio scan /ip4/127.0.0.1/tcp/15001/p2p/12D3KooW...
35
81
  ```
36
82
 
37
83
  ---
38
84
 
39
- ## 🛡️ Security Guardrails
40
- - **CWE-78 Command Injection Defense**: Stdio subprocess execution strictly enforces `{ shell: false }` with executable and argument sanitization.
41
- - **CWE-918 SSRF Protection**: All outbound HTTP probes validate URLs and block cloud metadata IP addresses (`169.254.169.254`).
42
- - **NIST SP 800-53 Timing Side-Channel Protection**: Deterministic instruction fuel quantized to 100 units.
43
- - **Zero-Trust Origin Execution**: V8 Isolate sandboxing with poisoned globals and frozen prototypes.
44
-
45
- ---
85
+ ## Security & Sandboxing Guardrails
46
86
 
47
- ## 📚 Documentation
48
- For complete guides, API references, and architecture specifications, visit the [official LIOP documentation](https://nekzus-32.mintlify.app/).
87
+ - **CWE-78 Command Injection Defense**: Stdio subprocess spawning strictly enforces `{ shell: false }` with rigorous argument and binary sanitization.
88
+ - **CWE-918 SSRF Protection**: All outbound HTTP probes validate destination addresses and explicitly block cloud metadata IPs (`169.254.169.254`).
89
+ - **NIST SP 800-53 Timing Side-Channel Elimination**: Deterministic AST instruction fuel scoring quantized into 100-unit buckets (`stddev = 0`).
90
+ - **Zero-Trust V8 Sandbox**: PAYLOAD execution occurs within isolated V8 contexts with 25 poisoned globals and 11 frozen prototypes.
49
91
 
50
92
  ---
51
93
 
52
- ## 📄 License
53
- Apache-2.0. Copyright 2026 Nekzus Solutions and contributors.
94
+ ## License
95
+
96
+ Licensed under the [Apache License, Version 2.0](../../LICENSE). Copyright 2026 [Nekzus Solutions](https://github.com/Nekzus) and contributors.
package/dist/cli/index.js CHANGED
@@ -5,9 +5,9 @@ import { Command } from "commander";
5
5
  import pc2 from "picocolors";
6
6
 
7
7
  // src/server/index.ts
8
- import path from "path";
8
+ import path2 from "path";
9
9
  import process3 from "process";
10
- import { fileURLToPath } from "url";
10
+ import { fileURLToPath as fileURLToPath2 } from "url";
11
11
  import { serve } from "@hono/node-server";
12
12
  import { serveStatic } from "@hono/node-server/serve-static";
13
13
  import { TokenTelemetryEngine as TokenTelemetryEngine5 } from "@nekzus/liop";
@@ -63,10 +63,29 @@ function validateHttpTarget(targetUrl) {
63
63
  }
64
64
  return parsed;
65
65
  }
66
- function validateHostHeader(host, allowedHosts = ["localhost", "127.0.0.1", "0.0.0.0"]) {
66
+ function validateHostHeader(host, allowedHosts = [
67
+ "localhost",
68
+ "127.0.0.1",
69
+ "0.0.0.0",
70
+ "::1",
71
+ "[::1]"
72
+ ]) {
67
73
  if (!host) return false;
68
- const hostname = host.split(":")[0].toLowerCase();
69
- return allowedHosts.includes(hostname);
74
+ const trimmed = host.trim();
75
+ let hostname;
76
+ if (trimmed.startsWith("[")) {
77
+ const closingIndex = trimmed.indexOf("]");
78
+ hostname = closingIndex !== -1 ? trimmed.slice(0, closingIndex + 1) : trimmed;
79
+ } else if (trimmed.includes(":") && trimmed.indexOf(":") !== trimmed.lastIndexOf(":")) {
80
+ hostname = trimmed;
81
+ } else {
82
+ hostname = trimmed.split(":")[0];
83
+ }
84
+ hostname = hostname.toLowerCase();
85
+ return allowedHosts.some((allowed) => {
86
+ const normAllowed = allowed.toLowerCase();
87
+ return normAllowed === hostname || hostname === "[::1]" && normAllowed === "::1" || hostname === "::1" && normAllowed === "[::1]";
88
+ });
70
89
  }
71
90
 
72
91
  // src/transports/grpc.transport.ts
@@ -91,7 +110,8 @@ var DEFAULT_PRESET_PROFILES = [
91
110
  role: "Clinical Healthcare & EHR Patient Records",
92
111
  isolation: "pnet Swarm Key (PSK) + HIPAA Strict Mode",
93
112
  dataset: "2,500 clinical EHR patient records",
94
- defaultTool: "Analyze_Synthetic_Medical_Records"
113
+ defaultTool: "Analyze_Synthetic_Medical_Records",
114
+ defaultTools: ["Analyze_Synthetic_Medical_Records"]
95
115
  },
96
116
  {
97
117
  id: "bank",
@@ -103,7 +123,8 @@ var DEFAULT_PRESET_PROFILES = [
103
123
  role: "Core Banking & Financial Settlement",
104
124
  isolation: "pnet Swarm Key (PSK) + Differential Privacy",
105
125
  dataset: "1,500 synthetic accounts ($148M balance)",
106
- defaultTool: "Analyze_Synthetic_Bank_Transactions"
126
+ defaultTool: "Analyze_Synthetic_Bank_Transactions",
127
+ defaultTools: ["Analyze_Synthetic_Bank_Transactions"]
107
128
  },
108
129
  {
109
130
  id: "blg",
@@ -114,7 +135,12 @@ var DEFAULT_PRESET_PROFILES = [
114
135
  tierLabel: "Tier 1: Sovereign Enclaves (In-Situ Origin)",
115
136
  role: "Dual-NIC Perimeter Security Bridge (Tier 1 <-> Tier 2)",
116
137
  isolation: "6-Layer Zero-Trust + AST Guardian + Egress Shield",
117
- defaultTool: "BLG_Inspect_Enclave_Perimeter"
138
+ defaultTool: "BLG_Inspect_Enclave_Perimeter",
139
+ defaultTools: [
140
+ "BLG_Inspect_Enclave_Perimeter",
141
+ "BLG_Execute_Healthcare_Analytics",
142
+ "BLG_Execute_Banking_Analytics"
143
+ ]
118
144
  },
119
145
  {
120
146
  id: "oracle",
@@ -126,7 +152,8 @@ var DEFAULT_PRESET_PROFILES = [
126
152
  role: "Real-time High Frequency Trading Market Simulator",
127
153
  isolation: "Consortium Node + 50ms Tick Streaming Buffer",
128
154
  dataset: "8 Instruments + L2 Orderbook",
129
- defaultTool: "Analyze_HFT_Market_Data"
155
+ defaultTool: "Analyze_HFT_Market_Data",
156
+ defaultTools: ["Analyze_HFT_Market_Data", "LiopMeshStatus"]
130
157
  },
131
158
  {
132
159
  id: "relay",
@@ -136,7 +163,8 @@ var DEFAULT_PRESET_PROFILES = [
136
163
  tierLabel: "Tier 2: Consortium Routing & Gateways",
137
164
  role: "Kademlia DHT & libp2p Circuit Relay v2 Node",
138
165
  isolation: "Public Swarm Mesh Relay",
139
- defaultTool: "LiopMeshStatus"
166
+ defaultTool: "LiopMeshStatus",
167
+ defaultTools: ["LiopMeshStatus"]
140
168
  },
141
169
  {
142
170
  id: "nexus",
@@ -146,7 +174,8 @@ var DEFAULT_PRESET_PROFILES = [
146
174
  tierLabel: "Tier 2: Consortium Routing & Gateways",
147
175
  role: "OAuth 2.1 RFC 8707 Auth Server & Mesh Authority",
148
176
  isolation: "Zero-Trust Identity Provider",
149
- defaultTool: "Authenticate_Client"
177
+ defaultTool: "Authenticate_Client",
178
+ defaultTools: ["Authenticate_Client"]
150
179
  },
151
180
  {
152
181
  id: "edge",
@@ -158,7 +187,8 @@ var DEFAULT_PRESET_PROFILES = [
158
187
  role: "Edge Telemetry & Hostile 3G WAN Industrial Node",
159
188
  isolation: "WAN Jitter/Loss Resistant Client",
160
189
  dataset: "Edge Telemetry Sensors (Pressure, RPM, Temp)",
161
- defaultTool: "Analyze_IoT_Sensor_Data"
190
+ defaultTool: "Analyze_IoT_Sensor_Data",
191
+ defaultTools: ["Analyze_IoT_Sensor_Data"]
162
192
  }
163
193
  ];
164
194
  var NetworkDiscoveryEngine = class _NetworkDiscoveryEngine {
@@ -191,7 +221,7 @@ var NetworkDiscoveryEngine = class _NetworkDiscoveryEngine {
191
221
  /**
192
222
  * Probe a specific HTTP/Health endpoint dynamically.
193
223
  */
194
- async probeHttpNode(host, port, timeoutMs = 1200) {
224
+ async probeHttpNode(host, port, timeoutMs = 3500) {
195
225
  const url = `http://${host}:${port}`;
196
226
  const t0 = performance.now();
197
227
  const controller = new AbortController();
@@ -278,7 +308,7 @@ var NetworkDiscoveryEngine = class _NetworkDiscoveryEngine {
278
308
  peerId: probe.peerId || `peer-${profile.id}`,
279
309
  multiaddrs: probe.multiaddrs || [],
280
310
  version: probe.version || "2.5.0",
281
- tools: probe.tools && probe.tools.length > 0 ? probe.tools : [],
311
+ tools: probe.tools && probe.tools.length > 0 ? probe.tools : profile.defaultTools || (profile.defaultTool ? [profile.defaultTool] : []),
282
312
  role: profile.role,
283
313
  isolation: profile.isolation,
284
314
  dataset: profile.dataset,
@@ -300,7 +330,7 @@ var NetworkDiscoveryEngine = class _NetworkDiscoveryEngine {
300
330
  peerId: `peer-${profile.id}`,
301
331
  multiaddrs: [],
302
332
  version: "2.5.0",
303
- tools: [],
333
+ tools: profile.defaultTools || (profile.defaultTool ? [profile.defaultTool] : []),
304
334
  role: profile.role,
305
335
  isolation: profile.isolation,
306
336
  dataset: profile.dataset,
@@ -338,7 +368,7 @@ var NetworkDiscoveryEngine = class _NetworkDiscoveryEngine {
338
368
  const probe = await this.probeHttpNode(host, matchedProfile.httpPort);
339
369
  const isOnline = status === "online" || Boolean(probe?.tools) && (probe?.tools?.length ?? 0) > 0;
340
370
  const nodeName = probe?.name?.replace("PRODUCTION-", "").replace(/-/g, " ") || matchedProfile.defaultName;
341
- const defaultTools = matchedProfile.defaultTool ? [matchedProfile.defaultTool] : ["Execute_WASI_Logic"];
371
+ const defaultTools = matchedProfile.defaultTools || (matchedProfile.defaultTool ? [matchedProfile.defaultTool] : ["Execute_WASI_Logic"]);
342
372
  const tools = isOnline ? probe?.tools && probe.tools.length > 0 ? probe.tools : defaultTools : [];
343
373
  const enriched = isOnline && probe?.enrichedTools && probe.enrichedTools.length > 0 ? probe.enrichedTools : tools.map((t) => ({
344
374
  name: t,
@@ -456,16 +486,22 @@ var GrpcTransport = class {
456
486
  connected = false;
457
487
  target;
458
488
  token;
489
+ tlsConfig;
459
490
  constructor(options) {
460
491
  this.target = options.target.replace(/^grpc:\/\//, "");
461
492
  this.token = options.token;
493
+ this.tlsConfig = options.tls;
462
494
  }
463
495
  isConnected() {
464
496
  return this.connected && this.client !== null;
465
497
  }
466
498
  async connect() {
467
499
  const tokenProvider = this.token || createStudioTokenProvider();
468
- this.client = new LiopRpcClient(this.target, void 0, tokenProvider);
500
+ const tls = this.tlsConfig || {
501
+ insecure: true,
502
+ suppressWarning: true
503
+ };
504
+ this.client = new LiopRpcClient(this.target, tls, tokenProvider);
469
505
  this.connected = true;
470
506
  }
471
507
  async disconnect() {
@@ -1180,9 +1216,7 @@ var MeshTransport = class {
1180
1216
  if (!this.isConnected()) {
1181
1217
  await this.connect();
1182
1218
  }
1183
- const clientAny = this.client;
1184
- const peerId = clientAny.meshNode?.getPeerId?.()?.toString() || "UnknownPeer";
1185
- const _connections = clientAny.meshNode?.node?.getConnections?.() || [];
1219
+ const peerId = this.client.peerId ?? "UnknownPeer";
1186
1220
  const latencyMs = Math.max(1, Math.round(performance.now() - tStart));
1187
1221
  const discovery = NetworkDiscoveryEngine.getInstance();
1188
1222
  const meshDiscovered = await discovery.scanNetwork("127.0.0.1").catch(() => []);
@@ -1867,12 +1901,40 @@ function createTransport(config) {
1867
1901
  }
1868
1902
  }
1869
1903
 
1870
- // src/server/index.ts
1904
+ // src/version.ts
1905
+ import fs from "fs";
1906
+ import path from "path";
1907
+ import { fileURLToPath } from "url";
1871
1908
  var __dirname = path.dirname(fileURLToPath(import.meta.url));
1909
+ function getPackageVersion() {
1910
+ try {
1911
+ let currentDir = __dirname;
1912
+ for (let i = 0; i < 4; i++) {
1913
+ const candidate = path.join(currentDir, "package.json");
1914
+ if (fs.existsSync(candidate)) {
1915
+ const raw = fs.readFileSync(candidate, "utf-8");
1916
+ const parsed = JSON.parse(raw);
1917
+ if (parsed.name === "@nekzus/liop-studio" && typeof parsed.version === "string") {
1918
+ return parsed.version;
1919
+ }
1920
+ }
1921
+ currentDir = path.dirname(currentDir);
1922
+ }
1923
+ } catch {
1924
+ }
1925
+ return "1.0.0-alpha.5";
1926
+ }
1927
+ var STUDIO_VERSION = getPackageVersion();
1928
+
1929
+ // src/server/index.ts
1930
+ var __dirname2 = path2.dirname(fileURLToPath2(import.meta.url));
1872
1931
  function createStudioServer(options = {}) {
1932
+ if (!process3.env.LIOP_SUPPRESS_TLS_WARNING) {
1933
+ process3.env.LIOP_SUPPRESS_TLS_WARNING = "true";
1934
+ }
1873
1935
  const app = new Hono();
1874
1936
  const port = options.port || 16e3;
1875
- const distPath = options.distPath || path.resolve(__dirname, "../../ui/dist");
1937
+ const distPath = options.distPath || path2.resolve(__dirname2, "../../ui/dist");
1876
1938
  let activeConfig = options.initialTarget || {
1877
1939
  type: "http",
1878
1940
  http: { url: process3.env.LIOP_MCP_URL || "http://127.0.0.1:15000/mcp" }
@@ -1892,7 +1954,7 @@ function createStudioServer(options = {}) {
1892
1954
  app.use(
1893
1955
  "/*",
1894
1956
  serveStatic({
1895
- root: path.relative(process3.cwd(), distPath).replace(/\\/g, "/"),
1957
+ root: distPath,
1896
1958
  rewriteRequestPath: (pathStr) => {
1897
1959
  if (!pathStr.includes(".") && !pathStr.startsWith("/api") && pathStr !== "/health") {
1898
1960
  return "/index.html";
@@ -1905,7 +1967,7 @@ function createStudioServer(options = {}) {
1905
1967
  const isConnected = activeTransport.isConnected() && lastReport?.status === "online";
1906
1968
  return c.json({
1907
1969
  status: isConnected ? "healthy" : "offline",
1908
- version: "1.0.0",
1970
+ version: STUDIO_VERSION,
1909
1971
  targetType: activeConfig.type,
1910
1972
  connected: isConnected
1911
1973
  });
@@ -1921,7 +1983,7 @@ function createStudioServer(options = {}) {
1921
1983
  connected: isConnected,
1922
1984
  toolsCount: isConnected ? lastReport?.totalTools || 0 : 0,
1923
1985
  latencyMs: isConnected ? lastReport?.latencyMs || 0 : 0,
1924
- version: "1.0.0",
1986
+ version: STUDIO_VERSION,
1925
1987
  serverInfo: lastReport?.serverInfo
1926
1988
  });
1927
1989
  });
@@ -2186,7 +2248,11 @@ function resolveTargetConfig(target, options = {}) {
2186
2248
  if (isGrpcExplicit || /^([a-zA-Z0-9.-]+):(\d+)$/.test(trimmed) || trimmed.startsWith("grpc://")) {
2187
2249
  return {
2188
2250
  type: "grpc",
2189
- grpc: { target: trimmed, token }
2251
+ grpc: {
2252
+ target: trimmed,
2253
+ token,
2254
+ tls: { insecure: true, suppressWarning: true }
2255
+ }
2190
2256
  };
2191
2257
  }
2192
2258
  const parts = trimmed.split(/\s+/);
@@ -2199,6 +2265,9 @@ function resolveTargetConfig(target, options = {}) {
2199
2265
  };
2200
2266
  }
2201
2267
  async function runScan(targetStr, options) {
2268
+ if (!process4.env.LIOP_SUPPRESS_TLS_WARNING) {
2269
+ process4.env.LIOP_SUPPRESS_TLS_WARNING = "true";
2270
+ }
2202
2271
  const config = resolveTargetConfig(targetStr, options.token);
2203
2272
  const transport = createTransport(config);
2204
2273
  try {
@@ -2226,14 +2295,24 @@ async function main() {
2226
2295
  const program = new Command();
2227
2296
  program.name("liop-studio").description(
2228
2297
  "LIOP Sovereign Studio & Mesh Scanner (Universal MCP / LIOP Inspector)"
2229
- ).version("1.0.0-alpha.0").option("-p, --port <port>", "Port to bind local Studio server", "16000").option("--no-open", "Do not automatically open browser on launch").option(
2298
+ ).version(STUDIO_VERSION).option("-p, --port <port>", "Port to bind local Studio server", "16000").option("--no-open", "Do not automatically open browser on launch").option(
2230
2299
  "--stdio <command>",
2231
2300
  "Initial target: Stdio command (e.g. 'node server.js')"
2232
2301
  ).option("--http <url>", "Initial target: HTTP / SSE MCP URL").option("--grpc <target>", "Initial target: Native LIOP gRPC endpoint").option("--mesh <multiaddr>", "Initial target: Libp2p P2P bootstrap node").option(
2233
2302
  "--token <token>",
2234
2303
  "Bearer or access token for target authentication"
2304
+ ).option("--tls-ca <path>", "Path to root CA certificate for gRPC target").option(
2305
+ "--tls-cert <path>",
2306
+ "Path to client certificate chain for gRPC target"
2307
+ ).option("--tls-key <path>", "Path to client private key for gRPC target").option(
2308
+ "--insecure",
2309
+ "Explicitly permit unencrypted gRPC credentials",
2310
+ true
2235
2311
  ).action(async (options) => {
2236
2312
  const port = Number.parseInt(options.port, 10) || 16e3;
2313
+ if (!options.tlsCa && !options.tlsCert && !options.tlsKey) {
2314
+ process5.env.LIOP_SUPPRESS_TLS_WARNING = "true";
2315
+ }
2237
2316
  let initialTarget;
2238
2317
  if (options.stdio) {
2239
2318
  const parts = options.stdio.trim().split(/\s+/);
@@ -2249,7 +2328,18 @@ async function main() {
2249
2328
  } else if (options.grpc) {
2250
2329
  initialTarget = {
2251
2330
  type: "grpc",
2252
- grpc: { target: options.grpc, token: options.token }
2331
+ grpc: {
2332
+ target: options.grpc,
2333
+ token: options.token,
2334
+ tls: options.tlsCa ? {
2335
+ rootCert: options.tlsCa,
2336
+ certChain: options.tlsCert,
2337
+ privateKey: options.tlsKey
2338
+ } : {
2339
+ insecure: true,
2340
+ suppressWarning: true
2341
+ }
2342
+ }
2253
2343
  };
2254
2344
  } else if (options.mesh) {
2255
2345
  initialTarget = {