cli-tunnel 1.3.1-beta.2 → 1.3.1-beta.3
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/dist/index.js +32 -17
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -23,6 +23,10 @@ import http from 'node:http';
|
|
|
23
23
|
import readline from 'node:readline';
|
|
24
24
|
import { WebSocketServer, WebSocket } from 'ws';
|
|
25
25
|
import os from 'node:os';
|
|
26
|
+
import headlessPkg from '@xterm/headless';
|
|
27
|
+
const HeadlessTerminal = headlessPkg.Terminal;
|
|
28
|
+
import serializePkg from '@xterm/addon-serialize';
|
|
29
|
+
const SerializeAddon = serializePkg.SerializeAddon;
|
|
26
30
|
import { redactSecrets } from './redact.js';
|
|
27
31
|
// F-15: Global error handlers to prevent unclean crashes
|
|
28
32
|
process.on('uncaughtException', (err) => {
|
|
@@ -58,7 +62,7 @@ ${BOLD}Options:${RESET}
|
|
|
58
62
|
--local Disable devtunnel (localhost only)
|
|
59
63
|
--port <n> Bridge port (default: random)
|
|
60
64
|
--name <name> Session name (shown in dashboard)
|
|
61
|
-
--
|
|
65
|
+
--replay (deprecated, screen buffer is always on)
|
|
62
66
|
--help, -h Show this help
|
|
63
67
|
|
|
64
68
|
${BOLD}Examples:${RESET}
|
|
@@ -525,10 +529,10 @@ wss.on('connection', (ws, req) => {
|
|
|
525
529
|
// F-10: WS ping/pong heartbeat
|
|
526
530
|
ws._isAlive = true;
|
|
527
531
|
ws.on('pong', () => { ws._isAlive = true; });
|
|
528
|
-
//
|
|
529
|
-
|
|
530
|
-
if (
|
|
531
|
-
ws.send(JSON.stringify({ type: 'pty', data:
|
|
532
|
+
// Send current screen state so new clients see what's on the terminal now
|
|
533
|
+
const screenState = getScreenState();
|
|
534
|
+
if (screenState) {
|
|
535
|
+
ws.send(JSON.stringify({ type: 'pty', data: screenState }));
|
|
532
536
|
}
|
|
533
537
|
ws.on('message', (data) => {
|
|
534
538
|
// F-13: Enforce WS message rate limit (100 msg/sec)
|
|
@@ -562,6 +566,8 @@ wss.on('connection', (ws, req) => {
|
|
|
562
566
|
const localRecentlyResized = Date.now() - localResizeAt < 2000;
|
|
563
567
|
if (Number.isFinite(cols) && Number.isFinite(rows) && ptyProcess && !localRecentlyResized) {
|
|
564
568
|
ptyProcess.resize(Math.max(1, Math.min(500, cols)), Math.max(1, Math.min(200, rows)));
|
|
569
|
+
if (screenTerminal)
|
|
570
|
+
screenTerminal.resize(Math.max(1, Math.min(500, cols)), Math.max(1, Math.min(200, rows)));
|
|
565
571
|
}
|
|
566
572
|
}
|
|
567
573
|
}
|
|
@@ -584,20 +590,26 @@ setInterval(() => {
|
|
|
584
590
|
ws.ping();
|
|
585
591
|
}
|
|
586
592
|
}, 30000);
|
|
587
|
-
//
|
|
588
|
-
//
|
|
589
|
-
|
|
590
|
-
let
|
|
593
|
+
// Screen buffer: headless xterm.js terminal that tracks the current screen state
|
|
594
|
+
// On client connect, serialize the screen and send it — renders correctly regardless of dimensions
|
|
595
|
+
let screenTerminal = null;
|
|
596
|
+
let serializeAddon = null;
|
|
597
|
+
function initScreenBuffer(cols, rows) {
|
|
598
|
+
screenTerminal = new HeadlessTerminal({ cols, rows, scrollback: 1000, allowProposedApi: true });
|
|
599
|
+
serializeAddon = new SerializeAddon();
|
|
600
|
+
screenTerminal.loadAddon(serializeAddon);
|
|
601
|
+
}
|
|
602
|
+
function getScreenState() {
|
|
603
|
+
if (!serializeAddon)
|
|
604
|
+
return '';
|
|
605
|
+
return serializeAddon.serialize();
|
|
606
|
+
}
|
|
591
607
|
function broadcast(data) {
|
|
592
608
|
const redacted = redactSecrets(data);
|
|
609
|
+
// Feed into headless terminal to track screen state
|
|
610
|
+
if (screenTerminal)
|
|
611
|
+
screenTerminal.write(redacted);
|
|
593
612
|
const msg = JSON.stringify({ type: 'pty', data: redacted });
|
|
594
|
-
if (hasReplay) {
|
|
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);
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
613
|
for (const [, ws] of connections) {
|
|
602
614
|
if (ws.readyState === WebSocket.OPEN)
|
|
603
615
|
ws.send(msg);
|
|
@@ -866,6 +878,8 @@ async function main() {
|
|
|
866
878
|
cols, rows, cwd,
|
|
867
879
|
env: safeEnv,
|
|
868
880
|
});
|
|
881
|
+
// Initialize headless terminal for screen state tracking
|
|
882
|
+
initScreenBuffer(cols, rows);
|
|
869
883
|
// Detect CSPRNG crash (rare Node.js + PTY issue) and show helpful message
|
|
870
884
|
let earlyExitCode = null;
|
|
871
885
|
const earlyExitCheck = new Promise((resolve) => {
|
|
@@ -903,6 +917,7 @@ async function main() {
|
|
|
903
917
|
process.stdin.setRawMode(true);
|
|
904
918
|
process.stdin.resume();
|
|
905
919
|
process.stdin.on('data', (data) => ptyProcess.write(data.toString()));
|
|
906
|
-
process.stdout.on('resize', () => { localResizeAt = Date.now();
|
|
920
|
+
process.stdout.on('resize', () => { localResizeAt = Date.now(); const c = process.stdout.columns || 120; const r = process.stdout.rows || 30; ptyProcess.resize(c, r); if (screenTerminal)
|
|
921
|
+
screenTerminal.resize(c, r); });
|
|
907
922
|
}
|
|
908
923
|
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.
|
|
3
|
+
"version": "1.3.1-beta.3",
|
|
4
4
|
"description": "Tunnel any CLI app to your phone — PTY + devtunnel + xterm.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -36,6 +36,8 @@
|
|
|
36
36
|
"node": ">=22.0.0"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
+
"@xterm/addon-serialize": "^0.14.0",
|
|
40
|
+
"@xterm/headless": "^6.0.0",
|
|
39
41
|
"node-pty": "1.1.0",
|
|
40
42
|
"qrcode-terminal": "0.12.0",
|
|
41
43
|
"ws": "8.19.0"
|