cli-tunnel 1.3.1-beta.0 → 1.3.1-beta.2

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/dist/index.js +19 -18
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -187,8 +187,8 @@ setInterval(() => {
187
187
  }, 30000);
188
188
  // ─── Security: Redact secrets from replay events ────────────
189
189
  // ─── Bridge server ──────────────────────────────────────────
190
- const acpEventLog = [];
191
190
  const connections = new Map();
191
+ let localResizeAt = 0; // Timestamp of last local terminal resize
192
192
  // #10: Session TTL enforcement — periodically close expired connections
193
193
  setInterval(() => {
194
194
  if (Date.now() - sessionCreatedAt > SESSION_TTL) {
@@ -525,12 +525,10 @@ wss.on('connection', (ws, req) => {
525
525
  // F-10: WS ping/pong heartbeat
526
526
  ws._isAlive = true;
527
527
  ws.on('pong', () => { ws._isAlive = true; });
528
- // Replay history with secrets redacted (only if replay is enabled)
529
- if (hasReplay) {
530
- for (const event of acpEventLog) {
531
- ws.send(JSON.stringify({ type: '_replay', data: redactSecrets(event) }));
532
- }
533
- ws.send(JSON.stringify({ type: '_replay_done' }));
528
+ // Replay: send accumulated PTY output as one bulk write
529
+ // xterm.js processes the full ANSI stream so cursor codes work correctly
530
+ if (hasReplay && replayBuffer.length > 0) {
531
+ ws.send(JSON.stringify({ type: 'pty', data: replayBuffer }));
534
532
  }
535
533
  ws.on('message', (data) => {
536
534
  // F-13: Enforce WS message rate limit (100 msg/sec)
@@ -557,11 +555,12 @@ wss.on('connection', (ws, req) => {
557
555
  ptyProcess.write(msg.data);
558
556
  }
559
557
  }
560
- // #7: NaN guard on pty_resize
558
+ // #7: NaN guard on pty_resize — remote resize only if local hasn't resized recently
561
559
  if (msg.type === 'pty_resize') {
562
560
  const cols = Number(msg.cols);
563
561
  const rows = Number(msg.rows);
564
- if (Number.isFinite(cols) && Number.isFinite(rows) && ptyProcess) {
562
+ const localRecentlyResized = Date.now() - localResizeAt < 2000;
563
+ if (Number.isFinite(cols) && Number.isFinite(rows) && ptyProcess && !localRecentlyResized) {
565
564
  ptyProcess.resize(Math.max(1, Math.min(500, cols)), Math.max(1, Math.min(200, rows)));
566
565
  }
567
566
  }
@@ -585,19 +584,19 @@ setInterval(() => {
585
584
  ws.ping();
586
585
  }
587
586
  }, 30000);
588
- // F-12: Cap per-entry replay buffer size (64KB)
589
- const MAX_REPLAY_ENTRY_SIZE = 65536;
587
+ // Replay buffer: store raw PTY bytes in a single rolling buffer
588
+ // xterm.js processes the full stream on connect, so ANSI cursor codes work correctly
589
+ const MAX_REPLAY_BYTES = 256 * 1024; // 256KB
590
+ let replayBuffer = '';
590
591
  function broadcast(data) {
591
- // F-01: Redact secrets from live broadcast (not just replay)
592
592
  const redacted = redactSecrets(data);
593
593
  const msg = JSON.stringify({ type: 'pty', data: redacted });
594
594
  if (hasReplay) {
595
- // F-12: Cap per-entry size to prevent memory exhaustion
596
- if (msg.length <= MAX_REPLAY_ENTRY_SIZE) {
597
- acpEventLog.push(msg);
595
+ replayBuffer += redacted;
596
+ // Trim from the front if too large
597
+ if (replayBuffer.length > MAX_REPLAY_BYTES) {
598
+ replayBuffer = replayBuffer.slice(replayBuffer.length - MAX_REPLAY_BYTES);
598
599
  }
599
- if (acpEventLog.length > 2000)
600
- acpEventLog.splice(0, acpEventLog.length - 2000);
601
600
  }
602
601
  for (const [, ws] of connections) {
603
602
  if (ws.readyState === WebSocket.OPEN)
@@ -814,6 +813,8 @@ async function main() {
814
813
  });
815
814
  }
816
815
  console.log(` ${DIM}Starting ${command}...${RESET}\n`);
816
+ // Clear screen before PTY takes over — prevents overlap with banner/QR output
817
+ process.stdout.write('\x1b[2J\x1b[H');
817
818
  // Spawn PTY
818
819
  const nodePty = await import('node-pty');
819
820
  const cols = process.stdout.columns || 120;
@@ -902,6 +903,6 @@ async function main() {
902
903
  process.stdin.setRawMode(true);
903
904
  process.stdin.resume();
904
905
  process.stdin.on('data', (data) => ptyProcess.write(data.toString()));
905
- process.stdout.on('resize', () => ptyProcess.resize(process.stdout.columns || 120, process.stdout.rows || 30));
906
+ process.stdout.on('resize', () => { localResizeAt = Date.now(); ptyProcess.resize(process.stdout.columns || 120, process.stdout.rows || 30); });
906
907
  }
907
908
  main().catch((err) => { console.error(err); process.exit(1); });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cli-tunnel",
3
- "version": "1.3.1-beta.0",
3
+ "version": "1.3.1-beta.2",
4
4
  "description": "Tunnel any CLI app to your phone — PTY + devtunnel + xterm.js",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",