@ellery/terminal-mcp 0.2.2 → 0.3.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.
Files changed (62) hide show
  1. package/README.md +159 -5
  2. package/dist/index.js +63 -2
  3. package/dist/index.js.map +1 -1
  4. package/dist/prompts/index.d.ts.map +1 -1
  5. package/dist/prompts/index.js +8 -0
  6. package/dist/prompts/index.js.map +1 -1
  7. package/dist/server.d.ts +2 -3
  8. package/dist/server.d.ts.map +1 -1
  9. package/dist/server.js +21 -0
  10. package/dist/server.js.map +1 -1
  11. package/dist/terminal/index.d.ts +1 -1
  12. package/dist/terminal/index.d.ts.map +1 -1
  13. package/dist/terminal/manager.d.ts +66 -63
  14. package/dist/terminal/manager.d.ts.map +1 -1
  15. package/dist/terminal/manager.js +255 -101
  16. package/dist/terminal/manager.js.map +1 -1
  17. package/dist/terminal/session.d.ts +13 -0
  18. package/dist/terminal/session.d.ts.map +1 -1
  19. package/dist/terminal/session.js +116 -0
  20. package/dist/terminal/session.js.map +1 -1
  21. package/dist/tools/createSession.d.ts +44 -0
  22. package/dist/tools/createSession.d.ts.map +1 -0
  23. package/dist/tools/createSession.js +41 -0
  24. package/dist/tools/createSession.js.map +1 -0
  25. package/dist/tools/definitions.d.ts.map +1 -1
  26. package/dist/tools/definitions.js +8 -2
  27. package/dist/tools/definitions.js.map +1 -1
  28. package/dist/tools/destroySession.d.ts +31 -0
  29. package/dist/tools/destroySession.d.ts.map +1 -0
  30. package/dist/tools/destroySession.js +32 -0
  31. package/dist/tools/destroySession.js.map +1 -0
  32. package/dist/tools/getContent.d.ts +7 -0
  33. package/dist/tools/getContent.d.ts.map +1 -1
  34. package/dist/tools/getContent.js +8 -3
  35. package/dist/tools/getContent.js.map +1 -1
  36. package/dist/tools/index.d.ts.map +1 -1
  37. package/dist/tools/index.js +20 -1
  38. package/dist/tools/index.js.map +1 -1
  39. package/dist/tools/listSessions.d.ts +19 -0
  40. package/dist/tools/listSessions.d.ts.map +1 -0
  41. package/dist/tools/listSessions.js +23 -0
  42. package/dist/tools/listSessions.js.map +1 -0
  43. package/dist/tools/screenshot.d.ts +26 -3
  44. package/dist/tools/screenshot.d.ts.map +1 -1
  45. package/dist/tools/screenshot.js +55 -5
  46. package/dist/tools/screenshot.js.map +1 -1
  47. package/dist/tools/sendKey.d.ts +7 -0
  48. package/dist/tools/sendKey.d.ts.map +1 -1
  49. package/dist/tools/sendKey.js +7 -2
  50. package/dist/tools/sendKey.js.map +1 -1
  51. package/dist/tools/startRecording.js +1 -1
  52. package/dist/tools/startRecording.js.map +1 -1
  53. package/dist/tools/type.d.ts +7 -0
  54. package/dist/tools/type.d.ts.map +1 -1
  55. package/dist/tools/type.js +7 -2
  56. package/dist/tools/type.js.map +1 -1
  57. package/dist/utils/render.d.ts +21 -0
  58. package/dist/utils/render.d.ts.map +1 -0
  59. package/dist/utils/render.js +162 -0
  60. package/dist/utils/render.js.map +1 -0
  61. package/package.json +6 -1
  62. package/sandbox-permissions.png +0 -0
@@ -2,9 +2,6 @@ import { TerminalSession, TerminalSessionOptions, ScreenshotResult } from "./ses
2
2
  import type { SandboxController } from "../sandbox/index.js";
3
3
  import { RecordingManager } from "../recording/index.js";
4
4
  import type { RecordingMode, RecordingFormat, RecordingMetadata } from "../recording/index.js";
5
- /**
6
- * Options for creating a TerminalManager
7
- */
8
5
  export interface TerminalManagerOptions extends TerminalSessionOptions {
9
6
  sandboxController?: SandboxController;
10
7
  record?: RecordingMode;
@@ -13,97 +10,103 @@ export interface TerminalManagerOptions extends TerminalSessionOptions {
13
10
  idleTimeLimit?: number;
14
11
  maxDuration?: number;
15
12
  inactivityTimeout?: number;
13
+ maxSessions?: number;
14
+ sessionIdleTimeout?: number;
15
+ }
16
+ export interface CreateSessionOptions {
17
+ shell?: string;
18
+ cols?: number;
19
+ rows?: number;
20
+ }
21
+ export interface SessionMetadata {
22
+ sessionId: string;
23
+ shell: string;
24
+ cols: number;
25
+ rows: number;
26
+ createdAt: string;
27
+ lastActivityAt: string;
28
+ isDefault: boolean;
16
29
  }
17
30
  /**
18
- * Manages the terminal session lifecycle
19
- * Currently supports a single session for simplicity
31
+ * Manages one or more terminal sessions in a single process.
32
+ *
33
+ * The "default" session is created lazily on first access and is never
34
+ * idle-evicted; it's what the existing single-session API operates on
35
+ * when no sessionId is passed. Additional sessions can be created via
36
+ * createSession() and addressed by ID.
20
37
  */
21
38
  export declare class TerminalManager {
22
- private session;
23
- private sessionPromise;
39
+ private sessions;
40
+ private defaultSessionId;
41
+ private defaultSessionPromise;
42
+ private idleCheckInterval;
24
43
  private options;
25
44
  private sandboxController?;
26
45
  private recordingManager;
27
46
  private autoRecordingId;
47
+ private maxSessions;
48
+ private sessionIdleTimeoutMs;
28
49
  constructor(options?: TerminalManagerOptions);
50
+ private generateSessionId;
51
+ private touchSession;
52
+ private cleanupIdleSessions;
29
53
  /**
30
- * Get or create the terminal session (async)
54
+ * Wire recording hooks onto a session so its output is captured by any
55
+ * active recordings.
31
56
  */
32
- getSessionAsync(): Promise<TerminalSession>;
57
+ private wireRecording;
33
58
  /**
34
- * Get the current session if it exists and is active
35
- * Returns null if no session exists yet
59
+ * Get or create the default session. Idempotent and concurrency-safe.
36
60
  */
61
+ getSessionAsync(): Promise<TerminalSession>;
37
62
  getCurrentSession(): TerminalSession | null;
38
63
  /**
39
- * @deprecated Use getSessionAsync() instead. This method exists for backwards compatibility
40
- * but will throw if the session hasn't been created yet.
64
+ * @deprecated Use getSessionAsync(). Throws if the default session
65
+ * hasn't been created yet.
41
66
  */
42
67
  getSession(): TerminalSession;
43
- /**
44
- * Initialize the session eagerly
45
- * Call this at startup to create the session before it's needed
46
- */
47
68
  initSession(): Promise<TerminalSession>;
48
- /**
49
- * Start auto-recording based on CLI options
50
- */
51
69
  private startAutoRecording;
52
70
  /**
53
- * Get the RecordingManager instance
71
+ * Create a new non-default session.
54
72
  */
55
- getRecordingManager(): RecordingManager;
73
+ createSession(opts?: CreateSessionOptions): Promise<SessionMetadata>;
74
+ destroySession(sessionId: string): {
75
+ success: boolean;
76
+ message: string;
77
+ };
78
+ listSessions(): {
79
+ sessions: SessionMetadata[];
80
+ maxSessions: number;
81
+ sessionIdleTimeout: number;
82
+ };
56
83
  /**
57
- * Finalize all recordings and return their metadata
58
- * Called by the caller when the session exits
84
+ * Resolve a session by ID. If sessionId is omitted/undefined, returns the
85
+ * default session (creating it if needed).
59
86
  */
60
- finalizeRecordings(exitCode: number): Promise<RecordingMetadata[]>;
87
+ resolveSession(sessionId?: string): Promise<TerminalSession>;
61
88
  /**
62
- * Check if a session exists and is active
89
+ * Synchronous variant of resolveSession() requires the session to
90
+ * already exist. Throws if not created yet (used by sync tool handlers).
63
91
  */
92
+ resolveSessionSync(sessionId?: string): TerminalSession;
64
93
  hasActiveSession(): boolean;
65
- /**
66
- * Write data to the terminal
67
- */
68
- write(data: string): void;
69
- /**
70
- * Get terminal content
71
- */
72
- getContent(): string;
73
- /**
74
- * Get visible content only
75
- */
76
- getVisibleContent(): string;
77
- /**
78
- * Take a screenshot
79
- */
80
- takeScreenshot(): ScreenshotResult;
81
- /**
82
- * Clear the terminal
83
- */
84
- clear(): void;
85
- /**
86
- * Resize the terminal
87
- */
88
- resize(cols: number, rows: number): void;
89
- /**
90
- * Get terminal dimensions
91
- */
92
- getDimensions(): {
94
+ write(data: string, sessionId?: string): void;
95
+ getContent(sessionId?: string): string;
96
+ getVisibleContent(sessionId?: string): string;
97
+ getAnsiContent(visibleOnly?: boolean, sessionId?: string): string;
98
+ getTerminal(sessionId?: string): import("@xterm/headless").Terminal;
99
+ takeScreenshot(sessionId?: string): ScreenshotResult;
100
+ clear(sessionId?: string): void;
101
+ resize(cols: number, rows: number, sessionId?: string): void;
102
+ getDimensions(sessionId?: string): {
93
103
  cols: number;
94
104
  rows: number;
95
105
  };
96
- /**
97
- * Dispose of the current session and cleanup sandbox
98
- */
106
+ getRecordingManager(): RecordingManager;
107
+ finalizeRecordings(exitCode: number): Promise<RecordingMetadata[]>;
99
108
  dispose(): void;
100
- /**
101
- * Async dispose that also cleans up sandbox resources
102
- */
103
109
  disposeAsync(): Promise<void>;
104
- /**
105
- * Get the sandbox controller if one is configured
106
- */
107
110
  getSandboxController(): SandboxController | undefined;
108
111
  }
109
112
  //# sourceMappingURL=manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/terminal/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACzF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAG/F;;GAEG;AACH,MAAM,WAAW,sBAAuB,SAAQ,sBAAsB;IACpE,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,eAAe,CAAC;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;GAGG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,OAAO,CAAgC;IAC/C,OAAO,CAAC,cAAc,CAAyC;IAC/D,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,iBAAiB,CAAC,CAAoB;IAC9C,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,eAAe,CAAuB;gBAElC,OAAO,GAAE,sBAA2B;IAahD;;OAEG;IACG,eAAe,IAAI,OAAO,CAAC,eAAe,CAAC;IAwBjD;;;OAGG;IACH,iBAAiB,IAAI,eAAe,GAAG,IAAI;IAO3C;;;OAGG;IACH,UAAU,IAAI,eAAe;IAS7B;;;OAGG;IACG,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAe7C;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAuB1B;;OAEG;IACH,mBAAmB,IAAI,gBAAgB;IAIvC;;;OAGG;IACG,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAIxE;;OAEG;IACH,gBAAgB,IAAI,OAAO;IAI3B;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIzB;;OAEG;IACH,UAAU,IAAI,MAAM;IAIpB;;OAEG;IACH,iBAAiB,IAAI,MAAM;IAI3B;;OAEG;IACH,cAAc,IAAI,gBAAgB;IAIlC;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAIxC;;OAEG;IACH,aAAa,IAAI;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;IAI/C;;OAEG;IACH,OAAO,IAAI,IAAI;IAOf;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAOnC;;OAEG;IACH,oBAAoB,IAAI,iBAAiB,GAAG,SAAS;CAGtD"}
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/terminal/manager.ts"],"names":[],"mappings":"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"}
@@ -1,17 +1,28 @@
1
+ import { randomBytes } from "crypto";
1
2
  import { TerminalSession } from "./session.js";
2
3
  import { RecordingManager } from "../recording/index.js";
3
4
  import { getDefaultRecordDir } from "../utils/platform.js";
5
+ const DEFAULT_MAX_SESSIONS = 5;
6
+ const DEFAULT_SESSION_IDLE_TIMEOUT = 600; // 10 minutes
4
7
  /**
5
- * Manages the terminal session lifecycle
6
- * Currently supports a single session for simplicity
8
+ * Manages one or more terminal sessions in a single process.
9
+ *
10
+ * The "default" session is created lazily on first access and is never
11
+ * idle-evicted; it's what the existing single-session API operates on
12
+ * when no sessionId is passed. Additional sessions can be created via
13
+ * createSession() and addressed by ID.
7
14
  */
8
15
  export class TerminalManager {
9
- session = null;
10
- sessionPromise = null;
16
+ sessions = new Map();
17
+ defaultSessionId = null;
18
+ defaultSessionPromise = null;
19
+ idleCheckInterval = null;
11
20
  options;
12
21
  sandboxController;
13
22
  recordingManager;
14
23
  autoRecordingId = null;
24
+ maxSessions;
25
+ sessionIdleTimeoutMs;
15
26
  constructor(options = {}) {
16
27
  this.options = options;
17
28
  this.sandboxController = options.sandboxController;
@@ -23,73 +34,128 @@ export class TerminalManager {
23
34
  maxDuration: options.maxDuration ?? 3600,
24
35
  inactivityTimeout: options.inactivityTimeout ?? 600,
25
36
  });
37
+ this.maxSessions = options.maxSessions ?? DEFAULT_MAX_SESSIONS;
38
+ this.sessionIdleTimeoutMs = (options.sessionIdleTimeout ?? DEFAULT_SESSION_IDLE_TIMEOUT) * 1000;
39
+ // Periodic idle cleanup for non-default sessions
40
+ this.idleCheckInterval = setInterval(() => this.cleanupIdleSessions(), 60 * 1000);
41
+ this.idleCheckInterval.unref();
42
+ }
43
+ // ---------------------------------------------------------------------------
44
+ // Session lookup helpers
45
+ // ---------------------------------------------------------------------------
46
+ generateSessionId() {
47
+ while (true) {
48
+ const id = randomBytes(3).toString("hex").slice(0, 5);
49
+ if (!this.sessions.has(id))
50
+ return id;
51
+ }
52
+ }
53
+ touchSession(sessionId) {
54
+ const entry = this.sessions.get(sessionId);
55
+ if (entry) {
56
+ entry.lastActivity = Date.now();
57
+ entry.metadata.lastActivityAt = new Date().toISOString();
58
+ }
59
+ }
60
+ cleanupIdleSessions() {
61
+ const now = Date.now();
62
+ // Snapshot to avoid mutation-during-iteration
63
+ for (const [sessionId, entry] of Array.from(this.sessions.entries())) {
64
+ if (entry.isDefault)
65
+ continue;
66
+ if (now - entry.lastActivity > this.sessionIdleTimeoutMs) {
67
+ entry.session.dispose();
68
+ this.sessions.delete(sessionId);
69
+ }
70
+ }
26
71
  }
27
72
  /**
28
- * Get or create the terminal session (async)
73
+ * Wire recording hooks onto a session so its output is captured by any
74
+ * active recordings.
75
+ */
76
+ wireRecording(session) {
77
+ session.onData((data) => this.recordingManager.recordOutputToAll(data));
78
+ session.onResize((cols, rows) => this.recordingManager.recordResizeToAll(cols, rows));
79
+ }
80
+ // ---------------------------------------------------------------------------
81
+ // Default session (single-session API surface)
82
+ // ---------------------------------------------------------------------------
83
+ /**
84
+ * Get or create the default session. Idempotent and concurrency-safe.
29
85
  */
30
86
  async getSessionAsync() {
31
- if (this.session && this.session.isActive()) {
32
- return this.session;
87
+ if (this.defaultSessionId) {
88
+ const entry = this.sessions.get(this.defaultSessionId);
89
+ if (entry && entry.session.isActive()) {
90
+ this.touchSession(this.defaultSessionId);
91
+ return entry.session;
92
+ }
93
+ this.defaultSessionId = null;
33
94
  }
34
- // If there's already a creation in progress, wait for it
35
- if (this.sessionPromise) {
36
- return this.sessionPromise;
95
+ if (this.defaultSessionPromise) {
96
+ return this.defaultSessionPromise;
37
97
  }
38
- // Create new session
39
- this.sessionPromise = TerminalSession.create({
40
- ...this.options,
41
- sandboxController: this.sandboxController,
42
- });
98
+ this.defaultSessionPromise = (async () => {
99
+ const session = await TerminalSession.create({
100
+ ...this.options,
101
+ sandboxController: this.sandboxController,
102
+ });
103
+ const id = this.generateSessionId();
104
+ const dims = session.getDimensions();
105
+ const now = new Date().toISOString();
106
+ const entry = {
107
+ session,
108
+ metadata: {
109
+ sessionId: id,
110
+ shell: this.options.shell ?? process.env.SHELL ?? "/bin/bash",
111
+ cols: dims.cols,
112
+ rows: dims.rows,
113
+ createdAt: now,
114
+ lastActivityAt: now,
115
+ isDefault: true,
116
+ },
117
+ lastActivity: Date.now(),
118
+ isDefault: true,
119
+ };
120
+ this.sessions.set(id, entry);
121
+ this.defaultSessionId = id;
122
+ this.wireRecording(session);
123
+ return session;
124
+ })();
43
125
  try {
44
- this.session = await this.sessionPromise;
45
- return this.session;
126
+ return await this.defaultSessionPromise;
46
127
  }
47
128
  finally {
48
- this.sessionPromise = null;
129
+ this.defaultSessionPromise = null;
49
130
  }
50
131
  }
51
- /**
52
- * Get the current session if it exists and is active
53
- * Returns null if no session exists yet
54
- */
55
132
  getCurrentSession() {
56
- if (this.session && this.session.isActive()) {
57
- return this.session;
58
- }
59
- return null;
133
+ if (!this.defaultSessionId)
134
+ return null;
135
+ const entry = this.sessions.get(this.defaultSessionId);
136
+ return entry && entry.session.isActive() ? entry.session : null;
60
137
  }
61
138
  /**
62
- * @deprecated Use getSessionAsync() instead. This method exists for backwards compatibility
63
- * but will throw if the session hasn't been created yet.
139
+ * @deprecated Use getSessionAsync(). Throws if the default session
140
+ * hasn't been created yet.
64
141
  */
65
142
  getSession() {
66
- if (!this.session || !this.session.isActive()) {
143
+ const session = this.getCurrentSession();
144
+ if (!session) {
67
145
  throw new Error("Session not initialized. Use getSessionAsync() or call initSession() first.");
68
146
  }
69
- return this.session;
147
+ return session;
70
148
  }
71
- /**
72
- * Initialize the session eagerly
73
- * Call this at startup to create the session before it's needed
74
- */
75
149
  async initSession() {
76
150
  const session = await this.getSessionAsync();
77
- // Wire up recording hooks
78
- session.onData((data) => this.recordingManager.recordOutputToAll(data));
79
- session.onResize((cols, rows) => this.recordingManager.recordResizeToAll(cols, rows));
80
- // Start auto-recording if CLI mode !== 'off'
81
151
  if (this.options.record && this.options.record !== 'off') {
82
152
  this.startAutoRecording();
83
153
  }
84
154
  return session;
85
155
  }
86
- /**
87
- * Start auto-recording based on CLI options
88
- */
89
156
  startAutoRecording() {
90
- if (this.autoRecordingId) {
91
- return; // Already recording
92
- }
157
+ if (this.autoRecordingId)
158
+ return;
93
159
  const recorder = this.recordingManager.createRecording({
94
160
  mode: this.options.record,
95
161
  outputDir: this.options.recordDir ?? getDefaultRecordDir(),
@@ -98,95 +164,183 @@ export class TerminalManager {
98
164
  maxDuration: this.options.maxDuration ?? 3600,
99
165
  inactivityTimeout: this.options.inactivityTimeout ?? 600,
100
166
  });
101
- const dimensions = this.session?.getDimensions() ?? { cols: 120, rows: 40 };
167
+ const session = this.getCurrentSession();
168
+ const dimensions = session?.getDimensions() ?? { cols: 120, rows: 40 };
102
169
  recorder.start(dimensions.cols, dimensions.rows, {
103
170
  SHELL: this.options.shell ?? process.env.SHELL,
104
171
  TERM: 'xterm-256color',
105
172
  });
106
173
  this.autoRecordingId = recorder.id;
107
174
  }
175
+ // ---------------------------------------------------------------------------
176
+ // Multi-session API
177
+ // ---------------------------------------------------------------------------
108
178
  /**
109
- * Get the RecordingManager instance
179
+ * Create a new non-default session.
110
180
  */
111
- getRecordingManager() {
112
- return this.recordingManager;
181
+ async createSession(opts = {}) {
182
+ if (this.sessions.size >= this.maxSessions) {
183
+ throw new Error(`Maximum session limit reached (${this.maxSessions}). ` +
184
+ `Destroy an existing session or raise --max-sessions.`);
185
+ }
186
+ const id = this.generateSessionId();
187
+ const session = await TerminalSession.create({
188
+ ...this.options,
189
+ shell: opts.shell ?? this.options.shell,
190
+ cols: opts.cols ?? this.options.cols,
191
+ rows: opts.rows ?? this.options.rows,
192
+ // Created sessions don't share the interactive startup banner
193
+ startupBanner: undefined,
194
+ sandboxController: this.sandboxController,
195
+ });
196
+ const dims = session.getDimensions();
197
+ const now = new Date().toISOString();
198
+ const metadata = {
199
+ sessionId: id,
200
+ shell: opts.shell ?? this.options.shell ?? process.env.SHELL ?? "/bin/bash",
201
+ cols: dims.cols,
202
+ rows: dims.rows,
203
+ createdAt: now,
204
+ lastActivityAt: now,
205
+ isDefault: false,
206
+ };
207
+ this.sessions.set(id, {
208
+ session,
209
+ metadata,
210
+ lastActivity: Date.now(),
211
+ isDefault: false,
212
+ });
213
+ this.wireRecording(session);
214
+ return metadata;
215
+ }
216
+ destroySession(sessionId) {
217
+ const entry = this.sessions.get(sessionId);
218
+ if (!entry) {
219
+ return { success: false, message: `Session '${sessionId}' not found` };
220
+ }
221
+ if (entry.isDefault) {
222
+ return {
223
+ success: false,
224
+ message: `Session '${sessionId}' is the default session and cannot be destroyed`,
225
+ };
226
+ }
227
+ entry.session.dispose();
228
+ this.sessions.delete(sessionId);
229
+ return { success: true, message: `Session '${sessionId}' destroyed` };
230
+ }
231
+ listSessions() {
232
+ const sessions = [];
233
+ for (const [sessionId, entry] of Array.from(this.sessions.entries())) {
234
+ if (entry.session.isActive()) {
235
+ sessions.push({ ...entry.metadata });
236
+ }
237
+ else {
238
+ this.sessions.delete(sessionId);
239
+ }
240
+ }
241
+ return {
242
+ sessions,
243
+ maxSessions: this.maxSessions,
244
+ sessionIdleTimeout: this.sessionIdleTimeoutMs / 1000,
245
+ };
113
246
  }
114
247
  /**
115
- * Finalize all recordings and return their metadata
116
- * Called by the caller when the session exits
248
+ * Resolve a session by ID. If sessionId is omitted/undefined, returns the
249
+ * default session (creating it if needed).
117
250
  */
118
- async finalizeRecordings(exitCode) {
119
- return this.recordingManager.finalizeAll(exitCode);
251
+ async resolveSession(sessionId) {
252
+ if (!sessionId) {
253
+ return this.getSessionAsync();
254
+ }
255
+ const entry = this.sessions.get(sessionId);
256
+ if (!entry) {
257
+ throw new Error(`Session '${sessionId}' not found`);
258
+ }
259
+ if (!entry.session.isActive()) {
260
+ this.sessions.delete(sessionId);
261
+ throw new Error(`Session '${sessionId}' is no longer active`);
262
+ }
263
+ this.touchSession(sessionId);
264
+ return entry.session;
120
265
  }
121
266
  /**
122
- * Check if a session exists and is active
267
+ * Synchronous variant of resolveSession() requires the session to
268
+ * already exist. Throws if not created yet (used by sync tool handlers).
123
269
  */
270
+ resolveSessionSync(sessionId) {
271
+ if (!sessionId) {
272
+ return this.getSession();
273
+ }
274
+ const entry = this.sessions.get(sessionId);
275
+ if (!entry) {
276
+ throw new Error(`Session '${sessionId}' not found`);
277
+ }
278
+ if (!entry.session.isActive()) {
279
+ this.sessions.delete(sessionId);
280
+ throw new Error(`Session '${sessionId}' is no longer active`);
281
+ }
282
+ this.touchSession(sessionId);
283
+ return entry.session;
284
+ }
285
+ // ---------------------------------------------------------------------------
286
+ // Single-session shortcuts (now sessionId-aware)
287
+ // ---------------------------------------------------------------------------
124
288
  hasActiveSession() {
125
- return this.session !== null && this.session.isActive();
289
+ return this.getCurrentSession() !== null;
126
290
  }
127
- /**
128
- * Write data to the terminal
129
- */
130
- write(data) {
131
- this.getSession().write(data);
291
+ write(data, sessionId) {
292
+ this.resolveSessionSync(sessionId).write(data);
132
293
  }
133
- /**
134
- * Get terminal content
135
- */
136
- getContent() {
137
- return this.getSession().getContent();
294
+ getContent(sessionId) {
295
+ return this.resolveSessionSync(sessionId).getContent();
138
296
  }
139
- /**
140
- * Get visible content only
141
- */
142
- getVisibleContent() {
143
- return this.getSession().getVisibleContent();
297
+ getVisibleContent(sessionId) {
298
+ return this.resolveSessionSync(sessionId).getVisibleContent();
144
299
  }
145
- /**
146
- * Take a screenshot
147
- */
148
- takeScreenshot() {
149
- return this.getSession().takeScreenshot();
300
+ getAnsiContent(visibleOnly = false, sessionId) {
301
+ return this.resolveSessionSync(sessionId).getAnsiContent(visibleOnly);
150
302
  }
151
- /**
152
- * Clear the terminal
153
- */
154
- clear() {
155
- this.getSession().clear();
303
+ getTerminal(sessionId) {
304
+ return this.resolveSessionSync(sessionId).getTerminal();
156
305
  }
157
- /**
158
- * Resize the terminal
159
- */
160
- resize(cols, rows) {
161
- this.getSession().resize(cols, rows);
306
+ takeScreenshot(sessionId) {
307
+ return this.resolveSessionSync(sessionId).takeScreenshot();
162
308
  }
163
- /**
164
- * Get terminal dimensions
165
- */
166
- getDimensions() {
167
- return this.getSession().getDimensions();
309
+ clear(sessionId) {
310
+ this.resolveSessionSync(sessionId).clear();
311
+ }
312
+ resize(cols, rows, sessionId) {
313
+ this.resolveSessionSync(sessionId).resize(cols, rows);
314
+ }
315
+ getDimensions(sessionId) {
316
+ return this.resolveSessionSync(sessionId).getDimensions();
317
+ }
318
+ // ---------------------------------------------------------------------------
319
+ // Recording / lifecycle
320
+ // ---------------------------------------------------------------------------
321
+ getRecordingManager() {
322
+ return this.recordingManager;
323
+ }
324
+ async finalizeRecordings(exitCode) {
325
+ return this.recordingManager.finalizeAll(exitCode);
168
326
  }
169
- /**
170
- * Dispose of the current session and cleanup sandbox
171
- */
172
327
  dispose() {
173
- if (this.session) {
174
- this.session.dispose();
175
- this.session = null;
328
+ if (this.idleCheckInterval) {
329
+ clearInterval(this.idleCheckInterval);
330
+ this.idleCheckInterval = null;
331
+ }
332
+ for (const [, entry] of this.sessions) {
333
+ entry.session.dispose();
176
334
  }
335
+ this.sessions.clear();
336
+ this.defaultSessionId = null;
177
337
  }
178
- /**
179
- * Async dispose that also cleans up sandbox resources
180
- */
181
338
  async disposeAsync() {
182
339
  this.dispose();
183
340
  if (this.sandboxController) {
184
341
  await this.sandboxController.cleanup();
185
342
  }
186
343
  }
187
- /**
188
- * Get the sandbox controller if one is configured
189
- */
190
344
  getSandboxController() {
191
345
  return this.sandboxController;
192
346
  }
@@ -1 +1 @@
1
- {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/terminal/manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/terminal/manager.ts"],"names":[],"mappings":"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"}