devtunnel-cli 3.0.8 → 3.0.10

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/core/start.js +314 -27
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devtunnel-cli",
3
- "version": "3.0.8",
3
+ "version": "3.0.10",
4
4
  "type": "module",
5
5
  "description": "DevTunnel - Share local dev servers worldwide. Zero configuration tunnel for any framework. Install via npm: npm install -g devtunnel-cli. Works with Vite, React, Next.js, Express, NestJS and more.",
6
6
  "main": "src/core/start.js",
package/src/core/start.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { spawn } from "child_process";
2
- import { existsSync } from "fs";
2
+ import { existsSync, readFileSync } from "fs";
3
3
  import { join, dirname, basename } from "path";
4
4
  import { fileURLToPath } from "url";
5
+ import http from "http";
5
6
  import prompts from "prompts";
6
7
  import { selectFolder } from "../utils/folder-picker.js";
7
8
 
@@ -35,6 +36,128 @@ async function commandExists(command) {
35
36
  return result.code === 0;
36
37
  }
37
38
 
39
+ // Check if a port is in use (dev server running)
40
+ function checkPortInUse(port) {
41
+ return new Promise((resolve) => {
42
+ const server = http.createServer();
43
+
44
+ server.once('error', (err) => {
45
+ // Port is in use
46
+ if (err.code === 'EADDRINUSE') {
47
+ resolve(true);
48
+ } else {
49
+ resolve(false);
50
+ }
51
+ });
52
+
53
+ server.listen(port, () => {
54
+ // Port is available (not in use)
55
+ server.once('close', () => resolve(false));
56
+ server.close();
57
+ });
58
+ });
59
+ }
60
+
61
+ // Detect port from package.json
62
+ function detectPortFromPackage(packagePath) {
63
+ try {
64
+ if (!existsSync(packagePath)) return null;
65
+ const packageJson = JSON.parse(readFileSync(packagePath, 'utf8'));
66
+ const scripts = packageJson.scripts || {};
67
+
68
+ // Check for common dev commands
69
+ const devScript = scripts.dev || scripts.start || scripts.serve;
70
+ if (!devScript) return null;
71
+
72
+ // Try to extract port from script
73
+ const portMatch = devScript.match(/--port\s+(\d+)|:(\d+)|port[=:](\d+)/i);
74
+ if (portMatch) {
75
+ return parseInt(portMatch[1] || portMatch[2] || portMatch[3]);
76
+ }
77
+
78
+ // Default ports based on framework
79
+ if (devScript.includes('vite')) return 5173;
80
+ if (devScript.includes('next')) return 3000;
81
+ if (devScript.includes('react-scripts')) return 3000;
82
+ if (devScript.includes('webpack')) return 8080;
83
+ if (devScript.includes('express')) return 3000;
84
+
85
+ return null;
86
+ } catch (err) {
87
+ return null;
88
+ }
89
+ }
90
+
91
+ // Check common ports for running dev servers
92
+ async function detectRunningDevServer() {
93
+ const commonPorts = [3000, 5173, 8080, 5000, 4000, 8000, 3001, 5174];
94
+ const detected = [];
95
+
96
+ for (const port of commonPorts) {
97
+ const inUse = await checkPortInUse(port);
98
+ if (inUse) {
99
+ // Try to verify it's actually a dev server by making a request
100
+ try {
101
+ const response = await new Promise((resolve) => {
102
+ const req = http.get(`http://localhost:${port}`, { timeout: 2000 }, (res) => {
103
+ resolve(res.statusCode);
104
+ });
105
+ req.on('error', () => resolve(null));
106
+ req.on('timeout', () => {
107
+ req.destroy();
108
+ resolve(null);
109
+ });
110
+ });
111
+ // If we get any HTTP response, it's likely a dev server
112
+ if (response !== null) {
113
+ detected.push(port);
114
+ }
115
+ } catch (err) {
116
+ // Port is in use, add it anyway (might be a dev server)
117
+ detected.push(port);
118
+ }
119
+ }
120
+ }
121
+
122
+ return detected;
123
+ }
124
+
125
+ // Auto-detect project in current directory
126
+ async function autoDetectProject() {
127
+ const currentDir = process.cwd();
128
+ const packagePath = join(currentDir, 'package.json');
129
+
130
+ // Check if package.json exists
131
+ if (!existsSync(packagePath)) {
132
+ return null;
133
+ }
134
+
135
+ try {
136
+ const packageJson = JSON.parse(readFileSync(packagePath, 'utf8'));
137
+ const projectName = packageJson.name || basename(currentDir);
138
+
139
+ // FIRST: Check for running dev servers (priority)
140
+ const runningPorts = await detectRunningDevServer();
141
+ let detectedPort = null;
142
+
143
+ if (runningPorts.length > 0) {
144
+ // Use running server port (most accurate)
145
+ detectedPort = runningPorts[0];
146
+ } else {
147
+ // Fallback: Try to detect port from package.json
148
+ detectedPort = detectPortFromPackage(packagePath);
149
+ }
150
+
151
+ return {
152
+ path: currentDir,
153
+ name: projectName,
154
+ port: detectedPort
155
+ };
156
+ } catch (err) {
157
+ return null;
158
+ }
159
+ }
160
+
38
161
  // ASCII Logo - Compatible with all OS and terminals
39
162
  function showLogo() {
40
163
  console.log("");
@@ -61,7 +184,7 @@ async function main() {
61
184
  // Show ASCII logo
62
185
  showLogo();
63
186
 
64
- console.log("DevTunnel v3.0.8");
187
+ console.log("DevTunnel v3.0.10");
65
188
  console.log("Share your local dev servers worldwide");
66
189
  console.log("");
67
190
  console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
@@ -147,36 +270,200 @@ async function main() {
147
270
  }
148
271
  console.log("");
149
272
 
150
- // Step 4: Select folder using native OS dialog
151
- console.log("[4/4] Select your project folder...");
152
- console.log("Opening folder picker...");
153
- console.log("");
273
+ // Step 4: Auto-detect or select project
274
+ console.log("[4/4] Detecting project...");
154
275
 
155
- const projectPath = await selectFolder();
276
+ let projectPath, projectName, devPort;
156
277
 
157
- if (!projectPath || projectPath.length === 0) {
158
- console.log("ERROR: No folder selected");
159
- process.exit(1);
160
- }
161
-
162
- const projectName = basename(projectPath);
163
- console.log(`Selected: ${projectPath}`);
164
- console.log("");
165
-
166
- // Get port
167
- const portResponse = await prompts({
168
- type: "number",
169
- name: "port",
170
- message: "Enter your dev server port:",
171
- initial: 5173
172
- });
278
+ // Try to auto-detect project in current directory
279
+ const autoDetected = await autoDetectProject();
173
280
 
174
- if (!portResponse.port) {
175
- console.log("ERROR: No port entered");
176
- process.exit(1);
281
+ if (autoDetected && autoDetected.port) {
282
+ // Auto-detected project with port
283
+ projectPath = autoDetected.path;
284
+ projectName = autoDetected.name;
285
+
286
+ // Double-check: verify the port is actually in use
287
+ const portInUse = await checkPortInUse(autoDetected.port);
288
+
289
+ if (!portInUse) {
290
+ // Detected port is not actually running, check for other running servers
291
+ console.log(`Detected port ${autoDetected.port} from package.json, but no server running on that port`);
292
+ console.log("Checking for running dev servers...");
293
+
294
+ const runningPorts = await detectRunningDevServer();
295
+ if (runningPorts.length > 0) {
296
+ if (runningPorts.length === 1) {
297
+ devPort = runningPorts[0];
298
+ console.log(`Found running dev server on port: ${devPort}`);
299
+ } else {
300
+ console.log(`Found ${runningPorts.length} running dev server(s) on port(s): ${runningPorts.join(', ')}`);
301
+ const portResponse = await prompts({
302
+ type: "select",
303
+ name: "port",
304
+ message: "Select port:",
305
+ choices: runningPorts.map(p => ({ title: `Port ${p}`, value: p }))
306
+ });
307
+
308
+ if (!portResponse.port) {
309
+ console.log("ERROR: No port selected");
310
+ process.exit(1);
311
+ }
312
+
313
+ devPort = portResponse.port;
314
+ }
315
+ } else {
316
+ // No running servers, use detected port (user might start it later)
317
+ devPort = autoDetected.port;
318
+ console.log(`Using detected port: ${devPort} (make sure dev server is running)`);
319
+ }
320
+ } else {
321
+ // Port is in use, use it
322
+ devPort = autoDetected.port;
323
+ }
324
+
325
+ console.log(`Detected project: ${projectName}`);
326
+ console.log(`Using port: ${devPort}`);
327
+ console.log(`Using current directory: ${projectPath}`);
328
+ console.log("");
329
+
330
+ // Confirm with user
331
+ const confirm = await prompts({
332
+ type: "confirm",
333
+ name: "value",
334
+ message: "Use detected project?",
335
+ initial: true
336
+ });
337
+
338
+ if (!confirm.value) {
339
+ // User wants to select manually
340
+ console.log("");
341
+ console.log("Selecting project manually...");
342
+ console.log("");
343
+
344
+ const selectedPath = await selectFolder();
345
+ if (!selectedPath || selectedPath.length === 0) {
346
+ console.log("ERROR: No folder selected");
347
+ process.exit(1);
348
+ }
349
+
350
+ projectPath = selectedPath;
351
+ projectName = basename(selectedPath);
352
+
353
+ // Try to detect port for selected project
354
+ const selectedPackagePath = join(selectedPath, 'package.json');
355
+ const detectedPort = detectPortFromPackage(selectedPackagePath);
356
+
357
+ const portResponse = await prompts({
358
+ type: "number",
359
+ name: "port",
360
+ message: "Enter your dev server port:",
361
+ initial: detectedPort || 5173
362
+ });
363
+
364
+ if (!portResponse.port) {
365
+ console.log("ERROR: No port entered");
366
+ process.exit(1);
367
+ }
368
+
369
+ devPort = portResponse.port;
370
+ }
371
+ } else if (autoDetected && !autoDetected.port) {
372
+ // Project detected but no port
373
+ projectPath = autoDetected.path;
374
+ projectName = autoDetected.name;
375
+
376
+ console.log(`Detected project: ${projectName}`);
377
+ console.log(`Using current directory: ${projectPath}`);
378
+ console.log("Checking for running dev servers...");
379
+
380
+ const runningPorts = await detectRunningDevServer();
381
+
382
+ if (runningPorts.length > 0) {
383
+ console.log(`Found ${runningPorts.length} running dev server(s) on port(s): ${runningPorts.join(', ')}`);
384
+
385
+ if (runningPorts.length === 1) {
386
+ devPort = runningPorts[0];
387
+ console.log(`Using port: ${devPort}`);
388
+ } else {
389
+ // Multiple ports detected, let user choose
390
+ const portResponse = await prompts({
391
+ type: "select",
392
+ name: "port",
393
+ message: "Select port:",
394
+ choices: runningPorts.map(p => ({ title: `Port ${p}`, value: p }))
395
+ });
396
+
397
+ if (!portResponse.port) {
398
+ console.log("ERROR: No port selected");
399
+ process.exit(1);
400
+ }
401
+
402
+ devPort = portResponse.port;
403
+ }
404
+ } else {
405
+ // No running server, ask for port
406
+ const portResponse = await prompts({
407
+ type: "number",
408
+ name: "port",
409
+ message: "Enter your dev server port:",
410
+ initial: 5173
411
+ });
412
+
413
+ if (!portResponse.port) {
414
+ console.log("ERROR: No port entered");
415
+ process.exit(1);
416
+ }
417
+
418
+ devPort = portResponse.port;
419
+ }
420
+
421
+ console.log("");
422
+ } else {
423
+ // No auto-detection, use folder picker
424
+ console.log("No project detected in current directory");
425
+ console.log("Opening folder picker...");
426
+ console.log("");
427
+
428
+ projectPath = await selectFolder();
429
+
430
+ if (!projectPath || projectPath.length === 0) {
431
+ console.log("ERROR: No folder selected");
432
+ process.exit(1);
433
+ }
434
+
435
+ projectName = basename(projectPath);
436
+ console.log(`Selected: ${projectPath}`);
437
+ console.log("");
438
+
439
+ // Try to detect port for selected project
440
+ const selectedPackagePath = join(projectPath, 'package.json');
441
+ const detectedPort = detectPortFromPackage(selectedPackagePath);
442
+
443
+ // Check for running servers
444
+ const runningPorts = await detectRunningDevServer();
445
+
446
+ let initialPort = detectedPort || 5173;
447
+ if (runningPorts.length > 0 && !detectedPort) {
448
+ initialPort = runningPorts[0];
449
+ }
450
+
451
+ const portResponse = await prompts({
452
+ type: "number",
453
+ name: "port",
454
+ message: "Enter your dev server port:",
455
+ initial: initialPort
456
+ });
457
+
458
+ if (!portResponse.port) {
459
+ console.log("ERROR: No port entered");
460
+ process.exit(1);
461
+ }
462
+
463
+ devPort = portResponse.port;
177
464
  }
178
465
 
179
- const devPort = portResponse.port;
466
+ console.log("");
180
467
  const proxyPort = devPort + 1000; // Use port 1000 higher for proxy
181
468
 
182
469
  console.log("");