devtunnel-cli 3.0.5 → 3.0.7

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 CHANGED
@@ -4,8 +4,10 @@
4
4
 
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
6
  [![Platform](https://img.shields.io/badge/Platform-Windows%20%7C%20macOS%20%7C%20Linux-lightgrey)](https://github.com/maiz-an/DevTunnel)
7
+ [![npm version](https://img.shields.io/npm/v/devtunnel-cli)](https://www.npmjs.com/package/devtunnel-cli)
8
+ [![GitHub Pages](https://img.shields.io/badge/GitHub-Pages-blue)](https://maiz-an.github.io/DevTunnel/)
7
9
 
8
- 🌐 **Website:** [devtunnel.vercel.app](https://devtunnel.vercel.app)
10
+ 🌐 **Website:** [devtunnel.vercel.app](https://devtunnel.vercel.app) | 📦 **npm:** [devtunnel-cli](https://www.npmjs.com/package/devtunnel-cli) | 💻 **GitHub Pages:** [maiz-an.github.io/DevTunnel](https://maiz-an.github.io/DevTunnel/)
9
11
 
10
12
  ---
11
13
 
@@ -46,6 +48,7 @@ npm start
46
48
  - 🌍 **Cross-Platform** - Windows, macOS, Linux
47
49
  - 🚀 **Any Framework** - Works with all
48
50
  - 🔄 **Multi-Service** - Cloudflare, Ngrok, LocalTunnel fallback
51
+ - 📹 **Streaming Support** - Handles video/audio files (with limitations for large files)
49
52
 
50
53
  ---
51
54
 
@@ -87,4 +90,21 @@ MIT License - see [LICENSE](docs/LICENSE)
87
90
 
88
91
  ---
89
92
 
90
- **Version 3.0.0** | Made with ❤️ for developers worldwide
93
+ **Version 3.0.6** | Made with ❤️ for developers worldwide
94
+
95
+ ---
96
+
97
+ ## 🔍 Search Keywords
98
+
99
+ **DevTunnel** | **dev tunnel** | **localhost tunnel** | **cloudflare tunnel** | **ngrok alternative** | **port forwarding** | **local development** | **vite tunnel** | **react dev server** | **nextjs tunnel** | **npm devtunnel** | **devtunnel-cli** | **share localhost** | **development tools** | **zero config tunnel**
100
+
101
+ ---
102
+
103
+ ## 📦 Installation & Links
104
+
105
+ - **npm Package**: [devtunnel-cli](https://www.npmjs.com/package/devtunnel-cli)
106
+ - **GitHub Repository**: [maiz-an/DevTunnel](https://github.com/maiz-an/DevTunnel)
107
+ - **GitHub Pages**: [maiz-an.github.io/DevTunnel](https://maiz-an.github.io/DevTunnel/)
108
+ - **Official Website**: [devtunnel.vercel.app](https://devtunnel.vercel.app)
109
+ - **Documentation**: [docs/README.md](docs/README.md)
110
+ - **Issues**: [GitHub Issues](https://github.com/maiz-an/DevTunnel/issues)
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "devtunnel-cli",
3
- "version": "3.0.5",
3
+ "version": "3.0.7",
4
4
  "type": "module",
5
- "description": "Share local dev servers worldwide - Zero config tunnel for any framework",
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",
7
7
  "bin": {
8
8
  "devtunnel": "src/core/RUN.js"
@@ -14,25 +14,36 @@
14
14
  "tunnel": "node src/core/index.js"
15
15
  },
16
16
  "keywords": [
17
- "tunnel",
17
+ "dev tunnel",
18
18
  "devtunnel",
19
- "cloudflare",
19
+ "localhost tunnel",
20
+ "cloudflare tunnel",
21
+ "ngrok alternative",
20
22
  "ngrok",
21
23
  "localtunnel",
22
24
  "dev-server",
23
- "share",
25
+ "share localhost",
24
26
  "public-url",
25
- "vite",
26
- "react",
27
- "nextjs",
27
+ "port forwarding",
28
+ "local development",
29
+ "vite tunnel",
30
+ "react dev server",
31
+ "nextjs tunnel",
28
32
  "nestjs",
29
33
  "express",
30
34
  "backend",
31
35
  "frontend",
32
36
  "proxy",
37
+ "development tools",
38
+ "web development",
39
+ "tunnel service",
40
+ "local server sharing",
41
+ "dev server proxy",
42
+ "cloudflare tunnel cli",
43
+ "ngrok alternative free",
44
+ "zero config tunnel",
33
45
  "development",
34
- "localhost",
35
- "port-forwarding"
46
+ "localhost"
36
47
  ],
37
48
  "author": "maiz",
38
49
  "license": "MIT",
package/src/core/index.js CHANGED
@@ -28,12 +28,15 @@ if (!PORT || isNaN(PORT) || PORT < 1 || PORT > 65535) {
28
28
  let tunnelProcess;
29
29
  let currentTunnelType = null;
30
30
 
31
+ console.log("");
31
32
  console.log("DevTunnel Tunnel Service");
32
33
  console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
33
34
  console.log(`Project: ${PROJECT_NAME}`);
34
35
  console.log(`Port: ${PORT}`);
35
- console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
36
- console.log("Ensure dev server is running on port " + PORT + "\n");
36
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
37
+ console.log("");
38
+ console.log("Ensure dev server is running on port " + PORT);
39
+ console.log("");
37
40
 
38
41
  // Check if project is Vite and auto-fix config for Cloudflare
39
42
  async function fixViteConfigForCloudflare() {
@@ -176,7 +179,9 @@ const TUNNEL_SERVICES = [
176
179
 
177
180
  // Try each tunnel service
178
181
  async function tryTunnelServices() {
179
- console.log("Checking available tunnel services...\n");
182
+ console.log("");
183
+ console.log("Checking available tunnel services...");
184
+ console.log("");
180
185
 
181
186
  let hasCloudflare = false;
182
187
 
@@ -207,7 +212,8 @@ async function tryTunnelServices() {
207
212
 
208
213
  // Skip Vite auto-fix - using proxy server instead
209
214
 
210
- console.log(`Starting ${service.name} tunnel...\n`);
215
+ console.log(`Starting ${service.name} tunnel...`);
216
+ console.log("");
211
217
 
212
218
  currentTunnelType = service.name;
213
219
  tunnelProcess = spawn(service.command, service.args, {
@@ -227,7 +233,8 @@ async function tryTunnelServices() {
227
233
  console.log("Note: First-time visitors need to enter tunnel password (your public IP)");
228
234
  console.log("Get password at: https://loca.lt/mytunnelpassword\n");
229
235
  }
230
- console.log("Press Ctrl+C to stop the tunnel\n");
236
+ console.log("Press Ctrl+C to stop the tunnel");
237
+ console.log("");
231
238
  return true;
232
239
  }
233
240
  } else {
@@ -263,11 +270,13 @@ function setupTunnelHandlers(serviceName) {
263
270
  const urlMatch = trimmed.match(/(https?:\/\/[^\s]+trycloudflare\.com[^\s]*)/);
264
271
  if (urlMatch) {
265
272
  const url = urlMatch[1];
266
- console.log("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
273
+ console.log("");
274
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
267
275
  console.log("PUBLIC URL:");
268
276
  console.log(url);
269
277
  console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
270
- console.log("Share this URL with anyone!\n");
278
+ console.log("Share this URL with anyone!");
279
+ console.log("");
271
280
  }
272
281
  }
273
282
  // Show other important messages (but filter out most INF/WRN logs)
@@ -277,10 +286,12 @@ function setupTunnelHandlers(serviceName) {
277
286
  } else if (serviceName === "Ngrok") {
278
287
  if (trimmed.includes("https://") || trimmed.includes("http://")) {
279
288
  const url = trimmed;
280
- console.log("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
289
+ console.log("");
290
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
281
291
  console.log("PUBLIC URL:");
282
292
  console.log(url);
283
- console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
293
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
294
+ console.log("");
284
295
  }
285
296
  } else {
286
297
  // LocalTunnel or other services
@@ -288,10 +299,12 @@ function setupTunnelHandlers(serviceName) {
288
299
  const urlMatch = trimmed.match(/https?:\/\/[^\s]+/);
289
300
  if (urlMatch) {
290
301
  const url = urlMatch[0];
291
- console.log("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
302
+ console.log("");
303
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
292
304
  console.log("PUBLIC URL:");
293
305
  console.log(url);
294
- console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
306
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
307
+ console.log("");
295
308
  }
296
309
  }
297
310
  }
@@ -306,11 +319,13 @@ function setupTunnelHandlers(serviceName) {
306
319
  const urlMatch = output.match(/(https?:\/\/[^\s]+trycloudflare\.com[^\s]*)/);
307
320
  if (urlMatch) {
308
321
  const url = urlMatch[1];
309
- console.log("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
322
+ console.log("");
323
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
310
324
  console.log("PUBLIC URL:");
311
325
  console.log(url);
312
326
  console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
313
- console.log("Share this URL with anyone!\n");
327
+ console.log("Share this URL with anyone!");
328
+ console.log("");
314
329
  }
315
330
  }
316
331
 
@@ -11,12 +11,15 @@ if (!TARGET_PORT || !PROXY_PORT) {
11
11
  process.exit(1);
12
12
  }
13
13
 
14
- // Create proxy
14
+ // Create proxy with streaming support
15
15
  const proxy = httpProxy.createProxyServer({
16
16
  target: `http://localhost:${TARGET_PORT}`,
17
17
  changeOrigin: true,
18
18
  ws: true, // Enable WebSocket proxying (for HMR)
19
- xfwd: true
19
+ xfwd: true,
20
+ timeout: 300000, // 5 minutes timeout for large files
21
+ proxyTimeout: 300000, // 5 minutes proxy timeout
22
+ followRedirects: true
20
23
  });
21
24
 
22
25
  // Handle proxy errors
@@ -28,13 +31,25 @@ proxy.on("error", (err, req, res) => {
28
31
  }
29
32
  });
30
33
 
31
- // Create HTTP server
34
+ // Create HTTP server with timeout handling
32
35
  const server = http.createServer((req, res) => {
36
+ // Set longer timeout for large file transfers
37
+ req.setTimeout(300000); // 5 minutes
38
+ res.setTimeout(300000); // 5 minutes
39
+
33
40
  // Add CORS headers
34
41
  res.setHeader("Access-Control-Allow-Origin", "*");
35
42
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
36
43
  res.setHeader("Access-Control-Allow-Headers", "*");
37
44
 
45
+ // Handle timeout
46
+ req.on('timeout', () => {
47
+ if (!res.headersSent) {
48
+ res.writeHead(408, { "Content-Type": "text/plain" });
49
+ res.end("Request timeout");
50
+ }
51
+ });
52
+
38
53
  if (req.method === "OPTIONS") {
39
54
  res.writeHead(200);
40
55
  res.end();
@@ -52,6 +67,7 @@ server.on("upgrade", (req, socket, head) => {
52
67
 
53
68
  // Start server
54
69
  server.listen(PROXY_PORT, () => {
70
+ console.log("");
55
71
  console.log("DevTunnel Proxy Server");
56
72
  console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
57
73
  console.log(`Project: ${PROJECT_NAME}`);
@@ -59,7 +75,8 @@ server.listen(PROXY_PORT, () => {
59
75
  console.log(`Proxy Port: ${PROXY_PORT}`);
60
76
  console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
61
77
  console.log("Ready! Tunnel will connect to proxy");
62
- console.log("No config changes needed\n");
78
+ console.log("No config changes needed");
79
+ console.log("");
63
80
  });
64
81
 
65
82
  // Handle shutdown
package/src/core/start.js CHANGED
@@ -38,16 +38,17 @@ async function commandExists(command) {
38
38
  // ASCII Logo - Compatible with all OS and terminals
39
39
  function showLogo() {
40
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
- 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
+ console.log(" ");
51
+ console.log("");
51
52
  }
52
53
 
53
54
  // Main function
@@ -60,13 +61,15 @@ async function main() {
60
61
  // Show ASCII logo
61
62
  showLogo();
62
63
 
63
- console.log("DevTunnel v3.0.5");
64
+ console.log("DevTunnel v3.0.7");
64
65
  console.log("Share your local dev servers worldwide");
66
+ console.log("");
65
67
  console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
66
68
  console.log("Developer: maiz");
67
69
  console.log("Repository: https://github.com/maiz-an/DevTunnel");
68
70
  console.log("Website: https://devtunnel.vercel.app");
69
- console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
71
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
72
+ console.log("");
70
73
 
71
74
  // Step 1: Check Node.js
72
75
  console.log("[1/4] Checking Node.js...");
@@ -75,7 +78,8 @@ async function main() {
75
78
  console.log("Install from: https://nodejs.org/");
76
79
  process.exit(1);
77
80
  }
78
- console.log("SUCCESS: Node.js installed\n");
81
+ console.log("SUCCESS: Node.js installed");
82
+ console.log("");
79
83
 
80
84
  // Step 2: Check Cloudflare (bundled or system-installed)
81
85
  console.log("[2/4] Checking Cloudflare...");
@@ -93,7 +97,8 @@ async function main() {
93
97
  cloudflareAvailable = true;
94
98
  } else {
95
99
  console.log("First time setup - Downloading Cloudflare...");
96
- console.log("This only happens once (~40MB, 10-30 seconds)\n");
100
+ console.log("This only happens once (~40MB, 10-30 seconds)");
101
+ console.log("");
97
102
 
98
103
  try {
99
104
  const bundledPath = await setupCloudflared();
@@ -103,11 +108,13 @@ async function main() {
103
108
  cloudflareAvailable = true;
104
109
  } else {
105
110
  console.log("Could not download Cloudflare");
106
- console.log("Will use alternative tunnel services\n");
111
+ console.log("Will use alternative tunnel services");
112
+ console.log("");
107
113
  }
108
114
  } catch (err) {
109
115
  console.log(`Setup error: ${err.message}`);
110
- console.log("Will use alternative tunnel services\n");
116
+ console.log("Will use alternative tunnel services");
117
+ console.log("");
111
118
  }
112
119
  }
113
120
 
@@ -124,14 +131,17 @@ async function main() {
124
131
  console.log("[3/4] Checking dependencies...");
125
132
  const nodeModulesPath = join(PROJECT_ROOT, "node_modules");
126
133
  if (!existsSync(nodeModulesPath)) {
127
- console.log("Installing dependencies...\n");
134
+ console.log("Installing dependencies...");
135
+ console.log("");
128
136
  // Run npm install in the project root directory
129
137
  const result = await runCommand("npm", ["install"], PROJECT_ROOT);
130
138
  if (result.code !== 0) {
131
- console.log("\nERROR: npm install failed");
139
+ console.log("");
140
+ console.log("ERROR: npm install failed");
132
141
  process.exit(1);
133
142
  }
134
- console.log("\nSUCCESS: Dependencies installed");
143
+ console.log("");
144
+ console.log("SUCCESS: Dependencies installed");
135
145
  } else {
136
146
  console.log("SUCCESS: Dependencies already installed");
137
147
  }
@@ -139,7 +149,8 @@ async function main() {
139
149
 
140
150
  // Step 4: Select folder using native OS dialog
141
151
  console.log("[4/4] Select your project folder...");
142
- console.log("Opening folder picker...\n");
152
+ console.log("Opening folder picker...");
153
+ console.log("");
143
154
 
144
155
  const projectPath = await selectFolder();
145
156
 
@@ -149,7 +160,8 @@ async function main() {
149
160
  }
150
161
 
151
162
  const projectName = basename(projectPath);
152
- console.log(`Selected: ${projectPath}\n`);
163
+ console.log(`Selected: ${projectPath}`);
164
+ console.log("");
153
165
 
154
166
  // Get port
155
167
  const portResponse = await prompts({
@@ -167,15 +179,18 @@ async function main() {
167
179
  const devPort = portResponse.port;
168
180
  const proxyPort = devPort + 1000; // Use port 1000 higher for proxy
169
181
 
170
- console.log("\nConfiguration:");
182
+ console.log("");
183
+ console.log("Configuration:");
171
184
  console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
172
185
  console.log(`Project: ${projectName}`);
173
186
  console.log(`Dev Server: localhost:${devPort}`);
174
187
  console.log(`Proxy Port: ${proxyPort}`);
175
- console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
188
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
189
+ console.log("");
176
190
 
177
191
  // Start proxy server
178
- console.log("Starting services...\n");
192
+ console.log("Starting services...");
193
+ console.log("");
179
194
  const proxyPath = join(__dirname, "proxy-server.js");
180
195
  const proxyProcess = spawn("node", [proxyPath, devPort.toString(), proxyPort.toString(), projectName], {
181
196
  stdio: "inherit",