api-response-manager 2.6.1 → 2.6.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.
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.0 | **Live Service:** https://tunnelapi.in
9
+ **Version:** 2.6.2 | **Live Service:** https://tunnelapi.in
10
10
 
11
11
  ## Installation
12
12
 
package/commands/login.js CHANGED
@@ -1,10 +1,57 @@
1
1
  const inquirer = require('inquirer');
2
2
  const chalk = require('chalk');
3
3
  const ora = require('ora');
4
- const open = require('open');
4
+ const { spawn } = require('child_process');
5
5
  const api = require('../utils/api');
6
6
  const config = require('../utils/config');
7
7
 
8
+ // Safe browser opener that never throws
9
+ function openBrowserSafe(url) {
10
+ return new Promise((resolve) => {
11
+ const platform = process.platform;
12
+ let cmd, args;
13
+
14
+ if (platform === 'win32') {
15
+ cmd = 'cmd.exe';
16
+ args = ['/c', 'start', '""', url];
17
+ } else if (platform === 'darwin') {
18
+ cmd = 'open';
19
+ args = [url];
20
+ } else {
21
+ cmd = 'xdg-open';
22
+ args = [url];
23
+ }
24
+
25
+ try {
26
+ const child = spawn(cmd, args, {
27
+ detached: true,
28
+ stdio: 'ignore'
29
+ });
30
+
31
+ let resolved = false;
32
+
33
+ child.on('error', () => {
34
+ if (!resolved) {
35
+ resolved = true;
36
+ resolve(false);
37
+ }
38
+ });
39
+
40
+ // On successful spawn, unref and resolve after brief delay
41
+ child.unref();
42
+
43
+ setTimeout(() => {
44
+ if (!resolved) {
45
+ resolved = true;
46
+ resolve(true);
47
+ }
48
+ }, 100);
49
+ } catch (e) {
50
+ resolve(false);
51
+ }
52
+ });
53
+ }
54
+
8
55
  async function login(options) {
9
56
  // Token-based login for CI/CD automation
10
57
  if (options.token) {
@@ -165,8 +212,13 @@ async function socialLogin(provider) {
165
212
  ]);
166
213
 
167
214
  if (openBrowser) {
168
- await open(verification_uri);
169
- console.log(chalk.gray(' ✓ Browser opened\n'));
215
+ const browserOpened = await openBrowserSafe(verification_uri);
216
+
217
+ if (browserOpened) {
218
+ console.log(chalk.gray(' ✓ Browser opened\n'));
219
+ } else {
220
+ console.log(chalk.yellow(' ⚠ Could not open browser automatically. Please open the URL manually.\n'));
221
+ }
170
222
  }
171
223
 
172
224
  const pollSpinner = ora('Waiting for authentication...').start();
package/commands/serve.js CHANGED
@@ -454,7 +454,10 @@ async function connectFileTunnel(tunnelId, subdomain, localPort) {
454
454
  function connect() {
455
455
  if (isShuttingDown) return;
456
456
 
457
- const ws = new WebSocket(tunnelServerUrl);
457
+ const ws = new WebSocket(tunnelServerUrl, {
458
+ // Keep connection alive with WebSocket-level ping/pong
459
+ perMessageDeflate: false
460
+ });
458
461
 
459
462
  ws.on('open', () => {
460
463
  reconnectAttempts = 0; // Reset on successful connection
@@ -476,12 +479,17 @@ async function connectFileTunnel(tunnelId, subdomain, localPort) {
476
479
  clearInterval(heartbeatInterval);
477
480
  }
478
481
 
479
- // Heartbeat every 15 seconds to keep connection alive
482
+ // Heartbeat every 10 seconds to keep connection alive during large transfers
480
483
  heartbeatInterval = setInterval(() => {
481
484
  if (ws.readyState === WebSocket.OPEN) {
482
485
  ws.send(JSON.stringify({ type: 'heartbeat', tunnelId, subdomain }));
483
486
  }
484
- }, 15000);
487
+ }, 10000);
488
+ });
489
+
490
+ // Respond to WebSocket-level ping from server
491
+ ws.on('ping', () => {
492
+ ws.pong();
485
493
  });
486
494
 
487
495
  ws.on('message', async (data) => {
@@ -524,7 +532,7 @@ async function connectFileTunnel(tunnelId, subdomain, localPort) {
524
532
  validateStatus: () => true,
525
533
  responseType: 'arraybuffer',
526
534
  maxRedirects: 0,
527
- timeout: 30000
535
+ timeout: 0 // No timeout for large file transfers
528
536
  });
529
537
 
530
538
  const cleanHeaders = { ...response.headers };
@@ -160,11 +160,11 @@ async function connectTunnelClient(tunnelId, subdomain, localPort, protocol = 'h
160
160
  console.log(chalk.white(` TCP Port: ${message.tcpPort}\n`));
161
161
  if (message.sshCommand) {
162
162
  console.log(chalk.gray('SSH Command (Linux/Mac):'));
163
- console.log(chalk.cyan(` ${message.sshCommand}\n`));
163
+ console.log(chalk.cyan(` ssh -o ProxyCommand="sh -c '{ printf \\"SUBDOMAIN:${subdomain}\\\\n\\"; cat; } | nc %h %p'" user@${message.tcpHost} -p ${message.tcpPort}\n`));
164
164
  console.log(chalk.gray('SSH Command (Windows with ncat):'));
165
- console.log(chalk.cyan(` ssh -o ProxyCommand="ncat ${message.tcpHost} ${message.tcpPort}" user@${message.tcpHost}\n`));
165
+ console.log(chalk.cyan(` ssh -o ProxyCommand="cmd /c \\"(echo SUBDOMAIN:${subdomain}& type CON) | ncat ${message.tcpHost} ${message.tcpPort}\\"" user@${message.tcpHost}\n`));
166
166
  console.log(chalk.gray('SSH with Key (passwordless):'));
167
- console.log(chalk.cyan(` ssh -i ~/.ssh/your_key.pem -o ProxyCommand="echo 'SUBDOMAIN:${subdomain}' | nc %h %p" user@${message.tcpHost} -p ${message.tcpPort}\n`));
167
+ console.log(chalk.cyan(` ssh -i ~/.ssh/your_key.pem -o ProxyCommand="sh -c '{ printf \\"SUBDOMAIN:${subdomain}\\\\n\\"; cat; } | nc %h %p'" user@${message.tcpHost} -p ${message.tcpPort}\n`));
168
168
  }
169
169
  } else {
170
170
  // Display QR code for HTTP/HTTPS tunnels (not for TCP/SSH)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "api-response-manager",
3
- "version": "2.6.1",
3
+ "version": "2.6.4",
4
4
  "description": "Command-line interface for API Response Manager",
5
5
  "main": "index.js",
6
6
  "bin": {