rostc 1.0.2 → 1.0.3
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/cli.js +127 -80
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -3,108 +3,155 @@
|
|
|
3
3
|
const WebSocket = require('ws');
|
|
4
4
|
const http = require('http');
|
|
5
5
|
const { randomBytes } = require('crypto');
|
|
6
|
+
const { exec } = require('child_process');
|
|
6
7
|
|
|
7
|
-
// Chalk v5+ fix
|
|
8
8
|
let chalk;
|
|
9
9
|
try {
|
|
10
10
|
chalk = require('chalk');
|
|
11
11
|
} catch (e) {
|
|
12
|
-
|
|
13
|
-
chalk = {
|
|
14
|
-
cyan: (t) => t,
|
|
15
|
-
green: (t) => t,
|
|
16
|
-
gray: (t) => t,
|
|
17
|
-
red: (t) => t,
|
|
18
|
-
yellow: (t) => t
|
|
19
|
-
};
|
|
12
|
+
chalk = { cyan: (t)=>t, green: (t)=>t, gray: (t)=>t, red: (t)=>t, yellow: (t)=>t };
|
|
20
13
|
}
|
|
21
14
|
|
|
22
|
-
// YOUR WORKER URL FROM DEPLOYMENT
|
|
23
15
|
const WORKER_URL = 'https://rostc.roygichira084.workers.dev';
|
|
24
|
-
|
|
25
16
|
const port = process.argv[2];
|
|
26
17
|
|
|
27
18
|
if (!port) {
|
|
28
19
|
console.log(chalk.red('✗ Error: Please specify a port'));
|
|
29
|
-
console.log(chalk.yellow('→ Usage:
|
|
20
|
+
console.log(chalk.yellow('→ Usage: rostc 8080'));
|
|
30
21
|
process.exit(1);
|
|
31
22
|
}
|
|
32
23
|
|
|
33
|
-
|
|
34
|
-
console.log(chalk.red('✗ Error: Invalid port number'));
|
|
35
|
-
process.exit(1);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
console.log(chalk.cyan(`→ Building tunnel on port ${port}...`));
|
|
39
|
-
|
|
40
|
-
// Generate random tunnel ID
|
|
41
|
-
const tunnelId = randomBytes(8).toString('hex');
|
|
42
|
-
const publicUrl = `${WORKER_URL}/${tunnelId}`;
|
|
24
|
+
console.log(chalk.cyan(`→ Starting tunnel on port ${port}...`));
|
|
43
25
|
|
|
44
|
-
//
|
|
45
|
-
const
|
|
26
|
+
// Check if something is already running on the port
|
|
27
|
+
const net = require('net');
|
|
28
|
+
const tester = net.createServer()
|
|
29
|
+
.once('error', (err) => {
|
|
30
|
+
if (err.code === 'EADDRINUSE') {
|
|
31
|
+
// Port is in use — just tunnel to existing server
|
|
32
|
+
console.log(chalk.gray(`→ Port ${port} already in use. Tunneling to existing server.`));
|
|
33
|
+
startTunnel(false);
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
.once('listening', () => {
|
|
37
|
+
// Port is free — start our own simple HTTP server
|
|
38
|
+
tester.close();
|
|
39
|
+
console.log(chalk.gray(`→ No server found on port ${port}. Starting built-in file server...`));
|
|
40
|
+
startTunnel(true);
|
|
41
|
+
})
|
|
42
|
+
.listen(port);
|
|
46
43
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
console.log(chalk.gray(`⌛ Expires in 4 minutes (240 seconds)`));
|
|
50
|
-
console.log(chalk.gray(`→ Press Ctrl+C to destroy early\n`));
|
|
44
|
+
function startTunnel(startOwnServer) {
|
|
45
|
+
let server;
|
|
51
46
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
47
|
+
if (startOwnServer) {
|
|
48
|
+
// Start a simple HTTP file server
|
|
49
|
+
const http = require('http');
|
|
50
|
+
const fs = require('fs');
|
|
51
|
+
const path = require('path');
|
|
52
|
+
|
|
53
|
+
server = http.createServer((req, res) => {
|
|
54
|
+
let filePath = '.' + req.url;
|
|
55
|
+
if (filePath === './') filePath = './index.html';
|
|
59
56
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
57
|
+
const ext = path.extname(filePath);
|
|
58
|
+
const contentType = {
|
|
59
|
+
'.html': 'text/html',
|
|
60
|
+
'.js': 'text/javascript',
|
|
61
|
+
'.css': 'text/css',
|
|
62
|
+
'.json': 'application/json',
|
|
63
|
+
'.png': 'image/png',
|
|
64
|
+
'.jpg': 'image/jpeg',
|
|
65
|
+
'.gif': 'image/gif',
|
|
66
|
+
}[ext] || 'text/plain';
|
|
68
67
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
68
|
+
fs.readFile(filePath, (err, content) => {
|
|
69
|
+
if (err) {
|
|
70
|
+
res.writeHead(404);
|
|
71
|
+
res.end('File not found');
|
|
72
|
+
} else {
|
|
73
|
+
res.writeHead(200, { 'Content-Type': contentType });
|
|
74
|
+
res.end(content);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
server.listen(port, () => {
|
|
80
|
+
console.log(chalk.gray(`→ Built-in server running on http://localhost:${port}`));
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Generate random tunnel ID
|
|
85
|
+
const tunnelId = randomBytes(8).toString('hex');
|
|
86
|
+
const publicUrl = `${WORKER_URL}/${tunnelId}`;
|
|
87
|
+
|
|
88
|
+
// Connect to Cloudflare Worker
|
|
89
|
+
const ws = new WebSocket(`${WORKER_URL.replace('https', 'wss')}/${tunnelId}`);
|
|
90
|
+
|
|
91
|
+
ws.on('open', () => {
|
|
92
|
+
console.log(chalk.green(`✓ ${publicUrl}`));
|
|
93
|
+
console.log(chalk.gray(`⌛ Expires in 4 minutes (240 seconds)`));
|
|
94
|
+
console.log(chalk.gray(`→ Press Ctrl+C to destroy\n`));
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Handle incoming requests through WebSocket
|
|
98
|
+
if (server) {
|
|
99
|
+
// Forward requests from the tunnel to our local server
|
|
100
|
+
ws.on('message', (data) => {
|
|
101
|
+
try {
|
|
102
|
+
const msg = JSON.parse(data);
|
|
103
|
+
if (msg.type === 'request') {
|
|
104
|
+
const options = {
|
|
105
|
+
hostname: 'localhost',
|
|
106
|
+
port: port,
|
|
107
|
+
path: msg.path,
|
|
108
|
+
method: msg.method,
|
|
109
|
+
headers: msg.headers
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const req = http.request(options, (res) => {
|
|
113
|
+
const chunks = [];
|
|
114
|
+
res.on('data', chunk => chunks.push(chunk));
|
|
115
|
+
res.on('end', () => {
|
|
116
|
+
ws.send(JSON.stringify({
|
|
117
|
+
type: 'response',
|
|
118
|
+
id: msg.id,
|
|
119
|
+
status: res.statusCode,
|
|
120
|
+
headers: res.headers,
|
|
121
|
+
body: Buffer.concat(chunks).toString('base64')
|
|
122
|
+
}));
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
if (msg.body) {
|
|
127
|
+
req.write(Buffer.from(msg.body, 'base64'));
|
|
76
128
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
129
|
+
req.end();
|
|
130
|
+
}
|
|
131
|
+
} catch(e) {}
|
|
80
132
|
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 4-minute timer
|
|
136
|
+
const timeout = setTimeout(() => {
|
|
137
|
+
console.log(chalk.red(`\n✗ Tunnel expired after 4 minutes`));
|
|
138
|
+
ws.close();
|
|
139
|
+
if (server) server.close();
|
|
140
|
+
process.exit(0);
|
|
141
|
+
}, 240000);
|
|
142
|
+
|
|
143
|
+
// Handle Ctrl+C
|
|
144
|
+
process.on('SIGINT', () => {
|
|
145
|
+
console.log(chalk.yellow(`\n→ Destroying tunnel...`));
|
|
146
|
+
clearTimeout(timeout);
|
|
147
|
+
ws.close();
|
|
148
|
+
if (server) server.close();
|
|
149
|
+
process.exit(0);
|
|
81
150
|
});
|
|
82
151
|
|
|
83
|
-
|
|
84
|
-
|
|
152
|
+
ws.on('error', (err) => {
|
|
153
|
+
console.log(chalk.red(`✗ Error: ${err.message}`));
|
|
154
|
+
if (server) server.close();
|
|
155
|
+
process.exit(1);
|
|
85
156
|
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// 4-minute timer
|
|
89
|
-
const timeout = setTimeout(() => {
|
|
90
|
-
console.log(chalk.red(`\n✗ Tunnel expired after 4 minutes`));
|
|
91
|
-
ws.close();
|
|
92
|
-
process.exit(0);
|
|
93
|
-
}, 240000);
|
|
94
|
-
|
|
95
|
-
// Handle Ctrl+C
|
|
96
|
-
process.on('SIGINT', () => {
|
|
97
|
-
console.log(chalk.yellow(`\n→ Destroying tunnel...`));
|
|
98
|
-
clearTimeout(timeout);
|
|
99
|
-
ws.close();
|
|
100
|
-
process.exit(0);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
ws.on('error', (err) => {
|
|
104
|
-
console.log(chalk.red(`✗ WebSocket error: ${err.message}`));
|
|
105
|
-
process.exit(1);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
ws.on('close', () => {
|
|
109
|
-
console.log(chalk.gray('\n→ Tunnel closed'));
|
|
110
|
-
});
|
|
157
|
+
}
|