devtunnel-cli 3.0.8 → 3.0.9

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 +275 -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.9",
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,127 @@ 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
+ // Try to detect port
140
+ let detectedPort = detectPortFromPackage(packagePath);
141
+
142
+ // If no port detected, check running servers
143
+ if (!detectedPort) {
144
+ const runningPorts = await detectRunningDevServer();
145
+ if (runningPorts.length > 0) {
146
+ detectedPort = runningPorts[0]; // Use first detected port
147
+ }
148
+ }
149
+
150
+ return {
151
+ path: currentDir,
152
+ name: projectName,
153
+ port: detectedPort
154
+ };
155
+ } catch (err) {
156
+ return null;
157
+ }
158
+ }
159
+
38
160
  // ASCII Logo - Compatible with all OS and terminals
39
161
  function showLogo() {
40
162
  console.log("");
@@ -61,7 +183,7 @@ async function main() {
61
183
  // Show ASCII logo
62
184
  showLogo();
63
185
 
64
- console.log("DevTunnel v3.0.8");
186
+ console.log("DevTunnel v3.0.9");
65
187
  console.log("Share your local dev servers worldwide");
66
188
  console.log("");
67
189
  console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
@@ -147,36 +269,162 @@ async function main() {
147
269
  }
148
270
  console.log("");
149
271
 
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("");
272
+ // Step 4: Auto-detect or select project
273
+ console.log("[4/4] Detecting project...");
154
274
 
155
- const projectPath = await selectFolder();
275
+ let projectPath, projectName, devPort;
156
276
 
157
- if (!projectPath || projectPath.length === 0) {
158
- console.log("ERROR: No folder selected");
159
- process.exit(1);
160
- }
277
+ // Try to auto-detect project in current directory
278
+ const autoDetected = await autoDetectProject();
161
279
 
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
- });
173
-
174
- if (!portResponse.port) {
175
- console.log("ERROR: No port entered");
176
- process.exit(1);
280
+ if (autoDetected && autoDetected.port) {
281
+ // Auto-detected project with port
282
+ projectPath = autoDetected.path;
283
+ projectName = autoDetected.name;
284
+ devPort = autoDetected.port;
285
+
286
+ console.log(`Detected project: ${projectName}`);
287
+ console.log(`Detected port: ${devPort}`);
288
+ console.log(`Using current directory: ${projectPath}`);
289
+ console.log("");
290
+
291
+ // Confirm with user
292
+ const confirm = await prompts({
293
+ type: "confirm",
294
+ name: "value",
295
+ message: "Use detected project?",
296
+ initial: true
297
+ });
298
+
299
+ if (!confirm.value) {
300
+ // User wants to select manually
301
+ console.log("");
302
+ console.log("Selecting project manually...");
303
+ console.log("");
304
+
305
+ const selectedPath = await selectFolder();
306
+ if (!selectedPath || selectedPath.length === 0) {
307
+ console.log("ERROR: No folder selected");
308
+ process.exit(1);
309
+ }
310
+
311
+ projectPath = selectedPath;
312
+ projectName = basename(selectedPath);
313
+
314
+ // Try to detect port for selected project
315
+ const selectedPackagePath = join(selectedPath, 'package.json');
316
+ const detectedPort = detectPortFromPackage(selectedPackagePath);
317
+
318
+ const portResponse = await prompts({
319
+ type: "number",
320
+ name: "port",
321
+ message: "Enter your dev server port:",
322
+ initial: detectedPort || 5173
323
+ });
324
+
325
+ if (!portResponse.port) {
326
+ console.log("ERROR: No port entered");
327
+ process.exit(1);
328
+ }
329
+
330
+ devPort = portResponse.port;
331
+ }
332
+ } else if (autoDetected && !autoDetected.port) {
333
+ // Project detected but no port
334
+ projectPath = autoDetected.path;
335
+ projectName = autoDetected.name;
336
+
337
+ console.log(`Detected project: ${projectName}`);
338
+ console.log(`Using current directory: ${projectPath}`);
339
+ console.log("Checking for running dev servers...");
340
+
341
+ const runningPorts = await detectRunningDevServer();
342
+
343
+ if (runningPorts.length > 0) {
344
+ console.log(`Found ${runningPorts.length} running dev server(s) on port(s): ${runningPorts.join(', ')}`);
345
+
346
+ if (runningPorts.length === 1) {
347
+ devPort = runningPorts[0];
348
+ console.log(`Using port: ${devPort}`);
349
+ } else {
350
+ // Multiple ports detected, let user choose
351
+ const portResponse = await prompts({
352
+ type: "select",
353
+ name: "port",
354
+ message: "Select port:",
355
+ choices: runningPorts.map(p => ({ title: `Port ${p}`, value: p }))
356
+ });
357
+
358
+ if (!portResponse.port) {
359
+ console.log("ERROR: No port selected");
360
+ process.exit(1);
361
+ }
362
+
363
+ devPort = portResponse.port;
364
+ }
365
+ } else {
366
+ // No running server, ask for port
367
+ const portResponse = await prompts({
368
+ type: "number",
369
+ name: "port",
370
+ message: "Enter your dev server port:",
371
+ initial: 5173
372
+ });
373
+
374
+ if (!portResponse.port) {
375
+ console.log("ERROR: No port entered");
376
+ process.exit(1);
377
+ }
378
+
379
+ devPort = portResponse.port;
380
+ }
381
+
382
+ console.log("");
383
+ } else {
384
+ // No auto-detection, use folder picker
385
+ console.log("No project detected in current directory");
386
+ console.log("Opening folder picker...");
387
+ console.log("");
388
+
389
+ projectPath = await selectFolder();
390
+
391
+ if (!projectPath || projectPath.length === 0) {
392
+ console.log("ERROR: No folder selected");
393
+ process.exit(1);
394
+ }
395
+
396
+ projectName = basename(projectPath);
397
+ console.log(`Selected: ${projectPath}`);
398
+ console.log("");
399
+
400
+ // Try to detect port for selected project
401
+ const selectedPackagePath = join(projectPath, 'package.json');
402
+ const detectedPort = detectPortFromPackage(selectedPackagePath);
403
+
404
+ // Check for running servers
405
+ const runningPorts = await detectRunningDevServer();
406
+
407
+ let initialPort = detectedPort || 5173;
408
+ if (runningPorts.length > 0 && !detectedPort) {
409
+ initialPort = runningPorts[0];
410
+ }
411
+
412
+ const portResponse = await prompts({
413
+ type: "number",
414
+ name: "port",
415
+ message: "Enter your dev server port:",
416
+ initial: initialPort
417
+ });
418
+
419
+ if (!portResponse.port) {
420
+ console.log("ERROR: No port entered");
421
+ process.exit(1);
422
+ }
423
+
424
+ devPort = portResponse.port;
177
425
  }
178
426
 
179
- const devPort = portResponse.port;
427
+ console.log("");
180
428
  const proxyPort = devPort + 1000; // Use port 1000 higher for proxy
181
429
 
182
430
  console.log("");