9remote 0.1.34 → 0.1.37
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 +56 -56
- package/index.js +20 -57
- package/package.json +1 -1
- package/utils/cloudflared.js +29 -33
package/index.js
CHANGED
|
@@ -7,10 +7,9 @@ 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 dns from "dns/promises";
|
|
11
10
|
import { getConsistentMachineId } from "./utils/machineId.js";
|
|
12
11
|
import { generateApiKeyWithMachine } from "./utils/apiKey.js";
|
|
13
|
-
import { loadKey, saveKey, saveState, clearState } from "./utils/state.js";
|
|
12
|
+
import { loadKey, saveKey, loadState, saveState, clearState } from "./utils/state.js";
|
|
14
13
|
import { createTempKey } from "./utils/token.js";
|
|
15
14
|
import { checkAndUpdate } from "./utils/updateChecker.js";
|
|
16
15
|
import { ensureCloudflared, spawnCloudflared, killCloudflared, resetRestartCounter } from "./utils/cloudflared.js";
|
|
@@ -26,6 +25,7 @@ const STANDALONE_SERVER = path.join(__dirname, "server.cjs");
|
|
|
26
25
|
const DEV_SERVER = path.join(PROJECT_ROOT, "server/index.js");
|
|
27
26
|
const WORKER_URL = "https://9remote.cc";
|
|
28
27
|
const SERVER_PORT = 2208;
|
|
28
|
+
const SHORT_ID_CHARS = "abcdefghijklmnpqrstuvwxyz23456789";
|
|
29
29
|
const MAX_RESTART_ATTEMPTS = 10;
|
|
30
30
|
const RESTART_WINDOW_MS = 60000; // 1 minute
|
|
31
31
|
|
|
@@ -88,6 +88,17 @@ function showBanner() {
|
|
|
88
88
|
console.log("");
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Generate short random ID for tunnel subdomain
|
|
93
|
+
*/
|
|
94
|
+
function generateShortId() {
|
|
95
|
+
let result = "";
|
|
96
|
+
for (let i = 0; i < 6; i++) {
|
|
97
|
+
result += SHORT_ID_CHARS.charAt(Math.floor(Math.random() * SHORT_ID_CHARS.length));
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
|
|
91
102
|
/**
|
|
92
103
|
* Helper: Show QR code for connect URL
|
|
93
104
|
*/
|
|
@@ -305,12 +316,16 @@ async function startServerAndTunnel(selectedKey) {
|
|
|
305
316
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
306
317
|
} catch { }
|
|
307
318
|
|
|
319
|
+
// Reuse existing shortId or generate new one
|
|
320
|
+
const existingState = loadState();
|
|
321
|
+
const shortId = existingState?.shortId || generateShortId();
|
|
322
|
+
|
|
308
323
|
// Create session first
|
|
309
324
|
try {
|
|
310
325
|
const sessionResponse = await fetch(`${WORKER_URL}/api/session/create`, {
|
|
311
326
|
method: "POST",
|
|
312
327
|
headers: { "Content-Type": "application/json" },
|
|
313
|
-
body: JSON.stringify({ apiKey: selectedKey })
|
|
328
|
+
body: JSON.stringify({ apiKey: selectedKey, shortId })
|
|
314
329
|
});
|
|
315
330
|
|
|
316
331
|
if (!sessionResponse.ok) {
|
|
@@ -435,65 +450,13 @@ async function startServerAndTunnel(selectedKey) {
|
|
|
435
450
|
}
|
|
436
451
|
|
|
437
452
|
|
|
438
|
-
// Wait for tunnel to be ready
|
|
439
453
|
console.log(ORANGE(`✅ Tunnel URL: ${tunnelUrl}`));
|
|
440
|
-
|
|
441
|
-
const maxWaitTime = 60000;
|
|
442
|
-
const checkInterval = 2000;
|
|
443
|
-
const maxRetries = Math.floor(maxWaitTime / checkInterval);
|
|
444
|
-
let tunnelReady = false;
|
|
445
|
-
|
|
446
|
-
// Resolve IP using Cloudflare DNS to bypass local cache
|
|
447
|
-
const hostname = new URL(tunnelUrl).hostname;
|
|
448
|
-
let resolvedIp = null;
|
|
449
|
-
|
|
450
|
-
for (let i = 0; i < maxRetries; i++) {
|
|
451
|
-
try {
|
|
452
|
-
// Resolve DNS using Cloudflare DNS server (bypass local cache)
|
|
453
|
-
if (!resolvedIp) {
|
|
454
|
-
const resolver = new dns.Resolver();
|
|
455
|
-
resolver.setServers(["1.1.1.1", "1.0.0.1"]);
|
|
456
|
-
const addresses = await resolver.resolve4(hostname);
|
|
457
|
-
if (addresses.length > 0) {
|
|
458
|
-
resolvedIp = addresses[0];
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
if (resolvedIp) {
|
|
463
|
-
// Use curl with --resolve to bypass DNS cache and SSL issues
|
|
464
|
-
const curlResult = execSync(
|
|
465
|
-
`curl -s --max-time 5 --resolve "${hostname}:443:${resolvedIp}" "${tunnelUrl}/api/health"`,
|
|
466
|
-
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
467
|
-
);
|
|
468
|
-
if (curlResult.includes("ok")) {
|
|
469
|
-
tunnelReady = true;
|
|
470
|
-
break;
|
|
471
|
-
} else {
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
} catch (err) {
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
const spinners = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
478
|
-
process.stdout.write(`\r${ORANGE(" Waiting for tunnel")} ${ORANGE(spinners[i % spinners.length])} ${chalk.gray(`(${i * 2}s)`)}`);
|
|
479
|
-
|
|
480
|
-
await new Promise(r => setTimeout(r, checkInterval));
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
process.stdout.write("\r" + " ".repeat(50) + "\r");
|
|
484
|
-
|
|
485
|
-
if (!tunnelReady) {
|
|
486
|
-
console.log(chalk.red("❌ Tunnel connection timeout"));
|
|
487
|
-
serverManager.shutdown();
|
|
488
|
-
tunnelProcess.kill();
|
|
489
|
-
return null;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
454
|
console.log(ORANGE(`✅ Connection established`));
|
|
493
455
|
|
|
494
|
-
// Save state
|
|
456
|
+
// Save state (persist shortId for reuse on restart)
|
|
495
457
|
saveState({
|
|
496
458
|
apiKey: selectedKey,
|
|
459
|
+
shortId,
|
|
497
460
|
tunnelUrl,
|
|
498
461
|
serverPid: serverManager.getProcess()?.pid,
|
|
499
462
|
tunnelPid: tunnelProcess.pid
|
package/package.json
CHANGED
package/utils/cloudflared.js
CHANGED
|
@@ -183,44 +183,40 @@ export async function spawnCloudflared(tunnelToken, onRestart = null) {
|
|
|
183
183
|
stdio: ["ignore", "pipe", "pipe"]
|
|
184
184
|
});
|
|
185
185
|
|
|
186
|
-
// Reset intentional shutdown flag immediately after spawn
|
|
187
|
-
// This ensures auto-restart works if cloudflared crashes
|
|
188
186
|
isIntentionalShutdown = false;
|
|
189
187
|
console.log(`✅ Cloudflared spawned with PID: ${child.pid}`);
|
|
190
188
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if (connectionCount === 4) {
|
|
212
|
-
process.stdout.write("\n");
|
|
189
|
+
// Wait for 4 connections before resolving (tunnel is truly ready)
|
|
190
|
+
await new Promise((resolve, reject) => {
|
|
191
|
+
let connectionCount = 0;
|
|
192
|
+
let resolved = false;
|
|
193
|
+
const timeout = setTimeout(() => {
|
|
194
|
+
if (!resolved) { resolved = true; resolve(child); }
|
|
195
|
+
}, 90000);
|
|
196
|
+
|
|
197
|
+
const handleLog = (data) => {
|
|
198
|
+
const msg = data.toString().trim();
|
|
199
|
+
if (LOG_IGNORE.some(pattern => msg.includes(pattern))) return;
|
|
200
|
+
if (isIntentionalShutdown) return;
|
|
201
|
+
if (msg.includes("Registered tunnel connection")) {
|
|
202
|
+
connectionCount++;
|
|
203
|
+
if (connectionCount <= 4) {
|
|
204
|
+
process.stdout.write(`\r ✔ Connection ${connectionCount}/4 established`);
|
|
205
|
+
if (connectionCount === 4) {
|
|
206
|
+
process.stdout.write("\n");
|
|
207
|
+
if (!resolved) { resolved = true; clearTimeout(timeout); resolve(child); }
|
|
208
|
+
}
|
|
213
209
|
}
|
|
210
|
+
return;
|
|
214
211
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
child.stdout.on("data", handleLog);
|
|
215
|
+
child.stderr.on("data", handleLog);
|
|
216
|
+
child.on("error", (err) => { if (!resolved) { resolved = true; clearTimeout(timeout); reject(err); } });
|
|
217
|
+
child.on("exit", (code) => { if (!resolved) { resolved = true; clearTimeout(timeout); reject(new Error(`cloudflared exited with code ${code}`)); } });
|
|
218
|
+
});
|
|
219
|
+
|
|
224
220
|
child.on("error", (error) => {
|
|
225
221
|
console.error("❌ cloudflared error:", error);
|
|
226
222
|
});
|