@promptbook/cli 0.112.0-97 → 0.112.0-98

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 (60) hide show
  1. package/apps/agents-server/README.md +3 -3
  2. package/apps/agents-server/src/app/admin/cli-access/CliAccessClient.tsx +99 -0
  3. package/apps/agents-server/src/app/admin/cli-access/page.tsx +14 -0
  4. package/apps/agents-server/src/app/admin/code-runners/CodeRunnersClient.tsx +76 -325
  5. package/apps/agents-server/src/app/admin/database/page.tsx +1 -2
  6. package/apps/agents-server/src/app/agents/[agentName]/chat/CanonicalAgentChatSurface.tsx +24 -0
  7. package/apps/agents-server/src/app/api/admin/cli-access/route.ts +137 -0
  8. package/apps/agents-server/src/app/api/admin/code-runners/authentication/route.ts +7 -64
  9. package/apps/agents-server/src/app/api/admin/servers/[serverId]/route.ts +3 -3
  10. package/apps/agents-server/src/app/api/admin/servers/route.ts +4 -4
  11. package/apps/agents-server/src/app/api/chat/export/pdf/route.ts +63 -0
  12. package/apps/agents-server/src/components/AdminTerminal/AdminTerminalCard.tsx +279 -0
  13. package/apps/agents-server/src/components/AdminTerminal/useAdminTerminalSession.ts +336 -0
  14. package/apps/agents-server/src/components/Header/buildHeaderSystemMenuItems.ts +4 -0
  15. package/apps/agents-server/src/database/$provideClientSql.ts +17 -4
  16. package/apps/agents-server/src/database/$provideDatabaseAdminExecutor.ts +24 -3
  17. package/apps/agents-server/src/database/$provideSupabaseForServer.ts +1 -11
  18. package/apps/agents-server/src/database/agentsServerDatabaseMode.ts +1 -20
  19. package/apps/agents-server/src/languages/ServerTranslationKeys.ts +1 -0
  20. package/apps/agents-server/src/languages/translations/czech.yaml +1 -0
  21. package/apps/agents-server/src/languages/translations/english.yaml +1 -0
  22. package/apps/agents-server/src/tools/$provideServer.ts +2 -2
  23. package/apps/agents-server/src/tools/BrowserConnectionProvider.ts +1 -1
  24. package/apps/agents-server/src/utils/chatExport/downloadChatPdfFromServer.ts +59 -0
  25. package/apps/agents-server/src/utils/chatExport/renderHtmlToPdfOnServer.ts +37 -0
  26. package/apps/agents-server/src/utils/codeRunnerAuthentication.ts +77 -237
  27. package/apps/agents-server/src/utils/createInteractiveTerminalEventStream.ts +84 -0
  28. package/apps/agents-server/src/utils/interactiveTerminalSession.ts +442 -0
  29. package/apps/agents-server/src/utils/serverCliAccess.ts +221 -0
  30. package/apps/agents-server/src/utils/serverRegistry.ts +4 -4
  31. package/apps/agents-server/src/utils/vpsConfiguration.ts +2 -0
  32. package/esm/apps/agents-server/src/database/agentsServerDatabaseMode.d.ts +1 -9
  33. package/esm/index.es.js +2 -2
  34. package/esm/src/book-components/Chat/Chat/ChatActionsBar.d.ts +2 -0
  35. package/esm/src/book-components/Chat/Chat/ChatProps.d.ts +6 -0
  36. package/esm/src/book-components/Chat/save/_common/ChatSaveFormatHandler.d.ts +35 -0
  37. package/esm/src/book-components/Chat/save/_common/createChatExportFilename.d.ts +11 -0
  38. package/esm/src/version.d.ts +1 -1
  39. package/package.json +1 -1
  40. package/src/book-components/Chat/Chat/Chat.tsx +2 -0
  41. package/src/book-components/Chat/Chat/ChatActionsBar.tsx +17 -9
  42. package/src/book-components/Chat/Chat/ChatProps.tsx +7 -0
  43. package/src/book-components/Chat/save/_common/ChatSaveFormatHandler.ts +40 -0
  44. package/src/book-components/Chat/save/_common/createChatExportFilename.ts +20 -0
  45. package/src/cli/cli-commands/agents-server/ensureAgentsServerEnvFile.ts +1 -1
  46. package/src/other/templates/getTemplatesPipelineCollection.ts +721 -736
  47. package/src/version.ts +2 -2
  48. package/src/versions.txt +1 -0
  49. package/umd/apps/agents-server/src/database/agentsServerDatabaseMode.d.ts +1 -9
  50. package/umd/index.umd.js +2 -2
  51. package/umd/src/book-components/Chat/Chat/ChatActionsBar.d.ts +2 -0
  52. package/umd/src/book-components/Chat/Chat/ChatProps.d.ts +6 -0
  53. package/umd/src/book-components/Chat/save/_common/ChatSaveFormatHandler.d.ts +35 -0
  54. package/umd/src/book-components/Chat/save/_common/createChatExportFilename.d.ts +11 -0
  55. package/umd/src/version.d.ts +1 -1
  56. package/apps/agents-server/src/database/$providePostgresPool.ts +0 -27
  57. package/apps/agents-server/src/database/postgres/$provideLocalPostgresSupabase.ts +0 -1261
  58. package/src/conversion/validation/_importPipeline.ts +0 -88
  59. /package/esm/src/conversion/validation/{_importPipeline.d.ts → _importPipeline.test.d.ts} +0 -0
  60. /package/umd/src/conversion/validation/{_importPipeline.d.ts → _importPipeline.test.d.ts} +0 -0
@@ -1,11 +1,14 @@
1
- import { spawn, type ChildProcessWithoutNullStreams } from 'child_process';
2
- import { randomUUID } from 'crypto';
3
- import { EventEmitter } from 'events';
4
- import { spaceTrim } from 'spacetrim';
5
1
  import { createVpsInstallerCommandEnvironment, resolveVpsInstallerScriptPath } from './vpsConfiguration';
6
-
7
- const MAX_SESSION_OUTPUT_LENGTH = 200_000;
8
- const SESSION_RETENTION_MILLISECONDS = 30 * 60 * 1000;
2
+ import {
3
+ getInteractiveTerminalSession,
4
+ getLatestInteractiveTerminalSession,
5
+ startInteractiveTerminalSession,
6
+ stopInteractiveTerminalSession,
7
+ subscribeToInteractiveTerminalSession,
8
+ type InteractiveTerminalSessionSnapshot,
9
+ type InteractiveTerminalSessionSubscriber,
10
+ writeInteractiveTerminalSessionInput,
11
+ } from './interactiveTerminalSession';
9
12
 
10
13
  /**
11
14
  * Serializable snapshot of one interactive code-runner authentication session.
@@ -52,50 +55,6 @@ export type CodeRunnerAuthenticationSessionSnapshot = {
52
55
  readonly signal: NodeJS.Signals | null;
53
56
  };
54
57
 
55
- /**
56
- * Output event emitted while the authentication terminal is running.
57
- */
58
- type CodeRunnerAuthenticationOutputEvent = {
59
- readonly type: 'output';
60
- readonly chunk: string;
61
- };
62
-
63
- /**
64
- * Exit event emitted when the authentication terminal finishes.
65
- */
66
- type CodeRunnerAuthenticationExitEvent = {
67
- readonly type: 'exit';
68
- readonly snapshot: CodeRunnerAuthenticationSessionSnapshot;
69
- };
70
-
71
- /**
72
- * Internal mutable session state stored in-process.
73
- */
74
- type CodeRunnerAuthenticationSession = {
75
- readonly id: string;
76
- readonly agent: string;
77
- readonly process: ChildProcessWithoutNullStreams;
78
- readonly events: EventEmitter<{
79
- output: [CodeRunnerAuthenticationOutputEvent];
80
- exit: [CodeRunnerAuthenticationExitEvent];
81
- }>;
82
- readonly startedAt: Date;
83
- cleanupTimeout: NodeJS.Timeout | null;
84
- output: string;
85
- isRunning: boolean;
86
- finishedAt: Date | null;
87
- exitCode: number | null;
88
- signal: NodeJS.Signals | null;
89
- };
90
-
91
- /**
92
- * Shared in-memory state reused across requests in the standalone server process.
93
- */
94
- type CodeRunnerAuthenticationState = {
95
- readonly sessionsById: Map<string, CodeRunnerAuthenticationSession>;
96
- readonly latestSessionIdByAgent: Map<string, string>;
97
- };
98
-
99
58
  /**
100
59
  * Browser stream callbacks used by one subscribed UI client.
101
60
  */
@@ -103,12 +62,12 @@ export type CodeRunnerAuthenticationSessionSubscriber = {
103
62
  /**
104
63
  * Called whenever new terminal output arrives.
105
64
  */
106
- readonly onOutput: (event: CodeRunnerAuthenticationOutputEvent) => void;
65
+ readonly onOutput: (event: Parameters<InteractiveTerminalSessionSubscriber['onOutput']>[0]) => void;
107
66
 
108
67
  /**
109
68
  * Called once the session exits.
110
69
  */
111
- readonly onExit: (event: CodeRunnerAuthenticationExitEvent) => void;
70
+ readonly onExit: (event: { readonly type: 'exit'; readonly snapshot: CodeRunnerAuthenticationSessionSnapshot }) => void;
112
71
  };
113
72
 
114
73
  /**
@@ -120,64 +79,28 @@ export type CodeRunnerAuthenticationSessionSubscriber = {
120
79
  export async function startCodeRunnerAuthenticationSession(
121
80
  agent: string,
122
81
  ): Promise<CodeRunnerAuthenticationSessionSnapshot> {
123
- const existingSession = getLatestCodeRunnerAuthenticationSession(agent);
124
- if (existingSession?.isRunning) {
125
- return existingSession;
126
- }
127
-
128
- if (process.platform !== 'linux') {
129
- throw new Error('Interactive code-runner authentication is available only on the Linux VPS runtime.');
130
- }
131
-
132
82
  const scriptPath = await resolveVpsInstallerScriptPath();
133
83
  if (!scriptPath) {
134
84
  throw new Error('The VPS installer script could not be found on this server.');
135
85
  }
136
86
 
137
- const sessionId = randomUUID();
138
- const childProcess = spawn('bash', [scriptPath, 'authenticate-runner'], {
139
- env: createVpsInstallerCommandEnvironment({
140
- isNonInteractiveModeEnabled: false,
141
- isProcessRestartEnabled: false,
87
+ return toRequiredCodeRunnerAuthenticationSessionSnapshot(
88
+ startInteractiveTerminalSession({
89
+ sessionKey: buildCodeRunnerAuthenticationSessionKey(agent),
90
+ title: `${agent} authentication`,
91
+ command: 'bash',
92
+ arguments: [scriptPath, 'authenticate-runner'],
93
+ env: createVpsInstallerCommandEnvironment({
94
+ isNonInteractiveModeEnabled: false,
95
+ isProcessRestartEnabled: false,
96
+ }),
97
+ metadata: {
98
+ agent,
99
+ },
100
+ unavailableErrorMessage: 'Interactive code-runner authentication is available only on the Linux VPS runtime.',
142
101
  }),
143
- stdio: ['pipe', 'pipe', 'pipe'],
144
- });
145
-
146
- const session: CodeRunnerAuthenticationSession = {
147
- id: sessionId,
148
102
  agent,
149
- process: childProcess,
150
- events: new EventEmitter(),
151
- startedAt: new Date(),
152
- cleanupTimeout: null,
153
- output: '',
154
- isRunning: true,
155
- finishedAt: null,
156
- exitCode: null,
157
- signal: null,
158
- };
159
-
160
- const state = getCodeRunnerAuthenticationState();
161
- state.sessionsById.set(sessionId, session);
162
- state.latestSessionIdByAgent.set(agent, sessionId);
163
-
164
- childProcess.stdout.setEncoding('utf-8');
165
- childProcess.stderr.setEncoding('utf-8');
166
- childProcess.stdout.on('data', (chunk: string) => appendCodeRunnerAuthenticationOutput(session, chunk));
167
- childProcess.stderr.on('data', (chunk: string) => appendCodeRunnerAuthenticationOutput(session, chunk));
168
- childProcess.on('close', (exitCode, signal) => finalizeCodeRunnerAuthenticationSession(session, exitCode, signal));
169
- childProcess.on('error', (error) => {
170
- appendCodeRunnerAuthenticationOutput(
171
- session,
172
- spaceTrim(`
173
- Failed to start the code-runner authentication process.
174
-
175
- ${error.message}
176
- `) + '\n',
177
- );
178
- });
179
-
180
- return createCodeRunnerAuthenticationSessionSnapshot(session);
103
+ );
181
104
  }
182
105
 
183
106
  /**
@@ -189,12 +112,10 @@ export async function startCodeRunnerAuthenticationSession(
189
112
  export function getLatestCodeRunnerAuthenticationSession(
190
113
  agent: string,
191
114
  ): CodeRunnerAuthenticationSessionSnapshot | null {
192
- const sessionId = getCodeRunnerAuthenticationState().latestSessionIdByAgent.get(agent);
193
- if (!sessionId) {
194
- return null;
195
- }
196
-
197
- return getCodeRunnerAuthenticationSession(sessionId);
115
+ return toCodeRunnerAuthenticationSessionSnapshot(
116
+ getLatestInteractiveTerminalSession(buildCodeRunnerAuthenticationSessionKey(agent)),
117
+ agent,
118
+ );
198
119
  }
199
120
 
200
121
  /**
@@ -206,8 +127,7 @@ export function getLatestCodeRunnerAuthenticationSession(
206
127
  export function getCodeRunnerAuthenticationSession(
207
128
  sessionId: string,
208
129
  ): CodeRunnerAuthenticationSessionSnapshot | null {
209
- const session = getCodeRunnerAuthenticationState().sessionsById.get(sessionId);
210
- return session ? createCodeRunnerAuthenticationSessionSnapshot(session) : null;
130
+ return toCodeRunnerAuthenticationSessionSnapshot(getInteractiveTerminalSession(sessionId));
211
131
  }
212
132
 
213
133
  /**
@@ -221,18 +141,14 @@ export function subscribeToCodeRunnerAuthenticationSession(
221
141
  sessionId: string,
222
142
  subscriber: CodeRunnerAuthenticationSessionSubscriber,
223
143
  ): (() => void) | null {
224
- const session = getCodeRunnerAuthenticationState().sessionsById.get(sessionId);
225
- if (!session) {
226
- return null;
227
- }
228
-
229
- session.events.on('output', subscriber.onOutput);
230
- session.events.on('exit', subscriber.onExit);
231
-
232
- return () => {
233
- session.events.off('output', subscriber.onOutput);
234
- session.events.off('exit', subscriber.onExit);
235
- };
144
+ return subscribeToInteractiveTerminalSession(sessionId, {
145
+ onOutput: subscriber.onOutput,
146
+ onExit: ({ snapshot }) =>
147
+ subscriber.onExit({
148
+ type: 'exit',
149
+ snapshot: toRequiredCodeRunnerAuthenticationSessionSnapshot(snapshot),
150
+ }),
151
+ });
236
152
  }
237
153
 
238
154
  /**
@@ -246,14 +162,7 @@ export function writeCodeRunnerAuthenticationSessionInput(
246
162
  sessionId: string,
247
163
  input: string,
248
164
  ): CodeRunnerAuthenticationSessionSnapshot {
249
- const session = getRequiredCodeRunnerAuthenticationSession(sessionId);
250
-
251
- if (!session.isRunning) {
252
- throw new Error('The authentication session has already finished.');
253
- }
254
-
255
- session.process.stdin.write(input);
256
- return createCodeRunnerAuthenticationSessionSnapshot(session);
165
+ return toRequiredCodeRunnerAuthenticationSessionSnapshot(writeInteractiveTerminalSessionInput(sessionId, input));
257
166
  }
258
167
 
259
168
  /**
@@ -265,130 +174,61 @@ export function writeCodeRunnerAuthenticationSessionInput(
265
174
  export function stopCodeRunnerAuthenticationSession(
266
175
  sessionId: string,
267
176
  ): CodeRunnerAuthenticationSessionSnapshot {
268
- const session = getRequiredCodeRunnerAuthenticationSession(sessionId);
269
-
270
- if (session.isRunning) {
271
- session.process.kill('SIGTERM');
272
- }
273
-
274
- return createCodeRunnerAuthenticationSessionSnapshot(session);
177
+ return toRequiredCodeRunnerAuthenticationSessionSnapshot(stopInteractiveTerminalSession(sessionId));
275
178
  }
276
179
 
277
180
  /**
278
- * Returns the mutable singleton state for authentication sessions.
181
+ * Builds the stable logical session key for one runner authentication terminal.
279
182
  *
280
- * @returns Shared in-memory session state.
183
+ * @param agent - Runner identifier.
184
+ * @returns Stable session key.
281
185
  */
282
- function getCodeRunnerAuthenticationState(): CodeRunnerAuthenticationState {
283
- const globalState = globalThis as typeof globalThis & {
284
- __promptbookCodeRunnerAuthenticationState?: CodeRunnerAuthenticationState;
285
- };
286
-
287
- if (!globalState.__promptbookCodeRunnerAuthenticationState) {
288
- globalState.__promptbookCodeRunnerAuthenticationState = {
289
- sessionsById: new Map(),
290
- latestSessionIdByAgent: new Map(),
291
- };
292
- }
293
-
294
- return globalState.__promptbookCodeRunnerAuthenticationState;
186
+ function buildCodeRunnerAuthenticationSessionKey(agent: string): string {
187
+ return `code-runner-authentication:${agent}`;
295
188
  }
296
189
 
297
190
  /**
298
- * Appends terminal output while keeping the buffer size bounded.
191
+ * Converts one generic terminal snapshot into the runner-specific browser shape.
299
192
  *
300
- * @param session - Active authentication session.
301
- * @param rawChunk - Terminal chunk from stdout or stderr.
193
+ * @param session - Generic terminal snapshot.
194
+ * @param fallbackAgent - Agent name used when older sessions miss metadata.
195
+ * @returns Runner-specific snapshot or `null`.
302
196
  */
303
- function appendCodeRunnerAuthenticationOutput(
304
- session: CodeRunnerAuthenticationSession,
305
- rawChunk: string | Buffer,
306
- ): void {
307
- const chunk = normalizeCodeRunnerAuthenticationOutput(rawChunk.toString());
308
- if (!chunk) {
309
- return;
197
+ function toCodeRunnerAuthenticationSessionSnapshot(
198
+ session: InteractiveTerminalSessionSnapshot | null,
199
+ fallbackAgent = '',
200
+ ): CodeRunnerAuthenticationSessionSnapshot | null {
201
+ if (!session) {
202
+ return null;
310
203
  }
311
204
 
312
- session.output = (session.output + chunk).slice(-MAX_SESSION_OUTPUT_LENGTH);
313
- session.events.emit('output', {
314
- type: 'output',
315
- chunk,
316
- });
317
- }
318
-
319
- /**
320
- * Normalizes streamed terminal output for browser rendering.
321
- *
322
- * @param output - Raw process output chunk.
323
- * @returns UI-friendly terminal text.
324
- */
325
- function normalizeCodeRunnerAuthenticationOutput(output: string): string {
326
- return output.replace(/\r\n/gu, '\n').replace(/\r/gu, '\n');
205
+ return {
206
+ id: session.id,
207
+ agent: session.metadata.agent || fallbackAgent,
208
+ isRunning: session.isRunning,
209
+ output: session.output,
210
+ startedAt: session.startedAt,
211
+ finishedAt: session.finishedAt,
212
+ exitCode: session.exitCode,
213
+ signal: session.signal,
214
+ };
327
215
  }
328
216
 
329
217
  /**
330
- * Marks one authentication session as finished and schedules retention cleanup.
218
+ * Converts one generic terminal snapshot into a required runner-specific snapshot.
331
219
  *
332
- * @param session - Finished authentication session.
333
- * @param exitCode - Process exit code.
334
- * @param signal - Process signal.
220
+ * @param session - Generic terminal snapshot.
221
+ * @returns Runner-specific snapshot.
335
222
  */
336
- function finalizeCodeRunnerAuthenticationSession(
337
- session: CodeRunnerAuthenticationSession,
338
- exitCode: number | null,
339
- signal: NodeJS.Signals | null,
340
- ): void {
341
- session.isRunning = false;
342
- session.finishedAt = new Date();
343
- session.exitCode = exitCode;
344
- session.signal = signal;
345
-
346
- session.events.emit('exit', {
347
- type: 'exit',
348
- snapshot: createCodeRunnerAuthenticationSessionSnapshot(session),
349
- });
350
-
351
- if (session.cleanupTimeout) {
352
- clearTimeout(session.cleanupTimeout);
353
- }
354
-
355
- session.cleanupTimeout = setTimeout(() => {
356
- getCodeRunnerAuthenticationState().sessionsById.delete(session.id);
357
- }, SESSION_RETENTION_MILLISECONDS);
358
- }
223
+ function toRequiredCodeRunnerAuthenticationSessionSnapshot(
224
+ session: InteractiveTerminalSessionSnapshot | null,
225
+ fallbackAgent = '',
226
+ ): CodeRunnerAuthenticationSessionSnapshot {
227
+ const mappedSession = toCodeRunnerAuthenticationSessionSnapshot(session, fallbackAgent);
359
228
 
360
- /**
361
- * Reads one required mutable session and throws when missing.
362
- *
363
- * @param sessionId - Session identifier.
364
- * @returns Mutable session state.
365
- */
366
- function getRequiredCodeRunnerAuthenticationSession(sessionId: string): CodeRunnerAuthenticationSession {
367
- const session = getCodeRunnerAuthenticationState().sessionsById.get(sessionId);
368
- if (!session) {
229
+ if (!mappedSession) {
369
230
  throw new Error('Authentication session was not found.');
370
231
  }
371
232
 
372
- return session;
373
- }
374
-
375
- /**
376
- * Converts mutable session state into a browser-safe snapshot.
377
- *
378
- * @param session - Mutable in-memory session.
379
- * @returns Serializable session snapshot.
380
- */
381
- function createCodeRunnerAuthenticationSessionSnapshot(
382
- session: CodeRunnerAuthenticationSession,
383
- ): CodeRunnerAuthenticationSessionSnapshot {
384
- return {
385
- id: session.id,
386
- agent: session.agent,
387
- isRunning: session.isRunning,
388
- output: session.output,
389
- startedAt: session.startedAt.toISOString(),
390
- finishedAt: session.finishedAt ? session.finishedAt.toISOString() : null,
391
- exitCode: session.exitCode,
392
- signal: session.signal,
393
- };
233
+ return mappedSession;
394
234
  }
@@ -0,0 +1,84 @@
1
+ import type { InteractiveTerminalSessionSubscriber } from './interactiveTerminalSession';
2
+
3
+ /**
4
+ * Minimal browser-safe terminal session shape needed by the shared SSE helper.
5
+ */
6
+ type InteractiveTerminalEventStreamSession = {
7
+ readonly isRunning: boolean;
8
+ };
9
+
10
+ /**
11
+ * Generic subscriber shape used by one streamed browser terminal route.
12
+ */
13
+ type InteractiveTerminalEventStreamSubscriber<TSession extends InteractiveTerminalEventStreamSession> = {
14
+ readonly onOutput: InteractiveTerminalSessionSubscriber['onOutput'];
15
+ readonly onExit: (event: { readonly type: 'exit'; readonly snapshot: TSession }) => void;
16
+ };
17
+
18
+ /**
19
+ * Creates one SSE response that replays buffered terminal output and then streams live events.
20
+ *
21
+ * @param request - Browser stream request.
22
+ * @param sessionId - Terminal session id.
23
+ * @param session - Existing session snapshot.
24
+ * @param subscribe - Subscription function for live terminal events.
25
+ * @returns Event stream response.
26
+ */
27
+ export function createInteractiveTerminalEventStream<TSession extends InteractiveTerminalEventStreamSession>(
28
+ request: Request,
29
+ sessionId: string,
30
+ session: TSession,
31
+ subscribe: (
32
+ sessionId: string,
33
+ subscriber: InteractiveTerminalEventStreamSubscriber<TSession>,
34
+ ) => (() => void) | null,
35
+ ): Response {
36
+ const encoder = new TextEncoder();
37
+
38
+ return new Response(
39
+ new ReadableStream({
40
+ start(controller) {
41
+ const emitEvent = (event: string, payload: unknown) => {
42
+ controller.enqueue(encoder.encode(`event: ${event}\ndata: ${JSON.stringify(payload)}\n\n`));
43
+ };
44
+
45
+ emitEvent('snapshot', session);
46
+
47
+ if (!session.isRunning) {
48
+ controller.close();
49
+ return;
50
+ }
51
+
52
+ const unsubscribe = subscribe(sessionId, {
53
+ onOutput: ({ chunk }) => emitEvent('output', { chunk }),
54
+ onExit: ({ snapshot: nextSession }) => {
55
+ emitEvent('exit', nextSession);
56
+ unsubscribe?.();
57
+ controller.close();
58
+ },
59
+ });
60
+
61
+ if (!unsubscribe) {
62
+ controller.error(new Error('Terminal session was not found.'));
63
+ return;
64
+ }
65
+
66
+ request.signal.addEventListener(
67
+ 'abort',
68
+ () => {
69
+ unsubscribe();
70
+ controller.close();
71
+ },
72
+ { once: true },
73
+ );
74
+ },
75
+ }),
76
+ {
77
+ headers: {
78
+ 'Content-Type': 'text/event-stream',
79
+ 'Cache-Control': 'no-cache, no-transform',
80
+ Connection: 'keep-alive',
81
+ },
82
+ },
83
+ );
84
+ }