@karthik_yk/ws-tunnel-cli 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.
package/LICENCE ADDED
File without changes
package/README.md ADDED
@@ -0,0 +1,10 @@
1
+ # ws-tunnel-cli
2
+
3
+ A lightweight TCP & HTTP tunneling CLI (ngrok-style).
4
+
5
+ ## Install
6
+ ```bash
7
+ npm install -g @karthik617/ws-tunnel-cli
8
+
9
+ # Custom host
10
+ ws-tunnel http 3000 wss://abcom.com
package/bin/tunnel.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import "../src/index.js";
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@karthik_yk/ws-tunnel-cli",
3
+ "version": "1.0.0",
4
+ "description": "websocket tunnel cli",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git@github-personal:karthik617/ws-tunnel-cli.git"
11
+ },
12
+ "bin": {
13
+ "ws-tunnel": "bin/tunnel.js"
14
+ },
15
+ "dependencies": {
16
+ "ws": "^8.16.0"
17
+ },
18
+ "keywords": [
19
+ "tunnel",
20
+ "ws",
21
+ "ngrok",
22
+ "http",
23
+ "cli"
24
+ ],
25
+ "license": "MIT",
26
+ "author": "karthik617",
27
+ "type": "module",
28
+ "main": "src/index.js",
29
+ "scripts": {
30
+ "start": "node src/index.js"
31
+ }
32
+ }
@@ -0,0 +1,97 @@
1
+ import WebSocket from "ws";
2
+ import http from "http";
3
+
4
+ export function createHttpTunnel(localPort, remoteHost) {
5
+ if (!remoteHost || !/^wss?:\/\//.test(remoteHost)) {
6
+ throw new Error("remoteHost must be ws:// or wss:// url");
7
+ }
8
+
9
+ const ws = new WebSocket(remoteHost);
10
+
11
+ ws.on("open", () => {
12
+ ws.send(JSON.stringify({
13
+ type: "register",
14
+ localPort
15
+ }));
16
+ });
17
+
18
+ ws.on("message", async (msg) => {
19
+ const data = JSON.parse(msg.toString());
20
+
21
+ // 🌍 PUBLIC URL LOGIC (IMPORTANT)
22
+ if (data.type === "registered") {
23
+ const url = getPublicUrl(remoteHost, data.id);
24
+ console.log(`🌍 Public URL: ${url}`);
25
+ return;
26
+ }
27
+
28
+ if (data.type === "http_request") {
29
+ const response = await forwardToLocal(data, localPort);
30
+ ws.send(JSON.stringify({
31
+ type: "http_response",
32
+ requestId: data.requestId,
33
+ ...response
34
+ }));
35
+ }
36
+ });
37
+
38
+ ws.on("close", () => {
39
+ console.error("❌ Tunnel disconnected");
40
+ process.exit(1);
41
+ });
42
+
43
+ ws.on("error", (err) => {
44
+ console.error("❌ Tunnel error:", err.message);
45
+ process.exit(1);
46
+ });
47
+ }
48
+
49
+ function getPublicUrl(remoteHost, id) {
50
+ const { protocol, host } = new URL(remoteHost.replace("ws", "http"));
51
+
52
+ // Local dev → path based
53
+ if (host.startsWith("localhost") || host.startsWith("127.")) {
54
+ return `${protocol}//${host}/tunnel/${id}`;
55
+ }
56
+
57
+ // Production (Render) → subdomain based
58
+ return `${protocol}//${id}.${host}`;
59
+ }
60
+
61
+ function forwardToLocal(req, localPort) {
62
+ return new Promise((resolve) => {
63
+ const headers = { ...req.headers };
64
+ delete headers.host; // 🔥 IMPORTANT
65
+
66
+ const localReq = http.request(
67
+ {
68
+ host: "localhost",
69
+ port: localPort,
70
+ method: req.method,
71
+ path: req.path,
72
+ headers
73
+ },
74
+ (res) => {
75
+ let body = "";
76
+ res.on("data", (c) => (body += c));
77
+ res.on("end", () => {
78
+ resolve({
79
+ status: res.statusCode,
80
+ headers: res.headers,
81
+ body
82
+ });
83
+ });
84
+ }
85
+ );
86
+
87
+ localReq.on("error", (err) => {
88
+ resolve({
89
+ status: 502,
90
+ body: err.message
91
+ });
92
+ });
93
+
94
+ if (req.body) localReq.write(req.body);
95
+ localReq.end();
96
+ });
97
+ }
package/src/index.js ADDED
@@ -0,0 +1,25 @@
1
+ import { createHttpTunnel } from "./httpClient.js";
2
+
3
+ const args = process.argv.slice(2);
4
+ if (args.length < 2) {
5
+ console.log(`
6
+ Usage:
7
+ ws-tunnel http <localPort> <wss:remoteHost>
8
+
9
+ Examples:
10
+ ws-tunnel http 8080 wss://localhost:8080
11
+ `);
12
+ process.exit(1);
13
+ }
14
+ const [mode, localPort, remoteHost] = args;
15
+
16
+ if (mode !== "http" || !localPort || !remoteHost) {
17
+ console.log("Usage: ws-tunnel http <localPort> <wss:remoteHost>");
18
+ process.exit(1);
19
+ }
20
+ try {
21
+ createHttpTunnel(Number(localPort), remoteHost);
22
+ } catch (err) {
23
+ console.error(err.message);
24
+ process.exit(1);
25
+ }