rostc 1.0.0

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/cli.js +96 -0
  2. package/package.json +12 -0
package/cli.js ADDED
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+
3
+ const WebSocket = require('ws');
4
+ const chalk = require('chalk');
5
+ const http = require('http');
6
+ const { randomBytes } = require('crypto');
7
+
8
+ // YOUR WORKER URL FROM DEPLOYMENT
9
+ const WORKER_URL = 'https://rostc.roygichira084.workers.dev';
10
+
11
+ const port = process.argv[2];
12
+
13
+ if (!port) {
14
+ console.log(chalk.red('✗ Error: Please specify a port'));
15
+ console.log(chalk.yellow('→ Usage: npx rostc 8080'));
16
+ process.exit(1);
17
+ }
18
+
19
+ if (isNaN(port) || port < 1 || port > 65535) {
20
+ console.log(chalk.red('✗ Error: Invalid port number'));
21
+ process.exit(1);
22
+ }
23
+
24
+ console.log(chalk.cyan(`→ Building tunnel on port ${port}...`));
25
+
26
+ // Generate random tunnel ID
27
+ const tunnelId = randomBytes(8).toString('hex');
28
+ const publicUrl = `${WORKER_URL}/${tunnelId}`;
29
+
30
+ // Connect to Cloudflare Worker
31
+ const ws = new WebSocket(`${WORKER_URL.replace('https', 'wss')}/${tunnelId}`);
32
+
33
+ ws.on('open', () => {
34
+ console.log(chalk.green(`✓ ${publicUrl}`));
35
+ console.log(chalk.gray(`⌛ Expires in 4 minutes (240 seconds)`));
36
+ console.log(chalk.gray(`→ Press Ctrl+C to destroy early\n`));
37
+
38
+ // Start local HTTP server
39
+ const server = http.createServer((req, res) => {
40
+ const chunks = [];
41
+ req.on('data', chunk => chunks.push(chunk));
42
+ req.on('end', () => {
43
+ const body = Buffer.concat(chunks);
44
+ const requestId = randomBytes(8).toString('hex');
45
+
46
+ ws.send(JSON.stringify({
47
+ type: 'request',
48
+ id: requestId,
49
+ method: req.method,
50
+ path: req.url,
51
+ headers: req.headers,
52
+ body: body.toString('base64')
53
+ }));
54
+
55
+ const handler = (data) => {
56
+ try {
57
+ const msg = JSON.parse(data);
58
+ if (msg.type === 'response' && msg.id === requestId) {
59
+ ws.removeListener('message', handler);
60
+ res.writeHead(msg.status || 200, msg.headers || {});
61
+ res.end(msg.body ? Buffer.from(msg.body, 'base64') : null);
62
+ }
63
+ } catch(e) {}
64
+ };
65
+ ws.on('message', handler);
66
+ });
67
+ });
68
+
69
+ server.listen(port, () => {
70
+ // Server ready
71
+ });
72
+ });
73
+
74
+ // 4-minute timer
75
+ const timeout = setTimeout(() => {
76
+ console.log(chalk.red(`\n✗ Tunnel expired after 4 minutes`));
77
+ ws.close();
78
+ process.exit(0);
79
+ }, 240000);
80
+
81
+ // Handle Ctrl+C
82
+ process.on('SIGINT', () => {
83
+ console.log(chalk.yellow(`\n→ Destroying tunnel...`));
84
+ clearTimeout(timeout);
85
+ ws.close();
86
+ process.exit(0);
87
+ });
88
+
89
+ ws.on('error', (err) => {
90
+ console.log(chalk.red(`✗ WebSocket error: ${err.message}`));
91
+ process.exit(1);
92
+ });
93
+
94
+ ws.on('close', () => {
95
+ console.log(chalk.gray('\n→ Tunnel closed'));
96
+ });
package/package.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "rostc",
3
+ "version": "1.0.0",
4
+ "description": "4-minute localhost tunnel",
5
+ "bin": {
6
+ "rostc": "./cli.js"
7
+ },
8
+ "dependencies": {
9
+ "ws": "^8.14.0",
10
+ "chalk": "^4.1.2"
11
+ }
12
+ }