x-shell.js 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -0
- package/README.md +292 -2
- package/dist/client/browser-bundle.js +197 -3
- package/dist/client/browser-bundle.js.map +2 -2
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/terminal-client.d.ts +59 -3
- package/dist/client/terminal-client.d.ts.map +1 -1
- package/dist/client/terminal-client.js +194 -4
- package/dist/client/terminal-client.js.map +1 -1
- package/dist/server/circular-buffer.d.ts +55 -0
- package/dist/server/circular-buffer.d.ts.map +1 -0
- package/dist/server/circular-buffer.js +91 -0
- package/dist/server/circular-buffer.js.map +1 -0
- package/dist/server/index.d.ts +4 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +3 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/session-manager.d.ts +195 -0
- package/dist/server/session-manager.d.ts.map +1 -0
- package/dist/server/session-manager.js +448 -0
- package/dist/server/session-manager.js.map +1 -0
- package/dist/server/terminal-server.d.ts +54 -6
- package/dist/server/terminal-server.d.ts.map +1 -1
- package/dist/server/terminal-server.js +313 -80
- package/dist/server/terminal-server.js.map +1 -1
- package/dist/shared/types.d.ts +122 -2
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/ui/browser-bundle.js +745 -47
- package/dist/ui/browser-bundle.js.map +3 -3
- package/dist/ui/x-shell-terminal.d.ts +99 -1
- package/dist/ui/x-shell-terminal.d.ts.map +1 -1
- package/dist/ui/x-shell-terminal.js +604 -48
- package/dist/ui/x-shell-terminal.js.map +1 -1
- package/package.json +5 -2
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session manager for multiplexed terminal sessions.
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - Manage shared session lifecycle
|
|
6
|
+
* - Track client connections per session
|
|
7
|
+
* - Handle orphan session cleanup
|
|
8
|
+
* - Maintain history buffers
|
|
9
|
+
* - Broadcast messages to session clients
|
|
10
|
+
*/
|
|
11
|
+
import { EventEmitter } from 'events';
|
|
12
|
+
import { CircularBuffer } from './circular-buffer.js';
|
|
13
|
+
export class SessionManager extends EventEmitter {
|
|
14
|
+
constructor(config = {}) {
|
|
15
|
+
super();
|
|
16
|
+
this.sessions = new Map();
|
|
17
|
+
this.clientToSessions = new Map(); // clientId -> sessionIds
|
|
18
|
+
this.orphanTimers = new Map();
|
|
19
|
+
this.config = {
|
|
20
|
+
maxClientsPerSession: config.maxClientsPerSession ?? 10,
|
|
21
|
+
orphanTimeout: config.orphanTimeout ?? 60000,
|
|
22
|
+
historySize: config.historySize ?? 50000,
|
|
23
|
+
historyEnabled: config.historyEnabled ?? true,
|
|
24
|
+
maxSessionsTotal: config.maxSessionsTotal ?? 100,
|
|
25
|
+
verbose: config.verbose ?? false,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
log(message, level = 'info') {
|
|
29
|
+
if (this.config.verbose || level === 'error') {
|
|
30
|
+
const prefix = `[SessionManager]`;
|
|
31
|
+
if (level === 'error') {
|
|
32
|
+
console.error(`${prefix} ${message}`);
|
|
33
|
+
}
|
|
34
|
+
else if (level === 'warn') {
|
|
35
|
+
console.warn(`${prefix} ${message}`);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
console.log(`${prefix} ${message}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// ==========================================================================
|
|
43
|
+
// Session CRUD
|
|
44
|
+
// ==========================================================================
|
|
45
|
+
/**
|
|
46
|
+
* Create a new shared session.
|
|
47
|
+
*/
|
|
48
|
+
createSession(options) {
|
|
49
|
+
if (this.sessions.size >= this.config.maxSessionsTotal) {
|
|
50
|
+
throw new Error('Maximum number of sessions reached');
|
|
51
|
+
}
|
|
52
|
+
const now = new Date();
|
|
53
|
+
const session = {
|
|
54
|
+
id: options.id,
|
|
55
|
+
type: options.type,
|
|
56
|
+
pty: options.pty,
|
|
57
|
+
shell: options.shell,
|
|
58
|
+
cwd: options.cwd,
|
|
59
|
+
cols: options.cols,
|
|
60
|
+
rows: options.rows,
|
|
61
|
+
createdAt: now,
|
|
62
|
+
lastActivity: now,
|
|
63
|
+
container: options.container,
|
|
64
|
+
clients: new Map(),
|
|
65
|
+
owner: options.ownerId,
|
|
66
|
+
label: options.label,
|
|
67
|
+
accepting: options.allowJoin !== false,
|
|
68
|
+
historyBuffer: new CircularBuffer(this.config.historySize),
|
|
69
|
+
historyEnabled: options.enableHistory !== false && this.config.historyEnabled,
|
|
70
|
+
orphanedAt: null,
|
|
71
|
+
};
|
|
72
|
+
// Add owner as first client
|
|
73
|
+
session.clients.set(options.ownerId, {
|
|
74
|
+
id: options.ownerId,
|
|
75
|
+
ws: options.ownerWs,
|
|
76
|
+
joinedAt: now,
|
|
77
|
+
lastActivity: now,
|
|
78
|
+
});
|
|
79
|
+
// Track client -> sessions mapping
|
|
80
|
+
this.trackClientSession(options.ownerId, session.id);
|
|
81
|
+
this.sessions.set(session.id, session);
|
|
82
|
+
this.log(`Created session ${session.id} (type: ${session.type}, owner: ${options.ownerId})`);
|
|
83
|
+
this.emit('sessionCreated', session);
|
|
84
|
+
return session;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Get a session by ID.
|
|
88
|
+
*/
|
|
89
|
+
getSession(sessionId) {
|
|
90
|
+
return this.sessions.get(sessionId);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get all sessions, optionally filtered.
|
|
94
|
+
*/
|
|
95
|
+
getSessions(filter) {
|
|
96
|
+
let sessions = Array.from(this.sessions.values());
|
|
97
|
+
if (filter) {
|
|
98
|
+
if (filter.type) {
|
|
99
|
+
sessions = sessions.filter((s) => s.type === filter.type);
|
|
100
|
+
}
|
|
101
|
+
if (filter.container) {
|
|
102
|
+
sessions = sessions.filter((s) => s.container === filter.container);
|
|
103
|
+
}
|
|
104
|
+
if (filter.accepting !== undefined) {
|
|
105
|
+
sessions = sessions.filter((s) => s.accepting === filter.accepting);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return sessions;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Close a session and clean up.
|
|
112
|
+
*/
|
|
113
|
+
closeSession(sessionId, reason) {
|
|
114
|
+
const session = this.sessions.get(sessionId);
|
|
115
|
+
if (!session)
|
|
116
|
+
return;
|
|
117
|
+
this.log(`Closing session ${sessionId} (reason: ${reason})`);
|
|
118
|
+
// Clear orphan timer if any
|
|
119
|
+
this.clearOrphanTimer(sessionId);
|
|
120
|
+
// Kill PTY
|
|
121
|
+
try {
|
|
122
|
+
session.pty.kill();
|
|
123
|
+
}
|
|
124
|
+
catch (e) {
|
|
125
|
+
this.log(`Error killing PTY for session ${sessionId}: ${e}`, 'warn');
|
|
126
|
+
}
|
|
127
|
+
// Remove session from all client mappings
|
|
128
|
+
for (const clientId of session.clients.keys()) {
|
|
129
|
+
const clientSessions = this.clientToSessions.get(clientId);
|
|
130
|
+
if (clientSessions) {
|
|
131
|
+
clientSessions.delete(sessionId);
|
|
132
|
+
if (clientSessions.size === 0) {
|
|
133
|
+
this.clientToSessions.delete(clientId);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Remove session
|
|
138
|
+
this.sessions.delete(sessionId);
|
|
139
|
+
this.emit('sessionClosed', sessionId, reason);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Check if a session exists.
|
|
143
|
+
*/
|
|
144
|
+
hasSession(sessionId) {
|
|
145
|
+
return this.sessions.has(sessionId);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Get session count.
|
|
149
|
+
*/
|
|
150
|
+
getSessionCount() {
|
|
151
|
+
return this.sessions.size;
|
|
152
|
+
}
|
|
153
|
+
// ==========================================================================
|
|
154
|
+
// Client Management
|
|
155
|
+
// ==========================================================================
|
|
156
|
+
/**
|
|
157
|
+
* Add a client to a session.
|
|
158
|
+
*/
|
|
159
|
+
addClient(sessionId, clientId, ws) {
|
|
160
|
+
const session = this.sessions.get(sessionId);
|
|
161
|
+
if (!session) {
|
|
162
|
+
this.log(`Cannot add client to non-existent session ${sessionId}`, 'warn');
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
if (!session.accepting) {
|
|
166
|
+
this.log(`Session ${sessionId} is not accepting new clients`, 'warn');
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
if (session.clients.size >= this.config.maxClientsPerSession) {
|
|
170
|
+
this.log(`Session ${sessionId} is full`, 'warn');
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
// If session was orphaned, clear the timer
|
|
174
|
+
if (session.orphanedAt) {
|
|
175
|
+
session.orphanedAt = null;
|
|
176
|
+
this.clearOrphanTimer(sessionId);
|
|
177
|
+
this.log(`Session ${sessionId} is no longer orphaned`);
|
|
178
|
+
}
|
|
179
|
+
const now = new Date();
|
|
180
|
+
session.clients.set(clientId, {
|
|
181
|
+
id: clientId,
|
|
182
|
+
ws,
|
|
183
|
+
joinedAt: now,
|
|
184
|
+
lastActivity: now,
|
|
185
|
+
});
|
|
186
|
+
this.trackClientSession(clientId, sessionId);
|
|
187
|
+
this.log(`Client ${clientId} joined session ${sessionId} (${session.clients.size} clients)`);
|
|
188
|
+
this.emit('clientJoined', sessionId, clientId, session.clients.size);
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Remove a client from a session.
|
|
193
|
+
*/
|
|
194
|
+
removeClient(sessionId, clientId) {
|
|
195
|
+
const session = this.sessions.get(sessionId);
|
|
196
|
+
if (!session)
|
|
197
|
+
return;
|
|
198
|
+
if (!session.clients.has(clientId))
|
|
199
|
+
return;
|
|
200
|
+
session.clients.delete(clientId);
|
|
201
|
+
// Update client -> sessions mapping
|
|
202
|
+
const clientSessions = this.clientToSessions.get(clientId);
|
|
203
|
+
if (clientSessions) {
|
|
204
|
+
clientSessions.delete(sessionId);
|
|
205
|
+
if (clientSessions.size === 0) {
|
|
206
|
+
this.clientToSessions.delete(clientId);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
this.log(`Client ${clientId} left session ${sessionId} (${session.clients.size} clients remaining)`);
|
|
210
|
+
this.emit('clientLeft', sessionId, clientId, session.clients.size);
|
|
211
|
+
// Check if session is now orphaned
|
|
212
|
+
if (session.clients.size === 0) {
|
|
213
|
+
this.handleOrphanedSession(session);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Remove a client from all sessions.
|
|
218
|
+
*/
|
|
219
|
+
removeClientFromAllSessions(clientId) {
|
|
220
|
+
const clientSessions = this.clientToSessions.get(clientId);
|
|
221
|
+
if (!clientSessions)
|
|
222
|
+
return [];
|
|
223
|
+
const sessionIds = Array.from(clientSessions);
|
|
224
|
+
for (const sessionId of sessionIds) {
|
|
225
|
+
this.removeClient(sessionId, clientId);
|
|
226
|
+
}
|
|
227
|
+
return sessionIds;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Get all clients in a session.
|
|
231
|
+
*/
|
|
232
|
+
getSessionClients(sessionId) {
|
|
233
|
+
const session = this.sessions.get(sessionId);
|
|
234
|
+
return session ? Array.from(session.clients.values()) : [];
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Get client count for a session.
|
|
238
|
+
*/
|
|
239
|
+
getClientCount(sessionId) {
|
|
240
|
+
const session = this.sessions.get(sessionId);
|
|
241
|
+
return session ? session.clients.size : 0;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Check if a client is in a session.
|
|
245
|
+
*/
|
|
246
|
+
isClientInSession(sessionId, clientId) {
|
|
247
|
+
const session = this.sessions.get(sessionId);
|
|
248
|
+
return session ? session.clients.has(clientId) : false;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Get all sessions a client is in.
|
|
252
|
+
*/
|
|
253
|
+
getClientSessions(clientId) {
|
|
254
|
+
const clientSessions = this.clientToSessions.get(clientId);
|
|
255
|
+
return clientSessions ? Array.from(clientSessions) : [];
|
|
256
|
+
}
|
|
257
|
+
trackClientSession(clientId, sessionId) {
|
|
258
|
+
let clientSessions = this.clientToSessions.get(clientId);
|
|
259
|
+
if (!clientSessions) {
|
|
260
|
+
clientSessions = new Set();
|
|
261
|
+
this.clientToSessions.set(clientId, clientSessions);
|
|
262
|
+
}
|
|
263
|
+
clientSessions.add(sessionId);
|
|
264
|
+
}
|
|
265
|
+
// ==========================================================================
|
|
266
|
+
// Broadcasting
|
|
267
|
+
// ==========================================================================
|
|
268
|
+
/**
|
|
269
|
+
* Broadcast a message to all clients in a session.
|
|
270
|
+
*/
|
|
271
|
+
broadcastToSession(sessionId, message, excludeClientId) {
|
|
272
|
+
const session = this.sessions.get(sessionId);
|
|
273
|
+
if (!session)
|
|
274
|
+
return;
|
|
275
|
+
const messageStr = JSON.stringify(message);
|
|
276
|
+
for (const [clientId, client] of session.clients) {
|
|
277
|
+
if (clientId === excludeClientId)
|
|
278
|
+
continue;
|
|
279
|
+
try {
|
|
280
|
+
if (client.ws.readyState === 1) {
|
|
281
|
+
// WebSocket.OPEN
|
|
282
|
+
client.ws.send(messageStr);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
catch (e) {
|
|
286
|
+
this.log(`Error broadcasting to client ${clientId}: ${e}`, 'warn');
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Send a message to a specific client in a session.
|
|
292
|
+
*/
|
|
293
|
+
sendToClient(sessionId, clientId, message) {
|
|
294
|
+
const session = this.sessions.get(sessionId);
|
|
295
|
+
if (!session)
|
|
296
|
+
return false;
|
|
297
|
+
const client = session.clients.get(clientId);
|
|
298
|
+
if (!client)
|
|
299
|
+
return false;
|
|
300
|
+
try {
|
|
301
|
+
if (client.ws.readyState === 1) {
|
|
302
|
+
client.ws.send(JSON.stringify(message));
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
catch (e) {
|
|
307
|
+
this.log(`Error sending to client ${clientId}: ${e}`, 'warn');
|
|
308
|
+
}
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
// ==========================================================================
|
|
312
|
+
// History
|
|
313
|
+
// ==========================================================================
|
|
314
|
+
/**
|
|
315
|
+
* Append data to a session's history buffer.
|
|
316
|
+
*/
|
|
317
|
+
appendHistory(sessionId, data) {
|
|
318
|
+
const session = this.sessions.get(sessionId);
|
|
319
|
+
if (session && session.historyEnabled) {
|
|
320
|
+
session.historyBuffer.append(data);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Get history from a session.
|
|
325
|
+
*/
|
|
326
|
+
getHistory(sessionId, limit) {
|
|
327
|
+
const session = this.sessions.get(sessionId);
|
|
328
|
+
if (!session || !session.historyEnabled)
|
|
329
|
+
return '';
|
|
330
|
+
return session.historyBuffer.toString(limit);
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Clear history for a session.
|
|
334
|
+
*/
|
|
335
|
+
clearHistory(sessionId) {
|
|
336
|
+
const session = this.sessions.get(sessionId);
|
|
337
|
+
if (session) {
|
|
338
|
+
session.historyBuffer.clear();
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
// ==========================================================================
|
|
342
|
+
// Activity Tracking
|
|
343
|
+
// ==========================================================================
|
|
344
|
+
/**
|
|
345
|
+
* Update last activity timestamp for a session.
|
|
346
|
+
*/
|
|
347
|
+
updateSessionActivity(sessionId) {
|
|
348
|
+
const session = this.sessions.get(sessionId);
|
|
349
|
+
if (session) {
|
|
350
|
+
session.lastActivity = new Date();
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Update last activity for a client.
|
|
355
|
+
*/
|
|
356
|
+
updateClientActivity(sessionId, clientId) {
|
|
357
|
+
const session = this.sessions.get(sessionId);
|
|
358
|
+
if (session) {
|
|
359
|
+
session.lastActivity = new Date();
|
|
360
|
+
const client = session.clients.get(clientId);
|
|
361
|
+
if (client) {
|
|
362
|
+
client.lastActivity = new Date();
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
// ==========================================================================
|
|
367
|
+
// Orphan Handling
|
|
368
|
+
// ==========================================================================
|
|
369
|
+
handleOrphanedSession(session) {
|
|
370
|
+
session.orphanedAt = new Date();
|
|
371
|
+
this.log(`Session ${session.id} is now orphaned, starting cleanup timer`);
|
|
372
|
+
this.emit('sessionOrphaned', session.id);
|
|
373
|
+
// Start orphan timer
|
|
374
|
+
const timer = setTimeout(() => {
|
|
375
|
+
// Check if still orphaned
|
|
376
|
+
if (session.clients.size === 0) {
|
|
377
|
+
this.log(`Orphan timeout for session ${session.id}, closing`);
|
|
378
|
+
this.closeSession(session.id, 'orphan_timeout');
|
|
379
|
+
}
|
|
380
|
+
}, this.config.orphanTimeout);
|
|
381
|
+
this.orphanTimers.set(session.id, timer);
|
|
382
|
+
}
|
|
383
|
+
clearOrphanTimer(sessionId) {
|
|
384
|
+
const timer = this.orphanTimers.get(sessionId);
|
|
385
|
+
if (timer) {
|
|
386
|
+
clearTimeout(timer);
|
|
387
|
+
this.orphanTimers.delete(sessionId);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
// ==========================================================================
|
|
391
|
+
// Session Info
|
|
392
|
+
// ==========================================================================
|
|
393
|
+
/**
|
|
394
|
+
* Convert a session to SharedSessionInfo for client consumption.
|
|
395
|
+
*/
|
|
396
|
+
toSharedSessionInfo(session) {
|
|
397
|
+
return {
|
|
398
|
+
sessionId: session.id,
|
|
399
|
+
type: session.type,
|
|
400
|
+
shell: session.shell,
|
|
401
|
+
cwd: session.cwd,
|
|
402
|
+
cols: session.cols,
|
|
403
|
+
rows: session.rows,
|
|
404
|
+
createdAt: session.createdAt,
|
|
405
|
+
container: session.container,
|
|
406
|
+
clientCount: session.clients.size,
|
|
407
|
+
accepting: session.accepting,
|
|
408
|
+
ownerId: session.owner,
|
|
409
|
+
label: session.label,
|
|
410
|
+
historyEnabled: session.historyEnabled,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
// ==========================================================================
|
|
414
|
+
// Cleanup
|
|
415
|
+
// ==========================================================================
|
|
416
|
+
/**
|
|
417
|
+
* Clean up all sessions and resources.
|
|
418
|
+
*/
|
|
419
|
+
cleanup() {
|
|
420
|
+
this.log('Cleaning up all sessions');
|
|
421
|
+
// Clear all orphan timers
|
|
422
|
+
for (const timer of this.orphanTimers.values()) {
|
|
423
|
+
clearTimeout(timer);
|
|
424
|
+
}
|
|
425
|
+
this.orphanTimers.clear();
|
|
426
|
+
// Close all sessions
|
|
427
|
+
for (const sessionId of this.sessions.keys()) {
|
|
428
|
+
this.closeSession(sessionId, 'cleanup');
|
|
429
|
+
}
|
|
430
|
+
this.clientToSessions.clear();
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Get manager statistics.
|
|
434
|
+
*/
|
|
435
|
+
getStats() {
|
|
436
|
+
let orphanedCount = 0;
|
|
437
|
+
for (const session of this.sessions.values()) {
|
|
438
|
+
if (session.orphanedAt)
|
|
439
|
+
orphanedCount++;
|
|
440
|
+
}
|
|
441
|
+
return {
|
|
442
|
+
sessionCount: this.sessions.size,
|
|
443
|
+
clientCount: this.clientToSessions.size,
|
|
444
|
+
orphanedCount,
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
//# sourceMappingURL=session-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-manager.js","sourceRoot":"","sources":["../../src/server/session-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
* Server-side terminal handler using node-pty
|
|
3
3
|
*
|
|
4
4
|
* Manages PTY sessions and WebSocket connections for web-based terminals.
|
|
5
|
+
* Supports session multiplexing - multiple clients can connect to the same session.
|
|
5
6
|
*/
|
|
6
7
|
import { WebSocket } from 'ws';
|
|
7
8
|
import type { Server as HttpServer } from 'http';
|
|
8
|
-
import type { ServerConfig, SessionInfo } from '../shared/types.js';
|
|
9
|
+
import type { ServerConfig, SessionInfo, SharedSessionInfo, SessionListFilter } from '../shared/types.js';
|
|
9
10
|
/**
|
|
10
11
|
* Terminal server options
|
|
11
12
|
*/
|
|
@@ -20,17 +21,36 @@ export interface TerminalServerOptions extends ServerConfig {
|
|
|
20
21
|
verbose?: boolean;
|
|
21
22
|
/** Path to Docker CLI (default: 'docker') */
|
|
22
23
|
dockerPath?: string;
|
|
24
|
+
/** Maximum clients per session (default: 10) */
|
|
25
|
+
maxClientsPerSession?: number;
|
|
26
|
+
/** Orphan session timeout in ms (default: 60000) */
|
|
27
|
+
orphanTimeout?: number;
|
|
28
|
+
/** History buffer size in characters (default: 50000) */
|
|
29
|
+
historySize?: number;
|
|
30
|
+
/** Enable session history (default: true) */
|
|
31
|
+
historyEnabled?: boolean;
|
|
32
|
+
/** Maximum total sessions (default: 100) */
|
|
33
|
+
maxSessionsTotal?: number;
|
|
23
34
|
}
|
|
24
35
|
/**
|
|
25
|
-
* Terminal server class
|
|
36
|
+
* Terminal server class with session multiplexing support
|
|
26
37
|
*/
|
|
27
38
|
export declare class TerminalServer {
|
|
28
39
|
private config;
|
|
29
|
-
private
|
|
40
|
+
private sessionManager;
|
|
30
41
|
private wss;
|
|
31
42
|
private pty;
|
|
32
43
|
private cleanupInterval;
|
|
44
|
+
private clientIds;
|
|
33
45
|
constructor(options?: TerminalServerOptions);
|
|
46
|
+
/**
|
|
47
|
+
* Generate a unique client ID
|
|
48
|
+
*/
|
|
49
|
+
private generateClientId;
|
|
50
|
+
/**
|
|
51
|
+
* Get or create client ID for a WebSocket
|
|
52
|
+
*/
|
|
53
|
+
private getClientId;
|
|
34
54
|
/**
|
|
35
55
|
* Initialize node-pty (lazy load)
|
|
36
56
|
*/
|
|
@@ -56,6 +76,18 @@ export declare class TerminalServer {
|
|
|
56
76
|
* Handle message from client
|
|
57
77
|
*/
|
|
58
78
|
private handleMessage;
|
|
79
|
+
/**
|
|
80
|
+
* Handle list sessions request
|
|
81
|
+
*/
|
|
82
|
+
private handleListSessions;
|
|
83
|
+
/**
|
|
84
|
+
* Handle join session request
|
|
85
|
+
*/
|
|
86
|
+
private handleJoinSession;
|
|
87
|
+
/**
|
|
88
|
+
* Handle leave session request
|
|
89
|
+
*/
|
|
90
|
+
private handleLeaveSession;
|
|
59
91
|
/**
|
|
60
92
|
* Validate shell path
|
|
61
93
|
*/
|
|
@@ -71,7 +103,11 @@ export declare class TerminalServer {
|
|
|
71
103
|
/**
|
|
72
104
|
* Spawn a Docker exec session
|
|
73
105
|
*/
|
|
74
|
-
private
|
|
106
|
+
private spawnDockerExecSession;
|
|
107
|
+
/**
|
|
108
|
+
* Spawn a Docker attach session (connects to container's main process)
|
|
109
|
+
*/
|
|
110
|
+
private spawnDockerAttachSession;
|
|
75
111
|
/**
|
|
76
112
|
* Spawn a new terminal session
|
|
77
113
|
*/
|
|
@@ -89,7 +125,7 @@ export declare class TerminalServer {
|
|
|
89
125
|
*/
|
|
90
126
|
private resizeSession;
|
|
91
127
|
/**
|
|
92
|
-
* Close session
|
|
128
|
+
* Close session (only owner can close, or force close)
|
|
93
129
|
*/
|
|
94
130
|
private closeSession;
|
|
95
131
|
/**
|
|
@@ -113,9 +149,21 @@ export declare class TerminalServer {
|
|
|
113
149
|
*/
|
|
114
150
|
private listContainers;
|
|
115
151
|
/**
|
|
116
|
-
* Get all active sessions
|
|
152
|
+
* Get all active sessions (for external access)
|
|
117
153
|
*/
|
|
118
154
|
getSessions(): SessionInfo[];
|
|
155
|
+
/**
|
|
156
|
+
* Get all active sessions with multiplexing info
|
|
157
|
+
*/
|
|
158
|
+
getSharedSessions(filter?: SessionListFilter): SharedSessionInfo[];
|
|
159
|
+
/**
|
|
160
|
+
* Get session manager statistics
|
|
161
|
+
*/
|
|
162
|
+
getStats(): {
|
|
163
|
+
sessionCount: number;
|
|
164
|
+
clientCount: number;
|
|
165
|
+
orphanedCount: number;
|
|
166
|
+
};
|
|
119
167
|
/**
|
|
120
168
|
* Close all sessions and stop server
|
|
121
169
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal-server.d.ts","sourceRoot":"","sources":["../../src/server/terminal-server.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"terminal-server.d.ts","sourceRoot":"","sources":["../../src/server/terminal-server.ts"],"names":[],"mappings":"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"}
|