lunel-cli 0.1.39 → 0.1.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +67 -11
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11,8 +11,8 @@ import * as os from "os";
|
|
|
11
11
|
import { spawn, execSync, execFileSync } from "child_process";
|
|
12
12
|
import { createServer, createConnection } from "net";
|
|
13
13
|
import { createInterface } from "readline";
|
|
14
|
-
const DEFAULT_PROXY_URL = process.env.LUNEL_PROXY_URL || "https://gateway.lunel.dev";
|
|
15
|
-
const MANAGER_URL = process.env.LUNEL_MANAGER_URL || "https://manager.lunel.dev";
|
|
14
|
+
const DEFAULT_PROXY_URL = normalizeGatewayUrl(process.env.LUNEL_PROXY_URL || "https://gateway.lunel.dev");
|
|
15
|
+
const MANAGER_URL = normalizeGatewayUrl(process.env.LUNEL_MANAGER_URL || "https://manager.lunel.dev");
|
|
16
16
|
const CLI_ARGS = process.argv.slice(2);
|
|
17
17
|
import { createRequire } from "module";
|
|
18
18
|
const __require = createRequire(import.meta.url);
|
|
@@ -198,6 +198,24 @@ function samePortSet(a, b) {
|
|
|
198
198
|
}
|
|
199
199
|
return true;
|
|
200
200
|
}
|
|
201
|
+
function isProtocolRequest(value) {
|
|
202
|
+
if (!value || typeof value !== "object")
|
|
203
|
+
return false;
|
|
204
|
+
const msg = value;
|
|
205
|
+
return (msg.v === 1 &&
|
|
206
|
+
typeof msg.id === "string" &&
|
|
207
|
+
typeof msg.ns === "string" &&
|
|
208
|
+
typeof msg.action === "string" &&
|
|
209
|
+
typeof msg.payload === "object" &&
|
|
210
|
+
msg.payload !== null &&
|
|
211
|
+
typeof msg.ok === "undefined");
|
|
212
|
+
}
|
|
213
|
+
function isProtocolResponse(value) {
|
|
214
|
+
if (!value || typeof value !== "object")
|
|
215
|
+
return false;
|
|
216
|
+
const msg = value;
|
|
217
|
+
return msg.v === 1 && typeof msg.id === "string" && typeof msg.ok === "boolean";
|
|
218
|
+
}
|
|
201
219
|
// ============================================================================
|
|
202
220
|
// Path Safety
|
|
203
221
|
// ============================================================================
|
|
@@ -1925,7 +1943,10 @@ async function handleProxyConnect(payload) {
|
|
|
1925
1943
|
throw tcpConnectError || Object.assign(new Error(`TCP connect failed to localhost:${port}`), { code: "ECONNREFUSED" });
|
|
1926
1944
|
}
|
|
1927
1945
|
// 2. Open proxy WebSocket to gateway
|
|
1928
|
-
const wsBase = activeGatewayUrl.replace(/^
|
|
1946
|
+
const wsBase = activeGatewayUrl.replace(/^https:/, "wss:");
|
|
1947
|
+
if (!wsBase.startsWith("wss://")) {
|
|
1948
|
+
throw Object.assign(new Error("Gateway URL must use https://"), { code: "EPROTO" });
|
|
1949
|
+
}
|
|
1929
1950
|
const authQuery = currentSessionPassword
|
|
1930
1951
|
? `password=${encodeURIComponent(currentSessionPassword)}`
|
|
1931
1952
|
: `code=${encodeURIComponent(currentSessionCode)}`;
|
|
@@ -2405,10 +2426,30 @@ async function processMessage(message) {
|
|
|
2405
2426
|
};
|
|
2406
2427
|
}
|
|
2407
2428
|
}
|
|
2429
|
+
function sendResponseOnData(response, dataWs) {
|
|
2430
|
+
dataWs.send(JSON.stringify(response));
|
|
2431
|
+
}
|
|
2408
2432
|
function normalizeGatewayUrl(input) {
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2433
|
+
const raw = input.trim();
|
|
2434
|
+
if (!raw) {
|
|
2435
|
+
throw new Error("Gateway URL is required");
|
|
2436
|
+
}
|
|
2437
|
+
if (raw.toLowerCase().startsWith("http://") || raw.toLowerCase().startsWith("ws://")) {
|
|
2438
|
+
throw new Error("Insecure gateway protocol is not allowed; use https://");
|
|
2439
|
+
}
|
|
2440
|
+
const withScheme = /^[a-z]+:\/\//i.test(raw) ? raw : `https://${raw}`;
|
|
2441
|
+
let parsed;
|
|
2442
|
+
try {
|
|
2443
|
+
parsed = new URL(withScheme);
|
|
2444
|
+
}
|
|
2445
|
+
catch {
|
|
2446
|
+
throw new Error(`Invalid gateway URL: ${input}`);
|
|
2447
|
+
}
|
|
2448
|
+
if (parsed.protocol !== "https:") {
|
|
2449
|
+
throw new Error("Gateway URL must use https://");
|
|
2450
|
+
}
|
|
2451
|
+
const path = parsed.pathname === "/" ? "" : parsed.pathname.replace(/\/+$/, "");
|
|
2452
|
+
return `${parsed.protocol}//${parsed.host}${path}`;
|
|
2412
2453
|
}
|
|
2413
2454
|
async function createSessionFromManager() {
|
|
2414
2455
|
const response = await fetch(`${MANAGER_URL}/v1/session`, {
|
|
@@ -2435,7 +2476,10 @@ function displayQR(primaryGateway, backupGateway, code) {
|
|
|
2435
2476
|
});
|
|
2436
2477
|
}
|
|
2437
2478
|
function buildWsUrl(gatewayUrl, role, channel) {
|
|
2438
|
-
const wsBase = gatewayUrl.replace(/^
|
|
2479
|
+
const wsBase = gatewayUrl.replace(/^https:/, "wss:");
|
|
2480
|
+
if (!wsBase.startsWith("wss://")) {
|
|
2481
|
+
throw new Error("Gateway URL must use https://");
|
|
2482
|
+
}
|
|
2439
2483
|
const query = new URLSearchParams();
|
|
2440
2484
|
if (currentSessionPassword) {
|
|
2441
2485
|
query.set("password", currentSessionPassword);
|
|
@@ -2561,10 +2605,16 @@ async function connectWebSocket() {
|
|
|
2561
2605
|
return;
|
|
2562
2606
|
}
|
|
2563
2607
|
}
|
|
2564
|
-
if (message
|
|
2608
|
+
if (isProtocolResponse(message)) {
|
|
2609
|
+
// Ignore server/app responses forwarded over WS; CLI only processes requests.
|
|
2610
|
+
return;
|
|
2611
|
+
}
|
|
2612
|
+
if (isProtocolRequest(message)) {
|
|
2565
2613
|
const response = await processMessage(message);
|
|
2566
|
-
|
|
2614
|
+
sendResponseOnData(response, dataWs);
|
|
2615
|
+
return;
|
|
2567
2616
|
}
|
|
2617
|
+
console.warn("[router] Ignoring non-request control frame");
|
|
2568
2618
|
}
|
|
2569
2619
|
catch (error) {
|
|
2570
2620
|
console.error("Error processing control message:", error);
|
|
@@ -2593,10 +2643,16 @@ async function connectWebSocket() {
|
|
|
2593
2643
|
const message = JSON.parse(data.toString());
|
|
2594
2644
|
if (message.type === "connected")
|
|
2595
2645
|
return;
|
|
2596
|
-
if (message
|
|
2646
|
+
if (isProtocolResponse(message)) {
|
|
2647
|
+
// Ignore server/app responses forwarded over WS; CLI only processes requests.
|
|
2648
|
+
return;
|
|
2649
|
+
}
|
|
2650
|
+
if (isProtocolRequest(message)) {
|
|
2597
2651
|
const response = await processMessage(message);
|
|
2598
|
-
|
|
2652
|
+
sendResponseOnData(response, dataWs);
|
|
2653
|
+
return;
|
|
2599
2654
|
}
|
|
2655
|
+
console.warn("[router] Ignoring non-request data frame");
|
|
2600
2656
|
}
|
|
2601
2657
|
catch (error) {
|
|
2602
2658
|
console.error("Error processing data message:", error);
|