@kapeta/local-cluster-service 0.62.1 → 0.62.2

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 CHANGED
@@ -1,3 +1,10 @@
1
+ ## [0.62.2](https://github.com/kapetacom/local-cluster-service/compare/v0.62.1...v0.62.2) (2024-08-14)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Single page adjustments ([#217](https://github.com/kapetacom/local-cluster-service/issues/217)) ([8d0f7e6](https://github.com/kapetacom/local-cluster-service/commit/8d0f7e6b9a3fdd971559a4c9b06b78db38145b4d))
7
+
1
8
  ## [0.62.1](https://github.com/kapetacom/local-cluster-service/compare/v0.62.0...v0.62.1) (2024-08-14)
2
9
 
3
10
 
@@ -4,18 +4,20 @@
4
4
  */
5
5
  /// <reference types="node" />
6
6
  import { UIPagePrompt } from './stormClient';
7
- import { ReferenceClassification, StormEvent, StormEventPage } from './events';
7
+ import { ReferenceClassification, StormEvent, StormEventPage, UIShell } from './events';
8
8
  import { EventEmitter } from 'node:events';
9
9
  export declare class PageQueue extends EventEmitter {
10
10
  private readonly queue;
11
11
  private readonly systemId;
12
12
  private readonly references;
13
+ private uiShells;
13
14
  constructor(systemId: string, concurrency?: number);
14
15
  on(event: 'event', listener: (data: StormEvent) => void): this;
15
16
  on(event: 'page', listener: (data: StormEventPage) => void): this;
16
17
  emit(type: 'event', event: StormEvent): boolean;
17
18
  emit(type: 'page', event: StormEventPage): boolean;
18
- addPrompt(initialPrompt: UIPagePrompt): Promise<void>;
19
+ addUiShell(uiShell: UIShell): void;
20
+ addPrompt(initialPrompt: Omit<UIPagePrompt, 'shell_page'>, conversationId?: string, overwrite?: boolean): Promise<void>;
19
21
  private addPageGenerator;
20
22
  cancel(): void;
21
23
  wait(): Promise<void>;
@@ -12,10 +12,12 @@ const node_uuid_1 = __importDefault(require("node-uuid"));
12
12
  const stormClient_1 = require("./stormClient");
13
13
  const node_events_1 = require("node:events");
14
14
  const PromiseQueue_1 = require("./PromiseQueue");
15
+ const page_utils_1 = require("./page-utils");
15
16
  class PageQueue extends node_events_1.EventEmitter {
16
17
  queue;
17
18
  systemId;
18
19
  references = new Map();
20
+ uiShells = [];
19
21
  constructor(systemId, concurrency = 5) {
20
22
  super();
21
23
  this.systemId = systemId;
@@ -27,14 +29,24 @@ class PageQueue extends node_events_1.EventEmitter {
27
29
  emit(eventName, ...args) {
28
30
  return super.emit(eventName, ...args);
29
31
  }
30
- addPrompt(initialPrompt) {
31
- if (this.references.has(initialPrompt.path)) {
32
+ addUiShell(uiShell) {
33
+ this.uiShells.push(uiShell);
34
+ }
35
+ addPrompt(initialPrompt, conversationId = node_uuid_1.default.v4(), overwrite = false) {
36
+ if (!overwrite && this.references.has(initialPrompt.path)) {
32
37
  console.log('Ignoring duplicate prompt', initialPrompt.path);
33
38
  return Promise.resolve();
34
39
  }
35
- console.log('processing prompt', initialPrompt.path);
36
- const generator = new PageGenerator(initialPrompt);
37
- this.references.set(initialPrompt.path, generator);
40
+ if (!overwrite && (0, page_utils_1.hasPageOnDisk)(this.systemId, initialPrompt.method, initialPrompt.path)) {
41
+ console.log('Ignoring prompt with existing page', initialPrompt.path);
42
+ return Promise.resolve();
43
+ }
44
+ const prompt = {
45
+ ...initialPrompt,
46
+ shell_page: this.uiShells.find((shell) => shell.screens.includes(initialPrompt.name))?.content,
47
+ };
48
+ const generator = new PageGenerator(prompt, conversationId);
49
+ this.references.set(prompt.path, generator);
38
50
  return this.addPageGenerator(generator);
39
51
  }
40
52
  async addPageGenerator(generator) {
@@ -9,6 +9,7 @@ export declare const SystemIdHeader = "System-Id";
9
9
  export declare function writePageToDisk(systemId: string, event: StormEventPage): Promise<{
10
10
  path: string;
11
11
  }>;
12
+ export declare function hasPageOnDisk(systemId: string, method: string, path: string): boolean;
12
13
  export declare function resolveReadPath(systemId: string, path: string, method: string): string | null;
13
14
  export declare function readPageFromDiskAsString(systemId: string, path: string, method: string): string | null;
14
15
  export declare function readPageFromDisk(systemId: string, path: string, method: string, res: Response): void;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.writeConversationToFile = exports.readConversationFromFile = exports.readPageFromDisk = exports.readPageFromDiskAsString = exports.resolveReadPath = exports.writePageToDisk = exports.SystemIdHeader = void 0;
6
+ exports.writeConversationToFile = exports.readConversationFromFile = exports.readPageFromDisk = exports.readPageFromDiskAsString = exports.resolveReadPath = exports.hasPageOnDisk = exports.writePageToDisk = exports.SystemIdHeader = void 0;
7
7
  const node_os_1 = __importDefault(require("node:os"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const fs_extra_1 = __importDefault(require("fs-extra"));
@@ -26,6 +26,13 @@ async function writePageToDisk(systemId, event) {
26
26
  };
27
27
  }
28
28
  exports.writePageToDisk = writePageToDisk;
29
+ function hasPageOnDisk(systemId, method, path) {
30
+ const baseDir = getBaseDir(systemId);
31
+ const filePath = getFilePath(method);
32
+ const fullPath = path_1.default.join(baseDir, normalizePath(path), filePath);
33
+ return fs_extra_1.default.existsSync(fullPath);
34
+ }
35
+ exports.hasPageOnDisk = hasPageOnDisk;
29
36
  function getBaseDir(systemId) {
30
37
  return path_1.default.join(node_os_1.default.tmpdir(), 'ai-systems', systemId);
31
38
  }
@@ -71,19 +71,13 @@ router.post('/ui/screen', async (req, res) => {
71
71
  onRequestAborted(req, res, () => {
72
72
  queue.cancel();
73
73
  });
74
- await queue.addPrompt(aiRequest);
75
74
  const promises = [];
76
75
  queue.on('page', (data) => {
77
- switch (data.type) {
78
- case 'PAGE':
79
- console.log('Processing page event', data);
80
- if (systemId) {
81
- promises.push(sendPageEvent(systemId, data, res));
82
- }
83
- break;
76
+ if (systemId) {
77
+ promises.push(sendPageEvent(systemId, data, res));
84
78
  }
85
- sendEvent(res, data);
86
79
  });
80
+ await queue.addPrompt(aiRequest, conversationId, true);
87
81
  await queue.wait();
88
82
  await Promise.allSettled(promises);
89
83
  sendDone(res);
@@ -233,7 +227,7 @@ router.post('/:handle/ui', async (req, res) => {
233
227
  onRequestAborted(req, res, () => {
234
228
  shellsStream.abort();
235
229
  });
236
- const uiShells = [];
230
+ const queue = new PageGenerator_1.PageQueue(outerConversationId, 5);
237
231
  shellsStream.on('data', (data) => {
238
232
  console.log('Processing shell event', data);
239
233
  sendEvent(res, data);
@@ -243,7 +237,7 @@ router.post('/:handle/ui', async (req, res) => {
243
237
  if (shellsStream.isAborted()) {
244
238
  return;
245
239
  }
246
- uiShells.push(data.payload);
240
+ queue.addUiShell(data.payload);
247
241
  });
248
242
  shellsStream.on('error', (error) => {
249
243
  console.error('Error on shellsStream', error);
@@ -254,7 +248,6 @@ router.post('/:handle/ui', async (req, res) => {
254
248
  UI_SERVERS[outerConversationId] = new UIServer_1.UIServer(outerConversationId);
255
249
  await UI_SERVERS[outerConversationId].start();
256
250
  // Get the pages (5 at a time)
257
- const queue = new PageGenerator_1.PageQueue(outerConversationId, 5);
258
251
  const pagePromises = [];
259
252
  onRequestAborted(req, res, () => {
260
253
  queue.cancel();
@@ -276,7 +269,6 @@ router.post('/:handle/ui', async (req, res) => {
276
269
  title: screen.title,
277
270
  filename: screen.filename,
278
271
  storage_prefix: outerConversationId + '_',
279
- shell_page: uiShells.find((shell) => shell.screens.includes(screen.name))?.content,
280
272
  }));
281
273
  }
282
274
  await queue.wait();
@@ -536,7 +528,7 @@ function streamStormPartialResponse(result, res) {
536
528
  switch (data.type) {
537
529
  // todo: temporarily (for demo purposes) disable error messages when codegen fails
538
530
  case 'ERROR_INTERNAL':
539
- console.log("Error internal", data);
531
+ console.log('Error internal', data);
540
532
  return;
541
533
  }
542
534
  sendEvent(res, data);
@@ -555,13 +547,13 @@ function onRequestAborted(req, res, onAborted) {
555
547
  onAborted();
556
548
  });
557
549
  }
558
- function sendPageEvent(mainConversationId, data, res) {
559
- return (0, page_utils_1.writePageToDisk)(mainConversationId, data)
560
- .catch((err) => {
550
+ async function sendPageEvent(mainConversationId, data, res) {
551
+ try {
552
+ await (0, page_utils_1.writePageToDisk)(mainConversationId, data);
553
+ }
554
+ catch (err) {
561
555
  console.error('Failed to write page to disk', err);
562
- })
563
- .then(() => {
564
- sendEvent(res, convertPageEvent(data, data.payload.conversationId, mainConversationId));
565
- });
556
+ }
557
+ sendEvent(res, convertPageEvent(data, data.payload.conversationId, mainConversationId));
566
558
  }
567
559
  exports.default = router;
@@ -4,18 +4,20 @@
4
4
  */
5
5
  /// <reference types="node" />
6
6
  import { UIPagePrompt } from './stormClient';
7
- import { ReferenceClassification, StormEvent, StormEventPage } from './events';
7
+ import { ReferenceClassification, StormEvent, StormEventPage, UIShell } from './events';
8
8
  import { EventEmitter } from 'node:events';
9
9
  export declare class PageQueue extends EventEmitter {
10
10
  private readonly queue;
11
11
  private readonly systemId;
12
12
  private readonly references;
13
+ private uiShells;
13
14
  constructor(systemId: string, concurrency?: number);
14
15
  on(event: 'event', listener: (data: StormEvent) => void): this;
15
16
  on(event: 'page', listener: (data: StormEventPage) => void): this;
16
17
  emit(type: 'event', event: StormEvent): boolean;
17
18
  emit(type: 'page', event: StormEventPage): boolean;
18
- addPrompt(initialPrompt: UIPagePrompt): Promise<void>;
19
+ addUiShell(uiShell: UIShell): void;
20
+ addPrompt(initialPrompt: Omit<UIPagePrompt, 'shell_page'>, conversationId?: string, overwrite?: boolean): Promise<void>;
19
21
  private addPageGenerator;
20
22
  cancel(): void;
21
23
  wait(): Promise<void>;
@@ -12,10 +12,12 @@ const node_uuid_1 = __importDefault(require("node-uuid"));
12
12
  const stormClient_1 = require("./stormClient");
13
13
  const node_events_1 = require("node:events");
14
14
  const PromiseQueue_1 = require("./PromiseQueue");
15
+ const page_utils_1 = require("./page-utils");
15
16
  class PageQueue extends node_events_1.EventEmitter {
16
17
  queue;
17
18
  systemId;
18
19
  references = new Map();
20
+ uiShells = [];
19
21
  constructor(systemId, concurrency = 5) {
20
22
  super();
21
23
  this.systemId = systemId;
@@ -27,14 +29,24 @@ class PageQueue extends node_events_1.EventEmitter {
27
29
  emit(eventName, ...args) {
28
30
  return super.emit(eventName, ...args);
29
31
  }
30
- addPrompt(initialPrompt) {
31
- if (this.references.has(initialPrompt.path)) {
32
+ addUiShell(uiShell) {
33
+ this.uiShells.push(uiShell);
34
+ }
35
+ addPrompt(initialPrompt, conversationId = node_uuid_1.default.v4(), overwrite = false) {
36
+ if (!overwrite && this.references.has(initialPrompt.path)) {
32
37
  console.log('Ignoring duplicate prompt', initialPrompt.path);
33
38
  return Promise.resolve();
34
39
  }
35
- console.log('processing prompt', initialPrompt.path);
36
- const generator = new PageGenerator(initialPrompt);
37
- this.references.set(initialPrompt.path, generator);
40
+ if (!overwrite && (0, page_utils_1.hasPageOnDisk)(this.systemId, initialPrompt.method, initialPrompt.path)) {
41
+ console.log('Ignoring prompt with existing page', initialPrompt.path);
42
+ return Promise.resolve();
43
+ }
44
+ const prompt = {
45
+ ...initialPrompt,
46
+ shell_page: this.uiShells.find((shell) => shell.screens.includes(initialPrompt.name))?.content,
47
+ };
48
+ const generator = new PageGenerator(prompt, conversationId);
49
+ this.references.set(prompt.path, generator);
38
50
  return this.addPageGenerator(generator);
39
51
  }
40
52
  async addPageGenerator(generator) {
@@ -9,6 +9,7 @@ export declare const SystemIdHeader = "System-Id";
9
9
  export declare function writePageToDisk(systemId: string, event: StormEventPage): Promise<{
10
10
  path: string;
11
11
  }>;
12
+ export declare function hasPageOnDisk(systemId: string, method: string, path: string): boolean;
12
13
  export declare function resolveReadPath(systemId: string, path: string, method: string): string | null;
13
14
  export declare function readPageFromDiskAsString(systemId: string, path: string, method: string): string | null;
14
15
  export declare function readPageFromDisk(systemId: string, path: string, method: string, res: Response): void;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.writeConversationToFile = exports.readConversationFromFile = exports.readPageFromDisk = exports.readPageFromDiskAsString = exports.resolveReadPath = exports.writePageToDisk = exports.SystemIdHeader = void 0;
6
+ exports.writeConversationToFile = exports.readConversationFromFile = exports.readPageFromDisk = exports.readPageFromDiskAsString = exports.resolveReadPath = exports.hasPageOnDisk = exports.writePageToDisk = exports.SystemIdHeader = void 0;
7
7
  const node_os_1 = __importDefault(require("node:os"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const fs_extra_1 = __importDefault(require("fs-extra"));
@@ -26,6 +26,13 @@ async function writePageToDisk(systemId, event) {
26
26
  };
27
27
  }
28
28
  exports.writePageToDisk = writePageToDisk;
29
+ function hasPageOnDisk(systemId, method, path) {
30
+ const baseDir = getBaseDir(systemId);
31
+ const filePath = getFilePath(method);
32
+ const fullPath = path_1.default.join(baseDir, normalizePath(path), filePath);
33
+ return fs_extra_1.default.existsSync(fullPath);
34
+ }
35
+ exports.hasPageOnDisk = hasPageOnDisk;
29
36
  function getBaseDir(systemId) {
30
37
  return path_1.default.join(node_os_1.default.tmpdir(), 'ai-systems', systemId);
31
38
  }
@@ -71,19 +71,13 @@ router.post('/ui/screen', async (req, res) => {
71
71
  onRequestAborted(req, res, () => {
72
72
  queue.cancel();
73
73
  });
74
- await queue.addPrompt(aiRequest);
75
74
  const promises = [];
76
75
  queue.on('page', (data) => {
77
- switch (data.type) {
78
- case 'PAGE':
79
- console.log('Processing page event', data);
80
- if (systemId) {
81
- promises.push(sendPageEvent(systemId, data, res));
82
- }
83
- break;
76
+ if (systemId) {
77
+ promises.push(sendPageEvent(systemId, data, res));
84
78
  }
85
- sendEvent(res, data);
86
79
  });
80
+ await queue.addPrompt(aiRequest, conversationId, true);
87
81
  await queue.wait();
88
82
  await Promise.allSettled(promises);
89
83
  sendDone(res);
@@ -233,7 +227,7 @@ router.post('/:handle/ui', async (req, res) => {
233
227
  onRequestAborted(req, res, () => {
234
228
  shellsStream.abort();
235
229
  });
236
- const uiShells = [];
230
+ const queue = new PageGenerator_1.PageQueue(outerConversationId, 5);
237
231
  shellsStream.on('data', (data) => {
238
232
  console.log('Processing shell event', data);
239
233
  sendEvent(res, data);
@@ -243,7 +237,7 @@ router.post('/:handle/ui', async (req, res) => {
243
237
  if (shellsStream.isAborted()) {
244
238
  return;
245
239
  }
246
- uiShells.push(data.payload);
240
+ queue.addUiShell(data.payload);
247
241
  });
248
242
  shellsStream.on('error', (error) => {
249
243
  console.error('Error on shellsStream', error);
@@ -254,7 +248,6 @@ router.post('/:handle/ui', async (req, res) => {
254
248
  UI_SERVERS[outerConversationId] = new UIServer_1.UIServer(outerConversationId);
255
249
  await UI_SERVERS[outerConversationId].start();
256
250
  // Get the pages (5 at a time)
257
- const queue = new PageGenerator_1.PageQueue(outerConversationId, 5);
258
251
  const pagePromises = [];
259
252
  onRequestAborted(req, res, () => {
260
253
  queue.cancel();
@@ -276,7 +269,6 @@ router.post('/:handle/ui', async (req, res) => {
276
269
  title: screen.title,
277
270
  filename: screen.filename,
278
271
  storage_prefix: outerConversationId + '_',
279
- shell_page: uiShells.find((shell) => shell.screens.includes(screen.name))?.content,
280
272
  }));
281
273
  }
282
274
  await queue.wait();
@@ -536,7 +528,7 @@ function streamStormPartialResponse(result, res) {
536
528
  switch (data.type) {
537
529
  // todo: temporarily (for demo purposes) disable error messages when codegen fails
538
530
  case 'ERROR_INTERNAL':
539
- console.log("Error internal", data);
531
+ console.log('Error internal', data);
540
532
  return;
541
533
  }
542
534
  sendEvent(res, data);
@@ -555,13 +547,13 @@ function onRequestAborted(req, res, onAborted) {
555
547
  onAborted();
556
548
  });
557
549
  }
558
- function sendPageEvent(mainConversationId, data, res) {
559
- return (0, page_utils_1.writePageToDisk)(mainConversationId, data)
560
- .catch((err) => {
550
+ async function sendPageEvent(mainConversationId, data, res) {
551
+ try {
552
+ await (0, page_utils_1.writePageToDisk)(mainConversationId, data);
553
+ }
554
+ catch (err) {
561
555
  console.error('Failed to write page to disk', err);
562
- })
563
- .then(() => {
564
- sendEvent(res, convertPageEvent(data, data.payload.conversationId, mainConversationId));
565
- });
556
+ }
557
+ sendEvent(res, convertPageEvent(data, data.payload.conversationId, mainConversationId));
566
558
  }
567
559
  exports.default = router;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kapeta/local-cluster-service",
3
- "version": "0.62.1",
3
+ "version": "0.62.2",
4
4
  "description": "Manages configuration, ports and service discovery for locally running Kapeta systems",
5
5
  "type": "commonjs",
6
6
  "exports": {
@@ -5,14 +5,22 @@
5
5
 
6
6
  import uuid from 'node-uuid';
7
7
  import { stormClient, UIPagePrompt } from './stormClient';
8
- import { ReferenceClassification, StormEvent, StormEventPage, StormEventReferenceClassification } from './events';
8
+ import {
9
+ ReferenceClassification,
10
+ StormEvent,
11
+ StormEventPage,
12
+ StormEventReferenceClassification,
13
+ UIShell,
14
+ } from './events';
9
15
  import { EventEmitter } from 'node:events';
10
16
  import { PromiseQueue } from './PromiseQueue';
17
+ import { hasPageOnDisk } from './page-utils';
11
18
 
12
19
  export class PageQueue extends EventEmitter {
13
20
  private readonly queue: PromiseQueue;
14
21
  private readonly systemId: string;
15
22
  private readonly references: Map<string, PageGenerator> = new Map();
23
+ private uiShells: UIShell[] = [];
16
24
 
17
25
  constructor(systemId: string, concurrency: number = 5) {
18
26
  super();
@@ -33,14 +41,33 @@ export class PageQueue extends EventEmitter {
33
41
  return super.emit(eventName, ...args);
34
42
  }
35
43
 
36
- public addPrompt(initialPrompt: UIPagePrompt) {
37
- if (this.references.has(initialPrompt.path)) {
44
+ public addUiShell(uiShell: UIShell) {
45
+ this.uiShells.push(uiShell);
46
+ }
47
+
48
+ public addPrompt(
49
+ initialPrompt: Omit<UIPagePrompt, 'shell_page'>,
50
+ conversationId: string = uuid.v4(),
51
+ overwrite: boolean = false
52
+ ) {
53
+ if (!overwrite && this.references.has(initialPrompt.path)) {
38
54
  console.log('Ignoring duplicate prompt', initialPrompt.path);
39
55
  return Promise.resolve();
40
56
  }
41
- console.log('processing prompt', initialPrompt.path);
42
- const generator = new PageGenerator(initialPrompt);
43
- this.references.set(initialPrompt.path, generator);
57
+
58
+ if (!overwrite && hasPageOnDisk(this.systemId, initialPrompt.method, initialPrompt.path)) {
59
+ console.log('Ignoring prompt with existing page', initialPrompt.path);
60
+ return Promise.resolve();
61
+ }
62
+
63
+ const prompt: UIPagePrompt = {
64
+ ...initialPrompt,
65
+ shell_page: this.uiShells.find((shell) => shell.screens.includes(initialPrompt.name))?.content,
66
+ };
67
+
68
+ const generator = new PageGenerator(prompt, conversationId);
69
+ this.references.set(prompt.path, generator);
70
+
44
71
  return this.addPageGenerator(generator);
45
72
  }
46
73
 
@@ -34,6 +34,13 @@ export async function writePageToDisk(systemId: string, event: StormEventPage) {
34
34
  };
35
35
  }
36
36
 
37
+ export function hasPageOnDisk(systemId: string, method: string, path: string) {
38
+ const baseDir = getBaseDir(systemId);
39
+ const filePath = getFilePath(method);
40
+ const fullPath = Path.join(baseDir, normalizePath(path), filePath);
41
+ return FS.existsSync(fullPath);
42
+ }
43
+
37
44
  function getBaseDir(systemId: string) {
38
45
  return Path.join(os.tmpdir(), 'ai-systems', systemId);
39
46
  }
@@ -106,22 +106,16 @@ router.post('/ui/screen', async (req: KapetaBodyRequest, res: Response) => {
106
106
  queue.cancel();
107
107
  });
108
108
 
109
- await queue.addPrompt(aiRequest);
110
-
111
109
  const promises: Promise<void>[] = [];
112
- queue.on('page', (data) => {
113
- switch (data.type) {
114
- case 'PAGE':
115
- console.log('Processing page event', data);
116
110
 
117
- if (systemId) {
118
- promises.push(sendPageEvent(systemId, data, res));
119
- }
120
- break;
111
+ queue.on('page', (data) => {
112
+ if (systemId) {
113
+ promises.push(sendPageEvent(systemId, data, res));
121
114
  }
122
- sendEvent(res, data);
123
115
  });
124
116
 
117
+ await queue.addPrompt(aiRequest, conversationId, true);
118
+
125
119
  await queue.wait();
126
120
  await Promise.allSettled(promises);
127
121
 
@@ -304,8 +298,7 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
304
298
  shellsStream.abort();
305
299
  });
306
300
 
307
- const uiShells: UIShell[] = [];
308
-
301
+ const queue = new PageQueue(outerConversationId, 5);
309
302
  shellsStream.on('data', (data: StormEvent) => {
310
303
  console.log('Processing shell event', data);
311
304
  sendEvent(res, data);
@@ -318,7 +311,7 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
318
311
  return;
319
312
  }
320
313
 
321
- uiShells.push(data.payload);
314
+ queue.addUiShell(data.payload);
322
315
  });
323
316
 
324
317
  shellsStream.on('error', (error) => {
@@ -333,7 +326,7 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
333
326
  await UI_SERVERS[outerConversationId].start();
334
327
 
335
328
  // Get the pages (5 at a time)
336
- const queue = new PageQueue(outerConversationId, 5);
329
+
337
330
  const pagePromises: Promise<void>[] = [];
338
331
  onRequestAborted(req, res, () => {
339
332
  queue.cancel();
@@ -359,7 +352,6 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
359
352
  title: screen.title,
360
353
  filename: screen.filename,
361
354
  storage_prefix: outerConversationId + '_',
362
- shell_page: uiShells.find((shell) => shell.screens.includes(screen.name))?.content,
363
355
  })
364
356
  );
365
357
  }
@@ -668,7 +660,7 @@ function streamStormPartialResponse(result: StormStream, res: Response) {
668
660
  switch (data.type) {
669
661
  // todo: temporarily (for demo purposes) disable error messages when codegen fails
670
662
  case 'ERROR_INTERNAL':
671
- console.log("Error internal", data);
663
+ console.log('Error internal', data);
672
664
  return;
673
665
  }
674
666
  sendEvent(res, data);
@@ -691,14 +683,13 @@ function onRequestAborted(req: KapetaBodyRequest, res: Response, onAborted: () =
691
683
  });
692
684
  }
693
685
 
694
- function sendPageEvent(mainConversationId: string, data: StormEventPage, res: Response) {
695
- return writePageToDisk(mainConversationId, data)
696
- .catch((err) => {
697
- console.error('Failed to write page to disk', err);
698
- })
699
- .then(() => {
700
- sendEvent(res, convertPageEvent(data, data.payload.conversationId, mainConversationId));
701
- });
686
+ async function sendPageEvent(mainConversationId: string, data: StormEventPage, res: Response) {
687
+ try {
688
+ await writePageToDisk(mainConversationId, data);
689
+ } catch (err) {
690
+ console.error('Failed to write page to disk', err);
691
+ }
692
+ sendEvent(res, convertPageEvent(data, data.payload.conversationId, mainConversationId));
702
693
  }
703
694
 
704
695
  export default router;