lit-shell.js 1.1.0 → 1.2.0
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/CHANGELOG.md +24 -0
- package/README.md +189 -62
- package/dist/client/browser-bundle.js +61 -1
- package/dist/client/browser-bundle.js.map +3 -3
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/terminal-client.d.ts +19 -0
- package/dist/client/terminal-client.d.ts.map +1 -1
- package/dist/client/terminal-client.js +61 -0
- package/dist/client/terminal-client.js.map +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/session-manager.d.ts +6 -0
- package/dist/server/session-manager.d.ts.map +1 -1
- package/dist/server/session-manager.js +12 -2
- package/dist/server/session-manager.js.map +1 -1
- package/dist/server/terminal-server.d.ts.map +1 -1
- package/dist/server/terminal-server.js +23 -4
- package/dist/server/terminal-server.js.map +1 -1
- package/dist/shared/types.d.ts +6 -0
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/ui/browser-bundle.js +1625 -96
- package/dist/ui/browser-bundle.js.map +4 -4
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +1 -0
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/lit-shell-terminal.d.ts +225 -6
- package/dist/ui/lit-shell-terminal.d.ts.map +1 -1
- package/dist/ui/lit-shell-terminal.js +1605 -60
- package/dist/ui/lit-shell-terminal.js.map +1 -1
- package/dist/ui/styles.d.ts.map +1 -1
- package/dist/ui/styles.js +22 -0
- package/dist/ui/styles.js.map +1 -1
- package/dist/version.d.ts +6 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +6 -0
- package/dist/version.js.map +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.2.0] - 2025-02-01
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Mobile Support**: Auto-detect mobile devices and show touch keyboard
|
|
13
|
+
- Termux-style layout: ESC, arrows, HOME/END, PGUP/PGDN, TAB
|
|
14
|
+
- Sticky CTRL and ALT modifiers
|
|
15
|
+
- Collapsible extra row with common control sequences (^C, ^D, ^Z, ^L, ^A, ^E, ^R)
|
|
16
|
+
- Hide/show toggle for entire keyboard
|
|
17
|
+
- Responsive viewport detection with media query listener
|
|
18
|
+
- **Reconnect Dialog**: After WebSocket reconnection, shows dialog to rejoin previous session
|
|
19
|
+
- Saves session ID on disconnect
|
|
20
|
+
- Checks if session still exists after reconnect
|
|
21
|
+
- Option to rejoin with history or start fresh
|
|
22
|
+
- **Session Persistence Options**: Connection panel now includes
|
|
23
|
+
- Configurable orphan timeout (1 min to 1 week)
|
|
24
|
+
- Tmux integration checkbox for permanent persistence
|
|
25
|
+
- **Updated Font Stack**: Terminal now uses Cascadia Mono as primary font for better cross-platform consistency
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- Improved README documentation with all new features
|
|
30
|
+
- Updated feature list to include Docker, multiplexing, mobile, and persistence
|
|
31
|
+
|
|
8
32
|
## [1.1.0] - 2025-01-09
|
|
9
33
|
|
|
10
34
|
### Added
|
package/README.md
CHANGED
|
@@ -9,8 +9,13 @@ A plug-and-play terminal solution for web applications. Includes a server compon
|
|
|
9
9
|
- **Server**: WebSocket server with node-pty for real shell sessions
|
|
10
10
|
- **Client**: Lightweight WebSocket client with auto-reconnection
|
|
11
11
|
- **UI**: `<lit-shell-terminal>` Lit web component with xterm.js
|
|
12
|
+
- **Docker Support**: Execute commands in Docker containers (exec or attach mode)
|
|
13
|
+
- **Session Multiplexing**: Multiple clients can share the same terminal session
|
|
14
|
+
- **Session Persistence**: Orphan timeout and tmux integration for long-running sessions
|
|
15
|
+
- **Mobile Support**: Touch keyboard with Termux-style layout for mobile devices
|
|
16
|
+
- **Multi-Tab**: Support for multiple terminal tabs in one component
|
|
12
17
|
- **Themes**: Built-in dark/light/auto theme support
|
|
13
|
-
- **Security**: Configurable shell and
|
|
18
|
+
- **Security**: Configurable shell, path, and container allowlists
|
|
14
19
|
- **Framework Agnostic**: Works with React, Vue, Angular, Svelte, or vanilla JS
|
|
15
20
|
|
|
16
21
|
## Installation
|
|
@@ -51,10 +56,7 @@ server.listen(3000, () => {
|
|
|
51
56
|
### Client Usage (Web Component)
|
|
52
57
|
|
|
53
58
|
```html
|
|
54
|
-
<!-- Load xterm.js
|
|
55
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css">
|
|
56
|
-
|
|
57
|
-
<!-- Load lit-shell.js UI bundle -->
|
|
59
|
+
<!-- Load lit-shell.js UI bundle (includes xterm.js) -->
|
|
58
60
|
<script type="module" src="https://unpkg.com/lit-shell.js/dist/ui/browser-bundle.js"></script>
|
|
59
61
|
|
|
60
62
|
<!-- Use the component -->
|
|
@@ -94,6 +96,97 @@ client.write('ls -la\n');
|
|
|
94
96
|
client.resize(120, 40);
|
|
95
97
|
```
|
|
96
98
|
|
|
99
|
+
## Docker Support
|
|
100
|
+
|
|
101
|
+
lit-shell supports executing commands inside Docker containers via two modes:
|
|
102
|
+
|
|
103
|
+
### Docker Exec Mode
|
|
104
|
+
|
|
105
|
+
Spawns a new shell inside a running container:
|
|
106
|
+
|
|
107
|
+
```javascript
|
|
108
|
+
const terminalServer = new TerminalServer({
|
|
109
|
+
allowDockerExec: true,
|
|
110
|
+
allowedContainerPatterns: ['my-app-.*', 'dev-container'],
|
|
111
|
+
defaultContainerShell: '/bin/bash',
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Client-side
|
|
115
|
+
await client.spawn({
|
|
116
|
+
container: 'my-container',
|
|
117
|
+
containerShell: '/bin/bash',
|
|
118
|
+
containerUser: 'node',
|
|
119
|
+
containerCwd: '/app',
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Docker Attach Mode
|
|
124
|
+
|
|
125
|
+
Attaches to a container's main process (stdin/stdout):
|
|
126
|
+
|
|
127
|
+
```javascript
|
|
128
|
+
await client.spawn({
|
|
129
|
+
container: 'my-container',
|
|
130
|
+
attachMode: true,
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Session Multiplexing
|
|
135
|
+
|
|
136
|
+
Multiple clients can share the same terminal session:
|
|
137
|
+
|
|
138
|
+
```javascript
|
|
139
|
+
// First client spawns a session
|
|
140
|
+
const session = await client1.spawn({ shell: '/bin/bash' });
|
|
141
|
+
|
|
142
|
+
// Second client joins the same session
|
|
143
|
+
const shared = await client2.join({
|
|
144
|
+
sessionId: session.sessionId,
|
|
145
|
+
requestHistory: true, // Replay terminal history
|
|
146
|
+
historyLimit: 10000,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Both clients now see the same terminal
|
|
150
|
+
// Either can type, both see the output
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Session List
|
|
154
|
+
|
|
155
|
+
```javascript
|
|
156
|
+
// Get available sessions
|
|
157
|
+
client.onSessionList((sessions) => {
|
|
158
|
+
sessions.forEach(s => {
|
|
159
|
+
console.log(`${s.sessionId}: ${s.shell} (${s.clientCount} clients)`);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
client.requestSessionList();
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Session Persistence
|
|
167
|
+
|
|
168
|
+
### Orphan Timeout
|
|
169
|
+
|
|
170
|
+
Sessions can persist after all clients disconnect:
|
|
171
|
+
|
|
172
|
+
```javascript
|
|
173
|
+
await client.spawn({
|
|
174
|
+
shell: '/bin/bash',
|
|
175
|
+
orphanTimeout: 3600000, // Keep alive 1 hour after last client leaves
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Tmux Integration
|
|
180
|
+
|
|
181
|
+
For permanent session persistence:
|
|
182
|
+
|
|
183
|
+
```javascript
|
|
184
|
+
await client.spawn({
|
|
185
|
+
shell: '/bin/bash',
|
|
186
|
+
useTmux: true, // Session persists indefinitely in tmux
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
97
190
|
## API Reference
|
|
98
191
|
|
|
99
192
|
### Server
|
|
@@ -104,28 +197,31 @@ client.resize(120, 40);
|
|
|
104
197
|
import { TerminalServer } from 'lit-shell.js/server';
|
|
105
198
|
|
|
106
199
|
const server = new TerminalServer({
|
|
107
|
-
//
|
|
108
|
-
allowedShells: ['/bin/bash', '/bin/zsh'
|
|
109
|
-
|
|
110
|
-
// Allowed working directories (empty = all allowed)
|
|
111
|
-
allowedPaths: ['/home/user', '/var/www'],
|
|
112
|
-
|
|
113
|
-
// Default shell if not specified
|
|
200
|
+
// Shell configuration
|
|
201
|
+
allowedShells: ['/bin/bash', '/bin/zsh'],
|
|
114
202
|
defaultShell: '/bin/bash',
|
|
115
203
|
|
|
116
|
-
//
|
|
204
|
+
// Path restrictions
|
|
205
|
+
allowedPaths: ['/home/user', '/var/www'],
|
|
117
206
|
defaultCwd: '/home/user',
|
|
118
207
|
|
|
119
|
-
//
|
|
120
|
-
|
|
208
|
+
// Docker configuration
|
|
209
|
+
allowDockerExec: false,
|
|
210
|
+
allowedContainerPatterns: ['.*'],
|
|
211
|
+
defaultContainerShell: '/bin/sh',
|
|
121
212
|
|
|
122
|
-
//
|
|
213
|
+
// Session limits
|
|
214
|
+
maxSessionsPerClient: 5,
|
|
123
215
|
idleTimeout: 30 * 60 * 1000,
|
|
124
216
|
|
|
125
|
-
//
|
|
217
|
+
// History
|
|
218
|
+
historyEnabled: true,
|
|
219
|
+
historySize: 50000,
|
|
220
|
+
|
|
221
|
+
// WebSocket path
|
|
126
222
|
path: '/terminal',
|
|
127
223
|
|
|
128
|
-
//
|
|
224
|
+
// Logging
|
|
129
225
|
verbose: false,
|
|
130
226
|
});
|
|
131
227
|
|
|
@@ -134,12 +230,6 @@ server.attach(httpServer);
|
|
|
134
230
|
|
|
135
231
|
// Or start standalone
|
|
136
232
|
server.listen(3001);
|
|
137
|
-
|
|
138
|
-
// Get active sessions
|
|
139
|
-
const sessions = server.getSessions();
|
|
140
|
-
|
|
141
|
-
// Close server
|
|
142
|
-
server.close();
|
|
143
233
|
```
|
|
144
234
|
|
|
145
235
|
### Client
|
|
@@ -151,48 +241,52 @@ import { TerminalClient } from 'lit-shell.js/client';
|
|
|
151
241
|
|
|
152
242
|
const client = new TerminalClient({
|
|
153
243
|
url: 'ws://localhost:3000/terminal',
|
|
154
|
-
reconnect: true,
|
|
155
|
-
maxReconnectAttempts: 10,
|
|
156
|
-
reconnectDelay: 1000,
|
|
244
|
+
reconnect: true,
|
|
245
|
+
maxReconnectAttempts: 10,
|
|
246
|
+
reconnectDelay: 1000,
|
|
157
247
|
});
|
|
158
248
|
|
|
159
|
-
// Connect
|
|
249
|
+
// Connect
|
|
160
250
|
await client.connect();
|
|
161
251
|
|
|
162
|
-
// Spawn
|
|
163
|
-
const
|
|
252
|
+
// Spawn session
|
|
253
|
+
const session = await client.spawn({
|
|
164
254
|
shell: '/bin/bash',
|
|
165
255
|
cwd: '/home/user',
|
|
166
256
|
env: { TERM: 'xterm-256color' },
|
|
167
257
|
cols: 80,
|
|
168
258
|
rows: 24,
|
|
259
|
+
container: 'optional-container-name',
|
|
260
|
+
orphanTimeout: 3600000,
|
|
261
|
+
useTmux: false,
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// Join existing session
|
|
265
|
+
const shared = await client.join({
|
|
266
|
+
sessionId: 'session-id',
|
|
267
|
+
requestHistory: true,
|
|
268
|
+
historyLimit: 50000,
|
|
169
269
|
});
|
|
170
270
|
|
|
171
|
-
//
|
|
172
|
-
client.
|
|
271
|
+
// Leave session (without killing it)
|
|
272
|
+
client.leave(sessionId);
|
|
173
273
|
|
|
174
|
-
//
|
|
274
|
+
// Terminal I/O
|
|
275
|
+
client.write('echo "Hello"\n');
|
|
175
276
|
client.resize(120, 40);
|
|
176
|
-
|
|
177
|
-
// Kill session
|
|
178
277
|
client.kill();
|
|
179
278
|
|
|
180
|
-
// Disconnect
|
|
181
|
-
client.disconnect();
|
|
182
|
-
|
|
183
279
|
// Event handlers
|
|
184
|
-
client.onConnect(() =>
|
|
185
|
-
client.onDisconnect(() =>
|
|
186
|
-
client.onData((data) =>
|
|
187
|
-
client.onExit((code) =>
|
|
188
|
-
client.onError((err) =>
|
|
189
|
-
client.onSpawned((info) =>
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
client.
|
|
193
|
-
client.
|
|
194
|
-
client.getSessionId(); // string | null
|
|
195
|
-
client.getSessionInfo(); // SessionInfo | null
|
|
280
|
+
client.onConnect(() => {});
|
|
281
|
+
client.onDisconnect(() => {});
|
|
282
|
+
client.onData((data) => {});
|
|
283
|
+
client.onExit((code) => {});
|
|
284
|
+
client.onError((err) => {});
|
|
285
|
+
client.onSpawned((info) => {});
|
|
286
|
+
client.onSessionList((sessions) => {});
|
|
287
|
+
client.onClientJoined((sessionId, count) => {});
|
|
288
|
+
client.onClientLeft((sessionId, count) => {});
|
|
289
|
+
client.onReconnectWithSession((sessionId) => {});
|
|
196
290
|
```
|
|
197
291
|
|
|
198
292
|
### UI Component
|
|
@@ -206,12 +300,14 @@ client.getSessionInfo(); // SessionInfo | null
|
|
|
206
300
|
cwd="/home/user"
|
|
207
301
|
theme="dark"
|
|
208
302
|
font-size="14"
|
|
209
|
-
font-family="Menlo, Monaco, monospace"
|
|
210
303
|
cols="80"
|
|
211
304
|
rows="24"
|
|
212
305
|
auto-connect
|
|
213
306
|
auto-spawn
|
|
214
|
-
|
|
307
|
+
show-connection-panel
|
|
308
|
+
show-status-bar
|
|
309
|
+
show-tabs
|
|
310
|
+
show-settings
|
|
215
311
|
></lit-shell-terminal>
|
|
216
312
|
```
|
|
217
313
|
|
|
@@ -222,28 +318,40 @@ client.getSessionInfo(); // SessionInfo | null
|
|
|
222
318
|
| `url` | string | `''` | WebSocket URL |
|
|
223
319
|
| `shell` | string | `''` | Shell to use |
|
|
224
320
|
| `cwd` | string | `''` | Working directory |
|
|
321
|
+
| `container` | string | `''` | Docker container name |
|
|
225
322
|
| `theme` | `'dark'` \| `'light'` \| `'auto'` | `'dark'` | Color theme |
|
|
226
323
|
| `font-size` | number | `14` | Terminal font size |
|
|
227
|
-
| `font-family` | string | `'
|
|
324
|
+
| `font-family` | string | `'Cascadia Mono, ...'` | Terminal font |
|
|
228
325
|
| `cols` | number | `80` | Initial columns |
|
|
229
326
|
| `rows` | number | `24` | Initial rows |
|
|
230
327
|
| `auto-connect` | boolean | `false` | Connect on mount |
|
|
231
328
|
| `auto-spawn` | boolean | `false` | Spawn on connect |
|
|
232
329
|
| `no-header` | boolean | `false` | Hide header bar |
|
|
330
|
+
| `show-connection-panel` | boolean | `false` | Show connection options |
|
|
331
|
+
| `show-status-bar` | boolean | `false` | Show status bar |
|
|
332
|
+
| `show-tabs` | boolean | `false` | Enable multi-tab mode |
|
|
333
|
+
| `show-settings` | boolean | `false` | Show settings menu |
|
|
233
334
|
|
|
234
335
|
**Methods:**
|
|
235
336
|
|
|
236
337
|
```javascript
|
|
237
338
|
const terminal = document.querySelector('lit-shell-terminal');
|
|
238
339
|
|
|
239
|
-
await terminal.connect();
|
|
240
|
-
terminal.disconnect();
|
|
241
|
-
await terminal.spawn();
|
|
242
|
-
terminal.
|
|
243
|
-
terminal.
|
|
244
|
-
terminal.
|
|
245
|
-
terminal.
|
|
246
|
-
terminal.
|
|
340
|
+
await terminal.connect();
|
|
341
|
+
terminal.disconnect();
|
|
342
|
+
await terminal.spawn(options);
|
|
343
|
+
await terminal.join(sessionId);
|
|
344
|
+
terminal.leave();
|
|
345
|
+
terminal.kill();
|
|
346
|
+
terminal.clear();
|
|
347
|
+
terminal.write('text');
|
|
348
|
+
terminal.writeln('line');
|
|
349
|
+
terminal.focus();
|
|
350
|
+
|
|
351
|
+
// Tab management (when show-tabs is enabled)
|
|
352
|
+
terminal.createTab('Label');
|
|
353
|
+
terminal.switchTab(tabId);
|
|
354
|
+
terminal.closeTab(tabId);
|
|
247
355
|
```
|
|
248
356
|
|
|
249
357
|
**Events:**
|
|
@@ -254,8 +362,23 @@ terminal.addEventListener('disconnect', () => {});
|
|
|
254
362
|
terminal.addEventListener('spawned', (e) => console.log(e.detail.session));
|
|
255
363
|
terminal.addEventListener('exit', (e) => console.log(e.detail.exitCode));
|
|
256
364
|
terminal.addEventListener('error', (e) => console.log(e.detail.error));
|
|
365
|
+
terminal.addEventListener('theme-change', (e) => console.log(e.detail.theme));
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
## Mobile Support
|
|
369
|
+
|
|
370
|
+
On mobile devices, lit-shell automatically shows a touch keyboard with common terminal keys:
|
|
371
|
+
|
|
372
|
+
```
|
|
373
|
+
Row 1: [ESC] [/] [-] [HOME] [↑] [END] [PGUP]
|
|
374
|
+
Row 2: [TAB] [CTRL] [ALT] [←] [↓] [→] [PGDN]
|
|
375
|
+
Row 3: [^C] [^D] [^Z] [^L] [^A] [^E] [^R] (expandable)
|
|
257
376
|
```
|
|
258
377
|
|
|
378
|
+
- CTRL and ALT are sticky modifiers (tap to activate, applies to next key)
|
|
379
|
+
- Collapsible extra row for common control sequences
|
|
380
|
+
- Hide/show toggle for the entire keyboard
|
|
381
|
+
|
|
259
382
|
## Theming
|
|
260
383
|
|
|
261
384
|
The component uses CSS custom properties for theming:
|
|
@@ -291,11 +414,15 @@ const server = new TerminalServer({
|
|
|
291
414
|
// Restrict working directories
|
|
292
415
|
allowedPaths: ['/home/app', '/var/www'],
|
|
293
416
|
|
|
417
|
+
// Restrict Docker containers
|
|
418
|
+
allowDockerExec: true,
|
|
419
|
+
allowedContainerPatterns: ['^myapp-'],
|
|
420
|
+
|
|
294
421
|
// Limit sessions per client
|
|
295
422
|
maxSessionsPerClient: 2,
|
|
296
423
|
|
|
297
424
|
// Set idle timeout
|
|
298
|
-
idleTimeout: 10 * 60 * 1000,
|
|
425
|
+
idleTimeout: 10 * 60 * 1000,
|
|
299
426
|
});
|
|
300
427
|
```
|
|
301
428
|
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// src/version.ts
|
|
2
|
+
var VERSION = "1.2.0";
|
|
3
|
+
|
|
1
4
|
// src/client/terminal-client.ts
|
|
2
5
|
var TerminalClient = class {
|
|
3
6
|
constructor(config) {
|
|
@@ -8,6 +11,8 @@ var TerminalClient = class {
|
|
|
8
11
|
this.serverInfo = null;
|
|
9
12
|
this.reconnectAttempts = 0;
|
|
10
13
|
this.reconnectTimeout = null;
|
|
14
|
+
this.previousSessionId = null;
|
|
15
|
+
this.isReconnecting = false;
|
|
11
16
|
// Event handlers
|
|
12
17
|
this.connectHandlers = [];
|
|
13
18
|
this.disconnectHandlers = [];
|
|
@@ -24,6 +29,7 @@ var TerminalClient = class {
|
|
|
24
29
|
this.clientJoinedHandlers = [];
|
|
25
30
|
this.clientLeftHandlers = [];
|
|
26
31
|
this.sessionClosedHandlers = [];
|
|
32
|
+
this.reconnectWithSessionHandlers = [];
|
|
27
33
|
// Promise resolvers for spawn/join
|
|
28
34
|
this.spawnResolve = null;
|
|
29
35
|
this.spawnReject = null;
|
|
@@ -58,17 +64,25 @@ var TerminalClient = class {
|
|
|
58
64
|
this.state = "connected";
|
|
59
65
|
this.reconnectAttempts = 0;
|
|
60
66
|
this.connectHandlers.forEach((handler) => handler());
|
|
67
|
+
if (this.isReconnecting && this.previousSessionId) {
|
|
68
|
+
this.checkPreviousSessionAndNotify();
|
|
69
|
+
}
|
|
70
|
+
this.isReconnecting = false;
|
|
61
71
|
resolve();
|
|
62
72
|
};
|
|
63
73
|
this.ws.onclose = () => {
|
|
64
74
|
const wasConnected = this.state === "connected";
|
|
65
75
|
this.state = "disconnected";
|
|
76
|
+
if (this.sessionId) {
|
|
77
|
+
this.previousSessionId = this.sessionId;
|
|
78
|
+
}
|
|
66
79
|
this.sessionId = null;
|
|
67
80
|
this.sessionInfo = null;
|
|
68
81
|
if (wasConnected) {
|
|
69
82
|
this.disconnectHandlers.forEach((handler) => handler());
|
|
70
83
|
}
|
|
71
84
|
if (this.config.reconnect && this.reconnectAttempts < this.config.maxReconnectAttempts) {
|
|
85
|
+
this.isReconnecting = true;
|
|
72
86
|
this.scheduleReconnect();
|
|
73
87
|
}
|
|
74
88
|
};
|
|
@@ -543,8 +557,54 @@ var TerminalClient = class {
|
|
|
543
557
|
getServerInfo() {
|
|
544
558
|
return this.serverInfo;
|
|
545
559
|
}
|
|
560
|
+
/**
|
|
561
|
+
* Get previous session ID (available after disconnect)
|
|
562
|
+
*/
|
|
563
|
+
getPreviousSessionId() {
|
|
564
|
+
return this.previousSessionId;
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Clear previous session ID (call after user declines to rejoin)
|
|
568
|
+
*/
|
|
569
|
+
clearPreviousSessionId() {
|
|
570
|
+
this.previousSessionId = null;
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Called when reconnected and previous session is available
|
|
574
|
+
*/
|
|
575
|
+
onReconnectWithSession(handler) {
|
|
576
|
+
this.reconnectWithSessionHandlers.push(handler);
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Check if previous session exists and notify handlers
|
|
580
|
+
*/
|
|
581
|
+
async checkPreviousSessionAndNotify() {
|
|
582
|
+
if (!this.previousSessionId)
|
|
583
|
+
return;
|
|
584
|
+
try {
|
|
585
|
+
const sessions = await this.listSessions();
|
|
586
|
+
const previousSession = sessions.find(
|
|
587
|
+
(s) => s.sessionId === this.previousSessionId
|
|
588
|
+
);
|
|
589
|
+
if (previousSession && previousSession.accepting) {
|
|
590
|
+
this.reconnectWithSessionHandlers.forEach((handler) => {
|
|
591
|
+
try {
|
|
592
|
+
handler(this.previousSessionId);
|
|
593
|
+
} catch (e) {
|
|
594
|
+
console.error("[lit-shell] Error in reconnectWithSession handler:", e);
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
} else {
|
|
598
|
+
this.previousSessionId = null;
|
|
599
|
+
}
|
|
600
|
+
} catch (e) {
|
|
601
|
+
console.error("[lit-shell] Failed to check previous session:", e);
|
|
602
|
+
this.previousSessionId = null;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
546
605
|
};
|
|
547
606
|
export {
|
|
548
|
-
TerminalClient
|
|
607
|
+
TerminalClient,
|
|
608
|
+
VERSION
|
|
549
609
|
};
|
|
550
610
|
//# sourceMappingURL=browser-bundle.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../src/client/terminal-client.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Terminal client for connecting to lit-shell server\n *\n * Example usage:\n * ```typescript\n * import { TerminalClient } from 'lit-shell.js/client';\n *\n * const client = new TerminalClient({ url: 'ws://localhost:3000/terminal' });\n * await client.connect();\n *\n * client.onData((data) => console.log(data));\n * client.onExit((code) => console.log('Exited with code:', code));\n *\n * await client.spawn({ shell: '/bin/bash', cwd: '/home/user' });\n * client.write('ls -la\\n');\n * client.resize(120, 40);\n * client.kill();\n *\n * // Session multiplexing example\n * const sessions = await client.listSessions();\n * if (sessions.length > 0) {\n * await client.join({ sessionId: sessions[0].sessionId, requestHistory: true });\n * }\n * ```\n */\n\nimport type {\n ClientConfig,\n TerminalOptions,\n TerminalMessage,\n SessionInfo,\n ContainerInfo,\n ServerInfo,\n SharedSessionInfo,\n SessionListFilter,\n JoinSessionOptions,\n} from '../shared/types.js';\n\n/**\n * Connection state\n */\nexport type ConnectionState = 'disconnected' | 'connecting' | 'connected';\n\n/**\n * Terminal client class with session multiplexing support\n */\nexport class TerminalClient {\n private config: Required<ClientConfig>;\n private ws: WebSocket | null = null;\n private state: ConnectionState = 'disconnected';\n private sessionId: string | null = null;\n private sessionInfo: SessionInfo | null = null;\n private serverInfo: ServerInfo | null = null;\n private reconnectAttempts = 0;\n private reconnectTimeout: ReturnType<typeof setTimeout> | null = null;\n\n // Event handlers\n private connectHandlers: (() => void)[] = [];\n private disconnectHandlers: (() => void)[] = [];\n private dataHandlers: ((data: string) => void)[] = [];\n private exitHandlers: ((code: number) => void)[] = [];\n private errorHandlers: ((error: Error) => void)[] = [];\n private spawnedHandlers: ((info: SessionInfo) => void)[] = [];\n private serverInfoHandlers: ((info: ServerInfo) => void)[] = [];\n private containerListHandlers: ((containers: ContainerInfo[]) => void)[] = [];\n // Session multiplexing handlers\n private sessionListHandlers: ((sessions: SharedSessionInfo[]) => void)[] = [];\n private joinedHandlers: ((session: SharedSessionInfo, history?: string) => void)[] = [];\n private leftHandlers: ((sessionId: string) => void)[] = [];\n private clientJoinedHandlers: ((sessionId: string, clientCount: number) => void)[] = [];\n private clientLeftHandlers: ((sessionId: string, clientCount: number) => void)[] = [];\n private sessionClosedHandlers: ((sessionId: string, reason: string) => void)[] = [];\n\n // Promise resolvers for spawn/join\n private spawnResolve: ((info: SessionInfo) => void) | null = null;\n private spawnReject: ((error: Error) => void) | null = null;\n private joinResolve: ((info: SharedSessionInfo) => void) | null = null;\n private joinReject: ((error: Error) => void) | null = null;\n private listSessionsResolve: ((sessions: SharedSessionInfo[]) => void) | null = null;\n\n constructor(config: ClientConfig) {\n this.config = {\n url: config.url,\n reconnect: config.reconnect ?? true,\n maxReconnectAttempts: config.maxReconnectAttempts ?? 10,\n reconnectDelay: config.reconnectDelay ?? 1000,\n };\n }\n\n /**\n * Connect to the terminal server\n */\n connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.state === 'connected') {\n resolve();\n return;\n }\n\n this.state = 'connecting';\n\n try {\n this.ws = new WebSocket(this.config.url);\n } catch (error) {\n this.state = 'disconnected';\n reject(error);\n return;\n }\n\n this.ws.onopen = () => {\n this.state = 'connected';\n this.reconnectAttempts = 0;\n this.connectHandlers.forEach((handler) => handler());\n resolve();\n };\n\n this.ws.onclose = () => {\n const wasConnected = this.state === 'connected';\n this.state = 'disconnected';\n this.sessionId = null;\n this.sessionInfo = null;\n\n if (wasConnected) {\n this.disconnectHandlers.forEach((handler) => handler());\n }\n\n // Attempt reconnection\n if (this.config.reconnect && this.reconnectAttempts < this.config.maxReconnectAttempts) {\n this.scheduleReconnect();\n }\n };\n\n this.ws.onerror = (event) => {\n const error = new Error('WebSocket error');\n this.errorHandlers.forEach((handler) => handler(error));\n\n if (this.state === 'connecting') {\n reject(error);\n }\n };\n\n this.ws.onmessage = (event) => {\n this.handleMessage(event.data);\n };\n });\n }\n\n /**\n * Disconnect from the terminal server\n */\n disconnect(): void {\n this.config.reconnect = false; // Prevent auto-reconnect\n\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\n\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n\n this.state = 'disconnected';\n this.sessionId = null;\n this.sessionInfo = null;\n }\n\n /**\n * Schedule a reconnection attempt\n */\n private scheduleReconnect(): void {\n if (this.reconnectTimeout) return;\n\n const delay = this.config.reconnectDelay * Math.pow(2, this.reconnectAttempts);\n const maxDelay = 30000; // 30 seconds max\n\n this.reconnectTimeout = setTimeout(() => {\n this.reconnectTimeout = null;\n this.reconnectAttempts++;\n this.connect().catch(() => {\n // Error handled by onclose\n });\n }, Math.min(delay, maxDelay));\n }\n\n /**\n * Handle incoming message\n */\n private handleMessage(data: string): void {\n let message: TerminalMessage;\n\n try {\n message = JSON.parse(data);\n } catch {\n console.error('[lit-shell] Invalid message:', data);\n return;\n }\n\n switch (message.type) {\n case 'spawned':\n this.sessionId = message.sessionId;\n this.sessionInfo = {\n sessionId: message.sessionId,\n shell: message.shell,\n cwd: message.cwd,\n cols: message.cols,\n rows: message.rows,\n createdAt: new Date(),\n container: message.container,\n };\n this.spawnedHandlers.forEach((handler) => handler(this.sessionInfo!));\n if (this.spawnResolve) {\n this.spawnResolve(this.sessionInfo);\n this.spawnResolve = null;\n this.spawnReject = null;\n }\n break;\n\n case 'data':\n this.dataHandlers.forEach((handler) => handler(message.data));\n break;\n\n case 'exit':\n const exitCode = message.exitCode;\n this.exitHandlers.forEach((handler) => handler(exitCode));\n this.sessionId = null;\n this.sessionInfo = null;\n break;\n\n case 'error':\n const error = new Error(message.error);\n this.errorHandlers.forEach((handler) => handler(error));\n if (this.spawnReject) {\n this.spawnReject(error);\n this.spawnResolve = null;\n this.spawnReject = null;\n }\n if (this.joinReject) {\n this.joinReject(error);\n this.joinResolve = null;\n this.joinReject = null;\n }\n break;\n\n case 'serverInfo':\n this.serverInfo = message.info;\n this.serverInfoHandlers.forEach((handler) => handler(message.info));\n break;\n\n case 'containerList':\n this.containerListHandlers.forEach((handler) => handler(message.containers));\n break;\n\n // Session multiplexing messages\n case 'sessionList':\n this.sessionListHandlers.forEach((handler) =>\n handler((message as any).sessions)\n );\n if (this.listSessionsResolve) {\n this.listSessionsResolve((message as any).sessions);\n this.listSessionsResolve = null;\n }\n break;\n\n case 'joined':\n const joinedSession = (message as any).session as SharedSessionInfo;\n const history = (message as any).history as string | undefined;\n this.sessionId = message.sessionId!;\n this.sessionInfo = {\n sessionId: joinedSession.sessionId,\n shell: joinedSession.shell,\n cwd: joinedSession.cwd,\n cols: joinedSession.cols,\n rows: joinedSession.rows,\n createdAt: joinedSession.createdAt,\n container: joinedSession.container,\n };\n this.joinedHandlers.forEach((handler) => handler(joinedSession, history));\n if (this.joinResolve) {\n this.joinResolve(joinedSession);\n this.joinResolve = null;\n this.joinReject = null;\n }\n break;\n\n case 'left':\n const leftSessionId = message.sessionId!;\n if (this.sessionId === leftSessionId) {\n this.sessionId = null;\n this.sessionInfo = null;\n }\n this.leftHandlers.forEach((handler) => handler(leftSessionId));\n break;\n\n case 'clientJoined':\n this.clientJoinedHandlers.forEach((handler) =>\n handler(message.sessionId!, (message as any).clientCount)\n );\n break;\n\n case 'clientLeft':\n this.clientLeftHandlers.forEach((handler) =>\n handler(message.sessionId!, (message as any).clientCount)\n );\n break;\n\n case 'sessionClosed':\n const closedSessionId = message.sessionId!;\n const reason = (message as any).reason as string;\n if (this.sessionId === closedSessionId) {\n this.sessionId = null;\n this.sessionInfo = null;\n }\n this.sessionClosedHandlers.forEach((handler) =>\n handler(closedSessionId, reason)\n );\n break;\n }\n }\n\n /**\n * Spawn a terminal session\n */\n spawn(options: TerminalOptions = {}): Promise<SessionInfo> {\n return new Promise((resolve, reject) => {\n if (this.state !== 'connected' || !this.ws) {\n reject(new Error('Not connected to server'));\n return;\n }\n\n if (this.sessionId) {\n reject(new Error('Session already active. Call kill() or leave() first.'));\n return;\n }\n\n this.spawnResolve = resolve;\n this.spawnReject = reject;\n\n this.ws.send(\n JSON.stringify({\n type: 'spawn',\n options,\n })\n );\n });\n }\n\n /**\n * Write data to the terminal\n */\n write(data: string): void {\n if (!this.ws || this.state !== 'connected') {\n console.error('[lit-shell] Cannot write: not connected');\n return;\n }\n\n if (!this.sessionId) {\n console.error('[lit-shell] Cannot write: no active session');\n return;\n }\n\n this.ws.send(\n JSON.stringify({\n type: 'data',\n sessionId: this.sessionId,\n data,\n })\n );\n }\n\n /**\n * Resize the terminal\n */\n resize(cols: number, rows: number): void {\n if (!this.ws || this.state !== 'connected') {\n console.error('[lit-shell] Cannot resize: not connected');\n return;\n }\n\n if (!this.sessionId) {\n console.error('[lit-shell] Cannot resize: no active session');\n return;\n }\n\n this.ws.send(\n JSON.stringify({\n type: 'resize',\n sessionId: this.sessionId,\n cols,\n rows,\n })\n );\n }\n\n /**\n * Kill the terminal session (close and terminate)\n */\n kill(): void {\n if (!this.ws || this.state !== 'connected') {\n return;\n }\n\n if (!this.sessionId) {\n return;\n }\n\n this.ws.send(\n JSON.stringify({\n type: 'close',\n sessionId: this.sessionId,\n })\n );\n\n this.sessionId = null;\n this.sessionInfo = null;\n }\n\n // ==========================================\n // Session Multiplexing Methods\n // ==========================================\n\n /**\n * List available sessions\n */\n listSessions(filter?: SessionListFilter): Promise<SharedSessionInfo[]> {\n return new Promise((resolve, reject) => {\n if (this.state !== 'connected' || !this.ws) {\n reject(new Error('Not connected to server'));\n return;\n }\n\n this.listSessionsResolve = resolve;\n\n this.ws.send(\n JSON.stringify({\n type: 'listSessions',\n filter,\n })\n );\n });\n }\n\n /**\n * Join an existing session\n */\n join(options: JoinSessionOptions): Promise<SharedSessionInfo> {\n return new Promise((resolve, reject) => {\n if (this.state !== 'connected' || !this.ws) {\n reject(new Error('Not connected to server'));\n return;\n }\n\n if (this.sessionId) {\n reject(new Error('Already in a session. Call leave() first.'));\n return;\n }\n\n this.joinResolve = resolve;\n this.joinReject = reject;\n\n this.ws.send(\n JSON.stringify({\n type: 'join',\n options,\n })\n );\n });\n }\n\n /**\n * Leave the current session without killing it\n */\n leave(sessionId?: string): void {\n if (!this.ws || this.state !== 'connected') {\n console.error('[lit-shell] Cannot leave: not connected');\n return;\n }\n\n const targetSession = sessionId || this.sessionId;\n if (!targetSession) {\n console.error('[lit-shell] Cannot leave: no active session');\n return;\n }\n\n this.ws.send(\n JSON.stringify({\n type: 'leave',\n sessionId: targetSession,\n })\n );\n\n if (targetSession === this.sessionId) {\n this.sessionId = null;\n this.sessionInfo = null;\n }\n }\n\n /**\n * Request session list and trigger onSessionList handlers\n * (Fire-and-forget version of listSessions)\n */\n requestSessionList(filter?: SessionListFilter): void {\n this.listSessions(filter)\n .then((sessions) => {\n this.sessionListHandlers.forEach((handler) => {\n try {\n handler(sessions);\n } catch (e) {\n console.error('[lit-shell] Error in sessionList handler:', e);\n }\n });\n })\n .catch((err) => {\n console.error('[lit-shell] Failed to list sessions:', err);\n });\n }\n\n // ==========================================\n // Event handlers\n // ==========================================\n\n /**\n * Called when connected to server\n */\n onConnect(handler: () => void): void {\n this.connectHandlers.push(handler);\n }\n\n /**\n * Called when disconnected from server\n */\n onDisconnect(handler: () => void): void {\n this.disconnectHandlers.push(handler);\n }\n\n /**\n * Called when data is received from the terminal\n */\n onData(handler: (data: string) => void): void {\n this.dataHandlers.push(handler);\n }\n\n /**\n * Called when the terminal session exits\n */\n onExit(handler: (code: number) => void): void {\n this.exitHandlers.push(handler);\n }\n\n /**\n * Called when an error occurs\n */\n onError(handler: (error: Error) => void): void {\n this.errorHandlers.push(handler);\n }\n\n /**\n * Called when a session is spawned\n */\n onSpawned(handler: (info: SessionInfo) => void): void {\n this.spawnedHandlers.push(handler);\n }\n\n /**\n * Called when server info is received\n */\n onServerInfo(handler: (info: ServerInfo) => void): void {\n this.serverInfoHandlers.push(handler);\n // If we already have server info, call immediately\n if (this.serverInfo) {\n handler(this.serverInfo);\n }\n }\n\n /**\n * Called when container list is received\n */\n onContainerList(handler: (containers: ContainerInfo[]) => void): void {\n this.containerListHandlers.push(handler);\n }\n\n /**\n * Called when session list is received\n */\n onSessionList(handler: (sessions: SharedSessionInfo[]) => void): void {\n this.sessionListHandlers.push(handler);\n }\n\n /**\n * Called when successfully joined a session\n */\n onJoined(handler: (session: SharedSessionInfo, history?: string) => void): void {\n this.joinedHandlers.push(handler);\n }\n\n /**\n * Called when left a session\n */\n onLeft(handler: (sessionId: string) => void): void {\n this.leftHandlers.push(handler);\n }\n\n /**\n * Called when another client joins the current session\n */\n onClientJoined(handler: (sessionId: string, clientCount: number) => void): void {\n this.clientJoinedHandlers.push(handler);\n }\n\n /**\n * Called when another client leaves the current session\n */\n onClientLeft(handler: (sessionId: string, clientCount: number) => void): void {\n this.clientLeftHandlers.push(handler);\n }\n\n /**\n * Called when the session is closed by owner or orphan timeout\n */\n onSessionClosed(handler: (sessionId: string, reason: string) => void): void {\n this.sessionClosedHandlers.push(handler);\n }\n\n /**\n * Request list of available containers\n */\n requestContainerList(): void {\n if (!this.ws || this.state !== 'connected') {\n console.error('[lit-shell] Cannot request containers: not connected');\n return;\n }\n\n this.ws.send(JSON.stringify({ type: 'listContainers' }));\n }\n\n // ==========================================\n // Getters\n // ==========================================\n\n /**\n * Get current connection state\n */\n getState(): ConnectionState {\n return this.state;\n }\n\n /**\n * Check if connected\n */\n isConnected(): boolean {\n return this.state === 'connected';\n }\n\n /**\n * Get current session ID\n */\n getSessionId(): string | null {\n return this.sessionId;\n }\n\n /**\n * Get current session info\n */\n getSessionInfo(): SessionInfo | null {\n return this.sessionInfo;\n }\n\n /**\n * Check if a session is active\n */\n hasActiveSession(): boolean {\n return this.sessionId !== null;\n }\n\n /**\n * Get server info\n */\n getServerInfo(): ServerInfo | null {\n return this.serverInfo;\n }\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
3
|
+
"sources": ["../../src/version.ts", "../../src/client/terminal-client.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * lit-shell.js version\n * This is automatically kept in sync with package.json\n */\nexport const VERSION = '1.2.0';\n", "/**\n * Terminal client for connecting to lit-shell server\n *\n * Example usage:\n * ```typescript\n * import { TerminalClient } from 'lit-shell.js/client';\n *\n * const client = new TerminalClient({ url: 'ws://localhost:3000/terminal' });\n * await client.connect();\n *\n * client.onData((data) => console.log(data));\n * client.onExit((code) => console.log('Exited with code:', code));\n *\n * await client.spawn({ shell: '/bin/bash', cwd: '/home/user' });\n * client.write('ls -la\\n');\n * client.resize(120, 40);\n * client.kill();\n *\n * // Session multiplexing example\n * const sessions = await client.listSessions();\n * if (sessions.length > 0) {\n * await client.join({ sessionId: sessions[0].sessionId, requestHistory: true });\n * }\n * ```\n */\n\nimport type {\n ClientConfig,\n TerminalOptions,\n TerminalMessage,\n SessionInfo,\n ContainerInfo,\n ServerInfo,\n SharedSessionInfo,\n SessionListFilter,\n JoinSessionOptions,\n} from '../shared/types.js';\n\n/**\n * Connection state\n */\nexport type ConnectionState = 'disconnected' | 'connecting' | 'connected';\n\n/**\n * Terminal client class with session multiplexing support\n */\nexport class TerminalClient {\n private config: Required<ClientConfig>;\n private ws: WebSocket | null = null;\n private state: ConnectionState = 'disconnected';\n private sessionId: string | null = null;\n private sessionInfo: SessionInfo | null = null;\n private serverInfo: ServerInfo | null = null;\n private reconnectAttempts = 0;\n private reconnectTimeout: ReturnType<typeof setTimeout> | null = null;\n private previousSessionId: string | null = null;\n private isReconnecting = false;\n\n // Event handlers\n private connectHandlers: (() => void)[] = [];\n private disconnectHandlers: (() => void)[] = [];\n private dataHandlers: ((data: string) => void)[] = [];\n private exitHandlers: ((code: number) => void)[] = [];\n private errorHandlers: ((error: Error) => void)[] = [];\n private spawnedHandlers: ((info: SessionInfo) => void)[] = [];\n private serverInfoHandlers: ((info: ServerInfo) => void)[] = [];\n private containerListHandlers: ((containers: ContainerInfo[]) => void)[] = [];\n // Session multiplexing handlers\n private sessionListHandlers: ((sessions: SharedSessionInfo[]) => void)[] = [];\n private joinedHandlers: ((session: SharedSessionInfo, history?: string) => void)[] = [];\n private leftHandlers: ((sessionId: string) => void)[] = [];\n private clientJoinedHandlers: ((sessionId: string, clientCount: number) => void)[] = [];\n private clientLeftHandlers: ((sessionId: string, clientCount: number) => void)[] = [];\n private sessionClosedHandlers: ((sessionId: string, reason: string) => void)[] = [];\n private reconnectWithSessionHandlers: ((sessionId: string) => void)[] = [];\n\n // Promise resolvers for spawn/join\n private spawnResolve: ((info: SessionInfo) => void) | null = null;\n private spawnReject: ((error: Error) => void) | null = null;\n private joinResolve: ((info: SharedSessionInfo) => void) | null = null;\n private joinReject: ((error: Error) => void) | null = null;\n private listSessionsResolve: ((sessions: SharedSessionInfo[]) => void) | null = null;\n\n constructor(config: ClientConfig) {\n this.config = {\n url: config.url,\n reconnect: config.reconnect ?? true,\n maxReconnectAttempts: config.maxReconnectAttempts ?? 10,\n reconnectDelay: config.reconnectDelay ?? 1000,\n };\n }\n\n /**\n * Connect to the terminal server\n */\n connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.state === 'connected') {\n resolve();\n return;\n }\n\n this.state = 'connecting';\n\n try {\n this.ws = new WebSocket(this.config.url);\n } catch (error) {\n this.state = 'disconnected';\n reject(error);\n return;\n }\n\n this.ws.onopen = () => {\n this.state = 'connected';\n this.reconnectAttempts = 0;\n this.connectHandlers.forEach((handler) => handler());\n\n // Check for previous session on reconnect\n if (this.isReconnecting && this.previousSessionId) {\n this.checkPreviousSessionAndNotify();\n }\n this.isReconnecting = false;\n\n resolve();\n };\n\n this.ws.onclose = () => {\n const wasConnected = this.state === 'connected';\n this.state = 'disconnected';\n\n // Save previous session ID before clearing (for reconnect dialog)\n if (this.sessionId) {\n this.previousSessionId = this.sessionId;\n }\n this.sessionId = null;\n this.sessionInfo = null;\n\n if (wasConnected) {\n this.disconnectHandlers.forEach((handler) => handler());\n }\n\n // Attempt reconnection\n if (this.config.reconnect && this.reconnectAttempts < this.config.maxReconnectAttempts) {\n this.isReconnecting = true;\n this.scheduleReconnect();\n }\n };\n\n this.ws.onerror = (event) => {\n const error = new Error('WebSocket error');\n this.errorHandlers.forEach((handler) => handler(error));\n\n if (this.state === 'connecting') {\n reject(error);\n }\n };\n\n this.ws.onmessage = (event) => {\n this.handleMessage(event.data);\n };\n });\n }\n\n /**\n * Disconnect from the terminal server\n */\n disconnect(): void {\n this.config.reconnect = false; // Prevent auto-reconnect\n\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\n\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n\n this.state = 'disconnected';\n this.sessionId = null;\n this.sessionInfo = null;\n }\n\n /**\n * Schedule a reconnection attempt\n */\n private scheduleReconnect(): void {\n if (this.reconnectTimeout) return;\n\n const delay = this.config.reconnectDelay * Math.pow(2, this.reconnectAttempts);\n const maxDelay = 30000; // 30 seconds max\n\n this.reconnectTimeout = setTimeout(() => {\n this.reconnectTimeout = null;\n this.reconnectAttempts++;\n this.connect().catch(() => {\n // Error handled by onclose\n });\n }, Math.min(delay, maxDelay));\n }\n\n /**\n * Handle incoming message\n */\n private handleMessage(data: string): void {\n let message: TerminalMessage;\n\n try {\n message = JSON.parse(data);\n } catch {\n console.error('[lit-shell] Invalid message:', data);\n return;\n }\n\n switch (message.type) {\n case 'spawned':\n this.sessionId = message.sessionId;\n this.sessionInfo = {\n sessionId: message.sessionId,\n shell: message.shell,\n cwd: message.cwd,\n cols: message.cols,\n rows: message.rows,\n createdAt: new Date(),\n container: message.container,\n };\n this.spawnedHandlers.forEach((handler) => handler(this.sessionInfo!));\n if (this.spawnResolve) {\n this.spawnResolve(this.sessionInfo);\n this.spawnResolve = null;\n this.spawnReject = null;\n }\n break;\n\n case 'data':\n this.dataHandlers.forEach((handler) => handler(message.data));\n break;\n\n case 'exit':\n const exitCode = message.exitCode;\n this.exitHandlers.forEach((handler) => handler(exitCode));\n this.sessionId = null;\n this.sessionInfo = null;\n break;\n\n case 'error':\n const error = new Error(message.error);\n this.errorHandlers.forEach((handler) => handler(error));\n if (this.spawnReject) {\n this.spawnReject(error);\n this.spawnResolve = null;\n this.spawnReject = null;\n }\n if (this.joinReject) {\n this.joinReject(error);\n this.joinResolve = null;\n this.joinReject = null;\n }\n break;\n\n case 'serverInfo':\n this.serverInfo = message.info;\n this.serverInfoHandlers.forEach((handler) => handler(message.info));\n break;\n\n case 'containerList':\n this.containerListHandlers.forEach((handler) => handler(message.containers));\n break;\n\n // Session multiplexing messages\n case 'sessionList':\n this.sessionListHandlers.forEach((handler) =>\n handler((message as any).sessions)\n );\n if (this.listSessionsResolve) {\n this.listSessionsResolve((message as any).sessions);\n this.listSessionsResolve = null;\n }\n break;\n\n case 'joined':\n const joinedSession = (message as any).session as SharedSessionInfo;\n const history = (message as any).history as string | undefined;\n this.sessionId = message.sessionId!;\n this.sessionInfo = {\n sessionId: joinedSession.sessionId,\n shell: joinedSession.shell,\n cwd: joinedSession.cwd,\n cols: joinedSession.cols,\n rows: joinedSession.rows,\n createdAt: joinedSession.createdAt,\n container: joinedSession.container,\n };\n this.joinedHandlers.forEach((handler) => handler(joinedSession, history));\n if (this.joinResolve) {\n this.joinResolve(joinedSession);\n this.joinResolve = null;\n this.joinReject = null;\n }\n break;\n\n case 'left':\n const leftSessionId = message.sessionId!;\n if (this.sessionId === leftSessionId) {\n this.sessionId = null;\n this.sessionInfo = null;\n }\n this.leftHandlers.forEach((handler) => handler(leftSessionId));\n break;\n\n case 'clientJoined':\n this.clientJoinedHandlers.forEach((handler) =>\n handler(message.sessionId!, (message as any).clientCount)\n );\n break;\n\n case 'clientLeft':\n this.clientLeftHandlers.forEach((handler) =>\n handler(message.sessionId!, (message as any).clientCount)\n );\n break;\n\n case 'sessionClosed':\n const closedSessionId = message.sessionId!;\n const reason = (message as any).reason as string;\n if (this.sessionId === closedSessionId) {\n this.sessionId = null;\n this.sessionInfo = null;\n }\n this.sessionClosedHandlers.forEach((handler) =>\n handler(closedSessionId, reason)\n );\n break;\n }\n }\n\n /**\n * Spawn a terminal session\n */\n spawn(options: TerminalOptions = {}): Promise<SessionInfo> {\n return new Promise((resolve, reject) => {\n if (this.state !== 'connected' || !this.ws) {\n reject(new Error('Not connected to server'));\n return;\n }\n\n if (this.sessionId) {\n reject(new Error('Session already active. Call kill() or leave() first.'));\n return;\n }\n\n this.spawnResolve = resolve;\n this.spawnReject = reject;\n\n this.ws.send(\n JSON.stringify({\n type: 'spawn',\n options,\n })\n );\n });\n }\n\n /**\n * Write data to the terminal\n */\n write(data: string): void {\n if (!this.ws || this.state !== 'connected') {\n console.error('[lit-shell] Cannot write: not connected');\n return;\n }\n\n if (!this.sessionId) {\n console.error('[lit-shell] Cannot write: no active session');\n return;\n }\n\n this.ws.send(\n JSON.stringify({\n type: 'data',\n sessionId: this.sessionId,\n data,\n })\n );\n }\n\n /**\n * Resize the terminal\n */\n resize(cols: number, rows: number): void {\n if (!this.ws || this.state !== 'connected') {\n console.error('[lit-shell] Cannot resize: not connected');\n return;\n }\n\n if (!this.sessionId) {\n console.error('[lit-shell] Cannot resize: no active session');\n return;\n }\n\n this.ws.send(\n JSON.stringify({\n type: 'resize',\n sessionId: this.sessionId,\n cols,\n rows,\n })\n );\n }\n\n /**\n * Kill the terminal session (close and terminate)\n */\n kill(): void {\n if (!this.ws || this.state !== 'connected') {\n return;\n }\n\n if (!this.sessionId) {\n return;\n }\n\n this.ws.send(\n JSON.stringify({\n type: 'close',\n sessionId: this.sessionId,\n })\n );\n\n this.sessionId = null;\n this.sessionInfo = null;\n }\n\n // ==========================================\n // Session Multiplexing Methods\n // ==========================================\n\n /**\n * List available sessions\n */\n listSessions(filter?: SessionListFilter): Promise<SharedSessionInfo[]> {\n return new Promise((resolve, reject) => {\n if (this.state !== 'connected' || !this.ws) {\n reject(new Error('Not connected to server'));\n return;\n }\n\n this.listSessionsResolve = resolve;\n\n this.ws.send(\n JSON.stringify({\n type: 'listSessions',\n filter,\n })\n );\n });\n }\n\n /**\n * Join an existing session\n */\n join(options: JoinSessionOptions): Promise<SharedSessionInfo> {\n return new Promise((resolve, reject) => {\n if (this.state !== 'connected' || !this.ws) {\n reject(new Error('Not connected to server'));\n return;\n }\n\n if (this.sessionId) {\n reject(new Error('Already in a session. Call leave() first.'));\n return;\n }\n\n this.joinResolve = resolve;\n this.joinReject = reject;\n\n this.ws.send(\n JSON.stringify({\n type: 'join',\n options,\n })\n );\n });\n }\n\n /**\n * Leave the current session without killing it\n */\n leave(sessionId?: string): void {\n if (!this.ws || this.state !== 'connected') {\n console.error('[lit-shell] Cannot leave: not connected');\n return;\n }\n\n const targetSession = sessionId || this.sessionId;\n if (!targetSession) {\n console.error('[lit-shell] Cannot leave: no active session');\n return;\n }\n\n this.ws.send(\n JSON.stringify({\n type: 'leave',\n sessionId: targetSession,\n })\n );\n\n if (targetSession === this.sessionId) {\n this.sessionId = null;\n this.sessionInfo = null;\n }\n }\n\n /**\n * Request session list and trigger onSessionList handlers\n * (Fire-and-forget version of listSessions)\n */\n requestSessionList(filter?: SessionListFilter): void {\n this.listSessions(filter)\n .then((sessions) => {\n this.sessionListHandlers.forEach((handler) => {\n try {\n handler(sessions);\n } catch (e) {\n console.error('[lit-shell] Error in sessionList handler:', e);\n }\n });\n })\n .catch((err) => {\n console.error('[lit-shell] Failed to list sessions:', err);\n });\n }\n\n // ==========================================\n // Event handlers\n // ==========================================\n\n /**\n * Called when connected to server\n */\n onConnect(handler: () => void): void {\n this.connectHandlers.push(handler);\n }\n\n /**\n * Called when disconnected from server\n */\n onDisconnect(handler: () => void): void {\n this.disconnectHandlers.push(handler);\n }\n\n /**\n * Called when data is received from the terminal\n */\n onData(handler: (data: string) => void): void {\n this.dataHandlers.push(handler);\n }\n\n /**\n * Called when the terminal session exits\n */\n onExit(handler: (code: number) => void): void {\n this.exitHandlers.push(handler);\n }\n\n /**\n * Called when an error occurs\n */\n onError(handler: (error: Error) => void): void {\n this.errorHandlers.push(handler);\n }\n\n /**\n * Called when a session is spawned\n */\n onSpawned(handler: (info: SessionInfo) => void): void {\n this.spawnedHandlers.push(handler);\n }\n\n /**\n * Called when server info is received\n */\n onServerInfo(handler: (info: ServerInfo) => void): void {\n this.serverInfoHandlers.push(handler);\n // If we already have server info, call immediately\n if (this.serverInfo) {\n handler(this.serverInfo);\n }\n }\n\n /**\n * Called when container list is received\n */\n onContainerList(handler: (containers: ContainerInfo[]) => void): void {\n this.containerListHandlers.push(handler);\n }\n\n /**\n * Called when session list is received\n */\n onSessionList(handler: (sessions: SharedSessionInfo[]) => void): void {\n this.sessionListHandlers.push(handler);\n }\n\n /**\n * Called when successfully joined a session\n */\n onJoined(handler: (session: SharedSessionInfo, history?: string) => void): void {\n this.joinedHandlers.push(handler);\n }\n\n /**\n * Called when left a session\n */\n onLeft(handler: (sessionId: string) => void): void {\n this.leftHandlers.push(handler);\n }\n\n /**\n * Called when another client joins the current session\n */\n onClientJoined(handler: (sessionId: string, clientCount: number) => void): void {\n this.clientJoinedHandlers.push(handler);\n }\n\n /**\n * Called when another client leaves the current session\n */\n onClientLeft(handler: (sessionId: string, clientCount: number) => void): void {\n this.clientLeftHandlers.push(handler);\n }\n\n /**\n * Called when the session is closed by owner or orphan timeout\n */\n onSessionClosed(handler: (sessionId: string, reason: string) => void): void {\n this.sessionClosedHandlers.push(handler);\n }\n\n /**\n * Request list of available containers\n */\n requestContainerList(): void {\n if (!this.ws || this.state !== 'connected') {\n console.error('[lit-shell] Cannot request containers: not connected');\n return;\n }\n\n this.ws.send(JSON.stringify({ type: 'listContainers' }));\n }\n\n // ==========================================\n // Getters\n // ==========================================\n\n /**\n * Get current connection state\n */\n getState(): ConnectionState {\n return this.state;\n }\n\n /**\n * Check if connected\n */\n isConnected(): boolean {\n return this.state === 'connected';\n }\n\n /**\n * Get current session ID\n */\n getSessionId(): string | null {\n return this.sessionId;\n }\n\n /**\n * Get current session info\n */\n getSessionInfo(): SessionInfo | null {\n return this.sessionInfo;\n }\n\n /**\n * Check if a session is active\n */\n hasActiveSession(): boolean {\n return this.sessionId !== null;\n }\n\n /**\n * Get server info\n */\n getServerInfo(): ServerInfo | null {\n return this.serverInfo;\n }\n\n /**\n * Get previous session ID (available after disconnect)\n */\n getPreviousSessionId(): string | null {\n return this.previousSessionId;\n }\n\n /**\n * Clear previous session ID (call after user declines to rejoin)\n */\n clearPreviousSessionId(): void {\n this.previousSessionId = null;\n }\n\n /**\n * Called when reconnected and previous session is available\n */\n onReconnectWithSession(handler: (sessionId: string) => void): void {\n this.reconnectWithSessionHandlers.push(handler);\n }\n\n /**\n * Check if previous session exists and notify handlers\n */\n private async checkPreviousSessionAndNotify(): Promise<void> {\n if (!this.previousSessionId) return;\n\n try {\n const sessions = await this.listSessions();\n const previousSession = sessions.find(\n (s) => s.sessionId === this.previousSessionId\n );\n\n if (previousSession && previousSession.accepting) {\n // Previous session still exists and accepting clients\n this.reconnectWithSessionHandlers.forEach((handler) => {\n try {\n handler(this.previousSessionId!);\n } catch (e) {\n console.error('[lit-shell] Error in reconnectWithSession handler:', e);\n }\n });\n } else {\n // Session no longer exists or not accepting\n this.previousSessionId = null;\n }\n } catch (e) {\n console.error('[lit-shell] Failed to check previous session:', e);\n this.previousSessionId = null;\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* lit-shell.js client exports
|
|
3
3
|
*/
|
|
4
|
+
export { VERSION } from '../version.js';
|
|
4
5
|
export { TerminalClient } from './terminal-client.js';
|
|
5
6
|
export type { ConnectionState } from './terminal-client.js';
|
|
6
7
|
export type { ClientConfig, TerminalOptions, SessionInfo, SharedSessionInfo, SessionType, SessionListFilter, JoinSessionOptions, } from '../shared/types.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,YAAY,EACV,YAAY,EACZ,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,YAAY,EACV,YAAY,EACZ,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC"}
|