devtunnel-cli 3.0.3 → 3.0.4

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 +220 -220
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devtunnel-cli",
3
- "version": "3.0.3",
3
+ "version": "3.0.4",
4
4
  "type": "module",
5
5
  "description": "Share local dev servers worldwide - Zero config tunnel for any framework",
6
6
  "main": "src/core/start.js",
package/src/core/start.js CHANGED
@@ -1,220 +1,220 @@
1
- import { spawn } from "child_process";
2
- import { existsSync } from "fs";
3
- import { join, dirname, basename } from "path";
4
- import { fileURLToPath } from "url";
5
- import prompts from "prompts";
6
- import { selectFolder } from "../utils/folder-picker.js";
7
-
8
- const __filename = fileURLToPath(import.meta.url);
9
- const __dirname = dirname(__filename);
10
-
11
- // Get project root directory dynamically (two levels up from src/core/)
12
- const PROJECT_ROOT = dirname(dirname(__dirname));
13
-
14
- // Helper to run command
15
- function runCommand(command, args = [], cwd = process.cwd()) {
16
- return new Promise((resolve) => {
17
- const proc = spawn(command, args, {
18
- shell: true,
19
- stdio: "pipe",
20
- cwd: cwd
21
- });
22
- let output = "";
23
-
24
- proc.stdout?.on("data", (data) => output += data.toString());
25
- proc.stderr?.on("data", (data) => output += data.toString());
26
-
27
- proc.on("close", (code) => resolve({ code, output }));
28
- proc.on("error", () => resolve({ code: 1, output: "" }));
29
- });
30
- }
31
-
32
- // Check if command exists
33
- async function commandExists(command) {
34
- const result = await runCommand("where", [command]);
35
- return result.code === 0;
36
- }
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
-
52
- // Main function
53
- async function main() {
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");
69
-
70
- // Step 1: Check Node.js
71
- console.log("[1/4] Checking Node.js...");
72
- if (!await commandExists("node")) {
73
- console.log("ERROR: Node.js not found!");
74
- console.log("Install from: https://nodejs.org/");
75
- process.exit(1);
76
- }
77
- console.log("SUCCESS: Node.js installed\n");
78
-
79
- // Step 2: Check Cloudflare (bundled or system-installed)
80
- console.log("[2/4] Checking Cloudflare...");
81
-
82
- // Import bundled cloudflared helpers
83
- const { setupCloudflared, hasBundledCloudflared } = await import("./setup-cloudflared.js");
84
-
85
- let cloudflareAvailable = false;
86
-
87
- if (hasBundledCloudflared()) {
88
- console.log("SUCCESS: Using bundled Cloudflare (no install needed)");
89
- cloudflareAvailable = true;
90
- } else if (await commandExists("cloudflared")) {
91
- console.log("SUCCESS: Cloudflare installed on system");
92
- cloudflareAvailable = true;
93
- } else {
94
- console.log("First time setup - Downloading Cloudflare...");
95
- console.log("This only happens once (~40MB, 10-30 seconds)\n");
96
-
97
- try {
98
- const bundledPath = await setupCloudflared();
99
-
100
- if (bundledPath) {
101
- console.log("SUCCESS: Cloudflare ready to use");
102
- cloudflareAvailable = true;
103
- } else {
104
- console.log("Could not download Cloudflare");
105
- console.log("Will use alternative tunnel services\n");
106
- }
107
- } catch (err) {
108
- console.log(`Setup error: ${err.message}`);
109
- console.log("Will use alternative tunnel services\n");
110
- }
111
- }
112
-
113
- // Show what's available
114
- if (!cloudflareAvailable) {
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)");
119
- console.log("");
120
- }
121
-
122
- // Step 3: Check dependencies
123
- console.log("[3/4] Checking dependencies...");
124
- const nodeModulesPath = join(PROJECT_ROOT, "node_modules");
125
- if (!existsSync(nodeModulesPath)) {
126
- console.log("Installing dependencies...\n");
127
- // Run npm install in the project root directory
128
- const result = await runCommand("npm", ["install"], PROJECT_ROOT);
129
- if (result.code !== 0) {
130
- console.log("\nERROR: npm install failed");
131
- process.exit(1);
132
- }
133
- console.log("\nSUCCESS: Dependencies installed");
134
- } else {
135
- console.log("SUCCESS: Dependencies already installed");
136
- }
137
- console.log("");
138
-
139
- // Step 4: Select folder using native OS dialog
140
- console.log("[4/4] Select your project folder...");
141
- console.log("Opening folder picker...\n");
142
-
143
- const projectPath = await selectFolder();
144
-
145
- if (!projectPath || projectPath.length === 0) {
146
- console.log("ERROR: No folder selected");
147
- process.exit(1);
148
- }
149
-
150
- const projectName = basename(projectPath);
151
- console.log(`Selected: ${projectPath}\n`);
152
-
153
- // Get port
154
- const portResponse = await prompts({
155
- type: "number",
156
- name: "port",
157
- message: "Enter your dev server port:",
158
- initial: 5173
159
- });
160
-
161
- if (!portResponse.port) {
162
- console.log("ERROR: No port entered");
163
- process.exit(1);
164
- }
165
-
166
- const devPort = portResponse.port;
167
- const proxyPort = devPort + 1000; // Use port 1000 higher for proxy
168
-
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");
175
-
176
- // Start proxy server
177
- console.log("Starting services...\n");
178
- const proxyPath = join(__dirname, "proxy-server.js");
179
- const proxyProcess = spawn("node", [proxyPath, devPort.toString(), proxyPort.toString(), projectName], {
180
- stdio: "inherit",
181
- shell: false
182
- });
183
-
184
- // Wait for proxy to start
185
- await new Promise(resolve => setTimeout(resolve, 2000));
186
-
187
- // Run main tunnel app (connects to proxy port)
188
- // Use shell: false to properly handle paths with spaces
189
- const indexPath = join(__dirname, "index.js");
190
- const tunnelProcess = spawn("node", [indexPath, proxyPort.toString(), projectName, projectPath], {
191
- stdio: "inherit",
192
- shell: false
193
- });
194
-
195
- // Handle cleanup
196
- const cleanup = () => {
197
- console.log("\nShutting down...");
198
- proxyProcess.kill();
199
- tunnelProcess.kill();
200
- process.exit(0);
201
- };
202
-
203
- tunnelProcess.on("close", (code) => {
204
- cleanup();
205
- });
206
-
207
- proxyProcess.on("close", () => {
208
- cleanup();
209
- });
210
-
211
- // Handle Ctrl+C
212
- process.on("SIGINT", cleanup);
213
- process.on("SIGTERM", cleanup);
214
- }
215
-
216
- // Run
217
- main().catch((error) => {
218
- console.error("\nERROR:", error.message);
219
- process.exit(1);
220
- });
1
+ import { spawn } from "child_process";
2
+ import { existsSync } from "fs";
3
+ import { join, dirname, basename } from "path";
4
+ import { fileURLToPath } from "url";
5
+ import prompts from "prompts";
6
+ import { selectFolder } from "../utils/folder-picker.js";
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+
11
+ // Get project root directory dynamically (two levels up from src/core/)
12
+ const PROJECT_ROOT = dirname(dirname(__dirname));
13
+
14
+ // Helper to run command
15
+ function runCommand(command, args = [], cwd = process.cwd()) {
16
+ return new Promise((resolve) => {
17
+ const proc = spawn(command, args, {
18
+ shell: true,
19
+ stdio: "pipe",
20
+ cwd: cwd
21
+ });
22
+ let output = "";
23
+
24
+ proc.stdout?.on("data", (data) => output += data.toString());
25
+ proc.stderr?.on("data", (data) => output += data.toString());
26
+
27
+ proc.on("close", (code) => resolve({ code, output }));
28
+ proc.on("error", () => resolve({ code: 1, output: "" }));
29
+ });
30
+ }
31
+
32
+ // Check if command exists
33
+ async function commandExists(command) {
34
+ const result = await runCommand("where", [command]);
35
+ return result.code === 0;
36
+ }
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
+
52
+ // Main function
53
+ async function main() {
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.4");
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");
69
+
70
+ // Step 1: Check Node.js
71
+ console.log("[1/4] Checking Node.js...");
72
+ if (!await commandExists("node")) {
73
+ console.log("ERROR: Node.js not found!");
74
+ console.log("Install from: https://nodejs.org/");
75
+ process.exit(1);
76
+ }
77
+ console.log("SUCCESS: Node.js installed\n");
78
+
79
+ // Step 2: Check Cloudflare (bundled or system-installed)
80
+ console.log("[2/4] Checking Cloudflare...");
81
+
82
+ // Import bundled cloudflared helpers
83
+ const { setupCloudflared, hasBundledCloudflared } = await import("./setup-cloudflared.js");
84
+
85
+ let cloudflareAvailable = false;
86
+
87
+ if (hasBundledCloudflared()) {
88
+ console.log("SUCCESS: Using bundled Cloudflare (no install needed)");
89
+ cloudflareAvailable = true;
90
+ } else if (await commandExists("cloudflared")) {
91
+ console.log("SUCCESS: Cloudflare installed on system");
92
+ cloudflareAvailable = true;
93
+ } else {
94
+ console.log("First time setup - Downloading Cloudflare...");
95
+ console.log("This only happens once (~40MB, 10-30 seconds)\n");
96
+
97
+ try {
98
+ const bundledPath = await setupCloudflared();
99
+
100
+ if (bundledPath) {
101
+ console.log("SUCCESS: Cloudflare ready to use");
102
+ cloudflareAvailable = true;
103
+ } else {
104
+ console.log("Could not download Cloudflare");
105
+ console.log("Will use alternative tunnel services\n");
106
+ }
107
+ } catch (err) {
108
+ console.log(`Setup error: ${err.message}`);
109
+ console.log("Will use alternative tunnel services\n");
110
+ }
111
+ }
112
+
113
+ // Show what's available
114
+ if (!cloudflareAvailable) {
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)");
119
+ console.log("");
120
+ }
121
+
122
+ // Step 3: Check dependencies
123
+ console.log("[3/4] Checking dependencies...");
124
+ const nodeModulesPath = join(PROJECT_ROOT, "node_modules");
125
+ if (!existsSync(nodeModulesPath)) {
126
+ console.log("Installing dependencies...\n");
127
+ // Run npm install in the project root directory
128
+ const result = await runCommand("npm", ["install"], PROJECT_ROOT);
129
+ if (result.code !== 0) {
130
+ console.log("\nERROR: npm install failed");
131
+ process.exit(1);
132
+ }
133
+ console.log("\nSUCCESS: Dependencies installed");
134
+ } else {
135
+ console.log("SUCCESS: Dependencies already installed");
136
+ }
137
+ console.log("");
138
+
139
+ // Step 4: Select folder using native OS dialog
140
+ console.log("[4/4] Select your project folder...");
141
+ console.log("Opening folder picker...\n");
142
+
143
+ const projectPath = await selectFolder();
144
+
145
+ if (!projectPath || projectPath.length === 0) {
146
+ console.log("ERROR: No folder selected");
147
+ process.exit(1);
148
+ }
149
+
150
+ const projectName = basename(projectPath);
151
+ console.log(`Selected: ${projectPath}\n`);
152
+
153
+ // Get port
154
+ const portResponse = await prompts({
155
+ type: "number",
156
+ name: "port",
157
+ message: "Enter your dev server port:",
158
+ initial: 5173
159
+ });
160
+
161
+ if (!portResponse.port) {
162
+ console.log("ERROR: No port entered");
163
+ process.exit(1);
164
+ }
165
+
166
+ const devPort = portResponse.port;
167
+ const proxyPort = devPort + 1000; // Use port 1000 higher for proxy
168
+
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");
175
+
176
+ // Start proxy server
177
+ console.log("Starting services...\n");
178
+ const proxyPath = join(__dirname, "proxy-server.js");
179
+ const proxyProcess = spawn("node", [proxyPath, devPort.toString(), proxyPort.toString(), projectName], {
180
+ stdio: "inherit",
181
+ shell: false
182
+ });
183
+
184
+ // Wait for proxy to start
185
+ await new Promise(resolve => setTimeout(resolve, 2000));
186
+
187
+ // Run main tunnel app (connects to proxy port)
188
+ // Use shell: false to properly handle paths with spaces
189
+ const indexPath = join(__dirname, "index.js");
190
+ const tunnelProcess = spawn("node", [indexPath, proxyPort.toString(), projectName, projectPath], {
191
+ stdio: "inherit",
192
+ shell: false
193
+ });
194
+
195
+ // Handle cleanup
196
+ const cleanup = () => {
197
+ console.log("\nShutting down...");
198
+ proxyProcess.kill();
199
+ tunnelProcess.kill();
200
+ process.exit(0);
201
+ };
202
+
203
+ tunnelProcess.on("close", (code) => {
204
+ cleanup();
205
+ });
206
+
207
+ proxyProcess.on("close", () => {
208
+ cleanup();
209
+ });
210
+
211
+ // Handle Ctrl+C
212
+ process.on("SIGINT", cleanup);
213
+ process.on("SIGTERM", cleanup);
214
+ }
215
+
216
+ // Run
217
+ main().catch((error) => {
218
+ console.error("\nERROR:", error.message);
219
+ process.exit(1);
220
+ });