@ellery/terminal-mcp 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/README.md +301 -9
  2. package/dist/client.d.ts.map +1 -1
  3. package/dist/client.js +2 -61
  4. package/dist/client.js.map +1 -1
  5. package/dist/index.js +286 -14
  6. package/dist/index.js.map +1 -1
  7. package/dist/prompts/index.d.ts.map +1 -1
  8. package/dist/prompts/index.js +8 -0
  9. package/dist/prompts/index.js.map +1 -1
  10. package/dist/recording/index.d.ts +4 -0
  11. package/dist/recording/index.d.ts.map +1 -0
  12. package/dist/recording/index.js +3 -0
  13. package/dist/recording/index.js.map +1 -0
  14. package/dist/recording/manager.d.ts +62 -0
  15. package/dist/recording/manager.d.ts.map +1 -0
  16. package/dist/recording/manager.js +123 -0
  17. package/dist/recording/manager.js.map +1 -0
  18. package/dist/recording/recorder.d.ts +95 -0
  19. package/dist/recording/recorder.d.ts.map +1 -0
  20. package/dist/recording/recorder.js +296 -0
  21. package/dist/recording/recorder.js.map +1 -0
  22. package/dist/recording/types.d.ts +65 -0
  23. package/dist/recording/types.d.ts.map +1 -0
  24. package/dist/recording/types.js +2 -0
  25. package/dist/recording/types.js.map +1 -0
  26. package/dist/sandbox/config.d.ts +46 -0
  27. package/dist/sandbox/config.d.ts.map +1 -0
  28. package/dist/sandbox/config.js +144 -0
  29. package/dist/sandbox/config.js.map +1 -0
  30. package/dist/sandbox/controller.d.ts +72 -0
  31. package/dist/sandbox/controller.d.ts.map +1 -0
  32. package/dist/sandbox/controller.js +208 -0
  33. package/dist/sandbox/controller.js.map +1 -0
  34. package/dist/sandbox/index.d.ts +6 -0
  35. package/dist/sandbox/index.d.ts.map +1 -0
  36. package/dist/sandbox/index.js +4 -0
  37. package/dist/sandbox/index.js.map +1 -0
  38. package/dist/sandbox/prompt.d.ts +10 -0
  39. package/dist/sandbox/prompt.d.ts.map +1 -0
  40. package/dist/sandbox/prompt.js +434 -0
  41. package/dist/sandbox/prompt.js.map +1 -0
  42. package/dist/server.d.ts +2 -3
  43. package/dist/server.d.ts.map +1 -1
  44. package/dist/server.js +21 -0
  45. package/dist/server.js.map +1 -1
  46. package/dist/terminal/index.d.ts +1 -0
  47. package/dist/terminal/index.d.ts.map +1 -1
  48. package/dist/terminal/manager.d.ts +89 -31
  49. package/dist/terminal/manager.d.ts.map +1 -1
  50. package/dist/terminal/manager.js +313 -44
  51. package/dist/terminal/manager.js.map +1 -1
  52. package/dist/terminal/session.d.ts +34 -1
  53. package/dist/terminal/session.d.ts.map +1 -1
  54. package/dist/terminal/session.js +160 -3
  55. package/dist/terminal/session.js.map +1 -1
  56. package/dist/tools/createSession.d.ts +44 -0
  57. package/dist/tools/createSession.d.ts.map +1 -0
  58. package/dist/tools/createSession.js +41 -0
  59. package/dist/tools/createSession.js.map +1 -0
  60. package/dist/tools/definitions.d.ts +18 -0
  61. package/dist/tools/definitions.d.ts.map +1 -0
  62. package/dist/tools/definitions.js +116 -0
  63. package/dist/tools/definitions.js.map +1 -0
  64. package/dist/tools/destroySession.d.ts +31 -0
  65. package/dist/tools/destroySession.d.ts.map +1 -0
  66. package/dist/tools/destroySession.js +32 -0
  67. package/dist/tools/destroySession.js.map +1 -0
  68. package/dist/tools/getContent.d.ts +7 -0
  69. package/dist/tools/getContent.d.ts.map +1 -1
  70. package/dist/tools/getContent.js +8 -3
  71. package/dist/tools/getContent.js.map +1 -1
  72. package/dist/tools/index.d.ts.map +1 -1
  73. package/dist/tools/index.js +26 -1
  74. package/dist/tools/index.js.map +1 -1
  75. package/dist/tools/listSessions.d.ts +19 -0
  76. package/dist/tools/listSessions.d.ts.map +1 -0
  77. package/dist/tools/listSessions.js +23 -0
  78. package/dist/tools/listSessions.js.map +1 -0
  79. package/dist/tools/screenshot.d.ts +26 -3
  80. package/dist/tools/screenshot.d.ts.map +1 -1
  81. package/dist/tools/screenshot.js +55 -5
  82. package/dist/tools/screenshot.js.map +1 -1
  83. package/dist/tools/sendKey.d.ts +7 -0
  84. package/dist/tools/sendKey.d.ts.map +1 -1
  85. package/dist/tools/sendKey.js +7 -2
  86. package/dist/tools/sendKey.js.map +1 -1
  87. package/dist/tools/startRecording.d.ts +68 -0
  88. package/dist/tools/startRecording.d.ts.map +1 -0
  89. package/dist/tools/startRecording.js +111 -0
  90. package/dist/tools/startRecording.js.map +1 -0
  91. package/dist/tools/stopRecording.d.ts +31 -0
  92. package/dist/tools/stopRecording.d.ts.map +1 -0
  93. package/dist/tools/stopRecording.js +76 -0
  94. package/dist/tools/stopRecording.js.map +1 -0
  95. package/dist/tools/type.d.ts +7 -0
  96. package/dist/tools/type.d.ts.map +1 -1
  97. package/dist/tools/type.js +7 -2
  98. package/dist/tools/type.js.map +1 -1
  99. package/dist/transport/socket.d.ts.map +1 -1
  100. package/dist/transport/socket.js +10 -0
  101. package/dist/transport/socket.js.map +1 -1
  102. package/dist/ui/index.d.ts +1 -0
  103. package/dist/ui/index.d.ts.map +1 -1
  104. package/dist/ui/index.js +17 -1
  105. package/dist/ui/index.js.map +1 -1
  106. package/dist/utils/platform.d.ts +6 -0
  107. package/dist/utils/platform.d.ts.map +1 -1
  108. package/dist/utils/platform.js +15 -0
  109. package/dist/utils/platform.js.map +1 -1
  110. package/dist/utils/render.d.ts +21 -0
  111. package/dist/utils/render.d.ts.map +1 -0
  112. package/dist/utils/render.js +162 -0
  113. package/dist/utils/render.js.map +1 -0
  114. package/package.json +6 -2
  115. package/sandbox-permissions.png +0 -0
@@ -1,54 +1,112 @@
1
1
  import { TerminalSession, TerminalSessionOptions, ScreenshotResult } from "./session.js";
2
+ import type { SandboxController } from "../sandbox/index.js";
3
+ import { RecordingManager } from "../recording/index.js";
4
+ import type { RecordingMode, RecordingFormat, RecordingMetadata } from "../recording/index.js";
5
+ export interface TerminalManagerOptions extends TerminalSessionOptions {
6
+ sandboxController?: SandboxController;
7
+ record?: RecordingMode;
8
+ recordDir?: string;
9
+ recordFormat?: RecordingFormat;
10
+ idleTimeLimit?: number;
11
+ maxDuration?: number;
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;
29
+ }
2
30
  /**
3
- * Manages the terminal session lifecycle
4
- * 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.
5
37
  */
6
38
  export declare class TerminalManager {
7
- private session;
39
+ private sessions;
40
+ private defaultSessionId;
41
+ private defaultSessionPromise;
42
+ private idleCheckInterval;
8
43
  private options;
9
- constructor(options?: TerminalSessionOptions);
10
- /**
11
- * Get or create the terminal session
12
- */
13
- getSession(): TerminalSession;
14
- /**
15
- * Check if a session exists and is active
16
- */
17
- hasActiveSession(): boolean;
44
+ private sandboxController?;
45
+ private recordingManager;
46
+ private autoRecordingId;
47
+ private maxSessions;
48
+ private sessionIdleTimeoutMs;
49
+ constructor(options?: TerminalManagerOptions);
50
+ private generateSessionId;
51
+ private touchSession;
52
+ private cleanupIdleSessions;
18
53
  /**
19
- * Write data to the terminal
54
+ * Wire recording hooks onto a session so its output is captured by any
55
+ * active recordings.
20
56
  */
21
- write(data: string): void;
57
+ private wireRecording;
22
58
  /**
23
- * Get terminal content
59
+ * Get or create the default session. Idempotent and concurrency-safe.
24
60
  */
25
- getContent(): string;
61
+ getSessionAsync(): Promise<TerminalSession>;
62
+ getCurrentSession(): TerminalSession | null;
26
63
  /**
27
- * Get visible content only
64
+ * @deprecated Use getSessionAsync(). Throws if the default session
65
+ * hasn't been created yet.
28
66
  */
29
- getVisibleContent(): string;
30
- /**
31
- * Take a screenshot
32
- */
33
- takeScreenshot(): ScreenshotResult;
67
+ getSession(): TerminalSession;
68
+ initSession(): Promise<TerminalSession>;
69
+ private startAutoRecording;
34
70
  /**
35
- * Clear the terminal
71
+ * Create a new non-default session.
36
72
  */
37
- clear(): void;
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
+ };
38
83
  /**
39
- * Resize the terminal
84
+ * Resolve a session by ID. If sessionId is omitted/undefined, returns the
85
+ * default session (creating it if needed).
40
86
  */
41
- resize(cols: number, rows: number): void;
87
+ resolveSession(sessionId?: string): Promise<TerminalSession>;
42
88
  /**
43
- * Get terminal dimensions
89
+ * Synchronous variant of resolveSession() — requires the session to
90
+ * already exist. Throws if not created yet (used by sync tool handlers).
44
91
  */
45
- getDimensions(): {
92
+ resolveSessionSync(sessionId?: string): TerminalSession;
93
+ hasActiveSession(): boolean;
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): {
46
103
  cols: number;
47
104
  rows: number;
48
105
  };
49
- /**
50
- * Dispose of the current session
51
- */
106
+ getRecordingManager(): RecordingManager;
107
+ finalizeRecordings(exitCode: number): Promise<RecordingMetadata[]>;
52
108
  dispose(): void;
109
+ disposeAsync(): Promise<void>;
110
+ getSandboxController(): SandboxController | undefined;
53
111
  }
54
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;AAEzF;;;GAGG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,OAAO,CAAgC;IAC/C,OAAO,CAAC,OAAO,CAAyB;gBAE5B,OAAO,GAAE,sBAA2B;IAIhD;;OAEG;IACH,UAAU,IAAI,eAAe;IAO7B;;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;CAMhB"}
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/terminal/manager.ts"],"names":[],"mappings":"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"}
@@ -1,79 +1,348 @@
1
+ import { randomBytes } from "crypto";
1
2
  import { TerminalSession } from "./session.js";
3
+ import { RecordingManager } from "../recording/index.js";
4
+ import { getDefaultRecordDir } from "../utils/platform.js";
5
+ const DEFAULT_MAX_SESSIONS = 5;
6
+ const DEFAULT_SESSION_IDLE_TIMEOUT = 600; // 10 minutes
2
7
  /**
3
- * Manages the terminal session lifecycle
4
- * 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.
5
14
  */
6
15
  export class TerminalManager {
7
- session = null;
16
+ sessions = new Map();
17
+ defaultSessionId = null;
18
+ defaultSessionPromise = null;
19
+ idleCheckInterval = null;
8
20
  options;
21
+ sandboxController;
22
+ recordingManager;
23
+ autoRecordingId = null;
24
+ maxSessions;
25
+ sessionIdleTimeoutMs;
9
26
  constructor(options = {}) {
10
27
  this.options = options;
28
+ this.sandboxController = options.sandboxController;
29
+ this.recordingManager = new RecordingManager({
30
+ mode: options.record ?? 'off',
31
+ outputDir: options.recordDir ?? getDefaultRecordDir(),
32
+ format: options.recordFormat ?? 'v2',
33
+ idleTimeLimit: options.idleTimeLimit ?? 2,
34
+ maxDuration: options.maxDuration ?? 3600,
35
+ inactivityTimeout: options.inactivityTimeout ?? 600,
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();
11
42
  }
12
- /**
13
- * Get or create the terminal session
14
- */
15
- getSession() {
16
- if (!this.session || !this.session.isActive()) {
17
- this.session = new TerminalSession(this.options);
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;
18
51
  }
19
- return this.session;
20
52
  }
21
- /**
22
- * Check if a session exists and is active
23
- */
24
- hasActiveSession() {
25
- return this.session !== null && this.session.isActive();
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
+ }
26
59
  }
27
- /**
28
- * Write data to the terminal
29
- */
30
- write(data) {
31
- this.getSession().write(data);
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
+ }
32
71
  }
33
72
  /**
34
- * Get terminal content
73
+ * Wire recording hooks onto a session so its output is captured by any
74
+ * active recordings.
35
75
  */
36
- getContent() {
37
- return this.getSession().getContent();
76
+ wireRecording(session) {
77
+ session.onData((data) => this.recordingManager.recordOutputToAll(data));
78
+ session.onResize((cols, rows) => this.recordingManager.recordResizeToAll(cols, rows));
38
79
  }
80
+ // ---------------------------------------------------------------------------
81
+ // Default session (single-session API surface)
82
+ // ---------------------------------------------------------------------------
39
83
  /**
40
- * Get visible content only
84
+ * Get or create the default session. Idempotent and concurrency-safe.
41
85
  */
42
- getVisibleContent() {
43
- return this.getSession().getVisibleContent();
86
+ async getSessionAsync() {
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;
94
+ }
95
+ if (this.defaultSessionPromise) {
96
+ return this.defaultSessionPromise;
97
+ }
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
+ })();
125
+ try {
126
+ return await this.defaultSessionPromise;
127
+ }
128
+ finally {
129
+ this.defaultSessionPromise = null;
130
+ }
44
131
  }
45
- /**
46
- * Take a screenshot
47
- */
48
- takeScreenshot() {
49
- return this.getSession().takeScreenshot();
132
+ getCurrentSession() {
133
+ if (!this.defaultSessionId)
134
+ return null;
135
+ const entry = this.sessions.get(this.defaultSessionId);
136
+ return entry && entry.session.isActive() ? entry.session : null;
50
137
  }
51
138
  /**
52
- * Clear the terminal
139
+ * @deprecated Use getSessionAsync(). Throws if the default session
140
+ * hasn't been created yet.
53
141
  */
54
- clear() {
55
- this.getSession().clear();
142
+ getSession() {
143
+ const session = this.getCurrentSession();
144
+ if (!session) {
145
+ throw new Error("Session not initialized. Use getSessionAsync() or call initSession() first.");
146
+ }
147
+ return session;
148
+ }
149
+ async initSession() {
150
+ const session = await this.getSessionAsync();
151
+ if (this.options.record && this.options.record !== 'off') {
152
+ this.startAutoRecording();
153
+ }
154
+ return session;
56
155
  }
156
+ startAutoRecording() {
157
+ if (this.autoRecordingId)
158
+ return;
159
+ const recorder = this.recordingManager.createRecording({
160
+ mode: this.options.record,
161
+ outputDir: this.options.recordDir ?? getDefaultRecordDir(),
162
+ format: this.options.recordFormat ?? 'v2',
163
+ idleTimeLimit: this.options.idleTimeLimit ?? 2,
164
+ maxDuration: this.options.maxDuration ?? 3600,
165
+ inactivityTimeout: this.options.inactivityTimeout ?? 600,
166
+ });
167
+ const session = this.getCurrentSession();
168
+ const dimensions = session?.getDimensions() ?? { cols: 120, rows: 40 };
169
+ recorder.start(dimensions.cols, dimensions.rows, {
170
+ SHELL: this.options.shell ?? process.env.SHELL,
171
+ TERM: 'xterm-256color',
172
+ });
173
+ this.autoRecordingId = recorder.id;
174
+ }
175
+ // ---------------------------------------------------------------------------
176
+ // Multi-session API
177
+ // ---------------------------------------------------------------------------
57
178
  /**
58
- * Resize the terminal
179
+ * Create a new non-default session.
59
180
  */
60
- resize(cols, rows) {
61
- this.getSession().resize(cols, rows);
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
+ };
62
246
  }
63
247
  /**
64
- * Get terminal dimensions
248
+ * Resolve a session by ID. If sessionId is omitted/undefined, returns the
249
+ * default session (creating it if needed).
65
250
  */
66
- getDimensions() {
67
- return this.getSession().getDimensions();
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;
68
265
  }
69
266
  /**
70
- * Dispose of the current session
267
+ * Synchronous variant of resolveSession() — requires the session to
268
+ * already exist. Throws if not created yet (used by sync tool handlers).
71
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
+ // ---------------------------------------------------------------------------
288
+ hasActiveSession() {
289
+ return this.getCurrentSession() !== null;
290
+ }
291
+ write(data, sessionId) {
292
+ this.resolveSessionSync(sessionId).write(data);
293
+ }
294
+ getContent(sessionId) {
295
+ return this.resolveSessionSync(sessionId).getContent();
296
+ }
297
+ getVisibleContent(sessionId) {
298
+ return this.resolveSessionSync(sessionId).getVisibleContent();
299
+ }
300
+ getAnsiContent(visibleOnly = false, sessionId) {
301
+ return this.resolveSessionSync(sessionId).getAnsiContent(visibleOnly);
302
+ }
303
+ getTerminal(sessionId) {
304
+ return this.resolveSessionSync(sessionId).getTerminal();
305
+ }
306
+ takeScreenshot(sessionId) {
307
+ return this.resolveSessionSync(sessionId).takeScreenshot();
308
+ }
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);
326
+ }
72
327
  dispose() {
73
- if (this.session) {
74
- this.session.dispose();
75
- 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();
76
334
  }
335
+ this.sessions.clear();
336
+ this.defaultSessionId = null;
337
+ }
338
+ async disposeAsync() {
339
+ this.dispose();
340
+ if (this.sandboxController) {
341
+ await this.sandboxController.cleanup();
342
+ }
343
+ }
344
+ getSandboxController() {
345
+ return this.sandboxController;
77
346
  }
78
347
  }
79
348
  //# sourceMappingURL=manager.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/terminal/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAA4C,MAAM,cAAc,CAAC;AAEzF;;;GAGG;AACH,MAAM,OAAO,eAAe;IAClB,OAAO,GAA2B,IAAI,CAAC;IACvC,OAAO,CAAyB;IAExC,YAAY,UAAkC,EAAE;QAC9C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED;;OAEG;IACH,UAAU;QACR,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;YAC9C,IAAI,CAAC,OAAO,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACnD,CAAC;QACD,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED;;OAEG;IACH,gBAAgB;QACd,OAAO,IAAI,CAAC,OAAO,KAAK,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;IAC1D,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAY;QAChB,IAAI,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,UAAU;QACR,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC,UAAU,EAAE,CAAC;IACxC,CAAC;IAED;;OAEG;IACH,iBAAiB;QACf,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC,iBAAiB,EAAE,CAAC;IAC/C,CAAC;IAED;;OAEG;IACH,cAAc;QACZ,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC,cAAc,EAAE,CAAC;IAC5C,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,CAAC;IAC5B,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,IAAY,EAAE,IAAY;QAC/B,IAAI,CAAC,UAAU,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACvC,CAAC;IAED;;OAEG;IACH,aAAa;QACX,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC,aAAa,EAAE,CAAC;IAC3C,CAAC;IAED;;OAEG;IACH,OAAO;QACL,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YACvB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACtB,CAAC;IACH,CAAC;CACF"}
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/terminal/manager.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,6 @@
1
+ import xtermHeadless from "@xterm/headless";
2
+ declare const Terminal: typeof xtermHeadless.Terminal;
3
+ import type { SandboxController } from "../sandbox/index.js";
1
4
  export interface TerminalSessionOptions {
2
5
  cols?: number;
3
6
  rows?: number;
@@ -5,6 +8,7 @@ export interface TerminalSessionOptions {
5
8
  cwd?: string;
6
9
  env?: Record<string, string>;
7
10
  startupBanner?: string;
11
+ sandboxController?: SandboxController;
8
12
  }
9
13
  export interface ScreenshotResult {
10
14
  content: string;
@@ -27,14 +31,28 @@ export declare class TerminalSession {
27
31
  private disposed;
28
32
  private dataListeners;
29
33
  private exitListeners;
34
+ private resizeListeners;
30
35
  private rcFile;
31
36
  private zdotdir;
37
+ /**
38
+ * Private constructor - use TerminalSession.create() instead
39
+ */
40
+ private constructor();
41
+ /**
42
+ * Factory method to create a TerminalSession
43
+ * Use this instead of the constructor to support async sandbox initialization
44
+ */
45
+ static create(options?: TerminalSessionOptions): Promise<TerminalSession>;
32
46
  /**
33
47
  * Set up shell-specific prompt customization
34
48
  * Returns args to pass to shell and env modifications
35
49
  */
36
50
  private setupShellPrompt;
37
- constructor(options?: TerminalSessionOptions);
51
+ /**
52
+ * Initialize the terminal session
53
+ * This is called by the create() factory method
54
+ */
55
+ private initialize;
38
56
  /**
39
57
  * Subscribe to PTY output data
40
58
  */
@@ -43,6 +61,10 @@ export declare class TerminalSession {
43
61
  * Subscribe to PTY exit
44
62
  */
45
63
  onExit(listener: (code: number) => void): void;
64
+ /**
65
+ * Subscribe to terminal resize events
66
+ */
67
+ onResize(listener: (cols: number, rows: number) => void): void;
46
68
  /**
47
69
  * Write data to the terminal (simulates typing)
48
70
  */
@@ -51,6 +73,11 @@ export declare class TerminalSession {
51
73
  * Get the current terminal buffer content as plain text
52
74
  */
53
75
  getContent(): string;
76
+ /**
77
+ * Get terminal content with ANSI color escape sequences preserved.
78
+ * Reads the xterm.js cell buffer and reconstructs SGR sequences.
79
+ */
80
+ getAnsiContent(visibleOnly?: boolean): string;
54
81
  /**
55
82
  * Get only the visible viewport content
56
83
  */
@@ -71,6 +98,11 @@ export declare class TerminalSession {
71
98
  * Check if the session is still active
72
99
  */
73
100
  isActive(): boolean;
101
+ /**
102
+ * Get the underlying xterm.js Terminal instance for direct buffer access.
103
+ * Used by the color screenshot renderer.
104
+ */
105
+ getTerminal(): InstanceType<typeof Terminal>;
74
106
  /**
75
107
  * Get terminal dimensions
76
108
  */
@@ -83,4 +115,5 @@ export declare class TerminalSession {
83
115
  */
84
116
  dispose(): void;
85
117
  }
118
+ export {};
86
119
  //# sourceMappingURL=session.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/terminal/session.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE;QACN,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;KACX,CAAC;IACF,UAAU,EAAE;QACV,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED;;;GAGG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,UAAU,CAAW;IAC7B,OAAO,CAAC,QAAQ,CAAgC;IAChD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,aAAa,CAAqC;IAC1D,OAAO,CAAC,aAAa,CAAqC;IAE1D,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,OAAO,CAAuB;IAEtC;;;OAGG;IACH,OAAO,CAAC,gBAAgB;gBA6EZ,OAAO,GAAE,sBAA2B;IA6ChD;;OAEG;IACH,MAAM,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAI9C;;OAEG;IACH,MAAM,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAI9C;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAOzB;;OAEG;IACH,UAAU,IAAI,MAAM;IAwBpB;;OAEG;IACH,iBAAiB,IAAI,MAAM;IAmB3B;;OAEG;IACH,cAAc,IAAI,gBAAgB;IAoBlC;;OAEG;IACH,KAAK,IAAI,IAAI;IAOb;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAQxC;;OAEG;IACH,QAAQ,IAAI,OAAO;IAInB;;OAEG;IACH,aAAa,IAAI;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;IAO/C;;OAEG;IACH,OAAO,IAAI,IAAI;CAuBhB"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/terminal/session.ts"],"names":[],"mappings":"AAIA,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAC5C,QAAA,MAAQ,QAAQ,+BAAkB,CAAC;AAEnC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAK7D,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACvC;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE;QACN,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;KACX,CAAC;IACF,UAAU,EAAE;QACV,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED;;;GAGG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,UAAU,CAAY;IAC9B,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,aAAa,CAAqC;IAC1D,OAAO,CAAC,aAAa,CAAqC;IAC1D,OAAO,CAAC,eAAe,CAAmD;IAE1E,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,OAAO,CAAuB;IAEtC;;OAEG;IACH,OAAO;IAEP;;;OAGG;WACU,MAAM,CAAC,OAAO,GAAE,sBAA2B,GAAG,OAAO,CAAC,eAAe,CAAC;IAMnF;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IA6ExB;;;OAGG;YACW,UAAU;IA6DxB;;OAEG;IACH,MAAM,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAI9C;;OAEG;IACH,MAAM,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAI9C;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAI9D;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAOzB;;OAEG;IACH,UAAU,IAAI,MAAM;IAwBpB;;;OAGG;IACH,cAAc,CAAC,WAAW,UAAQ,GAAG,MAAM;IAiH3C;;OAEG;IACH,iBAAiB,IAAI,MAAM;IAmB3B;;OAEG;IACH,cAAc,IAAI,gBAAgB;IAoBlC;;OAEG;IACH,KAAK,IAAI,IAAI;IAOb;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAaxC;;OAEG;IACH,QAAQ,IAAI,OAAO;IAInB;;;OAGG;IACH,WAAW,IAAI,YAAY,CAAC,OAAO,QAAQ,CAAC;IAO5C;;OAEG;IACH,aAAa,IAAI;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;IAO/C;;OAEG;IACH,OAAO,IAAI,IAAI;CAuBhB"}