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/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 from outside
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 < 15; i++) {
507
+ for (let i = 0; i < 30; i++) {
459
508
  try {
460
- const res = await fetch(`${tunnelUrl}/api/health`, { signal: AbortSignal.timeout(3000) });
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "9remote",
3
- "version": "0.1.46",
3
+ "version": "0.1.48",
4
4
  "type": "module",
5
5
  "description": "Remote terminal access from anywhere",
6
6
  "main": "index.js",