api-response-manager 2.6.5 → 2.6.6

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
@@ -6,7 +6,7 @@
6
6
 
7
7
  Command-line interface for API Response Manager. Manage tunnels, webhooks, and projects from your terminal.
8
8
 
9
- **Version:** 2.6.5 | **Live Service:** https://tunnelapi.in
9
+ **Version:** 2.6.6 | **Live Service:** https://tunnelapi.in
10
10
 
11
11
  ## Installation
12
12
 
@@ -153,17 +153,10 @@ async function connectTunnelClient(tunnelId, subdomain, localPort, protocol = 'h
153
153
  console.log(chalk.white('Your local server is now accessible at:'));
154
154
  console.log(chalk.cyan.bold(` ${message.publicUrl}\n`));
155
155
 
156
- // Show TCP/SSH specific info
156
+ // Show TCP/SSH specific info and start local proxy
157
157
  if (isTcpTunnel && message.tcpHost && message.tcpPort) {
158
- console.log(chalk.yellow.bold('šŸ”Œ TCP/SSH Connection Info:\n'));
159
- console.log(chalk.white(` TCP Host: ${message.tcpHost}`));
160
- console.log(chalk.white(` TCP Port: ${message.tcpPort}\n`));
161
-
162
- console.log(chalk.gray('Connect via SSH:'));
163
- console.log(chalk.cyan(` ssh <username>@${message.tcpHost} -p ${message.tcpPort}\n`));
164
- console.log(chalk.gray('SSH with Key:'));
165
- console.log(chalk.cyan(` ssh -i ~/.ssh/your_key <username>@${message.tcpHost} -p ${message.tcpPort}\n`));
166
- console.log(chalk.gray.italic(' Replace <username> with your device\'s SSH username\n'));
158
+ // Start a local TCP proxy that transparently handles subdomain multiplexing
159
+ startLocalTcpProxy(subdomain, message.tcpHost, message.tcpPort, silent);
167
160
  } else {
168
161
  // Display QR code for HTTP/HTTPS tunnels (not for TCP/SSH)
169
162
  displayQRCode(message.publicUrl);
@@ -282,6 +275,11 @@ async function connectTunnelClient(tunnelId, subdomain, localPort, protocol = 'h
282
275
  process.on('SIGINT', () => {
283
276
  if (heartbeatInterval) clearInterval(heartbeatInterval);
284
277
  console.log(chalk.yellow('\n\n⚠ Stopping tunnel...'));
278
+ // Close local TCP proxy if running
279
+ if (localProxyServer) {
280
+ localProxyServer.close();
281
+ localProxyServer = null;
282
+ }
285
283
  ws.close();
286
284
  });
287
285
  }
@@ -563,6 +561,76 @@ async function configureIngress(tunnelId, rules, options) {
563
561
  }
564
562
  }
565
563
 
564
+ // Local TCP proxy for SSH/TCP tunnels (ngrok-style)
565
+ // Starts a local TCP server that transparently handles subdomain multiplexing.
566
+ // User just connects to localhost:<localProxyPort> — the proxy injects the
567
+ // SUBDOMAIN header and relays all data bidirectionally to the tunnel server.
568
+ let localProxyServer = null;
569
+
570
+ function startLocalTcpProxy(subdomain, remoteHost, remotePort, silent) {
571
+ localProxyServer = net.createServer((clientSocket) => {
572
+ if (!silent) {
573
+ const timestamp = new Date().toLocaleTimeString();
574
+ console.log(chalk.gray(`[${timestamp}]`), chalk.magenta('SSH'), chalk.white('New connection via local proxy'));
575
+ }
576
+
577
+ // Connect to the tunnel server's TCP port
578
+ const remoteSocket = net.createConnection({
579
+ host: remoteHost,
580
+ port: remotePort
581
+ });
582
+
583
+ remoteSocket.on('connect', () => {
584
+ // Send subdomain header first, then pipe everything bidirectionally
585
+ remoteSocket.write(`SUBDOMAIN:${subdomain}\n`, () => {
586
+ // After header is sent, relay data both ways
587
+ clientSocket.pipe(remoteSocket, { end: true });
588
+ remoteSocket.pipe(clientSocket, { end: true });
589
+ });
590
+ });
591
+
592
+ remoteSocket.on('error', (error) => {
593
+ if (!silent) {
594
+ console.error(chalk.red(` Proxy remote error: ${error.message}`));
595
+ }
596
+ clientSocket.end();
597
+ });
598
+
599
+ clientSocket.on('error', (error) => {
600
+ if (!silent && error.code !== 'ECONNRESET') {
601
+ console.error(chalk.red(` Proxy client error: ${error.message}`));
602
+ }
603
+ remoteSocket.end();
604
+ });
605
+
606
+ clientSocket.on('end', () => {
607
+ remoteSocket.end();
608
+ });
609
+
610
+ remoteSocket.on('end', () => {
611
+ clientSocket.end();
612
+ });
613
+ });
614
+
615
+ // Listen on port 0 to get a random available port
616
+ localProxyServer.listen(0, '127.0.0.1', () => {
617
+ const localPort = localProxyServer.address().port;
618
+
619
+ if (!silent) {
620
+ console.log(chalk.yellow.bold('šŸ”Œ SSH/TCP Tunnel Ready!\n'));
621
+ console.log(chalk.white(' Connect via SSH:\n'));
622
+ console.log(chalk.cyan.bold(` ssh <username>@localhost -p ${localPort}\n`));
623
+ console.log(chalk.gray(' SSH with Key:'));
624
+ console.log(chalk.cyan(` ssh -i ~/.ssh/your_key <username>@localhost -p ${localPort}\n`));
625
+ console.log(chalk.gray.italic(` Replace <username> with your device's SSH username\n`));
626
+ }
627
+ });
628
+
629
+ localProxyServer.on('error', (error) => {
630
+ console.error(chalk.red(`Local proxy error: ${error.message}`));
631
+ });
632
+ }
633
+
566
634
  // TCP connection handlers for SSH/TCP tunnels
567
635
  function handleTcpConnect(message, localPort, ws, silent) {
568
636
  const { connectionId, remoteAddress, remotePort } = message;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "api-response-manager",
3
- "version": "2.6.5",
3
+ "version": "2.6.6",
4
4
  "description": "Command-line interface for API Response Manager",
5
5
  "main": "index.js",
6
6
  "bin": {