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.
Files changed (43) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +189 -62
  3. package/dist/client/browser-bundle.js +61 -1
  4. package/dist/client/browser-bundle.js.map +3 -3
  5. package/dist/client/index.d.ts +1 -0
  6. package/dist/client/index.d.ts.map +1 -1
  7. package/dist/client/index.js +1 -0
  8. package/dist/client/index.js.map +1 -1
  9. package/dist/client/terminal-client.d.ts +19 -0
  10. package/dist/client/terminal-client.d.ts.map +1 -1
  11. package/dist/client/terminal-client.js +61 -0
  12. package/dist/client/terminal-client.js.map +1 -1
  13. package/dist/server/index.d.ts +1 -0
  14. package/dist/server/index.d.ts.map +1 -1
  15. package/dist/server/index.js +1 -0
  16. package/dist/server/index.js.map +1 -1
  17. package/dist/server/session-manager.d.ts +6 -0
  18. package/dist/server/session-manager.d.ts.map +1 -1
  19. package/dist/server/session-manager.js +12 -2
  20. package/dist/server/session-manager.js.map +1 -1
  21. package/dist/server/terminal-server.d.ts.map +1 -1
  22. package/dist/server/terminal-server.js +23 -4
  23. package/dist/server/terminal-server.js.map +1 -1
  24. package/dist/shared/types.d.ts +6 -0
  25. package/dist/shared/types.d.ts.map +1 -1
  26. package/dist/ui/browser-bundle.js +1625 -96
  27. package/dist/ui/browser-bundle.js.map +4 -4
  28. package/dist/ui/index.d.ts +1 -0
  29. package/dist/ui/index.d.ts.map +1 -1
  30. package/dist/ui/index.js +1 -0
  31. package/dist/ui/index.js.map +1 -1
  32. package/dist/ui/lit-shell-terminal.d.ts +225 -6
  33. package/dist/ui/lit-shell-terminal.d.ts.map +1 -1
  34. package/dist/ui/lit-shell-terminal.js +1605 -60
  35. package/dist/ui/lit-shell-terminal.js.map +1 -1
  36. package/dist/ui/styles.d.ts.map +1 -1
  37. package/dist/ui/styles.js +22 -0
  38. package/dist/ui/styles.js.map +1 -1
  39. package/dist/version.d.ts +6 -0
  40. package/dist/version.d.ts.map +1 -0
  41. package/dist/version.js +6 -0
  42. package/dist/version.js.map +1 -0
  43. 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 path allowlists
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 CSS -->
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
- // Allowed shells (empty = all allowed)
108
- allowedShells: ['/bin/bash', '/bin/zsh', 'cmd.exe'],
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
- // Default working directory
204
+ // Path restrictions
205
+ allowedPaths: ['/home/user', '/var/www'],
117
206
  defaultCwd: '/home/user',
118
207
 
119
- // Max sessions per client (default: 5)
120
- maxSessionsPerClient: 5,
208
+ // Docker configuration
209
+ allowDockerExec: false,
210
+ allowedContainerPatterns: ['.*'],
211
+ defaultContainerShell: '/bin/sh',
121
212
 
122
- // Idle timeout in ms (default: 30 minutes, 0 = disabled)
213
+ // Session limits
214
+ maxSessionsPerClient: 5,
123
215
  idleTimeout: 30 * 60 * 1000,
124
216
 
125
- // WebSocket path (default: '/terminal')
217
+ // History
218
+ historyEnabled: true,
219
+ historySize: 50000,
220
+
221
+ // WebSocket path
126
222
  path: '/terminal',
127
223
 
128
- // Enable verbose logging
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, // Auto-reconnect (default: true)
155
- maxReconnectAttempts: 10, // Max attempts (default: 10)
156
- reconnectDelay: 1000, // Initial delay ms (default: 1000)
244
+ reconnect: true,
245
+ maxReconnectAttempts: 10,
246
+ reconnectDelay: 1000,
157
247
  });
158
248
 
159
- // Connect to server
249
+ // Connect
160
250
  await client.connect();
161
251
 
162
- // Spawn terminal session
163
- const sessionInfo = await client.spawn({
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
- // Write to terminal
172
- client.write('echo "Hello World"\n');
271
+ // Leave session (without killing it)
272
+ client.leave(sessionId);
173
273
 
174
- // Resize terminal
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(() => console.log('Connected'));
185
- client.onDisconnect(() => console.log('Disconnected'));
186
- client.onData((data) => console.log('Data:', data));
187
- client.onExit((code) => console.log('Exit:', code));
188
- client.onError((err) => console.log('Error:', err));
189
- client.onSpawned((info) => console.log('Spawned:', info));
190
-
191
- // State getters
192
- client.isConnected(); // boolean
193
- client.hasActiveSession(); // boolean
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
- no-header
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 | `'Menlo, Monaco, ...'` | Terminal font |
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(); // Connect to server
240
- terminal.disconnect(); // Disconnect
241
- await terminal.spawn(); // Spawn session
242
- terminal.kill(); // Kill session
243
- terminal.clear(); // Clear display
244
- terminal.write('text'); // Write to display
245
- terminal.writeln('line'); // Write line to display
246
- terminal.focus(); // Focus 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, // 10 minutes
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": ";AA8CO,IAAM,iBAAN,MAAqB;AAAA,EAkC1B,YAAY,QAAsB;AAhClC,SAAQ,KAAuB;AAC/B,SAAQ,QAAyB;AACjC,SAAQ,YAA2B;AACnC,SAAQ,cAAkC;AAC1C,SAAQ,aAAgC;AACxC,SAAQ,oBAAoB;AAC5B,SAAQ,mBAAyD;AAGjE;AAAA,SAAQ,kBAAkC,CAAC;AAC3C,SAAQ,qBAAqC,CAAC;AAC9C,SAAQ,eAA2C,CAAC;AACpD,SAAQ,eAA2C,CAAC;AACpD,SAAQ,gBAA4C,CAAC;AACrD,SAAQ,kBAAmD,CAAC;AAC5D,SAAQ,qBAAqD,CAAC;AAC9D,SAAQ,wBAAmE,CAAC;AAE5E;AAAA,SAAQ,sBAAmE,CAAC;AAC5E,SAAQ,iBAA6E,CAAC;AACtF,SAAQ,eAAgD,CAAC;AACzD,SAAQ,uBAA6E,CAAC;AACtF,SAAQ,qBAA2E,CAAC;AACpF,SAAQ,wBAAyE,CAAC;AAGlF;AAAA,SAAQ,eAAqD;AAC7D,SAAQ,cAA+C;AACvD,SAAQ,cAA0D;AAClE,SAAQ,aAA8C;AACtD,SAAQ,sBAAwE;AAG9E,SAAK,SAAS;AAAA,MACZ,KAAK,OAAO;AAAA,MACZ,WAAW,OAAO,aAAa;AAAA,MAC/B,sBAAsB,OAAO,wBAAwB;AAAA,MACrD,gBAAgB,OAAO,kBAAkB;AAAA,IAC3C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,UAAyB;AACvB,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,KAAK,UAAU,aAAa;AAC9B,gBAAQ;AACR;AAAA,MACF;AAEA,WAAK,QAAQ;AAEb,UAAI;AACF,aAAK,KAAK,IAAI,UAAU,KAAK,OAAO,GAAG;AAAA,MACzC,SAAS,OAAO;AACd,aAAK,QAAQ;AACb,eAAO,KAAK;AACZ;AAAA,MACF;AAEA,WAAK,GAAG,SAAS,MAAM;AACrB,aAAK,QAAQ;AACb,aAAK,oBAAoB;AACzB,aAAK,gBAAgB,QAAQ,CAAC,YAAY,QAAQ,CAAC;AACnD,gBAAQ;AAAA,MACV;AAEA,WAAK,GAAG,UAAU,MAAM;AACtB,cAAM,eAAe,KAAK,UAAU;AACpC,aAAK,QAAQ;AACb,aAAK,YAAY;AACjB,aAAK,cAAc;AAEnB,YAAI,cAAc;AAChB,eAAK,mBAAmB,QAAQ,CAAC,YAAY,QAAQ,CAAC;AAAA,QACxD;AAGA,YAAI,KAAK,OAAO,aAAa,KAAK,oBAAoB,KAAK,OAAO,sBAAsB;AACtF,eAAK,kBAAkB;AAAA,QACzB;AAAA,MACF;AAEA,WAAK,GAAG,UAAU,CAAC,UAAU;AAC3B,cAAM,QAAQ,IAAI,MAAM,iBAAiB;AACzC,aAAK,cAAc,QAAQ,CAAC,YAAY,QAAQ,KAAK,CAAC;AAEtD,YAAI,KAAK,UAAU,cAAc;AAC/B,iBAAO,KAAK;AAAA,QACd;AAAA,MACF;AAEA,WAAK,GAAG,YAAY,CAAC,UAAU;AAC7B,aAAK,cAAc,MAAM,IAAI;AAAA,MAC/B;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,aAAmB;AACjB,SAAK,OAAO,YAAY;AAExB,QAAI,KAAK,kBAAkB;AACzB,mBAAa,KAAK,gBAAgB;AAClC,WAAK,mBAAmB;AAAA,IAC1B;AAEA,QAAI,KAAK,IAAI;AACX,WAAK,GAAG,MAAM;AACd,WAAK,KAAK;AAAA,IACZ;AAEA,SAAK,QAAQ;AACb,SAAK,YAAY;AACjB,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA,EAKQ,oBAA0B;AAChC,QAAI,KAAK;AAAkB;AAE3B,UAAM,QAAQ,KAAK,OAAO,iBAAiB,KAAK,IAAI,GAAG,KAAK,iBAAiB;AAC7E,UAAM,WAAW;AAEjB,SAAK,mBAAmB,WAAW,MAAM;AACvC,WAAK,mBAAmB;AACxB,WAAK;AACL,WAAK,QAAQ,EAAE,MAAM,MAAM;AAAA,MAE3B,CAAC;AAAA,IACH,GAAG,KAAK,IAAI,OAAO,QAAQ,CAAC;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,MAAoB;AACxC,QAAI;AAEJ,QAAI;AACF,gBAAU,KAAK,MAAM,IAAI;AAAA,IAC3B,QAAQ;AACN,cAAQ,MAAM,gCAAgC,IAAI;AAClD;AAAA,IACF;AAEA,YAAQ,QAAQ,MAAM;AAAA,MACpB,KAAK;AACH,aAAK,YAAY,QAAQ;AACzB,aAAK,cAAc;AAAA,UACjB,WAAW,QAAQ;AAAA,UACnB,OAAO,QAAQ;AAAA,UACf,KAAK,QAAQ;AAAA,UACb,MAAM,QAAQ;AAAA,UACd,MAAM,QAAQ;AAAA,UACd,WAAW,oBAAI,KAAK;AAAA,UACpB,WAAW,QAAQ;AAAA,QACrB;AACA,aAAK,gBAAgB,QAAQ,CAAC,YAAY,QAAQ,KAAK,WAAY,CAAC;AACpE,YAAI,KAAK,cAAc;AACrB,eAAK,aAAa,KAAK,WAAW;AAClC,eAAK,eAAe;AACpB,eAAK,cAAc;AAAA,QACrB;AACA;AAAA,MAEF,KAAK;AACH,aAAK,aAAa,QAAQ,CAAC,YAAY,QAAQ,QAAQ,IAAI,CAAC;AAC5D;AAAA,MAEF,KAAK;AACH,cAAM,WAAW,QAAQ;AACzB,aAAK,aAAa,QAAQ,CAAC,YAAY,QAAQ,QAAQ,CAAC;AACxD,aAAK,YAAY;AACjB,aAAK,cAAc;AACnB;AAAA,MAEF,KAAK;AACH,cAAM,QAAQ,IAAI,MAAM,QAAQ,KAAK;AACrC,aAAK,cAAc,QAAQ,CAAC,YAAY,QAAQ,KAAK,CAAC;AACtD,YAAI,KAAK,aAAa;AACpB,eAAK,YAAY,KAAK;AACtB,eAAK,eAAe;AACpB,eAAK,cAAc;AAAA,QACrB;AACA,YAAI,KAAK,YAAY;AACnB,eAAK,WAAW,KAAK;AACrB,eAAK,cAAc;AACnB,eAAK,aAAa;AAAA,QACpB;AACA;AAAA,MAEF,KAAK;AACH,aAAK,aAAa,QAAQ;AAC1B,aAAK,mBAAmB,QAAQ,CAAC,YAAY,QAAQ,QAAQ,IAAI,CAAC;AAClE;AAAA,MAEF,KAAK;AACH,aAAK,sBAAsB,QAAQ,CAAC,YAAY,QAAQ,QAAQ,UAAU,CAAC;AAC3E;AAAA,MAGF,KAAK;AACH,aAAK,oBAAoB;AAAA,UAAQ,CAAC,YAChC,QAAS,QAAgB,QAAQ;AAAA,QACnC;AACA,YAAI,KAAK,qBAAqB;AAC5B,eAAK,oBAAqB,QAAgB,QAAQ;AAClD,eAAK,sBAAsB;AAAA,QAC7B;AACA;AAAA,MAEF,KAAK;AACH,cAAM,gBAAiB,QAAgB;AACvC,cAAM,UAAW,QAAgB;AACjC,aAAK,YAAY,QAAQ;AACzB,aAAK,cAAc;AAAA,UACjB,WAAW,cAAc;AAAA,UACzB,OAAO,cAAc;AAAA,UACrB,KAAK,cAAc;AAAA,UACnB,MAAM,cAAc;AAAA,UACpB,MAAM,cAAc;AAAA,UACpB,WAAW,cAAc;AAAA,UACzB,WAAW,cAAc;AAAA,QAC3B;AACA,aAAK,eAAe,QAAQ,CAAC,YAAY,QAAQ,eAAe,OAAO,CAAC;AACxE,YAAI,KAAK,aAAa;AACpB,eAAK,YAAY,aAAa;AAC9B,eAAK,cAAc;AACnB,eAAK,aAAa;AAAA,QACpB;AACA;AAAA,MAEF,KAAK;AACH,cAAM,gBAAgB,QAAQ;AAC9B,YAAI,KAAK,cAAc,eAAe;AACpC,eAAK,YAAY;AACjB,eAAK,cAAc;AAAA,QACrB;AACA,aAAK,aAAa,QAAQ,CAAC,YAAY,QAAQ,aAAa,CAAC;AAC7D;AAAA,MAEF,KAAK;AACH,aAAK,qBAAqB;AAAA,UAAQ,CAAC,YACjC,QAAQ,QAAQ,WAAa,QAAgB,WAAW;AAAA,QAC1D;AACA;AAAA,MAEF,KAAK;AACH,aAAK,mBAAmB;AAAA,UAAQ,CAAC,YAC/B,QAAQ,QAAQ,WAAa,QAAgB,WAAW;AAAA,QAC1D;AACA;AAAA,MAEF,KAAK;AACH,cAAM,kBAAkB,QAAQ;AAChC,cAAM,SAAU,QAAgB;AAChC,YAAI,KAAK,cAAc,iBAAiB;AACtC,eAAK,YAAY;AACjB,eAAK,cAAc;AAAA,QACrB;AACA,aAAK,sBAAsB;AAAA,UAAQ,CAAC,YAClC,QAAQ,iBAAiB,MAAM;AAAA,QACjC;AACA;AAAA,IACJ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAA2B,CAAC,GAAyB;AACzD,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,KAAK,UAAU,eAAe,CAAC,KAAK,IAAI;AAC1C,eAAO,IAAI,MAAM,yBAAyB,CAAC;AAC3C;AAAA,MACF;AAEA,UAAI,KAAK,WAAW;AAClB,eAAO,IAAI,MAAM,uDAAuD,CAAC;AACzE;AAAA,MACF;AAEA,WAAK,eAAe;AACpB,WAAK,cAAc;AAEnB,WAAK,GAAG;AAAA,QACN,KAAK,UAAU;AAAA,UACb,MAAM;AAAA,UACN;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAoB;AACxB,QAAI,CAAC,KAAK,MAAM,KAAK,UAAU,aAAa;AAC1C,cAAQ,MAAM,yCAAyC;AACvD;AAAA,IACF;AAEA,QAAI,CAAC,KAAK,WAAW;AACnB,cAAQ,MAAM,6CAA6C;AAC3D;AAAA,IACF;AAEA,SAAK,GAAG;AAAA,MACN,KAAK,UAAU;AAAA,QACb,MAAM;AAAA,QACN,WAAW,KAAK;AAAA,QAChB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,MAAc,MAAoB;AACvC,QAAI,CAAC,KAAK,MAAM,KAAK,UAAU,aAAa;AAC1C,cAAQ,MAAM,0CAA0C;AACxD;AAAA,IACF;AAEA,QAAI,CAAC,KAAK,WAAW;AACnB,cAAQ,MAAM,8CAA8C;AAC5D;AAAA,IACF;AAEA,SAAK,GAAG;AAAA,MACN,KAAK,UAAU;AAAA,QACb,MAAM;AAAA,QACN,WAAW,KAAK;AAAA,QAChB;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,OAAa;AACX,QAAI,CAAC,KAAK,MAAM,KAAK,UAAU,aAAa;AAC1C;AAAA,IACF;AAEA,QAAI,CAAC,KAAK,WAAW;AACnB;AAAA,IACF;AAEA,SAAK,GAAG;AAAA,MACN,KAAK,UAAU;AAAA,QACb,MAAM;AAAA,QACN,WAAW,KAAK;AAAA,MAClB,CAAC;AAAA,IACH;AAEA,SAAK,YAAY;AACjB,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,aAAa,QAA0D;AACrE,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,KAAK,UAAU,eAAe,CAAC,KAAK,IAAI;AAC1C,eAAO,IAAI,MAAM,yBAAyB,CAAC;AAC3C;AAAA,MACF;AAEA,WAAK,sBAAsB;AAE3B,WAAK,GAAG;AAAA,QACN,KAAK,UAAU;AAAA,UACb,MAAM;AAAA,UACN;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,KAAK,SAAyD;AAC5D,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,KAAK,UAAU,eAAe,CAAC,KAAK,IAAI;AAC1C,eAAO,IAAI,MAAM,yBAAyB,CAAC;AAC3C;AAAA,MACF;AAEA,UAAI,KAAK,WAAW;AAClB,eAAO,IAAI,MAAM,2CAA2C,CAAC;AAC7D;AAAA,MACF;AAEA,WAAK,cAAc;AACnB,WAAK,aAAa;AAElB,WAAK,GAAG;AAAA,QACN,KAAK,UAAU;AAAA,UACb,MAAM;AAAA,UACN;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAA0B;AAC9B,QAAI,CAAC,KAAK,MAAM,KAAK,UAAU,aAAa;AAC1C,cAAQ,MAAM,yCAAyC;AACvD;AAAA,IACF;AAEA,UAAM,gBAAgB,aAAa,KAAK;AACxC,QAAI,CAAC,eAAe;AAClB,cAAQ,MAAM,6CAA6C;AAC3D;AAAA,IACF;AAEA,SAAK,GAAG;AAAA,MACN,KAAK,UAAU;AAAA,QACb,MAAM;AAAA,QACN,WAAW;AAAA,MACb,CAAC;AAAA,IACH;AAEA,QAAI,kBAAkB,KAAK,WAAW;AACpC,WAAK,YAAY;AACjB,WAAK,cAAc;AAAA,IACrB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,mBAAmB,QAAkC;AACnD,SAAK,aAAa,MAAM,EACrB,KAAK,CAAC,aAAa;AAClB,WAAK,oBAAoB,QAAQ,CAAC,YAAY;AAC5C,YAAI;AACF,kBAAQ,QAAQ;AAAA,QAClB,SAAS,GAAG;AACV,kBAAQ,MAAM,6CAA6C,CAAC;AAAA,QAC9D;AAAA,MACF,CAAC;AAAA,IACH,CAAC,EACA,MAAM,CAAC,QAAQ;AACd,cAAQ,MAAM,wCAAwC,GAAG;AAAA,IAC3D,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,UAAU,SAA2B;AACnC,SAAK,gBAAgB,KAAK,OAAO;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,SAA2B;AACtC,SAAK,mBAAmB,KAAK,OAAO;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,SAAuC;AAC5C,SAAK,aAAa,KAAK,OAAO;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,SAAuC;AAC5C,SAAK,aAAa,KAAK,OAAO;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ,SAAuC;AAC7C,SAAK,cAAc,KAAK,OAAO;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,SAA4C;AACpD,SAAK,gBAAgB,KAAK,OAAO;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,SAA2C;AACtD,SAAK,mBAAmB,KAAK,OAAO;AAEpC,QAAI,KAAK,YAAY;AACnB,cAAQ,KAAK,UAAU;AAAA,IACzB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgB,SAAsD;AACpE,SAAK,sBAAsB,KAAK,OAAO;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,SAAwD;AACpE,SAAK,oBAAoB,KAAK,OAAO;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKA,SAAS,SAAuE;AAC9E,SAAK,eAAe,KAAK,OAAO;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,SAA4C;AACjD,SAAK,aAAa,KAAK,OAAO;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKA,eAAe,SAAiE;AAC9E,SAAK,qBAAqB,KAAK,OAAO;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,SAAiE;AAC5E,SAAK,mBAAmB,KAAK,OAAO;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgB,SAA4D;AAC1E,SAAK,sBAAsB,KAAK,OAAO;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKA,uBAA6B;AAC3B,QAAI,CAAC,KAAK,MAAM,KAAK,UAAU,aAAa;AAC1C,cAAQ,MAAM,sDAAsD;AACpE;AAAA,IACF;AAEA,SAAK,GAAG,KAAK,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC,CAAC;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,WAA4B;AAC1B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,cAAuB;AACrB,WAAO,KAAK,UAAU;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,eAA8B;AAC5B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAqC;AACnC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,mBAA4B;AAC1B,WAAO,KAAK,cAAc;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAmC;AACjC,WAAO,KAAK;AAAA,EACd;AACF;",
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
  }
@@ -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"}