9remote 0.1.46 → 0.1.48
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/cli.cjs +53 -53
- package/dist/server.cjs +49 -49
- package/index.js +69 -4
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -7,6 +7,10 @@ import { spawn, execSync } from "child_process";
|
|
|
7
7
|
import path from "path";
|
|
8
8
|
import { fileURLToPath } from "url";
|
|
9
9
|
import fs from "fs";
|
|
10
|
+
import os from "os";
|
|
11
|
+
import dns from "dns";
|
|
12
|
+
import https from "https";
|
|
13
|
+
import { promisify } from "util";
|
|
10
14
|
import { getConsistentMachineId } from "./utils/machineId.js";
|
|
11
15
|
import { generateApiKeyWithMachine } from "./utils/apiKey.js";
|
|
12
16
|
import { loadKey, saveKey, loadState, saveState, clearState } from "./utils/state.js";
|
|
@@ -14,6 +18,39 @@ import { createTempKey } from "./utils/token.js";
|
|
|
14
18
|
import { checkAndUpdate } from "./utils/updateChecker.js";
|
|
15
19
|
import { ensureCloudflared, spawnCloudflared, killCloudflared, resetRestartCounter } from "./utils/cloudflared.js";
|
|
16
20
|
|
|
21
|
+
// DNS resolver using Cloudflare — ensures tunnel domains resolve immediately
|
|
22
|
+
const cfResolver = new dns.Resolver();
|
|
23
|
+
cfResolver.setServers(["1.1.1.1", "1.0.0.1"]);
|
|
24
|
+
const cfResolve4 = promisify(cfResolver.resolve4.bind(cfResolver));
|
|
25
|
+
|
|
26
|
+
/** Fetch via IP with correct TLS SNI — bypasses system DNS */
|
|
27
|
+
function fetchWithCfDns(url, timeoutMs = 5000) {
|
|
28
|
+
return new Promise(async (resolve, reject) => {
|
|
29
|
+
try {
|
|
30
|
+
const parsed = new URL(url);
|
|
31
|
+
const [ip] = await cfResolve4(parsed.hostname);
|
|
32
|
+
const req = https.request({
|
|
33
|
+
hostname: ip,
|
|
34
|
+
port: 443,
|
|
35
|
+
path: parsed.pathname + parsed.search,
|
|
36
|
+
method: "GET",
|
|
37
|
+
headers: { host: parsed.hostname },
|
|
38
|
+
servername: parsed.hostname,
|
|
39
|
+
rejectUnauthorized: true
|
|
40
|
+
}, (res) => {
|
|
41
|
+
let body = "";
|
|
42
|
+
res.on("data", d => { body += d; });
|
|
43
|
+
res.on("end", () => resolve({ ok: res.statusCode >= 200 && res.statusCode < 300, status: res.statusCode, body }));
|
|
44
|
+
});
|
|
45
|
+
req.setTimeout(timeoutMs, () => { req.destroy(); reject(new Error("timeout")); });
|
|
46
|
+
req.on("error", reject);
|
|
47
|
+
req.end();
|
|
48
|
+
} catch (err) {
|
|
49
|
+
reject(err);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
17
54
|
// Parse --skip-update flag
|
|
18
55
|
const skipUpdate = process.argv.includes("--skip-update");
|
|
19
56
|
|
|
@@ -284,6 +321,19 @@ function setupExitHandler(serverManager, tunnelProcess, apiKey) {
|
|
|
284
321
|
});
|
|
285
322
|
}
|
|
286
323
|
|
|
324
|
+
/**
|
|
325
|
+
* Get first non-internal LAN IPv4 address
|
|
326
|
+
*/
|
|
327
|
+
function getLanIp() {
|
|
328
|
+
const interfaces = os.networkInterfaces();
|
|
329
|
+
for (const iface of Object.values(interfaces)) {
|
|
330
|
+
for (const addr of iface) {
|
|
331
|
+
if (addr.family === "IPv4" && !addr.internal) return addr.address;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
|
|
287
337
|
/**
|
|
288
338
|
* Helper: Create Named Tunnel via Worker API
|
|
289
339
|
*/
|
|
@@ -450,14 +500,13 @@ async function startServerAndTunnel(selectedKey) {
|
|
|
450
500
|
serverManager.shutdown();
|
|
451
501
|
return null;
|
|
452
502
|
}
|
|
453
|
-
|
|
454
503
|
|
|
455
|
-
// Verify tunnel reachable
|
|
504
|
+
// Verify tunnel reachable via Cloudflare DNS + https.request (bypasses system DNS)
|
|
456
505
|
const spinners = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
457
506
|
let tunnelReady = false;
|
|
458
|
-
for (let i = 0; i <
|
|
507
|
+
for (let i = 0; i < 30; i++) {
|
|
459
508
|
try {
|
|
460
|
-
const res = await
|
|
509
|
+
const res = await fetchWithCfDns(`${tunnelUrl}/api/health`);
|
|
461
510
|
if (res.ok) { tunnelReady = true; break; }
|
|
462
511
|
} catch { }
|
|
463
512
|
process.stdout.write(`\r Verifying tunnel ${spinners[i % spinners.length]} (${i * 2}s)`);
|
|
@@ -473,8 +522,24 @@ async function startServerAndTunnel(selectedKey) {
|
|
|
473
522
|
}
|
|
474
523
|
|
|
475
524
|
console.log(ORANGE(`✅ Tunnel URL: ${tunnelUrl}`));
|
|
525
|
+
const lanIpLog = getLanIp();
|
|
526
|
+
if (lanIpLog) console.log(ORANGE(`✅ Local IP: ${lanIpLog}:${SERVER_PORT} (LAN direct available)`));
|
|
476
527
|
console.log(ORANGE(`✅ Connection established`));
|
|
477
528
|
|
|
529
|
+
// Update session with tunnelUrl + localIp (worker detects publicIp via CF-Connecting-IP)
|
|
530
|
+
const lanIp = lanIpLog;
|
|
531
|
+
try {
|
|
532
|
+
await fetch(`${WORKER_URL}/api/session/update`, {
|
|
533
|
+
method: "POST",
|
|
534
|
+
headers: { "Content-Type": "application/json" },
|
|
535
|
+
body: JSON.stringify({
|
|
536
|
+
apiKey: selectedKey,
|
|
537
|
+
tunnelUrl,
|
|
538
|
+
localIp: lanIp ? `${lanIp}:${SERVER_PORT}` : null
|
|
539
|
+
})
|
|
540
|
+
});
|
|
541
|
+
} catch { }
|
|
542
|
+
|
|
478
543
|
// Save state (persist shortId for reuse on restart)
|
|
479
544
|
saveState({
|
|
480
545
|
apiKey: selectedKey,
|