devtunnel-cli 3.0.37 → 3.0.39
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/README.md +0 -86
- package/package.json +6 -4
- package/src/core/start.js +66 -62
- package/src/utils/pages/index.html +7 -7
package/README.md
CHANGED
|
@@ -96,92 +96,6 @@ devtunnel-cli
|
|
|
96
96
|
|
|
97
97
|
---
|
|
98
98
|
|
|
99
|
-
## 🔒 Security & Access Model
|
|
100
|
-
|
|
101
|
-
DevTunnel-CLI intentionally **does NOT use authentication or access control**. This is a deliberate design choice to ensure fast, frictionless development workflows.
|
|
102
|
-
|
|
103
|
-
**How Access Works:**
|
|
104
|
-
- Anyone with the generated temporary URL can access the tunnel until it is stopped
|
|
105
|
-
- No login, password, or identity verification required
|
|
106
|
-
- Access is limited by **possession of the URL only**
|
|
107
|
-
|
|
108
|
-
**Why This Design?**
|
|
109
|
-
- **Speed**: Get public URLs instantly without authentication setup
|
|
110
|
-
- **Simplicity**: Zero configuration — just run and share
|
|
111
|
-
- **Friction-free collaboration**: Share with teammates without managing accounts or permissions
|
|
112
|
-
|
|
113
|
-
**Temporary URL Behavior:**
|
|
114
|
-
- URLs are **short-lived** and **unlisted**
|
|
115
|
-
- URLs are **destroyed** when the tunnel stops
|
|
116
|
-
- New random URLs are generated each time you run DevTunnel-CLI
|
|
117
|
-
- URLs are not indexed by search engines
|
|
118
|
-
|
|
119
|
-
**Best Practices:**
|
|
120
|
-
- Only share URLs with trusted collaborators
|
|
121
|
-
- Stop the tunnel when not in use
|
|
122
|
-
- Never expose sensitive data or production databases through DevTunnel-CLI
|
|
123
|
-
- Use for development and testing only
|
|
124
|
-
|
|
125
|
-
---
|
|
126
|
-
|
|
127
|
-
## ⚠️ Limitations
|
|
128
|
-
|
|
129
|
-
DevTunnel-CLI has intentional limitations that make it ideal for development but unsuitable for other use cases:
|
|
130
|
-
|
|
131
|
-
### No Authentication or Access Control
|
|
132
|
-
- **By design**: No identity-based access control
|
|
133
|
-
- **By design**: No user-level permission management
|
|
134
|
-
- **By design**: No password protection or login system
|
|
135
|
-
- Anyone with the URL can access your tunnel
|
|
136
|
-
|
|
137
|
-
### Not Suitable For
|
|
138
|
-
- ❌ Production environments
|
|
139
|
-
- ❌ Long-lived public services
|
|
140
|
-
- ❌ Hosting production traffic
|
|
141
|
-
- ❌ Sensitive data exposure
|
|
142
|
-
- ❌ Public-facing applications
|
|
143
|
-
|
|
144
|
-
### Perfect For
|
|
145
|
-
- ✅ Development and testing
|
|
146
|
-
- ✅ Team collaboration and code reviews
|
|
147
|
-
- ✅ Mobile device testing
|
|
148
|
-
- ✅ Client demos and work-in-progress sharing
|
|
149
|
-
- ✅ Webhook debugging with third-party services
|
|
150
|
-
- ✅ Temporary public access to localhost
|
|
151
|
-
|
|
152
|
-
### File Size & Streaming Limits
|
|
153
|
-
- ✅ Small files (<10MB): Works perfectly
|
|
154
|
-
- ✅ Medium files (10-50MB): Works well, may have slight delays
|
|
155
|
-
- ⚠️ Large files (>50MB): May timeout depending on connection speed
|
|
156
|
-
- ⚠️ Very large files (>100MB): Not recommended for Cloudflare free tier
|
|
157
|
-
|
|
158
|
-
---
|
|
159
|
-
|
|
160
|
-
## 🆚 Comparison: DevTunnel-CLI vs. Enterprise Tunnels
|
|
161
|
-
|
|
162
|
-
DevTunnel-CLI is optimized for **speed and simplicity** rather than governance and authentication.
|
|
163
|
-
|
|
164
|
-
| Feature | DevTunnel-CLI | Enterprise Tunnels (e.g., Microsoft Dev Tunnels) |
|
|
165
|
-
|---------|---------------|--------------------------------------------------|
|
|
166
|
-
| **Setup Time** | Instant (0 config) | Requires account, authentication setup |
|
|
167
|
-
| **Authentication** | None (by design) | User-based auth, SSO, identity management |
|
|
168
|
-
| **Access Control** | URL possession only | Fine-grained permissions, user/group policies |
|
|
169
|
-
| **Use Case** | Development, testing, demos | Enterprise dev, governed access, compliance |
|
|
170
|
-
| **Speed** | Instant sharing | May require approval workflows |
|
|
171
|
-
| **Ideal For** | Solo devs, small teams, fast iteration | Large orgs, regulated industries, prod-like envs |
|
|
172
|
-
|
|
173
|
-
**Choose DevTunnel-CLI when:**
|
|
174
|
-
- You need instant, frictionless sharing
|
|
175
|
-
- You're working on non-sensitive development projects
|
|
176
|
-
- Speed and simplicity are priorities
|
|
177
|
-
|
|
178
|
-
**Choose enterprise tunnels when:**
|
|
179
|
-
- You need identity-based access control
|
|
180
|
-
- You're in a regulated or compliance-heavy environment
|
|
181
|
-
- You need audit logs and governance
|
|
182
|
-
|
|
183
|
-
---
|
|
184
|
-
|
|
185
99
|
## 📖 Documentation
|
|
186
100
|
|
|
187
101
|
- [Features](docs/FEATURES.md)
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devtunnel-cli",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.39",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "DevTunnel-CLI
|
|
5
|
+
"description": "DevTunnel-CLI — fast, zero-config tool to share local servers for development, testing, demos, and webhook debugging. npm install -g devtunnel-cli.",
|
|
6
6
|
"main": "src/core/start.js",
|
|
7
7
|
"files": [
|
|
8
8
|
"README.md",
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"start": "node src/core/RUN.js",
|
|
16
16
|
"dev": "node src/core/RUN.js",
|
|
17
17
|
"run": "node src/core/RUN.js",
|
|
18
|
-
"tunnel": "node src/core/index.js"
|
|
18
|
+
"tunnel": "node src/core/index.js",
|
|
19
|
+
"sync-version": "node sync-version.js"
|
|
19
20
|
},
|
|
20
21
|
"keywords": [
|
|
21
22
|
"DevTunnel-CLI",
|
|
@@ -62,6 +63,7 @@
|
|
|
62
63
|
],
|
|
63
64
|
"author": "maiz",
|
|
64
65
|
"license": "MIT",
|
|
66
|
+
"preferGlobal": true,
|
|
65
67
|
"repository": {
|
|
66
68
|
"type": "git",
|
|
67
69
|
"url": "git+https://github.com/maiz-an/DevTunnel-CLI.git"
|
|
@@ -79,4 +81,4 @@
|
|
|
79
81
|
"prompts": "^2.4.2"
|
|
80
82
|
},
|
|
81
83
|
"devDependencies": {}
|
|
82
|
-
}
|
|
84
|
+
}
|
package/src/core/start.js
CHANGED
|
@@ -17,25 +17,25 @@ function getPackageVersion() {
|
|
|
17
17
|
const pkgPath = join(PROJECT_ROOT, "package.json");
|
|
18
18
|
if (existsSync(pkgPath)) {
|
|
19
19
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
20
|
-
return pkg.version || "3.0.
|
|
20
|
+
return pkg.version || "3.0.38";
|
|
21
21
|
}
|
|
22
|
-
} catch (err) {}
|
|
23
|
-
return "3.0.
|
|
22
|
+
} catch (err) { }
|
|
23
|
+
return "3.0.38";
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
// Helper to run command
|
|
27
27
|
function runCommand(command, args = [], cwd = process.cwd()) {
|
|
28
28
|
return new Promise((resolve) => {
|
|
29
|
-
const proc = spawn(command, args, {
|
|
30
|
-
shell: true,
|
|
29
|
+
const proc = spawn(command, args, {
|
|
30
|
+
shell: true,
|
|
31
31
|
stdio: "pipe",
|
|
32
32
|
cwd: cwd
|
|
33
33
|
});
|
|
34
34
|
let output = "";
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
proc.stdout?.on("data", (data) => output += data.toString());
|
|
37
37
|
proc.stderr?.on("data", (data) => output += data.toString());
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
proc.on("close", (code) => resolve({ code, output }));
|
|
40
40
|
proc.on("error", () => resolve({ code: 1, output: "" }));
|
|
41
41
|
});
|
|
@@ -51,7 +51,7 @@ async function commandExists(command) {
|
|
|
51
51
|
function checkPortInUse(port) {
|
|
52
52
|
return new Promise((resolve) => {
|
|
53
53
|
const server = http.createServer();
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
server.once('error', (err) => {
|
|
56
56
|
// Port is in use
|
|
57
57
|
if (err.code === 'EADDRINUSE') {
|
|
@@ -60,7 +60,7 @@ function checkPortInUse(port) {
|
|
|
60
60
|
resolve(false);
|
|
61
61
|
}
|
|
62
62
|
});
|
|
63
|
-
|
|
63
|
+
|
|
64
64
|
server.listen(port, () => {
|
|
65
65
|
// Port is available (not in use)
|
|
66
66
|
server.once('close', () => resolve(false));
|
|
@@ -79,7 +79,7 @@ async function waitForServerReady(port, timeoutMs = 10000) {
|
|
|
79
79
|
req.on("error", () => resolve(null));
|
|
80
80
|
});
|
|
81
81
|
if (code !== null && code >= 200 && code < 500) return true;
|
|
82
|
-
} catch (err) {}
|
|
82
|
+
} catch (err) { }
|
|
83
83
|
await new Promise((r) => setTimeout(r, 300));
|
|
84
84
|
}
|
|
85
85
|
return false;
|
|
@@ -91,24 +91,24 @@ function detectPortFromPackage(packagePath) {
|
|
|
91
91
|
if (!existsSync(packagePath)) return null;
|
|
92
92
|
const packageJson = JSON.parse(readFileSync(packagePath, 'utf8'));
|
|
93
93
|
const scripts = packageJson.scripts || {};
|
|
94
|
-
|
|
94
|
+
|
|
95
95
|
// Check for common dev commands
|
|
96
96
|
const devScript = scripts.dev || scripts.start || scripts.serve;
|
|
97
97
|
if (!devScript) return null;
|
|
98
|
-
|
|
98
|
+
|
|
99
99
|
// Try to extract port from script
|
|
100
100
|
const portMatch = devScript.match(/--port\s+(\d+)|:(\d+)|port[=:](\d+)/i);
|
|
101
101
|
if (portMatch) {
|
|
102
102
|
return parseInt(portMatch[1] || portMatch[2] || portMatch[3]);
|
|
103
103
|
}
|
|
104
|
-
|
|
104
|
+
|
|
105
105
|
// Default ports based on framework
|
|
106
106
|
if (devScript.includes('vite')) return 5173;
|
|
107
107
|
if (devScript.includes('next')) return 3000;
|
|
108
108
|
if (devScript.includes('react-scripts')) return 3000;
|
|
109
109
|
if (devScript.includes('webpack')) return 8080;
|
|
110
110
|
if (devScript.includes('express')) return 3000;
|
|
111
|
-
|
|
111
|
+
|
|
112
112
|
return null;
|
|
113
113
|
} catch (err) {
|
|
114
114
|
return null;
|
|
@@ -148,7 +148,7 @@ function detectPhpProject(currentDir) {
|
|
|
148
148
|
async function detectRunningDevServer() {
|
|
149
149
|
const commonPorts = [3000, 5173, 5500, 8080, 8000, 80, 5000, 4000, 3001, 5174]; // 80 for XAMPP
|
|
150
150
|
const detected = [];
|
|
151
|
-
|
|
151
|
+
|
|
152
152
|
for (const port of commonPorts) {
|
|
153
153
|
const inUse = await checkPortInUse(port);
|
|
154
154
|
if (inUse) {
|
|
@@ -174,7 +174,7 @@ async function detectRunningDevServer() {
|
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
|
-
|
|
177
|
+
|
|
178
178
|
return detected;
|
|
179
179
|
}
|
|
180
180
|
|
|
@@ -244,6 +244,8 @@ async function autoDetectProject() {
|
|
|
244
244
|
// ASCII Logo - Compatible with all OS and terminals
|
|
245
245
|
function showLogo() {
|
|
246
246
|
console.log("");
|
|
247
|
+
console.log(" ");
|
|
248
|
+
console.log(" ");
|
|
247
249
|
console.log("8888888b. 88888888888 888 .d8888b. 888 8888888 ");
|
|
248
250
|
console.log('888 "Y88b 888 888 d88P Y88b 888 888 ');
|
|
249
251
|
console.log("888 888 888 888 888 888 888 888 ");
|
|
@@ -262,17 +264,19 @@ async function main() {
|
|
|
262
264
|
// ANSI escape codes for clear screen + cursor to top
|
|
263
265
|
process.stdout.write('\x1B[2J\x1B[0f');
|
|
264
266
|
console.clear(); // Fallback for terminals that don't support ANSI
|
|
265
|
-
|
|
267
|
+
|
|
266
268
|
// Show ASCII logo
|
|
267
269
|
showLogo();
|
|
268
|
-
|
|
270
|
+
|
|
269
271
|
console.log(`DevTunnel v${getPackageVersion()}`);
|
|
270
272
|
console.log("Share your local dev servers worldwide");
|
|
271
273
|
console.log("");
|
|
272
274
|
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
|
275
|
+
console.log("");
|
|
273
276
|
console.log("Repository: https://github.com/maiz-an/DevTunnel-CLI");
|
|
274
277
|
console.log("npm Package: https://www.npmjs.com/package/devtunnel-cli");
|
|
275
|
-
console.log("Website: https://
|
|
278
|
+
console.log("Website: https://devtunnel-cli.vercel.app");
|
|
279
|
+
console.log("");
|
|
276
280
|
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
|
277
281
|
console.log("");
|
|
278
282
|
|
|
@@ -288,12 +292,12 @@ async function main() {
|
|
|
288
292
|
|
|
289
293
|
// Step 2: Check Cloudflare (bundled or system-installed)
|
|
290
294
|
console.log("[2/4] Checking Cloudflare...");
|
|
291
|
-
|
|
295
|
+
|
|
292
296
|
// Import bundled cloudflared helpers
|
|
293
297
|
const { setupCloudflared, hasBundledCloudflared } = await import("./setup-cloudflared.js");
|
|
294
|
-
|
|
298
|
+
|
|
295
299
|
let cloudflareAvailable = false;
|
|
296
|
-
|
|
300
|
+
|
|
297
301
|
if (hasBundledCloudflared()) {
|
|
298
302
|
console.log("SUCCESS: Using bundled Cloudflare (no install needed)");
|
|
299
303
|
cloudflareAvailable = true;
|
|
@@ -304,10 +308,10 @@ async function main() {
|
|
|
304
308
|
console.log("First time setup - Downloading Cloudflare...");
|
|
305
309
|
console.log("This only happens once (~40MB, 10-30 seconds)");
|
|
306
310
|
console.log("");
|
|
307
|
-
|
|
311
|
+
|
|
308
312
|
try {
|
|
309
313
|
const bundledPath = await setupCloudflared();
|
|
310
|
-
|
|
314
|
+
|
|
311
315
|
if (bundledPath) {
|
|
312
316
|
console.log("SUCCESS: Cloudflare ready to use");
|
|
313
317
|
cloudflareAvailable = true;
|
|
@@ -322,7 +326,7 @@ async function main() {
|
|
|
322
326
|
console.log("");
|
|
323
327
|
}
|
|
324
328
|
}
|
|
325
|
-
|
|
329
|
+
|
|
326
330
|
// Show what's available
|
|
327
331
|
if (!cloudflareAvailable) {
|
|
328
332
|
console.log("DevTunnel has multi-service fallback:");
|
|
@@ -354,20 +358,20 @@ async function main() {
|
|
|
354
358
|
|
|
355
359
|
// Step 4: Auto-detect or select project
|
|
356
360
|
console.log("[4/4] Detecting project...");
|
|
357
|
-
|
|
361
|
+
|
|
358
362
|
let projectPath, projectName, devPort;
|
|
359
|
-
|
|
363
|
+
|
|
360
364
|
// Try to auto-detect project in current directory
|
|
361
365
|
const autoDetected = await autoDetectProject();
|
|
362
|
-
|
|
366
|
+
|
|
363
367
|
if (autoDetected && autoDetected.port) {
|
|
364
368
|
// Auto-detected project with port
|
|
365
369
|
projectPath = autoDetected.path;
|
|
366
370
|
projectName = autoDetected.name;
|
|
367
|
-
|
|
371
|
+
|
|
368
372
|
// Double-check: verify the port is actually in use
|
|
369
373
|
const portInUse = await checkPortInUse(autoDetected.port);
|
|
370
|
-
|
|
374
|
+
|
|
371
375
|
if (!portInUse) {
|
|
372
376
|
// Detected port is not actually running, check for other running servers
|
|
373
377
|
const portSource =
|
|
@@ -380,7 +384,7 @@ async function main() {
|
|
|
380
384
|
: "package.json";
|
|
381
385
|
console.log(`Detected port ${autoDetected.port} (${portSource}), but no server running on that port`);
|
|
382
386
|
console.log("Checking for running dev servers...");
|
|
383
|
-
|
|
387
|
+
|
|
384
388
|
const runningPorts = await detectRunningDevServer();
|
|
385
389
|
if (runningPorts.length > 0) {
|
|
386
390
|
if (runningPorts.length === 1) {
|
|
@@ -394,12 +398,12 @@ async function main() {
|
|
|
394
398
|
message: "Select port:",
|
|
395
399
|
choices: runningPorts.map(p => ({ title: `Port ${p}`, value: p }))
|
|
396
400
|
});
|
|
397
|
-
|
|
401
|
+
|
|
398
402
|
if (!portResponse.port) {
|
|
399
403
|
console.log("ERROR: No port selected");
|
|
400
404
|
process.exit(1);
|
|
401
405
|
}
|
|
402
|
-
|
|
406
|
+
|
|
403
407
|
devPort = portResponse.port;
|
|
404
408
|
}
|
|
405
409
|
} else {
|
|
@@ -411,12 +415,12 @@ async function main() {
|
|
|
411
415
|
// Port is in use, use it
|
|
412
416
|
devPort = autoDetected.port;
|
|
413
417
|
}
|
|
414
|
-
|
|
418
|
+
|
|
415
419
|
console.log(`Detected project: ${projectName}`);
|
|
416
420
|
console.log(`Using port: ${devPort}`);
|
|
417
421
|
console.log(`Using current directory: ${projectPath}`);
|
|
418
422
|
console.log("");
|
|
419
|
-
|
|
423
|
+
|
|
420
424
|
// Confirm with user
|
|
421
425
|
const confirm = await prompts({
|
|
422
426
|
type: "confirm",
|
|
@@ -424,22 +428,22 @@ async function main() {
|
|
|
424
428
|
message: "Use detected project?",
|
|
425
429
|
initial: true
|
|
426
430
|
});
|
|
427
|
-
|
|
431
|
+
|
|
428
432
|
if (!confirm.value) {
|
|
429
433
|
// User wants to select manually
|
|
430
434
|
console.log("");
|
|
431
435
|
console.log("Selecting project manually...");
|
|
432
436
|
console.log("");
|
|
433
|
-
|
|
437
|
+
|
|
434
438
|
const selectedPath = await selectFolder();
|
|
435
439
|
if (!selectedPath || selectedPath.length === 0) {
|
|
436
440
|
console.log("ERROR: No folder selected");
|
|
437
441
|
process.exit(1);
|
|
438
442
|
}
|
|
439
|
-
|
|
443
|
+
|
|
440
444
|
projectPath = selectedPath;
|
|
441
445
|
projectName = basename(selectedPath);
|
|
442
|
-
|
|
446
|
+
|
|
443
447
|
// Try to detect port for selected project (Laravel → 8000, HTML → 5500, Node from package.json)
|
|
444
448
|
const selectedPackagePath = join(selectedPath, "package.json");
|
|
445
449
|
const laravelSelected = detectLaravelProject(selectedPath);
|
|
@@ -449,19 +453,19 @@ async function main() {
|
|
|
449
453
|
: htmlSelected
|
|
450
454
|
? htmlSelected.defaultPort
|
|
451
455
|
: detectPortFromPackage(selectedPackagePath);
|
|
452
|
-
|
|
456
|
+
|
|
453
457
|
const portResponse = await prompts({
|
|
454
458
|
type: "number",
|
|
455
459
|
name: "port",
|
|
456
460
|
message: "Enter your dev server port:",
|
|
457
461
|
initial: detectedPort || 5173
|
|
458
462
|
});
|
|
459
|
-
|
|
463
|
+
|
|
460
464
|
if (!portResponse.port) {
|
|
461
465
|
console.log("ERROR: No port entered");
|
|
462
466
|
process.exit(1);
|
|
463
467
|
}
|
|
464
|
-
|
|
468
|
+
|
|
465
469
|
devPort = portResponse.port;
|
|
466
470
|
} else {
|
|
467
471
|
// User confirmed – let them keep default port or type another (e.g. HTML default 5500, can change)
|
|
@@ -479,16 +483,16 @@ async function main() {
|
|
|
479
483
|
// Project detected but no port
|
|
480
484
|
projectPath = autoDetected.path;
|
|
481
485
|
projectName = autoDetected.name;
|
|
482
|
-
|
|
486
|
+
|
|
483
487
|
console.log(`Detected project: ${projectName}`);
|
|
484
488
|
console.log(`Using current directory: ${projectPath}`);
|
|
485
489
|
console.log("Checking for running dev servers...");
|
|
486
|
-
|
|
490
|
+
|
|
487
491
|
const runningPorts = await detectRunningDevServer();
|
|
488
|
-
|
|
492
|
+
|
|
489
493
|
if (runningPorts.length > 0) {
|
|
490
494
|
console.log(`Found ${runningPorts.length} running dev server(s) on port(s): ${runningPorts.join(', ')}`);
|
|
491
|
-
|
|
495
|
+
|
|
492
496
|
if (runningPorts.length === 1) {
|
|
493
497
|
devPort = runningPorts[0];
|
|
494
498
|
console.log(`Using port: ${devPort}`);
|
|
@@ -500,12 +504,12 @@ async function main() {
|
|
|
500
504
|
message: "Select port:",
|
|
501
505
|
choices: runningPorts.map(p => ({ title: `Port ${p}`, value: p }))
|
|
502
506
|
});
|
|
503
|
-
|
|
507
|
+
|
|
504
508
|
if (!portResponse.port) {
|
|
505
509
|
console.log("ERROR: No port selected");
|
|
506
510
|
process.exit(1);
|
|
507
511
|
}
|
|
508
|
-
|
|
512
|
+
|
|
509
513
|
devPort = portResponse.port;
|
|
510
514
|
}
|
|
511
515
|
} else {
|
|
@@ -516,33 +520,33 @@ async function main() {
|
|
|
516
520
|
message: "Enter your dev server port:",
|
|
517
521
|
initial: 5173
|
|
518
522
|
});
|
|
519
|
-
|
|
523
|
+
|
|
520
524
|
if (!portResponse.port) {
|
|
521
525
|
console.log("ERROR: No port entered");
|
|
522
526
|
process.exit(1);
|
|
523
527
|
}
|
|
524
|
-
|
|
528
|
+
|
|
525
529
|
devPort = portResponse.port;
|
|
526
530
|
}
|
|
527
|
-
|
|
531
|
+
|
|
528
532
|
console.log("");
|
|
529
533
|
} else {
|
|
530
534
|
// No auto-detection, use folder picker
|
|
531
535
|
console.log("No project detected in current directory");
|
|
532
536
|
console.log("Opening folder picker...");
|
|
533
537
|
console.log("");
|
|
534
|
-
|
|
538
|
+
|
|
535
539
|
projectPath = await selectFolder();
|
|
536
|
-
|
|
540
|
+
|
|
537
541
|
if (!projectPath || projectPath.length === 0) {
|
|
538
542
|
console.log("ERROR: No folder selected");
|
|
539
543
|
process.exit(1);
|
|
540
544
|
}
|
|
541
|
-
|
|
545
|
+
|
|
542
546
|
projectName = basename(projectPath);
|
|
543
547
|
console.log(`Selected: ${projectPath}`);
|
|
544
548
|
console.log("");
|
|
545
|
-
|
|
549
|
+
|
|
546
550
|
// Try to detect port for selected project (Laravel → 8000, HTML → 5500, PHP → 80, Node from package.json)
|
|
547
551
|
const selectedPackagePath = join(projectPath, "package.json");
|
|
548
552
|
const laravelSelected = detectLaravelProject(projectPath);
|
|
@@ -555,30 +559,30 @@ async function main() {
|
|
|
555
559
|
: phpSelected
|
|
556
560
|
? phpSelected.defaultPort // 80
|
|
557
561
|
: detectPortFromPackage(selectedPackagePath);
|
|
558
|
-
|
|
562
|
+
|
|
559
563
|
// Check for running servers
|
|
560
564
|
const runningPorts = await detectRunningDevServer();
|
|
561
|
-
|
|
565
|
+
|
|
562
566
|
let initialPort = detectedPort || 5173;
|
|
563
567
|
if (runningPorts.length > 0 && !detectedPort) {
|
|
564
568
|
initialPort = runningPorts[0];
|
|
565
569
|
}
|
|
566
|
-
|
|
570
|
+
|
|
567
571
|
const portResponse = await prompts({
|
|
568
572
|
type: "number",
|
|
569
573
|
name: "port",
|
|
570
574
|
message: "Enter your dev server port:",
|
|
571
575
|
initial: initialPort
|
|
572
576
|
});
|
|
573
|
-
|
|
577
|
+
|
|
574
578
|
if (!portResponse.port) {
|
|
575
579
|
console.log("ERROR: No port entered");
|
|
576
580
|
process.exit(1);
|
|
577
581
|
}
|
|
578
|
-
|
|
582
|
+
|
|
579
583
|
devPort = portResponse.port;
|
|
580
584
|
}
|
|
581
|
-
|
|
585
|
+
|
|
582
586
|
console.log("");
|
|
583
587
|
const proxyPort = devPort + 1000; // Use port 1000 higher for proxy
|
|
584
588
|
|
|
@@ -608,7 +612,7 @@ async function main() {
|
|
|
608
612
|
stdio: "pipe",
|
|
609
613
|
shell: false
|
|
610
614
|
});
|
|
611
|
-
staticServerProcess.on("error", () => {});
|
|
615
|
+
staticServerProcess.on("error", () => { });
|
|
612
616
|
const ready = await waitForServerReady(devPort, 10000);
|
|
613
617
|
if (!ready) {
|
|
614
618
|
if (staticServerProcess) staticServerProcess.kill();
|
|
@@ -10,17 +10,17 @@
|
|
|
10
10
|
content="dev tunnel, localhost tunnel, cloudflare, ngrok, port forwarding, local development, vite, react, nextjs, laravel, php, html, npm install, devtunnel">
|
|
11
11
|
<meta name="author" content="maiz">
|
|
12
12
|
<meta name="robots" content="index, follow">
|
|
13
|
-
<link rel="canonical" href="https://
|
|
13
|
+
<link rel="canonical" href="https://devtunnel-cli.vercel.app/">
|
|
14
14
|
<meta property="og:title" content="DevTunnel - Share Local Servers Worldwide">
|
|
15
15
|
<meta property="og:description"
|
|
16
16
|
content="Zero configuration tunnel for any framework. Install via npm: npm install -g devtunnel-cli">
|
|
17
|
-
<meta property="og:url" content="https://
|
|
17
|
+
<meta property="og:url" content="https://devtunnel-cli.vercel.app/">
|
|
18
18
|
<meta property="og:type" content="website">
|
|
19
|
-
<meta property="og:image" content="https://
|
|
19
|
+
<meta property="og:image" content="https://devtunnel-cli.vercel.app/og-image.png">
|
|
20
20
|
<meta name="twitter:card" content="summary_large_image">
|
|
21
21
|
<meta name="twitter:title" content="DevTunnel - Share Local Servers Worldwide">
|
|
22
22
|
<meta name="twitter:description" content="Zero configuration tunnel for any framework">
|
|
23
|
-
<meta name="twitter:image" content="https://
|
|
23
|
+
<meta name="twitter:image" content="https://devtunnel-cli.vercel.app/og-image.png">
|
|
24
24
|
<title>DevTunnel - Share Local Servers Worldwide</title>
|
|
25
25
|
<style>
|
|
26
26
|
body {
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
|
|
78
78
|
<div class="links">
|
|
79
79
|
<a href="https://www.npmjs.com/package/devtunnel-cli" class="badge">📦 npm Package</a>
|
|
80
|
-
<a href="https://
|
|
80
|
+
<a href="https://devtunnel-cli.vercel.app" class="badge">🌐 Website</a>
|
|
81
81
|
<a href="https://github.com/maiz-an/DevTunnel-CLI" class="badge">💻 GitHub</a>
|
|
82
82
|
</div>
|
|
83
83
|
|
|
@@ -106,13 +106,13 @@ devtunnel-cli</code></pre>
|
|
|
106
106
|
<h2>🔗 Links</h2>
|
|
107
107
|
<ul>
|
|
108
108
|
<li><a href="https://www.npmjs.com/package/devtunnel-cli">npm Package (devtunnel-cli)</a></li>
|
|
109
|
-
<li><a href="https://
|
|
109
|
+
<li><a href="https://devtunnel-cli.vercel.app">Official Website</a></li>
|
|
110
110
|
<li><a href="https://github.com/maiz-an/DevTunnel-CLI">GitHub Repository</a></li>
|
|
111
111
|
<li><a href="https://github.com/maiz-an/DevTunnel-CLI/issues">Report Issues</a></li>
|
|
112
112
|
</ul>
|
|
113
113
|
|
|
114
114
|
<hr>
|
|
115
|
-
<p><small>Version 3.0.
|
|
115
|
+
<p><small>Version 3.0.38 | Made with ❤︎ for developers worldwide</small></p>
|
|
116
116
|
</body>
|
|
117
117
|
|
|
118
118
|
</html>
|