devtunnel-cli 3.0.1 ā 3.0.3
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/package.json +1 -1
- package/src/core/RUN.js +3 -2
- package/src/core/index.js +42 -77
- package/src/core/proxy-server.js +10 -12
- package/src/core/start.js +61 -41
package/package.json
CHANGED
package/src/core/RUN.js
CHANGED
|
@@ -9,8 +9,9 @@ import { dirname, join } from "path";
|
|
|
9
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
10
|
const __dirname = dirname(__filename);
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
// Clear screen before starting
|
|
13
|
+
process.stdout.write('\x1B[2J\x1B[0f');
|
|
14
|
+
console.clear();
|
|
14
15
|
|
|
15
16
|
// Start the main app
|
|
16
17
|
// start.js is in the same directory as RUN.js (src/core/)
|
package/src/core/index.js
CHANGED
|
@@ -28,13 +28,12 @@ if (!PORT || isNaN(PORT) || PORT < 1 || PORT > 65535) {
|
|
|
28
28
|
let tunnelProcess;
|
|
29
29
|
let currentTunnelType = null;
|
|
30
30
|
|
|
31
|
-
console.log("
|
|
32
|
-
console.log("
|
|
33
|
-
console.log(
|
|
34
|
-
console.log(
|
|
35
|
-
console.log(
|
|
36
|
-
console.log("
|
|
37
|
-
console.log("š” Ensure dev server is running on port " + PORT + "\n");
|
|
31
|
+
console.log("DevTunnel Tunnel Service");
|
|
32
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
33
|
+
console.log(`Project: ${PROJECT_NAME}`);
|
|
34
|
+
console.log(`Port: ${PORT}`);
|
|
35
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n");
|
|
36
|
+
console.log("Ensure dev server is running on port " + PORT + "\n");
|
|
38
37
|
|
|
39
38
|
// Check if project is Vite and auto-fix config for Cloudflare
|
|
40
39
|
async function fixViteConfigForCloudflare() {
|
|
@@ -171,13 +170,13 @@ const TUNNEL_SERVICES = [
|
|
|
171
170
|
}
|
|
172
171
|
},
|
|
173
172
|
needsViteFix: false,
|
|
174
|
-
warning: "
|
|
173
|
+
warning: "Note: LocalTunnel shows a password page on first visit (uses your public IP)"
|
|
175
174
|
}
|
|
176
175
|
];
|
|
177
176
|
|
|
178
177
|
// Try each tunnel service
|
|
179
178
|
async function tryTunnelServices() {
|
|
180
|
-
console.log("
|
|
179
|
+
console.log("Checking available tunnel services...\n");
|
|
181
180
|
|
|
182
181
|
let hasCloudflare = false;
|
|
183
182
|
|
|
@@ -191,15 +190,15 @@ async function tryTunnelServices() {
|
|
|
191
190
|
|
|
192
191
|
// Show tip if Cloudflare not installed
|
|
193
192
|
if (!hasCloudflare) {
|
|
194
|
-
console.log("
|
|
195
|
-
console.log("
|
|
193
|
+
console.log("TIP: Install Cloudflare for best experience (no password, fastest)");
|
|
194
|
+
console.log(" winget install Cloudflare.cloudflared\n");
|
|
196
195
|
}
|
|
197
196
|
|
|
198
197
|
for (const service of TUNNEL_SERVICES) {
|
|
199
198
|
const available = await service.available();
|
|
200
199
|
|
|
201
200
|
if (available) {
|
|
202
|
-
console.log(
|
|
201
|
+
console.log(`${service.name} is available`);
|
|
203
202
|
|
|
204
203
|
// Show warning if exists
|
|
205
204
|
if (service.warning) {
|
|
@@ -208,7 +207,7 @@ async function tryTunnelServices() {
|
|
|
208
207
|
|
|
209
208
|
// Skip Vite auto-fix - using proxy server instead
|
|
210
209
|
|
|
211
|
-
console.log(
|
|
210
|
+
console.log(`Starting ${service.name} tunnel...\n`);
|
|
212
211
|
|
|
213
212
|
currentTunnelType = service.name;
|
|
214
213
|
tunnelProcess = spawn(service.command, service.args, {
|
|
@@ -223,25 +222,25 @@ async function tryTunnelServices() {
|
|
|
223
222
|
|
|
224
223
|
// Check if process is still running
|
|
225
224
|
if (tunnelProcess && !tunnelProcess.killed) {
|
|
226
|
-
console.log(
|
|
225
|
+
console.log(`Successfully connected via ${service.name}!`);
|
|
227
226
|
if (service.name === "LocalTunnel") {
|
|
228
|
-
console.log("
|
|
229
|
-
console.log("
|
|
227
|
+
console.log("Note: First-time visitors need to enter tunnel password (your public IP)");
|
|
228
|
+
console.log("Get password at: https://loca.lt/mytunnelpassword\n");
|
|
230
229
|
}
|
|
231
230
|
console.log("Press Ctrl+C to stop the tunnel\n");
|
|
232
231
|
return true;
|
|
233
232
|
}
|
|
234
233
|
} else {
|
|
235
|
-
console.log(
|
|
234
|
+
console.log(`${service.name} not available`);
|
|
236
235
|
}
|
|
237
236
|
}
|
|
238
237
|
|
|
239
|
-
console.log("\
|
|
240
|
-
console.log("\
|
|
238
|
+
console.log("\nNo tunnel services available!");
|
|
239
|
+
console.log("\nRecommended: Install Cloudflare (fastest, no password):");
|
|
241
240
|
console.log(" winget install Cloudflare.cloudflared");
|
|
242
|
-
console.log("\
|
|
241
|
+
console.log("\nOr install Ngrok:");
|
|
243
242
|
console.log(" Download from: https://ngrok.com/download");
|
|
244
|
-
console.log("\
|
|
243
|
+
console.log("\nLocalTunnel is already installed but may require restart");
|
|
245
244
|
process.exit(1);
|
|
246
245
|
}
|
|
247
246
|
|
|
@@ -264,22 +263,11 @@ function setupTunnelHandlers(serviceName) {
|
|
|
264
263
|
const urlMatch = trimmed.match(/(https?:\/\/[^\s]+trycloudflare\.com[^\s]*)/);
|
|
265
264
|
if (urlMatch) {
|
|
266
265
|
const url = urlMatch[1];
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
console.log("
|
|
272
|
-
const headerText = "ā
PUBLIC URL";
|
|
273
|
-
const headerPadding = boxWidth - headerText.length;
|
|
274
|
-
console.log("ā " + headerText + " ".repeat(Math.max(0, headerPadding)) + "ā");
|
|
275
|
-
console.log("ā " + "ā".repeat(boxWidth) + "ā£");
|
|
276
|
-
const urlPadding = boxWidth - url.length;
|
|
277
|
-
console.log("ā " + url + " ".repeat(Math.max(0, urlPadding)) + "ā");
|
|
278
|
-
console.log("ā " + "ā".repeat(boxWidth) + "ā£");
|
|
279
|
-
const shareText = "š” Share this URL with anyone!";
|
|
280
|
-
const sharePadding = boxWidth - shareText.length;
|
|
281
|
-
console.log("ā " + shareText + " ".repeat(Math.max(0, sharePadding)) + "ā");
|
|
282
|
-
console.log("ā" + "ā".repeat(boxWidth) + "ā\n");
|
|
266
|
+
console.log("\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
267
|
+
console.log("PUBLIC URL:");
|
|
268
|
+
console.log(url);
|
|
269
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
270
|
+
console.log("Share this URL with anyone!\n");
|
|
283
271
|
}
|
|
284
272
|
}
|
|
285
273
|
// Show other important messages (but filter out most INF/WRN logs)
|
|
@@ -289,18 +277,10 @@ function setupTunnelHandlers(serviceName) {
|
|
|
289
277
|
} else if (serviceName === "Ngrok") {
|
|
290
278
|
if (trimmed.includes("https://") || trimmed.includes("http://")) {
|
|
291
279
|
const url = trimmed;
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
console.log("\nā" + "ā".repeat(boxWidth) + "ā");
|
|
297
|
-
const headerText = "ā
PUBLIC URL";
|
|
298
|
-
const headerPadding = boxWidth - headerText.length;
|
|
299
|
-
console.log("ā " + headerText + " ".repeat(Math.max(0, headerPadding)) + "ā");
|
|
300
|
-
console.log("ā " + "ā".repeat(boxWidth) + "ā£");
|
|
301
|
-
const urlPadding = boxWidth - url.length;
|
|
302
|
-
console.log("ā " + url + " ".repeat(Math.max(0, urlPadding)) + "ā");
|
|
303
|
-
console.log("ā" + "ā".repeat(boxWidth) + "ā\n");
|
|
280
|
+
console.log("\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
281
|
+
console.log("PUBLIC URL:");
|
|
282
|
+
console.log(url);
|
|
283
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n");
|
|
304
284
|
}
|
|
305
285
|
} else {
|
|
306
286
|
// LocalTunnel or other services
|
|
@@ -308,18 +288,10 @@ function setupTunnelHandlers(serviceName) {
|
|
|
308
288
|
const urlMatch = trimmed.match(/https?:\/\/[^\s]+/);
|
|
309
289
|
if (urlMatch) {
|
|
310
290
|
const url = urlMatch[0];
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
console.log("\nā" + "ā".repeat(boxWidth) + "ā");
|
|
316
|
-
const headerText = "ā
PUBLIC URL";
|
|
317
|
-
const headerPadding = boxWidth - headerText.length;
|
|
318
|
-
console.log("ā " + headerText + " ".repeat(Math.max(0, headerPadding)) + "ā");
|
|
319
|
-
console.log("ā " + "ā".repeat(boxWidth) + "ā£");
|
|
320
|
-
const urlPadding = boxWidth - url.length;
|
|
321
|
-
console.log("ā " + url + " ".repeat(Math.max(0, urlPadding)) + "ā");
|
|
322
|
-
console.log("ā" + "ā".repeat(boxWidth) + "ā\n");
|
|
291
|
+
console.log("\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
292
|
+
console.log("PUBLIC URL:");
|
|
293
|
+
console.log(url);
|
|
294
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n");
|
|
323
295
|
}
|
|
324
296
|
}
|
|
325
297
|
}
|
|
@@ -334,18 +306,11 @@ function setupTunnelHandlers(serviceName) {
|
|
|
334
306
|
const urlMatch = output.match(/(https?:\/\/[^\s]+trycloudflare\.com[^\s]*)/);
|
|
335
307
|
if (urlMatch) {
|
|
336
308
|
const url = urlMatch[1];
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
console.log("
|
|
342
|
-
const headerText = "ā
PUBLIC URL";
|
|
343
|
-
const headerPadding = boxWidth - headerText.length;
|
|
344
|
-
console.log("ā " + headerText + " ".repeat(Math.max(0, headerPadding)) + "ā");
|
|
345
|
-
console.log("ā " + "ā".repeat(boxWidth) + "ā£");
|
|
346
|
-
const urlPadding = boxWidth - url.length;
|
|
347
|
-
console.log("ā " + url + " ".repeat(Math.max(0, urlPadding)) + "ā");
|
|
348
|
-
console.log("ā" + "ā".repeat(boxWidth) + "ā\n");
|
|
309
|
+
console.log("\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
310
|
+
console.log("PUBLIC URL:");
|
|
311
|
+
console.log(url);
|
|
312
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
313
|
+
console.log("Share this URL with anyone!\n");
|
|
349
314
|
}
|
|
350
315
|
}
|
|
351
316
|
|
|
@@ -353,25 +318,25 @@ function setupTunnelHandlers(serviceName) {
|
|
|
353
318
|
if (!output.includes("INF") && !output.includes("WRN")) {
|
|
354
319
|
const trimmed = output.trim();
|
|
355
320
|
if (trimmed && !trimmed.includes("originCertPath")) {
|
|
356
|
-
console.error(
|
|
321
|
+
console.error(`Error: ${trimmed}`);
|
|
357
322
|
}
|
|
358
323
|
}
|
|
359
324
|
});
|
|
360
325
|
|
|
361
326
|
tunnelProcess.on("error", (error) => {
|
|
362
|
-
console.error(`\n
|
|
327
|
+
console.error(`\n${serviceName} error:`, error.message);
|
|
363
328
|
});
|
|
364
329
|
|
|
365
330
|
tunnelProcess.on("exit", (code) => {
|
|
366
331
|
if (code !== 0 && code !== null) {
|
|
367
|
-
console.error(`\n
|
|
332
|
+
console.error(`\n${serviceName} exited with code ${code}`);
|
|
368
333
|
}
|
|
369
334
|
});
|
|
370
335
|
}
|
|
371
336
|
|
|
372
337
|
// Handle cleanup on exit
|
|
373
338
|
function cleanup() {
|
|
374
|
-
console.log("\
|
|
339
|
+
console.log("\nShutting down tunnel...");
|
|
375
340
|
try {
|
|
376
341
|
if (tunnelProcess) {
|
|
377
342
|
tunnelProcess.kill();
|
package/src/core/proxy-server.js
CHANGED
|
@@ -21,7 +21,7 @@ const proxy = httpProxy.createProxyServer({
|
|
|
21
21
|
|
|
22
22
|
// Handle proxy errors
|
|
23
23
|
proxy.on("error", (err, req, res) => {
|
|
24
|
-
console.error("
|
|
24
|
+
console.error("Proxy error:", err.message);
|
|
25
25
|
if (res.writeHead) {
|
|
26
26
|
res.writeHead(502, { "Content-Type": "text/plain" });
|
|
27
27
|
res.end("Bad Gateway: Could not connect to your dev server.\nMake sure it's running on port " + TARGET_PORT);
|
|
@@ -52,21 +52,19 @@ server.on("upgrade", (req, socket, head) => {
|
|
|
52
52
|
|
|
53
53
|
// Start server
|
|
54
54
|
server.listen(PROXY_PORT, () => {
|
|
55
|
-
console.log("
|
|
56
|
-
console.log("
|
|
57
|
-
console.log(
|
|
58
|
-
console.log(
|
|
59
|
-
console.log(
|
|
60
|
-
console.log(
|
|
61
|
-
console.log("
|
|
62
|
-
console.log("
|
|
63
|
-
console.log("ā š” No config changes needed ā");
|
|
64
|
-
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n");
|
|
55
|
+
console.log("DevTunnel Proxy Server");
|
|
56
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
57
|
+
console.log(`Project: ${PROJECT_NAME}`);
|
|
58
|
+
console.log(`Dev Server: http://localhost:${TARGET_PORT}`);
|
|
59
|
+
console.log(`Proxy Port: ${PROXY_PORT}`);
|
|
60
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
61
|
+
console.log("Ready! Tunnel will connect to proxy");
|
|
62
|
+
console.log("No config changes needed\n");
|
|
65
63
|
});
|
|
66
64
|
|
|
67
65
|
// Handle shutdown
|
|
68
66
|
process.on("SIGINT", () => {
|
|
69
|
-
console.log("\
|
|
67
|
+
console.log("\nShutting down proxy...");
|
|
70
68
|
server.close();
|
|
71
69
|
process.exit(0);
|
|
72
70
|
});
|
package/src/core/start.js
CHANGED
|
@@ -35,25 +35,46 @@ async function commandExists(command) {
|
|
|
35
35
|
return result.code === 0;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
// ASCII Logo - Compatible with all OS and terminals
|
|
39
|
+
function showLogo() {
|
|
40
|
+
console.log("");
|
|
41
|
+
console.log(" _____ _____ _ _ ");
|
|
42
|
+
console.log(" | __ \\ |_ _| | | |");
|
|
43
|
+
console.log(" | | | |_ _ ___ | |_ _ _ __ ___ ___| | |");
|
|
44
|
+
console.log(" | | | | | | |/ _ \\| | | | | '_ \\ / _ \\/ _ \\ | |");
|
|
45
|
+
console.log(" | |__| | |_| | __/| | |_| | | | | __/ __/ | |");
|
|
46
|
+
console.log(" |_____/ \\__, |\\___|\\_/\\__,_|_| |_|\\___|\\___|_|_|");
|
|
47
|
+
console.log(" __/ | ");
|
|
48
|
+
console.log(" |___/ ");
|
|
49
|
+
console.log("");
|
|
50
|
+
}
|
|
51
|
+
|
|
38
52
|
// Main function
|
|
39
53
|
async function main() {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
console.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
// Clear screen - works on Windows, macOS, Linux
|
|
55
|
+
// ANSI escape codes for clear screen + cursor to top
|
|
56
|
+
process.stdout.write('\x1B[2J\x1B[0f');
|
|
57
|
+
console.clear(); // Fallback for terminals that don't support ANSI
|
|
58
|
+
|
|
59
|
+
// Show ASCII logo
|
|
60
|
+
showLogo();
|
|
61
|
+
|
|
62
|
+
console.log("DevTunnel v3.0.3");
|
|
63
|
+
console.log("Share your local dev servers worldwide");
|
|
64
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
65
|
+
console.log("Developer: maiz");
|
|
66
|
+
console.log("Repository: https://github.com/maiz-an/DevTunnel");
|
|
67
|
+
console.log("Website: https://devtunnel.vercel.app");
|
|
68
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n");
|
|
48
69
|
|
|
49
70
|
// Step 1: Check Node.js
|
|
50
71
|
console.log("[1/4] Checking Node.js...");
|
|
51
72
|
if (!await commandExists("node")) {
|
|
52
|
-
console.log("
|
|
73
|
+
console.log("ERROR: Node.js not found!");
|
|
53
74
|
console.log("Install from: https://nodejs.org/");
|
|
54
75
|
process.exit(1);
|
|
55
76
|
}
|
|
56
|
-
console.log("
|
|
77
|
+
console.log("SUCCESS: Node.js installed\n");
|
|
57
78
|
|
|
58
79
|
// Step 2: Check Cloudflare (bundled or system-installed)
|
|
59
80
|
console.log("[2/4] Checking Cloudflare...");
|
|
@@ -64,37 +85,37 @@ async function main() {
|
|
|
64
85
|
let cloudflareAvailable = false;
|
|
65
86
|
|
|
66
87
|
if (hasBundledCloudflared()) {
|
|
67
|
-
console.log("
|
|
88
|
+
console.log("SUCCESS: Using bundled Cloudflare (no install needed)");
|
|
68
89
|
cloudflareAvailable = true;
|
|
69
90
|
} else if (await commandExists("cloudflared")) {
|
|
70
|
-
console.log("
|
|
91
|
+
console.log("SUCCESS: Cloudflare installed on system");
|
|
71
92
|
cloudflareAvailable = true;
|
|
72
93
|
} else {
|
|
73
|
-
console.log("
|
|
74
|
-
console.log("
|
|
94
|
+
console.log("First time setup - Downloading Cloudflare...");
|
|
95
|
+
console.log("This only happens once (~40MB, 10-30 seconds)\n");
|
|
75
96
|
|
|
76
97
|
try {
|
|
77
98
|
const bundledPath = await setupCloudflared();
|
|
78
99
|
|
|
79
100
|
if (bundledPath) {
|
|
80
|
-
console.log("
|
|
101
|
+
console.log("SUCCESS: Cloudflare ready to use");
|
|
81
102
|
cloudflareAvailable = true;
|
|
82
103
|
} else {
|
|
83
|
-
console.log("
|
|
84
|
-
console.log("
|
|
104
|
+
console.log("Could not download Cloudflare");
|
|
105
|
+
console.log("Will use alternative tunnel services\n");
|
|
85
106
|
}
|
|
86
107
|
} catch (err) {
|
|
87
|
-
console.log(
|
|
88
|
-
console.log("
|
|
108
|
+
console.log(`Setup error: ${err.message}`);
|
|
109
|
+
console.log("Will use alternative tunnel services\n");
|
|
89
110
|
}
|
|
90
111
|
}
|
|
91
112
|
|
|
92
113
|
// Show what's available
|
|
93
114
|
if (!cloudflareAvailable) {
|
|
94
|
-
console.log("
|
|
95
|
-
console.log("
|
|
96
|
-
console.log("
|
|
97
|
-
console.log("
|
|
115
|
+
console.log("DevTunnel has multi-service fallback:");
|
|
116
|
+
console.log(" Cloudflare (fastest, no password)");
|
|
117
|
+
console.log(" Ngrok (fast alternative)");
|
|
118
|
+
console.log(" LocalTunnel (backup option)");
|
|
98
119
|
console.log("");
|
|
99
120
|
}
|
|
100
121
|
|
|
@@ -102,32 +123,32 @@ async function main() {
|
|
|
102
123
|
console.log("[3/4] Checking dependencies...");
|
|
103
124
|
const nodeModulesPath = join(PROJECT_ROOT, "node_modules");
|
|
104
125
|
if (!existsSync(nodeModulesPath)) {
|
|
105
|
-
console.log("
|
|
126
|
+
console.log("Installing dependencies...\n");
|
|
106
127
|
// Run npm install in the project root directory
|
|
107
128
|
const result = await runCommand("npm", ["install"], PROJECT_ROOT);
|
|
108
129
|
if (result.code !== 0) {
|
|
109
|
-
console.log("\
|
|
130
|
+
console.log("\nERROR: npm install failed");
|
|
110
131
|
process.exit(1);
|
|
111
132
|
}
|
|
112
|
-
console.log("\
|
|
133
|
+
console.log("\nSUCCESS: Dependencies installed");
|
|
113
134
|
} else {
|
|
114
|
-
console.log("
|
|
135
|
+
console.log("SUCCESS: Dependencies already installed");
|
|
115
136
|
}
|
|
116
137
|
console.log("");
|
|
117
138
|
|
|
118
139
|
// Step 4: Select folder using native OS dialog
|
|
119
140
|
console.log("[4/4] Select your project folder...");
|
|
120
|
-
console.log("
|
|
141
|
+
console.log("Opening folder picker...\n");
|
|
121
142
|
|
|
122
143
|
const projectPath = await selectFolder();
|
|
123
144
|
|
|
124
145
|
if (!projectPath || projectPath.length === 0) {
|
|
125
|
-
console.log("
|
|
146
|
+
console.log("ERROR: No folder selected");
|
|
126
147
|
process.exit(1);
|
|
127
148
|
}
|
|
128
149
|
|
|
129
150
|
const projectName = basename(projectPath);
|
|
130
|
-
console.log(
|
|
151
|
+
console.log(`Selected: ${projectPath}\n`);
|
|
131
152
|
|
|
132
153
|
// Get port
|
|
133
154
|
const portResponse = await prompts({
|
|
@@ -138,23 +159,22 @@ async function main() {
|
|
|
138
159
|
});
|
|
139
160
|
|
|
140
161
|
if (!portResponse.port) {
|
|
141
|
-
console.log("
|
|
162
|
+
console.log("ERROR: No port entered");
|
|
142
163
|
process.exit(1);
|
|
143
164
|
}
|
|
144
165
|
|
|
145
166
|
const devPort = portResponse.port;
|
|
146
167
|
const proxyPort = devPort + 1000; // Use port 1000 higher for proxy
|
|
147
168
|
|
|
148
|
-
console.log("\
|
|
149
|
-
console.log("
|
|
150
|
-
console.log(
|
|
151
|
-
console.log(
|
|
152
|
-
console.log(
|
|
153
|
-
console.log(
|
|
154
|
-
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n");
|
|
169
|
+
console.log("\nConfiguration:");
|
|
170
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
171
|
+
console.log(`Project: ${projectName}`);
|
|
172
|
+
console.log(`Dev Server: localhost:${devPort}`);
|
|
173
|
+
console.log(`Proxy Port: ${proxyPort}`);
|
|
174
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n");
|
|
155
175
|
|
|
156
176
|
// Start proxy server
|
|
157
|
-
console.log("
|
|
177
|
+
console.log("Starting services...\n");
|
|
158
178
|
const proxyPath = join(__dirname, "proxy-server.js");
|
|
159
179
|
const proxyProcess = spawn("node", [proxyPath, devPort.toString(), proxyPort.toString(), projectName], {
|
|
160
180
|
stdio: "inherit",
|
|
@@ -174,7 +194,7 @@ async function main() {
|
|
|
174
194
|
|
|
175
195
|
// Handle cleanup
|
|
176
196
|
const cleanup = () => {
|
|
177
|
-
console.log("\
|
|
197
|
+
console.log("\nShutting down...");
|
|
178
198
|
proxyProcess.kill();
|
|
179
199
|
tunnelProcess.kill();
|
|
180
200
|
process.exit(0);
|
|
@@ -195,6 +215,6 @@ async function main() {
|
|
|
195
215
|
|
|
196
216
|
// Run
|
|
197
217
|
main().catch((error) => {
|
|
198
|
-
console.error("\
|
|
218
|
+
console.error("\nERROR:", error.message);
|
|
199
219
|
process.exit(1);
|
|
200
220
|
});
|