peer-term 1.4.5 → 1.4.7

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
@@ -37,10 +37,38 @@ peer-term --path . # starts in current directory
37
37
  peer-term --readonly # view-only session
38
38
  peer-term --secure # require fingerprint verification
39
39
  peer-term --expiry 10m # custom expiry time
40
+ peer-term --rejoin 5m # custom reconnection window
40
41
  peer-term --relay wss://custom # use a custom relay server
41
42
  peer-term --verbose # enable debug logging
42
43
  ```
43
44
 
45
+ ### CLI Highlights
46
+
47
+ | Flag | Description | Default |
48
+ | :--- | :--- | :--- |
49
+ | `--expiry <time>` | Session expiry duration (e.g. `5m`, `30s`, `1h`) | `5m` |
50
+ | `--rejoin <time>` | Reconnection window if connection drops (e.g. `2m`, `10m`) | `2m` |
51
+ | `--readonly` | Disable client keystrokes (view-only terminal mode) | Off |
52
+ | `--secure` | Enable fingerprint verification for MITM protection | Off |
53
+ | `--path <dir>` | Starting directory for the terminal session | `~` (Home) |
54
+ | `--relay <url>` | Custom relay server WebSocket URL(s) | `wss://relay.dhananjaybalekar.in` |
55
+ | `-V, --verbose` | Enable verbose debug logging | Off |
56
+ | `-h, --help` | Display usage information and exit | — |
57
+ | `-v, --version` | Output version number and exit | — |
58
+
59
+ ### Interactive Controls
60
+
61
+ While running, you can manage sessions directly from the host CLI:
62
+
63
+ | Command | Description |
64
+ | :--- | :--- |
65
+ | `n` | Create a new session |
66
+ | `l` | List all active sessions |
67
+ | `a <code>` | Authorize verified fingerprint (in `--secure` mode) |
68
+ | `k <code>` | Kill a specific session |
69
+ | `q` | Quit all sessions and exit |
70
+
71
+
44
72
  ## Features
45
73
 
46
74
  - **No Config**: Works instantly. No port forwarding or firewall configuration needed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "peer-term",
3
- "version": "1.4.5",
3
+ "version": "1.4.7",
4
4
  "license": "MIT",
5
5
  "description": "Share your terminal instantly using a 6-digit code or QR code. Encrypted. No config.",
6
6
  "repository": {
package/src/cli.js CHANGED
@@ -4,13 +4,13 @@
4
4
  * Top-level orchestration:
5
5
  * 1. Detects shell, resolves start path, runs interactive prompts
6
6
  * 2. Creates a SessionManager and starts the first session
7
- * 3. Provides a readline-based menu (n/l/k/a/q) for managing sessions
7
+ * 3. Provides a readline-based menu (n/l/r/k/a/q) for managing sessions
8
8
  * 4. Handles SIGINT and global error handlers
9
9
  */
10
10
 
11
11
  import readline from 'readline';
12
12
  import logger from './logger.js';
13
- import { printBanner } from './ui.js';
13
+ import { printBanner, printCommandsMenu } from './ui.js';
14
14
  import { argv, RELAY_URLS } from './config.js';
15
15
  import { formatDuration, detectShell, resolveStartPath } from './utils.js';
16
16
  import { getExpiryInteractive, getRejoinInteractive } from './prompts.js';
@@ -50,14 +50,7 @@ async function main() {
50
50
  }
51
51
 
52
52
  // ─── Interactive CLI menu ──────────────────────────────────────────
53
- console.log(' ─────────────────────────────────────────');
54
- console.log(' Commands:');
55
- console.log(' [n] New session');
56
- console.log(' [l] List sessions');
57
- console.log(' [a <code>] Authorize verified fingerprint');
58
- console.log(' [k <code>] Kill session');
59
- console.log(' [q] Quit all');
60
- console.log(' ─────────────────────────────────────────');
53
+ printCommandsMenu();
61
54
  console.log('');
62
55
 
63
56
  const rl = readline.createInterface({
@@ -76,6 +69,13 @@ async function main() {
76
69
  await manager.createSession();
77
70
  } else if (input === 'l') {
78
71
  manager.listSessions();
72
+ } else if (input.startsWith('r ') || input.startsWith('rt ') || input === 'r' || input === 'rt') {
73
+ const code = input.startsWith('rt ')
74
+ ? input.slice(3).trim()
75
+ : input.startsWith('r ')
76
+ ? input.slice(2).trim()
77
+ : '';
78
+ await manager.toggleReadonlySession(code);
79
79
  } else if (input.startsWith('a ')) {
80
80
  const code = input.slice(2).trim();
81
81
  await manager.authorizeSession(code);
@@ -88,7 +88,7 @@ async function main() {
88
88
  rl.close();
89
89
  process.exit(0);
90
90
  } else if (input) {
91
- console.log(' Unknown command. Use n, l, a <code>, k <code>, or q.');
91
+ console.log(' Unknown command. Use n, l, r <code>, a <code>, k <code>, or q.');
92
92
  }
93
93
 
94
94
  rl.prompt();
package/src/index.js CHANGED
@@ -46,7 +46,7 @@ import { ViewerServer } from './viewer.js';
46
46
 
47
47
  // ─── Session Class ───────────────────────────────────────────────────────────
48
48
 
49
- class Session {
49
+ export class Session {
50
50
  constructor(shell, expiryMs, rejoinMs, readOnly, startPath, onDestroy) {
51
51
  this.shell = shell;
52
52
  this.expiryMs = expiryMs;
@@ -92,6 +92,25 @@ class Session {
92
92
  this.sessionDurationInterval = null; // Interval: periodic CLI status log
93
93
  }
94
94
 
95
+ get readonly() {
96
+ return this.readOnly;
97
+ }
98
+
99
+ set readonly(val) {
100
+ this.readOnly = Boolean(val);
101
+ }
102
+
103
+ async toggleReadonly() {
104
+ this.readonly = !this.readonly;
105
+ const modeStr = this.readonly ? 'READ-ONLY' : 'INTERACTIVE (write enabled)';
106
+ const codeLabel = this.code ? `Session ${this.code}` : 'Session';
107
+ this.log(`${codeLabel} is now ${modeStr}`);
108
+ if (this.sharedKey) {
109
+ await this._sendSessionConfig();
110
+ }
111
+ return this.readonly;
112
+ }
113
+
95
114
  /**
96
115
  * Build the context object for FileTransferHandler.
97
116
  * Called on construction and whenever shared key changes.
@@ -233,18 +252,18 @@ class Session {
233
252
  }
234
253
 
235
254
  async _sendSessionConfig() {
236
- if (!this.sharedKey || !this.ws || this.ws.readyState !== WebSocket.OPEN) return;
255
+ if (!this.sharedKey) return;
237
256
 
238
257
  const config = JSON.stringify({
239
258
  type: 'session-config',
240
- readonly: this.readOnly,
259
+ readonly: this.readonly,
241
260
  version: version,
242
261
  shell: this.shell,
243
262
  startPath: this.startPath,
244
263
  });
245
264
  try {
246
265
  const payload = await encrypt(this.sharedKey, config);
247
- this.ws.send(JSON.stringify({ type: 'data', payload }));
266
+ this._sendEncryptedToClient(payload);
248
267
  } catch (err) {
249
268
  this.log(`Failed to send session config: ${err.message}`);
250
269
  }
@@ -941,8 +960,8 @@ class Session {
941
960
  } else {
942
961
  status = `waiting for client (expires in ${formatDuration(remaining)})`;
943
962
  }
944
- if (this.readOnly) status += ' (readonly)';
945
- return status;
963
+ const mode = this.readonly ? '[READ-ONLY]' : '[INTERACTIVE]';
964
+ return `${status} ${mode}`;
946
965
  }
947
966
 
948
967
  // ─── Session Duration Logging ────────────────────────────────────────
@@ -1034,6 +1053,19 @@ export class SessionManager {
1034
1053
  await session.authorizeFingerprint();
1035
1054
  }
1036
1055
 
1056
+ async toggleReadonlySession(code) {
1057
+ const session = this.sessions.get(code);
1058
+ if (!session) {
1059
+ logger.warn(`Session ${code} not found.`);
1060
+ return;
1061
+ }
1062
+ await session.toggleReadonly();
1063
+ }
1064
+
1065
+ async toggleReadonly(code) {
1066
+ return this.toggleReadonlySession(code);
1067
+ }
1068
+
1037
1069
  killAll() {
1038
1070
  for (const [, session] of this.sessions) {
1039
1071
  session.destroy();
package/src/ui.js CHANGED
@@ -164,3 +164,21 @@ export function printVersion() {
164
164
  console.log('peer-term (version unknown)');
165
165
  }
166
166
  }
167
+
168
+ // ─── Commands Menu ───────────────────────────────────────────────────────────
169
+
170
+ /**
171
+ * Print the interactive CLI commands menu.
172
+ */
173
+ export function printCommandsMenu() {
174
+ console.log(' ─────────────────────────────────────────');
175
+ console.log(' Commands:');
176
+ console.log(' [n] New session');
177
+ console.log(' [l] List sessions');
178
+ console.log(' [r <code>] Toggle read-only mode');
179
+ console.log(' [a <code>] Authorize verified fingerprint');
180
+ console.log(' [k <code>] Kill session');
181
+ console.log(' [q] Quit all');
182
+ console.log(' ─────────────────────────────────────────');
183
+ }
184
+