x-shell.js 0.1.1 → 1.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +183 -3
- package/dist/client/browser-bundle.js +43 -1
- package/dist/client/browser-bundle.js.map +2 -2
- package/dist/client/terminal-client.d.ts +20 -1
- package/dist/client/terminal-client.d.ts.map +1 -1
- package/dist/client/terminal-client.js +43 -0
- package/dist/client/terminal-client.js.map +1 -1
- package/dist/server/terminal-server.d.ts +25 -1
- package/dist/server/terminal-server.d.ts.map +1 -1
- package/dist/server/terminal-server.js +210 -25
- package/dist/server/terminal-server.js.map +1 -1
- package/dist/shared/types.d.ts +59 -2
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/ui/browser-bundle.js +571 -15
- package/dist/ui/browser-bundle.js.map +3 -3
- 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/ui/x-shell-terminal.d.ts +65 -2
- package/dist/ui/x-shell-terminal.d.ts.map +1 -1
- package/dist/ui/x-shell-terminal.js +536 -13
- package/dist/ui/x-shell-terminal.js.map +1 -1
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -9,17 +9,52 @@ 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**: `<x-shell-terminal>` Lit web component with xterm.js
|
|
12
|
+
- **Docker**: Connect to Docker containers via `docker exec`
|
|
12
13
|
- **Themes**: Built-in dark/light/auto theme support
|
|
13
|
-
- **Security**: Configurable shell and
|
|
14
|
+
- **Security**: Configurable shell, path, and container allowlists
|
|
14
15
|
- **Framework Agnostic**: Works with React, Vue, Angular, Svelte, or vanilla JS
|
|
15
16
|
|
|
16
17
|
## Installation
|
|
17
18
|
|
|
18
19
|
```bash
|
|
19
|
-
npm install x-shell.js
|
|
20
|
+
npm install x-shell.js
|
|
20
21
|
```
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
### Server-Side Requirements (node-pty)
|
|
24
|
+
|
|
25
|
+
The server component requires `node-pty` for spawning terminal processes. Install it as a dev dependency:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install node-pty --save-dev
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Important:** `node-pty` requires native compilation. If you encounter installation issues:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# Linux - install build essentials
|
|
35
|
+
sudo apt-get install build-essential python3
|
|
36
|
+
|
|
37
|
+
# macOS - install Xcode command line tools
|
|
38
|
+
xcode-select --install
|
|
39
|
+
|
|
40
|
+
# If npm install fails, try:
|
|
41
|
+
npm install node-pty --save-dev --legacy-peer-deps
|
|
42
|
+
|
|
43
|
+
# Or rebuild native modules:
|
|
44
|
+
npm rebuild node-pty
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
See [node-pty docs](https://github.com/microsoft/node-pty) for platform-specific requirements.
|
|
48
|
+
|
|
49
|
+
### Client-Side (Browser)
|
|
50
|
+
|
|
51
|
+
The client and UI components can be loaded directly from a CDN:
|
|
52
|
+
|
|
53
|
+
```html
|
|
54
|
+
<script type="module" src="https://unpkg.com/x-shell.js/dist/ui/browser-bundle.js"></script>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
No build step required for browser usage.
|
|
23
58
|
|
|
24
59
|
## Quick Start
|
|
25
60
|
|
|
@@ -212,6 +247,9 @@ client.getSessionInfo(); // SessionInfo | null
|
|
|
212
247
|
auto-connect
|
|
213
248
|
auto-spawn
|
|
214
249
|
no-header
|
|
250
|
+
show-connection-panel
|
|
251
|
+
show-settings
|
|
252
|
+
show-status-bar
|
|
215
253
|
></x-shell-terminal>
|
|
216
254
|
```
|
|
217
255
|
|
|
@@ -230,6 +268,9 @@ client.getSessionInfo(); // SessionInfo | null
|
|
|
230
268
|
| `auto-connect` | boolean | `false` | Connect on mount |
|
|
231
269
|
| `auto-spawn` | boolean | `false` | Spawn on connect |
|
|
232
270
|
| `no-header` | boolean | `false` | Hide header bar |
|
|
271
|
+
| `show-connection-panel` | boolean | `false` | Show connection panel with container/shell selector |
|
|
272
|
+
| `show-settings` | boolean | `false` | Show settings dropdown (theme, font size) |
|
|
273
|
+
| `show-status-bar` | boolean | `false` | Show status bar with connection info and errors |
|
|
233
274
|
|
|
234
275
|
**Methods:**
|
|
235
276
|
|
|
@@ -254,6 +295,30 @@ terminal.addEventListener('disconnect', () => {});
|
|
|
254
295
|
terminal.addEventListener('spawned', (e) => console.log(e.detail.session));
|
|
255
296
|
terminal.addEventListener('exit', (e) => console.log(e.detail.exitCode));
|
|
256
297
|
terminal.addEventListener('error', (e) => console.log(e.detail.error));
|
|
298
|
+
terminal.addEventListener('theme-change', (e) => console.log(e.detail.theme));
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Built-in Connection Panel
|
|
302
|
+
|
|
303
|
+
When `show-connection-panel` is enabled, the terminal component provides a built-in UI for:
|
|
304
|
+
|
|
305
|
+
- **Mode Selection**: Switch between local shell and Docker container modes
|
|
306
|
+
- **Container Picker**: Dropdown of running containers (when Docker is enabled on server)
|
|
307
|
+
- **Shell Selection**: Choose from server-allowed shells
|
|
308
|
+
- **Connect/Disconnect**: One-click session management
|
|
309
|
+
|
|
310
|
+
The connection panel automatically queries the server for:
|
|
311
|
+
- Docker availability and allowed containers
|
|
312
|
+
- Allowed shells and default configuration
|
|
313
|
+
|
|
314
|
+
```html
|
|
315
|
+
<!-- Full-featured terminal with all UI panels -->
|
|
316
|
+
<x-shell-terminal
|
|
317
|
+
url="ws://localhost:3000/terminal"
|
|
318
|
+
show-connection-panel
|
|
319
|
+
show-settings
|
|
320
|
+
show-status-bar
|
|
321
|
+
></x-shell-terminal>
|
|
257
322
|
```
|
|
258
323
|
|
|
259
324
|
## Theming
|
|
@@ -279,6 +344,91 @@ x-shell-terminal {
|
|
|
279
344
|
}
|
|
280
345
|
```
|
|
281
346
|
|
|
347
|
+
## Docker Container Support
|
|
348
|
+
|
|
349
|
+
x-shell.js can connect to Docker containers, allowing you to exec into running containers directly from the browser.
|
|
350
|
+
|
|
351
|
+
### Server Configuration
|
|
352
|
+
|
|
353
|
+
```javascript
|
|
354
|
+
const server = new TerminalServer({
|
|
355
|
+
// Enable Docker exec feature
|
|
356
|
+
allowDockerExec: true,
|
|
357
|
+
|
|
358
|
+
// Restrict which containers can be accessed (regex patterns)
|
|
359
|
+
allowedContainerPatterns: [
|
|
360
|
+
'^myapp-', // Containers starting with 'myapp-'
|
|
361
|
+
'^dev-container$', // Exact match
|
|
362
|
+
'backend', // Contains 'backend'
|
|
363
|
+
],
|
|
364
|
+
|
|
365
|
+
// Default shell for containers
|
|
366
|
+
defaultContainerShell: '/bin/bash',
|
|
367
|
+
|
|
368
|
+
// Path to Docker CLI (default: 'docker')
|
|
369
|
+
dockerPath: '/usr/bin/docker',
|
|
370
|
+
|
|
371
|
+
verbose: true,
|
|
372
|
+
});
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Client Usage
|
|
376
|
+
|
|
377
|
+
```javascript
|
|
378
|
+
// Connect to a Docker container
|
|
379
|
+
await client.spawn({
|
|
380
|
+
container: 'my-container-name', // Container ID or name
|
|
381
|
+
containerShell: '/bin/sh', // Shell inside container
|
|
382
|
+
containerUser: 'root', // User to run as
|
|
383
|
+
containerCwd: '/app', // Working directory in container
|
|
384
|
+
env: { DEBUG: 'true' }, // Environment variables
|
|
385
|
+
});
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Web Component
|
|
389
|
+
|
|
390
|
+
```html
|
|
391
|
+
<x-shell-terminal
|
|
392
|
+
url="ws://localhost:3000/terminal"
|
|
393
|
+
container="my-container-name"
|
|
394
|
+
container-shell="/bin/bash"
|
|
395
|
+
container-user="node"
|
|
396
|
+
container-cwd="/app"
|
|
397
|
+
theme="dark"
|
|
398
|
+
auto-connect
|
|
399
|
+
auto-spawn
|
|
400
|
+
></x-shell-terminal>
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
**Container Attributes:**
|
|
404
|
+
|
|
405
|
+
| Attribute | Type | Description |
|
|
406
|
+
|-----------|------|-------------|
|
|
407
|
+
| `container` | string | Docker container ID or name |
|
|
408
|
+
| `container-shell` | string | Shell to use inside container |
|
|
409
|
+
| `container-user` | string | User to run as in container |
|
|
410
|
+
| `container-cwd` | string | Working directory in container |
|
|
411
|
+
|
|
412
|
+
### Security Considerations
|
|
413
|
+
|
|
414
|
+
When enabling Docker exec:
|
|
415
|
+
|
|
416
|
+
1. **Use allowedContainerPatterns** - Always restrict which containers can be accessed
|
|
417
|
+
2. **Run x-shell server securely** - The server needs Docker socket access
|
|
418
|
+
3. **Network isolation** - Consider running x-shell in the same Docker network
|
|
419
|
+
4. **Audit logging** - Enable verbose mode in production for audit trails
|
|
420
|
+
|
|
421
|
+
```javascript
|
|
422
|
+
// Production Docker configuration
|
|
423
|
+
const server = new TerminalServer({
|
|
424
|
+
allowDockerExec: true,
|
|
425
|
+
allowedContainerPatterns: ['^prod-app-'], // Only production app containers
|
|
426
|
+
maxSessionsPerClient: 1, // One session at a time
|
|
427
|
+
idleTimeout: 5 * 60 * 1000, // 5 minute timeout
|
|
428
|
+
verbose: true, // Log all activity
|
|
429
|
+
});
|
|
430
|
+
```
|
|
431
|
+
|
|
282
432
|
## Security
|
|
283
433
|
|
|
284
434
|
**Always configure security for production:**
|
|
@@ -299,6 +449,36 @@ const server = new TerminalServer({
|
|
|
299
449
|
});
|
|
300
450
|
```
|
|
301
451
|
|
|
452
|
+
## Examples
|
|
453
|
+
|
|
454
|
+
See the [examples](./examples) directory for complete working examples:
|
|
455
|
+
|
|
456
|
+
- [**docker-container**](./examples/docker-container) - Connect to Docker containers from the browser
|
|
457
|
+
|
|
458
|
+
### Quick Start with Docker Compose
|
|
459
|
+
|
|
460
|
+
Run the full demo with Docker Compose (no local node-pty installation required):
|
|
461
|
+
|
|
462
|
+
```bash
|
|
463
|
+
cd docker
|
|
464
|
+
docker compose up -d
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
This starts:
|
|
468
|
+
- x-shell server on http://localhost:3000
|
|
469
|
+
- Two test containers (Alpine and Ubuntu) to exec into
|
|
470
|
+
|
|
471
|
+
Open http://localhost:3000 and use the connection panel to:
|
|
472
|
+
1. Select "Docker Container" mode
|
|
473
|
+
2. Choose a container from the dropdown
|
|
474
|
+
3. Click "Start Session"
|
|
475
|
+
|
|
476
|
+
Stop the demo:
|
|
477
|
+
|
|
478
|
+
```bash
|
|
479
|
+
docker compose down
|
|
480
|
+
```
|
|
481
|
+
|
|
302
482
|
## License
|
|
303
483
|
|
|
304
484
|
MIT
|
|
@@ -5,6 +5,7 @@ var TerminalClient = class {
|
|
|
5
5
|
this.state = "disconnected";
|
|
6
6
|
this.sessionId = null;
|
|
7
7
|
this.sessionInfo = null;
|
|
8
|
+
this.serverInfo = null;
|
|
8
9
|
this.reconnectAttempts = 0;
|
|
9
10
|
this.reconnectTimeout = null;
|
|
10
11
|
// Event handlers
|
|
@@ -14,6 +15,8 @@ var TerminalClient = class {
|
|
|
14
15
|
this.exitHandlers = [];
|
|
15
16
|
this.errorHandlers = [];
|
|
16
17
|
this.spawnedHandlers = [];
|
|
18
|
+
this.serverInfoHandlers = [];
|
|
19
|
+
this.containerListHandlers = [];
|
|
17
20
|
// Promise resolvers for spawn
|
|
18
21
|
this.spawnResolve = null;
|
|
19
22
|
this.spawnReject = null;
|
|
@@ -123,7 +126,8 @@ var TerminalClient = class {
|
|
|
123
126
|
cwd: message.cwd,
|
|
124
127
|
cols: message.cols,
|
|
125
128
|
rows: message.rows,
|
|
126
|
-
createdAt: /* @__PURE__ */ new Date()
|
|
129
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
130
|
+
container: message.container
|
|
127
131
|
};
|
|
128
132
|
this.spawnedHandlers.forEach((handler) => handler(this.sessionInfo));
|
|
129
133
|
if (this.spawnResolve) {
|
|
@@ -150,6 +154,13 @@ var TerminalClient = class {
|
|
|
150
154
|
this.spawnReject = null;
|
|
151
155
|
}
|
|
152
156
|
break;
|
|
157
|
+
case "serverInfo":
|
|
158
|
+
this.serverInfo = message.info;
|
|
159
|
+
this.serverInfoHandlers.forEach((handler) => handler(message.info));
|
|
160
|
+
break;
|
|
161
|
+
case "containerList":
|
|
162
|
+
this.containerListHandlers.forEach((handler) => handler(message.containers));
|
|
163
|
+
break;
|
|
153
164
|
}
|
|
154
165
|
}
|
|
155
166
|
/**
|
|
@@ -274,6 +285,31 @@ var TerminalClient = class {
|
|
|
274
285
|
onSpawned(handler) {
|
|
275
286
|
this.spawnedHandlers.push(handler);
|
|
276
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* Called when server info is received
|
|
290
|
+
*/
|
|
291
|
+
onServerInfo(handler) {
|
|
292
|
+
this.serverInfoHandlers.push(handler);
|
|
293
|
+
if (this.serverInfo) {
|
|
294
|
+
handler(this.serverInfo);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Called when container list is received
|
|
299
|
+
*/
|
|
300
|
+
onContainerList(handler) {
|
|
301
|
+
this.containerListHandlers.push(handler);
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Request list of available containers
|
|
305
|
+
*/
|
|
306
|
+
requestContainerList() {
|
|
307
|
+
if (!this.ws || this.state !== "connected") {
|
|
308
|
+
console.error("[x-shell] Cannot request containers: not connected");
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
this.ws.send(JSON.stringify({ type: "listContainers" }));
|
|
312
|
+
}
|
|
277
313
|
// ==========================================
|
|
278
314
|
// Getters
|
|
279
315
|
// ==========================================
|
|
@@ -307,6 +343,12 @@ var TerminalClient = class {
|
|
|
307
343
|
hasActiveSession() {
|
|
308
344
|
return this.sessionId !== null;
|
|
309
345
|
}
|
|
346
|
+
/**
|
|
347
|
+
* Get server info
|
|
348
|
+
*/
|
|
349
|
+
getServerInfo() {
|
|
350
|
+
return this.serverInfo;
|
|
351
|
+
}
|
|
310
352
|
};
|
|
311
353
|
export {
|
|
312
354
|
TerminalClient
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/client/terminal-client.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Terminal client for connecting to x-shell server\n *\n * Example usage:\n * ```typescript\n * import { TerminalClient } from 'x-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 */\n\nimport type {\n ClientConfig,\n TerminalOptions,\n TerminalMessage,\n SessionInfo,\n} from '../shared/types.js';\n\n/**\n * Connection state\n */\nexport type ConnectionState = 'disconnected' | 'connecting' | 'connected';\n\n/**\n * Terminal client class\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 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\n // Promise resolvers for spawn\n private spawnResolve: ((info: SessionInfo) => void) | null = null;\n private spawnReject: ((error: Error) => 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('[x-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 };\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 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 spawned. Call kill() 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('[x-shell] Cannot write: not connected');\n return;\n }\n\n if (!this.sessionId) {\n console.error('[x-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('[x-shell] Cannot resize: not connected');\n return;\n }\n\n if (!this.sessionId) {\n console.error('[x-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\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 // 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 // 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"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["/**\n * Terminal client for connecting to x-shell server\n *\n * Example usage:\n * ```typescript\n * import { TerminalClient } from 'x-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 */\n\nimport type {\n ClientConfig,\n TerminalOptions,\n TerminalMessage,\n SessionInfo,\n ContainerInfo,\n ServerInfo,\n} from '../shared/types.js';\n\n/**\n * Connection state\n */\nexport type ConnectionState = 'disconnected' | 'connecting' | 'connected';\n\n/**\n * Terminal client class\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\n // Promise resolvers for spawn\n private spawnResolve: ((info: SessionInfo) => void) | null = null;\n private spawnReject: ((error: Error) => 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('[x-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 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 }\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 spawned. Call kill() 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('[x-shell] Cannot write: not connected');\n return;\n }\n\n if (!this.sessionId) {\n console.error('[x-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('[x-shell] Cannot resize: not connected');\n return;\n }\n\n if (!this.sessionId) {\n console.error('[x-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\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 // 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 * Request list of available containers\n */\n requestContainerList(): void {\n if (!this.ws || this.state !== 'connected') {\n console.error('[x-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": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* client.kill();
|
|
18
18
|
* ```
|
|
19
19
|
*/
|
|
20
|
-
import type { ClientConfig, TerminalOptions, SessionInfo } from '../shared/types.js';
|
|
20
|
+
import type { ClientConfig, TerminalOptions, SessionInfo, ContainerInfo, ServerInfo } from '../shared/types.js';
|
|
21
21
|
/**
|
|
22
22
|
* Connection state
|
|
23
23
|
*/
|
|
@@ -31,6 +31,7 @@ export declare class TerminalClient {
|
|
|
31
31
|
private state;
|
|
32
32
|
private sessionId;
|
|
33
33
|
private sessionInfo;
|
|
34
|
+
private serverInfo;
|
|
34
35
|
private reconnectAttempts;
|
|
35
36
|
private reconnectTimeout;
|
|
36
37
|
private connectHandlers;
|
|
@@ -39,6 +40,8 @@ export declare class TerminalClient {
|
|
|
39
40
|
private exitHandlers;
|
|
40
41
|
private errorHandlers;
|
|
41
42
|
private spawnedHandlers;
|
|
43
|
+
private serverInfoHandlers;
|
|
44
|
+
private containerListHandlers;
|
|
42
45
|
private spawnResolve;
|
|
43
46
|
private spawnReject;
|
|
44
47
|
constructor(config: ClientConfig);
|
|
@@ -98,6 +101,18 @@ export declare class TerminalClient {
|
|
|
98
101
|
* Called when a session is spawned
|
|
99
102
|
*/
|
|
100
103
|
onSpawned(handler: (info: SessionInfo) => void): void;
|
|
104
|
+
/**
|
|
105
|
+
* Called when server info is received
|
|
106
|
+
*/
|
|
107
|
+
onServerInfo(handler: (info: ServerInfo) => void): void;
|
|
108
|
+
/**
|
|
109
|
+
* Called when container list is received
|
|
110
|
+
*/
|
|
111
|
+
onContainerList(handler: (containers: ContainerInfo[]) => void): void;
|
|
112
|
+
/**
|
|
113
|
+
* Request list of available containers
|
|
114
|
+
*/
|
|
115
|
+
requestContainerList(): void;
|
|
101
116
|
/**
|
|
102
117
|
* Get current connection state
|
|
103
118
|
*/
|
|
@@ -118,5 +133,9 @@ export declare class TerminalClient {
|
|
|
118
133
|
* Check if a session is active
|
|
119
134
|
*/
|
|
120
135
|
hasActiveSession(): boolean;
|
|
136
|
+
/**
|
|
137
|
+
* Get server info
|
|
138
|
+
*/
|
|
139
|
+
getServerInfo(): ServerInfo | null;
|
|
121
140
|
}
|
|
122
141
|
//# sourceMappingURL=terminal-client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal-client.d.ts","sourceRoot":"","sources":["../../src/client/terminal-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,eAAe,EAEf,WAAW,
|
|
1
|
+
{"version":3,"file":"terminal-client.d.ts","sourceRoot":"","sources":["../../src/client/terminal-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,eAAe,EAEf,WAAW,EACX,aAAa,EACb,UAAU,EACX,MAAM,oBAAoB,CAAC;AAE5B;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,YAAY,GAAG,WAAW,CAAC;AAE1E;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,KAAK,CAAmC;IAChD,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,gBAAgB,CAA8C;IAGtE,OAAO,CAAC,eAAe,CAAsB;IAC7C,OAAO,CAAC,kBAAkB,CAAsB;IAChD,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,aAAa,CAAkC;IACvD,OAAO,CAAC,eAAe,CAAuC;IAC9D,OAAO,CAAC,kBAAkB,CAAsC;IAChE,OAAO,CAAC,qBAAqB,CAAiD;IAG9E,OAAO,CAAC,YAAY,CAA8C;IAClE,OAAO,CAAC,WAAW,CAAyC;gBAEhD,MAAM,EAAE,YAAY;IAShC;;OAEG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAuDxB;;OAEG;IACH,UAAU,IAAI,IAAI;IAkBlB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAezB;;OAEG;IACH,OAAO,CAAC,aAAa;IA8DrB;;OAEG;IACH,KAAK,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,WAAW,CAAC;IAwB1D;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAoBzB;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAqBxC;;OAEG;IACH,IAAI,IAAI,IAAI;IAwBZ;;OAEG;IACH,SAAS,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI;IAIpC;;OAEG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI;IAIvC;;OAEG;IACH,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAI7C;;OAEG;IACH,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAI7C;;OAEG;IACH,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI;IAI9C;;OAEG;IACH,SAAS,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,GAAG,IAAI;IAIrD;;OAEG;IACH,YAAY,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI;IAQvD;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,CAAC,UAAU,EAAE,aAAa,EAAE,KAAK,IAAI,GAAG,IAAI;IAIrE;;OAEG;IACH,oBAAoB,IAAI,IAAI;IAa5B;;OAEG;IACH,QAAQ,IAAI,eAAe;IAI3B;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,YAAY,IAAI,MAAM,GAAG,IAAI;IAI7B;;OAEG;IACH,cAAc,IAAI,WAAW,GAAG,IAAI;IAIpC;;OAEG;IACH,gBAAgB,IAAI,OAAO;IAI3B;;OAEG;IACH,aAAa,IAAI,UAAU,GAAG,IAAI;CAGnC"}
|
|
@@ -26,6 +26,7 @@ export class TerminalClient {
|
|
|
26
26
|
this.state = 'disconnected';
|
|
27
27
|
this.sessionId = null;
|
|
28
28
|
this.sessionInfo = null;
|
|
29
|
+
this.serverInfo = null;
|
|
29
30
|
this.reconnectAttempts = 0;
|
|
30
31
|
this.reconnectTimeout = null;
|
|
31
32
|
// Event handlers
|
|
@@ -35,6 +36,8 @@ export class TerminalClient {
|
|
|
35
36
|
this.exitHandlers = [];
|
|
36
37
|
this.errorHandlers = [];
|
|
37
38
|
this.spawnedHandlers = [];
|
|
39
|
+
this.serverInfoHandlers = [];
|
|
40
|
+
this.containerListHandlers = [];
|
|
38
41
|
// Promise resolvers for spawn
|
|
39
42
|
this.spawnResolve = null;
|
|
40
43
|
this.spawnReject = null;
|
|
@@ -149,6 +152,7 @@ export class TerminalClient {
|
|
|
149
152
|
cols: message.cols,
|
|
150
153
|
rows: message.rows,
|
|
151
154
|
createdAt: new Date(),
|
|
155
|
+
container: message.container,
|
|
152
156
|
};
|
|
153
157
|
this.spawnedHandlers.forEach((handler) => handler(this.sessionInfo));
|
|
154
158
|
if (this.spawnResolve) {
|
|
@@ -175,6 +179,13 @@ export class TerminalClient {
|
|
|
175
179
|
this.spawnReject = null;
|
|
176
180
|
}
|
|
177
181
|
break;
|
|
182
|
+
case 'serverInfo':
|
|
183
|
+
this.serverInfo = message.info;
|
|
184
|
+
this.serverInfoHandlers.forEach((handler) => handler(message.info));
|
|
185
|
+
break;
|
|
186
|
+
case 'containerList':
|
|
187
|
+
this.containerListHandlers.forEach((handler) => handler(message.containers));
|
|
188
|
+
break;
|
|
178
189
|
}
|
|
179
190
|
}
|
|
180
191
|
/**
|
|
@@ -291,6 +302,32 @@ export class TerminalClient {
|
|
|
291
302
|
onSpawned(handler) {
|
|
292
303
|
this.spawnedHandlers.push(handler);
|
|
293
304
|
}
|
|
305
|
+
/**
|
|
306
|
+
* Called when server info is received
|
|
307
|
+
*/
|
|
308
|
+
onServerInfo(handler) {
|
|
309
|
+
this.serverInfoHandlers.push(handler);
|
|
310
|
+
// If we already have server info, call immediately
|
|
311
|
+
if (this.serverInfo) {
|
|
312
|
+
handler(this.serverInfo);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Called when container list is received
|
|
317
|
+
*/
|
|
318
|
+
onContainerList(handler) {
|
|
319
|
+
this.containerListHandlers.push(handler);
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Request list of available containers
|
|
323
|
+
*/
|
|
324
|
+
requestContainerList() {
|
|
325
|
+
if (!this.ws || this.state !== 'connected') {
|
|
326
|
+
console.error('[x-shell] Cannot request containers: not connected');
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
this.ws.send(JSON.stringify({ type: 'listContainers' }));
|
|
330
|
+
}
|
|
294
331
|
// ==========================================
|
|
295
332
|
// Getters
|
|
296
333
|
// ==========================================
|
|
@@ -324,5 +361,11 @@ export class TerminalClient {
|
|
|
324
361
|
hasActiveSession() {
|
|
325
362
|
return this.sessionId !== null;
|
|
326
363
|
}
|
|
364
|
+
/**
|
|
365
|
+
* Get server info
|
|
366
|
+
*/
|
|
367
|
+
getServerInfo() {
|
|
368
|
+
return this.serverInfo;
|
|
369
|
+
}
|
|
327
370
|
}
|
|
328
371
|
//# sourceMappingURL=terminal-client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal-client.js","sourceRoot":"","sources":["../../src/client/terminal-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;
|
|
1
|
+
{"version":3,"file":"terminal-client.js","sourceRoot":"","sources":["../../src/client/terminal-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Manages PTY sessions and WebSocket connections for web-based terminals.
|
|
5
5
|
*/
|
|
6
|
+
import { WebSocket } from 'ws';
|
|
6
7
|
import type { Server as HttpServer } from 'http';
|
|
7
8
|
import type { ServerConfig, SessionInfo } from '../shared/types.js';
|
|
8
9
|
/**
|
|
@@ -17,6 +18,8 @@ export interface TerminalServerOptions extends ServerConfig {
|
|
|
17
18
|
port?: number;
|
|
18
19
|
/** Enable verbose logging */
|
|
19
20
|
verbose?: boolean;
|
|
21
|
+
/** Path to Docker CLI (default: 'docker') */
|
|
22
|
+
dockerPath?: string;
|
|
20
23
|
}
|
|
21
24
|
/**
|
|
22
25
|
* Terminal server class
|
|
@@ -46,8 +49,9 @@ export declare class TerminalServer {
|
|
|
46
49
|
private setupWebSocketServer;
|
|
47
50
|
/**
|
|
48
51
|
* Handle WebSocket connection
|
|
52
|
+
* Can be called directly for manual WebSocket upgrade handling
|
|
49
53
|
*/
|
|
50
|
-
|
|
54
|
+
handleConnection(ws: WebSocket, req: any): Promise<void>;
|
|
51
55
|
/**
|
|
52
56
|
* Handle message from client
|
|
53
57
|
*/
|
|
@@ -60,10 +64,22 @@ export declare class TerminalServer {
|
|
|
60
64
|
* Validate working directory
|
|
61
65
|
*/
|
|
62
66
|
private isCwdAllowed;
|
|
67
|
+
/**
|
|
68
|
+
* Validate container name/ID against allowed patterns
|
|
69
|
+
*/
|
|
70
|
+
private isContainerAllowed;
|
|
71
|
+
/**
|
|
72
|
+
* Spawn a Docker exec session
|
|
73
|
+
*/
|
|
74
|
+
private spawnDockerSession;
|
|
63
75
|
/**
|
|
64
76
|
* Spawn a new terminal session
|
|
65
77
|
*/
|
|
66
78
|
private spawnSession;
|
|
79
|
+
/**
|
|
80
|
+
* Setup PTY event handlers for a session
|
|
81
|
+
*/
|
|
82
|
+
private setupSessionHandlers;
|
|
67
83
|
/**
|
|
68
84
|
* Write data to session
|
|
69
85
|
*/
|
|
@@ -88,6 +104,14 @@ export declare class TerminalServer {
|
|
|
88
104
|
* Log message
|
|
89
105
|
*/
|
|
90
106
|
private log;
|
|
107
|
+
/**
|
|
108
|
+
* Send server info to client
|
|
109
|
+
*/
|
|
110
|
+
private sendServerInfo;
|
|
111
|
+
/**
|
|
112
|
+
* List available Docker containers
|
|
113
|
+
*/
|
|
114
|
+
private listContainers;
|
|
91
115
|
/**
|
|
92
116
|
* Get all active sessions
|
|
93
117
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal-server.d.ts","sourceRoot":"","sources":["../../src/server/terminal-server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"terminal-server.d.ts","sourceRoot":"","sources":["../../src/server/terminal-server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAmB,MAAM,IAAI,CAAC;AAGhD,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,MAAM,CAAC;AACjD,OAAO,KAAK,EACV,YAAY,EAGZ,WAAW,EAGZ,MAAM,oBAAoB,CAAC;AA8B5B;;GAEG;AACH,MAAM,WAAW,qBAAsB,SAAQ,YAAY;IACzD,sDAAsD;IACtD,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,4CAA4C;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6BAA6B;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAA2D;IACzE,OAAO,CAAC,QAAQ,CAAsC;IACtD,OAAO,CAAC,GAAG,CAAgC;IAC3C,OAAO,CAAC,GAAG,CAAa;IACxB,OAAO,CAAC,eAAe,CAA+C;gBAE1D,OAAO,GAAE,qBAA0B;IAqB/C;;OAEG;YACW,OAAO;IAarB;;OAEG;IACH,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAUhC;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAM1B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAS5B;;;OAGG;IACG,gBAAgB,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAsC9D;;OAEG;IACH,OAAO,CAAC,aAAa;IAiCrB;;OAEG;IACH,OAAO,CAAC,cAAc;IAgBtB;;OAEG;IACH,OAAO,CAAC,YAAY;IASpB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAmB1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAoE1B;;OAEG;IACH,OAAO,CAAC,YAAY;IAyHpB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IA+B5B;;OAEG;IACH,OAAO,CAAC,cAAc;IAWtB;;OAEG;IACH,OAAO,CAAC,aAAa;IAYrB;;OAEG;IACH,OAAO,CAAC,YAAY;IAcpB;;OAEG;IACH,OAAO,CAAC,eAAe;IAoBvB;;OAEG;IACH,OAAO,CAAC,SAAS;IAUjB;;OAEG;IACH,OAAO,CAAC,GAAG;IAgBX;;OAEG;IACH,OAAO,CAAC,cAAc;IActB;;OAEG;IACH,OAAO,CAAC,cAAc;IAmDtB;;OAEG;IACH,WAAW,IAAI,WAAW,EAAE;IAY5B;;OAEG;IACH,KAAK,IAAI,IAAI;CAoBd;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,GAAE,IAAI,CAAC,qBAAqB,EAAE,QAAQ,GAAG,MAAM,CAAM,GAC3D;IACD,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;CAC1C,CAMA"}
|