lunel-cli 0.1.93 → 0.1.95
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 +86 -6
- package/dist/transport/v2.d.ts +1 -1
- package/dist/transport/v2.js +4 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -251,7 +251,7 @@ function parseExtraPortsFromArgs(args) {
|
|
|
251
251
|
const EXTRA_PORTS = parseExtraPortsFromArgs(CLI_ARGS);
|
|
252
252
|
const USE_APPLE_REVIEW_CODE = hasAnyFlag(CLI_ARGS, "--abcd-code");
|
|
253
253
|
const FORCE_NEW_CODE = hasAnyFlag(CLI_ARGS, "--new", "-n");
|
|
254
|
-
const
|
|
254
|
+
const trackedProxyPorts = new Set(EXTRA_PORTS);
|
|
255
255
|
function samePortSet(a, b) {
|
|
256
256
|
if (a.length !== b.length)
|
|
257
257
|
return false;
|
|
@@ -1994,7 +1994,8 @@ async function handleHttpRequest(payload) {
|
|
|
1994
1994
|
// ============================================================================
|
|
1995
1995
|
async function scanDevPorts() {
|
|
1996
1996
|
const openPorts = [];
|
|
1997
|
-
const
|
|
1997
|
+
const scanPorts = Array.from(new Set([...DEV_PORTS, ...trackedProxyPorts])).sort((a, b) => a - b);
|
|
1998
|
+
const checks = scanPorts.map((port) => {
|
|
1998
1999
|
return new Promise((resolve) => {
|
|
1999
2000
|
let finished = false;
|
|
2000
2001
|
let pending = LOOPBACK_HOSTS.length;
|
|
@@ -2031,6 +2032,9 @@ async function scanDevPorts() {
|
|
|
2031
2032
|
await Promise.all(checks);
|
|
2032
2033
|
return openPorts.sort((a, b) => a - b);
|
|
2033
2034
|
}
|
|
2035
|
+
function getTrackedProxyPorts() {
|
|
2036
|
+
return Array.from(trackedProxyPorts).sort((a, b) => a - b);
|
|
2037
|
+
}
|
|
2034
2038
|
async function publishDiscoveredPorts(force = false) {
|
|
2035
2039
|
if (portScanInFlight)
|
|
2036
2040
|
return;
|
|
@@ -2059,6 +2063,46 @@ async function publishDiscoveredPorts(force = false) {
|
|
|
2059
2063
|
portScanInFlight = false;
|
|
2060
2064
|
}
|
|
2061
2065
|
}
|
|
2066
|
+
async function getProxyState() {
|
|
2067
|
+
const openPorts = await scanDevPorts();
|
|
2068
|
+
lastDiscoveredPorts = openPorts;
|
|
2069
|
+
return {
|
|
2070
|
+
trackedPorts: getTrackedProxyPorts(),
|
|
2071
|
+
openPorts,
|
|
2072
|
+
};
|
|
2073
|
+
}
|
|
2074
|
+
async function handleTrackProxyPort(payload) {
|
|
2075
|
+
const port = Number(payload.port);
|
|
2076
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
2077
|
+
throw Object.assign(new Error("port must be an integer between 1 and 65535"), { code: "EINVAL" });
|
|
2078
|
+
}
|
|
2079
|
+
trackedProxyPorts.add(port);
|
|
2080
|
+
debugLog("[proxy] tracking custom port", {
|
|
2081
|
+
port,
|
|
2082
|
+
trackedPorts: getTrackedProxyPorts(),
|
|
2083
|
+
});
|
|
2084
|
+
await publishDiscoveredPorts(true);
|
|
2085
|
+
return {
|
|
2086
|
+
trackedPorts: getTrackedProxyPorts(),
|
|
2087
|
+
openPorts: lastDiscoveredPorts,
|
|
2088
|
+
};
|
|
2089
|
+
}
|
|
2090
|
+
async function handleUntrackProxyPort(payload) {
|
|
2091
|
+
const port = Number(payload.port);
|
|
2092
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
2093
|
+
throw Object.assign(new Error("port must be an integer between 1 and 65535"), { code: "EINVAL" });
|
|
2094
|
+
}
|
|
2095
|
+
trackedProxyPorts.delete(port);
|
|
2096
|
+
debugLog("[proxy] removed custom port tracking", {
|
|
2097
|
+
port,
|
|
2098
|
+
trackedPorts: getTrackedProxyPorts(),
|
|
2099
|
+
});
|
|
2100
|
+
await publishDiscoveredPorts(true);
|
|
2101
|
+
return {
|
|
2102
|
+
trackedPorts: getTrackedProxyPorts(),
|
|
2103
|
+
openPorts: lastDiscoveredPorts,
|
|
2104
|
+
};
|
|
2105
|
+
}
|
|
2062
2106
|
function stopPortSync() {
|
|
2063
2107
|
if (portSyncTimer) {
|
|
2064
2108
|
clearInterval(portSyncTimer);
|
|
@@ -2078,6 +2122,13 @@ async function handleProxyConnect(payload) {
|
|
|
2078
2122
|
const port = payload.port;
|
|
2079
2123
|
const setupStartedAt = Date.now();
|
|
2080
2124
|
const getRemainingSetupMs = () => TUNNEL_SETUP_BUDGET_MS - (Date.now() - setupStartedAt);
|
|
2125
|
+
debugLog("[proxy] handleProxyConnect received", {
|
|
2126
|
+
tunnelId,
|
|
2127
|
+
port,
|
|
2128
|
+
hasSessionCode: Boolean(currentSessionCode),
|
|
2129
|
+
hasSessionPassword: Boolean(currentSessionPassword),
|
|
2130
|
+
activeGatewayUrl,
|
|
2131
|
+
});
|
|
2081
2132
|
if (!tunnelId)
|
|
2082
2133
|
throw Object.assign(new Error("tunnelId is required"), { code: "EINVAL" });
|
|
2083
2134
|
if (!port)
|
|
@@ -2126,8 +2177,14 @@ async function handleProxyConnect(payload) {
|
|
|
2126
2177
|
}
|
|
2127
2178
|
}
|
|
2128
2179
|
if (!tcpSocket) {
|
|
2180
|
+
debugLog("[proxy] local tcp connect failed", {
|
|
2181
|
+
tunnelId,
|
|
2182
|
+
port,
|
|
2183
|
+
error: tcpConnectError?.message ?? null,
|
|
2184
|
+
});
|
|
2129
2185
|
throw tcpConnectError || Object.assign(new Error(`TCP connect failed to localhost:${port}`), { code: "ECONNREFUSED" });
|
|
2130
2186
|
}
|
|
2187
|
+
debugLog("[proxy] local tcp connected", { tunnelId, port });
|
|
2131
2188
|
// 2. Open proxy WebSocket to gateway
|
|
2132
2189
|
const wsBase = activeGatewayUrl.replace(/^https:/, "wss:");
|
|
2133
2190
|
if (!wsBase.startsWith("wss://")) {
|
|
@@ -2137,6 +2194,12 @@ async function handleProxyConnect(payload) {
|
|
|
2137
2194
|
? `password=${encodeURIComponent(currentSessionPassword)}`
|
|
2138
2195
|
: `code=${encodeURIComponent(currentSessionCode)}`;
|
|
2139
2196
|
const proxyWsUrl = `${wsBase}/v1/ws/proxy?${authQuery}&tunnelId=${encodeURIComponent(tunnelId)}&role=cli`;
|
|
2197
|
+
debugLog("[proxy] connecting cli proxy websocket", {
|
|
2198
|
+
tunnelId,
|
|
2199
|
+
port,
|
|
2200
|
+
authMode: currentSessionPassword ? "password" : "code",
|
|
2201
|
+
wsBase,
|
|
2202
|
+
});
|
|
2140
2203
|
let proxyWs = null;
|
|
2141
2204
|
let lastProxyError = null;
|
|
2142
2205
|
for (let attempt = 0; attempt <= PROXY_WS_CONNECT_RETRY_ATTEMPTS; attempt++) {
|
|
@@ -2191,8 +2254,14 @@ async function handleProxyConnect(payload) {
|
|
|
2191
2254
|
if (!proxyWs) {
|
|
2192
2255
|
tcpSocket.destroy();
|
|
2193
2256
|
const err = lastProxyError || Object.assign(new Error("Proxy WS connect failed"), { code: "ECONNREFUSED" });
|
|
2257
|
+
debugLog("[proxy] cli proxy websocket connect failed", {
|
|
2258
|
+
tunnelId,
|
|
2259
|
+
port,
|
|
2260
|
+
error: err.message,
|
|
2261
|
+
});
|
|
2194
2262
|
throw err;
|
|
2195
2263
|
}
|
|
2264
|
+
debugLog("[proxy] cli proxy websocket connected", { tunnelId, port });
|
|
2196
2265
|
// 3. Store the tunnel
|
|
2197
2266
|
activeTunnels.set(tunnelId, {
|
|
2198
2267
|
tunnelId,
|
|
@@ -2267,6 +2336,7 @@ async function handleProxyConnect(payload) {
|
|
|
2267
2336
|
markLocalEnded();
|
|
2268
2337
|
});
|
|
2269
2338
|
tcpSocket.on("error", () => {
|
|
2339
|
+
debugLog("[proxy] local tcp socket error", { tunnelId, port });
|
|
2270
2340
|
const tunnel = activeTunnels.get(tunnelId);
|
|
2271
2341
|
if (tunnel && !tunnel.finSent) {
|
|
2272
2342
|
sendProxyControl(tunnel, "rst", "tcp_error");
|
|
@@ -2284,6 +2354,7 @@ async function handleProxyConnect(payload) {
|
|
|
2284
2354
|
});
|
|
2285
2355
|
// 7. Close cascade: WS closes -> close TCP
|
|
2286
2356
|
proxyWs.on("close", () => {
|
|
2357
|
+
debugLog("[proxy] cli proxy websocket closed", { tunnelId, port });
|
|
2287
2358
|
const tunnel = activeTunnels.get(tunnelId);
|
|
2288
2359
|
if (tunnel) {
|
|
2289
2360
|
tunnel.closing = true;
|
|
@@ -2297,6 +2368,7 @@ async function handleProxyConnect(payload) {
|
|
|
2297
2368
|
}
|
|
2298
2369
|
});
|
|
2299
2370
|
proxyWs.on("error", () => {
|
|
2371
|
+
debugLog("[proxy] cli proxy websocket error", { tunnelId, port });
|
|
2300
2372
|
const tunnel = activeTunnels.get(tunnelId);
|
|
2301
2373
|
if (tunnel) {
|
|
2302
2374
|
tunnel.closing = true;
|
|
@@ -2363,6 +2435,7 @@ async function processMessage(message) {
|
|
|
2363
2435
|
switch (action) {
|
|
2364
2436
|
case "capabilities":
|
|
2365
2437
|
result = handleSystemCapabilities();
|
|
2438
|
+
void publishDiscoveredPorts(true);
|
|
2366
2439
|
break;
|
|
2367
2440
|
case "ping":
|
|
2368
2441
|
result = handleSystemPing();
|
|
@@ -2626,6 +2699,15 @@ async function processMessage(message) {
|
|
|
2626
2699
|
case "connect":
|
|
2627
2700
|
result = await handleProxyConnect(payload);
|
|
2628
2701
|
break;
|
|
2702
|
+
case "getState":
|
|
2703
|
+
result = await getProxyState();
|
|
2704
|
+
break;
|
|
2705
|
+
case "trackPort":
|
|
2706
|
+
result = await handleTrackProxyPort(payload);
|
|
2707
|
+
break;
|
|
2708
|
+
case "untrackPort":
|
|
2709
|
+
result = await handleUntrackProxyPort(payload);
|
|
2710
|
+
break;
|
|
2629
2711
|
default:
|
|
2630
2712
|
throw Object.assign(new Error(`Unknown action: ${ns}.${action}`), { code: "EINVAL" });
|
|
2631
2713
|
}
|
|
@@ -2836,15 +2918,12 @@ async function connectWebSocketV2() {
|
|
|
2836
2918
|
if (!currentSessionPassword) {
|
|
2837
2919
|
throw new Error("missing password for websocket connect");
|
|
2838
2920
|
}
|
|
2839
|
-
if (!currentSessionCode) {
|
|
2840
|
-
throw new Error("missing session code for secure transport");
|
|
2841
|
-
}
|
|
2842
2921
|
console.log(`Connecting to gateway ${gatewayUrl}...`);
|
|
2843
2922
|
activeGatewayUrl = gatewayUrl;
|
|
2844
2923
|
const transport = new V2SessionTransport({
|
|
2845
2924
|
gatewayUrl,
|
|
2846
2925
|
password: currentSessionPassword,
|
|
2847
|
-
|
|
2926
|
+
sessionSecret: currentSessionPassword,
|
|
2848
2927
|
role: "cli",
|
|
2849
2928
|
debugLog: DEBUG_MODE ? debugLog : undefined,
|
|
2850
2929
|
handlers: {
|
|
@@ -2853,6 +2932,7 @@ async function connectWebSocketV2() {
|
|
|
2853
2932
|
return;
|
|
2854
2933
|
if (message.type === "peer_connected") {
|
|
2855
2934
|
console.log("App connected!\n");
|
|
2935
|
+
void publishDiscoveredPorts(true);
|
|
2856
2936
|
return;
|
|
2857
2937
|
}
|
|
2858
2938
|
if (message.type === "peer_disconnected") {
|
package/dist/transport/v2.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export interface V2TransportHandlers {
|
|
|
9
9
|
export interface V2TransportOptions {
|
|
10
10
|
gatewayUrl: string;
|
|
11
11
|
password: string;
|
|
12
|
-
|
|
12
|
+
sessionSecret: string;
|
|
13
13
|
role: "cli" | "app";
|
|
14
14
|
handlers: V2TransportHandlers;
|
|
15
15
|
debugLog?: (message: string, ...args: unknown[]) => void;
|
package/dist/transport/v2.js
CHANGED
|
@@ -127,6 +127,9 @@ export class V2SessionTransport {
|
|
|
127
127
|
this.resetPeerSession();
|
|
128
128
|
await this.maybeStartHandshake();
|
|
129
129
|
}
|
|
130
|
+
else if (raw.type === "peer_disconnected" || raw.type === "app_disconnected") {
|
|
131
|
+
this.resetPeerSession();
|
|
132
|
+
}
|
|
130
133
|
return;
|
|
131
134
|
}
|
|
132
135
|
if (isV2HandshakeFrame(raw)) {
|
|
@@ -302,7 +305,7 @@ export class V2SessionTransport {
|
|
|
302
305
|
}
|
|
303
306
|
}
|
|
304
307
|
computeHandshakeAuth(phase, senderRole, peerPubkeyB64, nonce, boxed) {
|
|
305
|
-
const authKey = sodium.crypto_generichash(sodium.crypto_auth_KEYBYTES, encodeUtf8(this.options.
|
|
308
|
+
const authKey = sodium.crypto_generichash(sodium.crypto_auth_KEYBYTES, encodeUtf8(this.options.sessionSecret), undefined);
|
|
306
309
|
const parts = [
|
|
307
310
|
phase,
|
|
308
311
|
senderRole,
|