peer-term 1.1.0 → 1.1.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 (3) hide show
  1. package/README.md +15 -8
  2. package/package.json +1 -1
  3. package/src/index.js +16 -1
package/README.md CHANGED
@@ -36,17 +36,24 @@ peer-term --path ~/projects # starts at ~/projects
36
36
  peer-term --path . # starts in current directory
37
37
  peer-term --readonly # view-only session
38
38
  peer-term --expiry 10m # custom expiry time
39
+ peer-term --relay wss://custom # use a custom relay server
39
40
  peer-term --verbose # enable debug logging
40
41
  ```
41
42
 
42
43
  ## Features
43
44
 
44
45
  - **No Config**: Works instantly. No port forwarding or firewall configuration needed.
45
- - **End-to-End Encrypted**: Terminal data is encrypted locally using AES-GCM before being sent.
46
- - **WebRTC P2P**: Creates a direct Peer-to-Peer connection when possible for minimal latency.
47
- - **Read-Only Mode**: Guests can view your terminal but cannot type commands, ensuring your system remains secure.
48
- - **Custom Start Path**: Set the starting directory with `--path` so guests land right where you want them.
49
-
50
- ## License
51
-
52
- MIT
46
+ - **End-to-End Encrypted**: Terminal data is encrypted locally using AES-256-GCM with ECDH P-256 key exchange. The relay never sees plaintext.
47
+ - **WebRTC P2P**: Creates a direct Peer-to-Peer connection on the same LAN for zero-latency, relay-free sessions.
48
+ - **Read-Only Mode**: Guests can view your terminal but cannot type commands.
49
+ - **Custom Start Path**: Set the starting directory with `--path`.
50
+ - **Resilient Connections**: Both host and client get a 2-minute rejoin window if their IP changes or connection drops. PTY state is fully preserved — no session restart needed.
51
+ - **Multi-Session**: Manage multiple simultaneous sessions with the interactive CLI menu.
52
+
53
+ ## How It Works
54
+
55
+ 1. **Host** registers a session on the relay and gets a 6-digit code
56
+ 2. **Client** opens the web UI and enters the code
57
+ 3. Both sides perform an **ECDH key exchange** to derive a shared AES-256-GCM secret
58
+ 4. All terminal I/O is encrypted end-to-end — the relay only forwards opaque blobs
59
+ 5. If both peers are on the same LAN, a **WebRTC DataChannel** is established to bypass the relay entirely
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "peer-term",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "Share your terminal instantly using a 6-digit code. Encrypted. No config.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/index.js CHANGED
@@ -602,8 +602,15 @@ class Session {
602
602
 
603
603
  this.log(`⏳ Reconnect attempt ${attempts}/${maxAttempts}...`);
604
604
 
605
+ // Close any in-flight reconnect socket from the previous tick
606
+ if (this._pendingReconnectWs) {
607
+ try { this._pendingReconnectWs.removeAllListeners(); this._pendingReconnectWs.close(); } catch {}
608
+ this._pendingReconnectWs = null;
609
+ }
610
+
605
611
  try {
606
612
  const newWs = new WebSocket(this.relayUrl);
613
+ this._pendingReconnectWs = newWs;
607
614
 
608
615
  newWs.on('open', () => {
609
616
  newWs.send(JSON.stringify({ type: 'host-rejoin', code: this.code }));
@@ -619,6 +626,7 @@ class Session {
619
626
 
620
627
  if (msg.type === 'rejoined') {
621
628
  // Success — replace the old ws with this new one
629
+ this._pendingReconnectWs = null;
622
630
  this.ws = newWs;
623
631
  this._stopReconnecting();
624
632
  this.log(`✅ Reconnected. Session restored.`);
@@ -632,6 +640,7 @@ class Session {
632
640
  }
633
641
  } else if (msg.type === 'error') {
634
642
  this.log(`Rejoin failed: ${msg.msg}`);
643
+ this._pendingReconnectWs = null;
635
644
  this._stopReconnecting();
636
645
  this.destroy();
637
646
  try { newWs.close(); } catch {}
@@ -640,11 +649,13 @@ class Session {
640
649
 
641
650
  newWs.on('error', () => {
642
651
  // Connection failed, next retry in 5s
652
+ if (this._pendingReconnectWs === newWs) this._pendingReconnectWs = null;
643
653
  try { newWs.close(); } catch {}
644
654
  });
645
655
 
646
656
  newWs.on('close', () => {
647
- // If we haven't adopted this ws, nothing to do — retry will fire
657
+ // Clean up reference if this was the pending socket
658
+ if (this._pendingReconnectWs === newWs) this._pendingReconnectWs = null;
648
659
  });
649
660
  } catch (e) {
650
661
  // Connection failed, next retry in 5s
@@ -657,6 +668,10 @@ class Session {
657
668
  clearInterval(this.reconnectTimer);
658
669
  this.reconnectTimer = null;
659
670
  }
671
+ if (this._pendingReconnectWs) {
672
+ try { this._pendingReconnectWs.removeAllListeners(); this._pendingReconnectWs.close(); } catch {}
673
+ this._pendingReconnectWs = null;
674
+ }
660
675
  }
661
676
 
662
677
  /**