termites 1.0.30 → 1.0.32

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 +25 -42
  2. package/package.json +1 -1
  3. package/server.js +85 -0
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Termites
2
2
 
3
- A web-based terminal with server-client architecture for remote shell access.
3
+ Local multi-terminal manager with web interface.
4
4
 
5
5
  ```bash
6
6
  npm install -g termites
@@ -9,58 +9,41 @@ npm install -g termites
9
9
  ## Architecture
10
10
 
11
11
  ```
12
- ┌─────────────────┐ WebSocket ┌─────────────────┐
13
- Client 1 │◄──────────────────►│
14
- (node-pty) │ │
15
- └─────────────────┘ Server
16
- │ (Web UI) │◄──► Browser
17
- ┌─────────────────┐ WebSocket
18
- Client 2 │◄──────────────────►│
19
- (node-pty) └─────────────────┘
20
- └─────────────────┘
21
- ```
22
-
23
- ## Installation
24
-
25
- ```bash
26
- npm install -g termites
12
+ ┌─────────────────────────────────────────────────────┐
13
+ Server
14
+ ┌─────────┐ ┌─────────┐ ┌─────────┐
15
+ Terminal│ │ Terminal│ │ Terminal│ ... │
16
+ (PTY) │ │ (PTY) │ │ (PTY) │ │
17
+ └─────────┘ └─────────┘ └─────────┘
18
+
19
+ WebSocket + HTTP
20
+ └─────────────────────┼───────────────────────────────┘
21
+
22
+
23
+ Browser
27
24
  ```
28
25
 
29
26
  ## Usage
30
27
 
31
- ### Start Server
32
-
33
28
  ```bash
34
- termites server
35
- ```
29
+ # Start server (default port 6789)
30
+ termites
36
31
 
37
- The server starts on port 6789 by default. Access the web UI at `http://localhost:6789`.
38
-
39
- To use a different port:
40
-
41
- ```bash
42
- PORT=8080 termites server
32
+ # Or with custom port
33
+ PORT=8080 termites
43
34
  ```
44
35
 
45
- ### Connect Client
46
-
47
- ```bash
48
- # Connect to localhost:6789
49
- termites client
50
-
51
- # Connect to a remote server
52
- termites client 192.168.1.100:6789
53
- termites client ws://example.com:6789
54
- ```
36
+ Open browser at `http://localhost:6789`
55
37
 
56
38
  ## Features
57
39
 
58
- - Multi-client support - manage multiple shell sessions from one web interface
59
- - Mobile-friendly UI with collapsible drawer menu
60
- - Multiple color themes (Solarized, Monokai, Dracula, Nord, GitHub Dark)
61
- - Customizable font family and size
62
- - Auto-reconnect for clients
63
- - Output buffering for terminal history
40
+ - **Multi-terminal** - Create and manage multiple terminals from one web interface
41
+ - **Auto hostname detection** - Panel updates automatically when SSH to different machines
42
+ - **Mobile-friendly** - Touch support, virtual keyboard buttons, long-press to select
43
+ - **History scrollback** - iTerm-like history overlay (scroll up or tap Hist button)
44
+ - **Themes** - Solarized Light/Dark, Monokai, Dracula, Nord, GitHub Dark
45
+ - **Customizable** - Font family, font size, toolbar visibility
46
+ - **Password protection** - Set password on first visit
64
47
 
65
48
  ## License
66
49
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "termites",
3
- "version": "1.0.30",
3
+ "version": "1.0.32",
4
4
  "description": "Local multi-terminal manager with web interface",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/server.js CHANGED
@@ -132,6 +132,53 @@ class TermitesServer {
132
132
  if (terminal.outputBuffer.length > 1000) {
133
133
  terminal.outputBuffer = terminal.outputBuffer.slice(-500);
134
134
  }
135
+
136
+ // Parse terminal output to detect user@host changes (e.g., after SSH)
137
+ // Method 1: OSC 0 sequences (window title)
138
+ const oscMatch = data.match(/\x1b\]0;([^\x07\x1b]+)[\x07\x1b]/);
139
+ if (oscMatch) {
140
+ const title = oscMatch[1];
141
+ const match = title.match(/^([^@]+)@([^:]+)(?::(.*))?$/);
142
+ if (match) {
143
+ this.updateTerminalInfo(terminalId, terminal, match[1], match[2], match[3]);
144
+ }
145
+ }
146
+
147
+ // Method 2: Parse shell prompt patterns (works without any config)
148
+ // Look for the LAST (most recent) prompt pattern in output
149
+ const lines = data.split(/\r?\n/);
150
+ let lastMatch = null;
151
+ for (const line of lines) {
152
+ // Strip ANSI escape codes for matching
153
+ const cleanLine = line.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '').replace(/\x1b\][^\x07]*\x07/g, '');
154
+
155
+ // Pattern 1: user@host format (e.g., "user@host:path$", "[user@host path]$")
156
+ const userHostMatch = cleanLine.match(/[\[\s]?([a-zA-Z0-9_-]+)@([a-zA-Z0-9_.-]+)[\s:\]]/);
157
+ if (userHostMatch) {
158
+ const [, username, hostname] = userHostMatch;
159
+ if (hostname && !hostname.includes('/') && hostname.length < 64) {
160
+ lastMatch = { username, hostname };
161
+ }
162
+ }
163
+
164
+ // Pattern 2: hostname:path format (e.g., "seis10:/gds/zhfu[22] > ")
165
+ const hostPathMatch = cleanLine.match(/^([a-zA-Z0-9_-]+):([\/~][^\s\[]*)/);
166
+ if (hostPathMatch) {
167
+ const [, hostname, cwd] = hostPathMatch;
168
+ if (hostname && hostname.length < 64) {
169
+ lastMatch = { hostname, cwd, keepUsername: true };
170
+ }
171
+ }
172
+ }
173
+ // Use only the last (most recent) match
174
+ if (lastMatch) {
175
+ if (lastMatch.keepUsername) {
176
+ this.updateTerminalInfo(terminalId, terminal, null, lastMatch.hostname, lastMatch.cwd);
177
+ } else {
178
+ this.updateTerminalInfo(terminalId, terminal, lastMatch.username, lastMatch.hostname);
179
+ }
180
+ }
181
+
135
182
  this.broadcastToBrowsers({
136
183
  type: 'output',
137
184
  clientId: terminalId,
@@ -168,6 +215,32 @@ class TermitesServer {
168
215
  }
169
216
  }
170
217
 
218
+ // Update terminal info (user@host) when detected from output
219
+ updateTerminalInfo(terminalId, terminal, username, hostname, cwd) {
220
+ let infoChanged = false;
221
+ if (username && terminal.info.username !== username) {
222
+ terminal.info.username = username;
223
+ infoChanged = true;
224
+ }
225
+ if (hostname && terminal.info.hostname !== hostname) {
226
+ terminal.info.hostname = hostname;
227
+ infoChanged = true;
228
+ }
229
+ if (cwd !== undefined && terminal.info.cwd !== cwd) {
230
+ terminal.info.cwd = cwd || '~';
231
+ infoChanged = true;
232
+ }
233
+ if (infoChanged) {
234
+ console.log(`Terminal ${terminalId} info updated: ${username}@${hostname}`);
235
+ this.broadcastToBrowsers({
236
+ type: 'client-info-updated',
237
+ clientId: terminalId,
238
+ info: terminal.info
239
+ });
240
+ this.broadcastTerminalList();
241
+ }
242
+ }
243
+
171
244
  // Handle browser connections
172
245
  handleBrowserConnection(ws) {
173
246
  console.log('New browser connected');
@@ -1567,6 +1640,18 @@ class TermitesServer {
1567
1640
  if (clients.length > 0) selectClient(clients[0].id);
1568
1641
  }
1569
1642
  break;
1643
+ case 'client-info-updated':
1644
+ const clientToUpdate = clients.find(c => c.id === d.clientId);
1645
+ if (clientToUpdate && d.info) {
1646
+ Object.assign(clientToUpdate, d.info);
1647
+ updateClientList();
1648
+ if (selectedClientId === d.clientId) {
1649
+ document.getElementById('header-title').innerHTML =
1650
+ '<span class="user">' + d.info.username + '</span>' +
1651
+ '<span class="sep">@</span><span class="host">' + d.info.hostname + '</span>';
1652
+ }
1653
+ }
1654
+ break;
1570
1655
  case 'output':
1571
1656
  if (d.clientId === selectedClientId) {
1572
1657
  addToHistory(d.data);