@kapeta/local-cluster-service 0.67.0 → 0.67.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,18 @@
1
+ ## [0.67.2](https://github.com/kapetacom/local-cluster-service/compare/v0.67.1...v0.67.2) (2024-08-28)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Add stack trace to error event ([ad52d4d](https://github.com/kapetacom/local-cluster-service/commit/ad52d4d22b20c0f4e34f83e0a9bc167599b02a08))
7
+ * Don't check file existence when path is empty ([8d9aa1a](https://github.com/kapetacom/local-cluster-service/commit/8d9aa1a180c38f53a0ae6cd122fedc20c8a78307))
8
+
9
+ ## [0.67.1](https://github.com/kapetacom/local-cluster-service/compare/v0.67.0...v0.67.1) (2024-08-28)
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * edit bulk pages in parallel using the page agent ([#227](https://github.com/kapetacom/local-cluster-service/issues/227)) ([ac0d5d6](https://github.com/kapetacom/local-cluster-service/commit/ac0d5d611d9a3b84119b3ff5a272c2f2293236a1))
15
+
1
16
  # [0.67.0](https://github.com/kapetacom/local-cluster-service/compare/v0.66.0...v0.67.0) (2024-08-27)
2
17
 
3
18
 
@@ -111,6 +111,7 @@ export interface StormEventError {
111
111
  created: number;
112
112
  payload: {
113
113
  error: string;
114
+ stack?: string;
114
115
  };
115
116
  }
116
117
  export interface StormEventErrorClassifier {
@@ -53,6 +53,9 @@ async function writeImageToDisk(systemId, event, prompt) {
53
53
  }
54
54
  exports.writeImageToDisk = writeImageToDisk;
55
55
  function hasPageOnDisk(systemId, method, path) {
56
+ if (!systemId || !method || !path) {
57
+ return false;
58
+ }
56
59
  const baseDir = getSystemBaseDir(systemId);
57
60
  const filePath = getFilePath(method);
58
61
  const fullPath = path_1.default.join(baseDir, normalizePath(path), filePath);
@@ -382,58 +382,40 @@ router.post('/:handle/ui', async (req, res) => {
382
382
  });
383
383
  router.post('/ui/edit', async (req, res) => {
384
384
  try {
385
- const conversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()];
385
+ const systemId = (req.headers[page_utils_1.SystemIdHeader.toLowerCase()] ||
386
+ req.headers[stormClient_1.ConversationIdHeader.toLowerCase()]);
386
387
  const aiRequest = JSON.parse(req.stringBody ?? '{}');
387
- const pages = aiRequest.prompt.pages
388
- .map((page) => {
389
- const content = (0, page_utils_1.readPageFromDiskAsString)(conversationId, page.path, page.method);
390
- if (!content) {
391
- console.warn('Page not found', page);
392
- return undefined;
393
- }
394
- return {
395
- filename: page.filename,
396
- path: page.path,
397
- method: page.method,
398
- title: page.title,
399
- conversationId: page.conversationId,
400
- prompt: page.prompt,
401
- name: page.name,
402
- description: page.description,
403
- content,
404
- };
405
- })
406
- .filter((page) => !!page);
407
- const editStream = await stormClient_1.stormClient.editPages({
408
- prompt: aiRequest.prompt.prompt.prompt,
409
- blockDescription: aiRequest.prompt.blockDescription,
410
- planDescription: aiRequest.prompt.planDescription,
411
- pages,
412
- }, conversationId);
388
+ const storagePrefix = systemId ? systemId + '_' : 'mock_';
389
+ const queue = new PageGenerator_1.PageQueue(storagePrefix, 5);
413
390
  onRequestAborted(req, res, () => {
414
- editStream.abort();
391
+ queue.cancel();
415
392
  });
416
- res.set('Content-Type', 'application/x-ndjson');
417
- res.set('Access-Control-Expose-Headers', stormClient_1.ConversationIdHeader);
418
- res.set(stormClient_1.ConversationIdHeader, editStream.getConversationId());
419
393
  const promises = [];
420
- editStream.on('data', (data) => {
421
- try {
422
- if (data.type === 'PAGE') {
423
- promises.push(sendPageEvent(editStream.getConversationId(), data, res));
424
- }
425
- else {
426
- sendEvent(res, data);
427
- }
394
+ queue.on('page', (data) => {
395
+ if (systemId) {
396
+ promises.push(sendPageEvent(systemId, data, res));
428
397
  }
429
- catch (e) {
430
- console.error('Failed to process event', e);
398
+ });
399
+ queue.on('event', (data) => {
400
+ if (data.type === 'FILE_START' || data.type === 'FILE_DONE' || data.type === 'FILE_STATE') {
401
+ sendEvent(res, data);
431
402
  }
432
403
  });
433
- await waitForStormStream(editStream);
434
- if (editStream.isAborted()) {
435
- return;
436
- }
404
+ await Promise.allSettled(aiRequest.prompt.pages.map((page) => {
405
+ if (page.conversationId) {
406
+ return queue.addPrompt({
407
+ title: page.title,
408
+ name: page.name,
409
+ method: page.method,
410
+ path: page.path,
411
+ description: page.description ?? '',
412
+ filename: page.filename,
413
+ prompt: aiRequest.prompt.prompt.prompt,
414
+ storage_prefix: storagePrefix,
415
+ }, page.conversationId, true);
416
+ }
417
+ }));
418
+ await queue.wait();
437
419
  await Promise.all(promises);
438
420
  sendDone(res);
439
421
  }
@@ -614,17 +596,21 @@ function sendError(err, res) {
614
596
  if (res.closed) {
615
597
  return;
616
598
  }
599
+ const errorPayload = {
600
+ error: err.message,
601
+ stack: err.stack,
602
+ };
617
603
  console.error('Failed to send prompt', err);
618
604
  if (res.headersSent) {
619
605
  sendEvent(res, {
620
606
  type: 'ERROR_INTERNAL',
621
607
  created: Date.now(),
622
- payload: { error: err.message },
608
+ payload: errorPayload,
623
609
  reason: 'Failed while sending prompt',
624
610
  });
625
611
  }
626
612
  else {
627
- res.status(400).send({ error: err.message });
613
+ res.status(400).send(errorPayload);
628
614
  }
629
615
  }
630
616
  function waitForStormStream(result) {
@@ -24,7 +24,7 @@ export interface UIPagePrompt {
24
24
  description: string;
25
25
  storage_prefix: string;
26
26
  shell_page?: string;
27
- theme: string;
27
+ theme?: string;
28
28
  }
29
29
  export interface UIPageSamplePrompt extends UIPagePrompt {
30
30
  variantId: string;
@@ -111,6 +111,7 @@ export interface StormEventError {
111
111
  created: number;
112
112
  payload: {
113
113
  error: string;
114
+ stack?: string;
114
115
  };
115
116
  }
116
117
  export interface StormEventErrorClassifier {
@@ -53,6 +53,9 @@ async function writeImageToDisk(systemId, event, prompt) {
53
53
  }
54
54
  exports.writeImageToDisk = writeImageToDisk;
55
55
  function hasPageOnDisk(systemId, method, path) {
56
+ if (!systemId || !method || !path) {
57
+ return false;
58
+ }
56
59
  const baseDir = getSystemBaseDir(systemId);
57
60
  const filePath = getFilePath(method);
58
61
  const fullPath = path_1.default.join(baseDir, normalizePath(path), filePath);
@@ -382,58 +382,40 @@ router.post('/:handle/ui', async (req, res) => {
382
382
  });
383
383
  router.post('/ui/edit', async (req, res) => {
384
384
  try {
385
- const conversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()];
385
+ const systemId = (req.headers[page_utils_1.SystemIdHeader.toLowerCase()] ||
386
+ req.headers[stormClient_1.ConversationIdHeader.toLowerCase()]);
386
387
  const aiRequest = JSON.parse(req.stringBody ?? '{}');
387
- const pages = aiRequest.prompt.pages
388
- .map((page) => {
389
- const content = (0, page_utils_1.readPageFromDiskAsString)(conversationId, page.path, page.method);
390
- if (!content) {
391
- console.warn('Page not found', page);
392
- return undefined;
393
- }
394
- return {
395
- filename: page.filename,
396
- path: page.path,
397
- method: page.method,
398
- title: page.title,
399
- conversationId: page.conversationId,
400
- prompt: page.prompt,
401
- name: page.name,
402
- description: page.description,
403
- content,
404
- };
405
- })
406
- .filter((page) => !!page);
407
- const editStream = await stormClient_1.stormClient.editPages({
408
- prompt: aiRequest.prompt.prompt.prompt,
409
- blockDescription: aiRequest.prompt.blockDescription,
410
- planDescription: aiRequest.prompt.planDescription,
411
- pages,
412
- }, conversationId);
388
+ const storagePrefix = systemId ? systemId + '_' : 'mock_';
389
+ const queue = new PageGenerator_1.PageQueue(storagePrefix, 5);
413
390
  onRequestAborted(req, res, () => {
414
- editStream.abort();
391
+ queue.cancel();
415
392
  });
416
- res.set('Content-Type', 'application/x-ndjson');
417
- res.set('Access-Control-Expose-Headers', stormClient_1.ConversationIdHeader);
418
- res.set(stormClient_1.ConversationIdHeader, editStream.getConversationId());
419
393
  const promises = [];
420
- editStream.on('data', (data) => {
421
- try {
422
- if (data.type === 'PAGE') {
423
- promises.push(sendPageEvent(editStream.getConversationId(), data, res));
424
- }
425
- else {
426
- sendEvent(res, data);
427
- }
394
+ queue.on('page', (data) => {
395
+ if (systemId) {
396
+ promises.push(sendPageEvent(systemId, data, res));
428
397
  }
429
- catch (e) {
430
- console.error('Failed to process event', e);
398
+ });
399
+ queue.on('event', (data) => {
400
+ if (data.type === 'FILE_START' || data.type === 'FILE_DONE' || data.type === 'FILE_STATE') {
401
+ sendEvent(res, data);
431
402
  }
432
403
  });
433
- await waitForStormStream(editStream);
434
- if (editStream.isAborted()) {
435
- return;
436
- }
404
+ await Promise.allSettled(aiRequest.prompt.pages.map((page) => {
405
+ if (page.conversationId) {
406
+ return queue.addPrompt({
407
+ title: page.title,
408
+ name: page.name,
409
+ method: page.method,
410
+ path: page.path,
411
+ description: page.description ?? '',
412
+ filename: page.filename,
413
+ prompt: aiRequest.prompt.prompt.prompt,
414
+ storage_prefix: storagePrefix,
415
+ }, page.conversationId, true);
416
+ }
417
+ }));
418
+ await queue.wait();
437
419
  await Promise.all(promises);
438
420
  sendDone(res);
439
421
  }
@@ -614,17 +596,21 @@ function sendError(err, res) {
614
596
  if (res.closed) {
615
597
  return;
616
598
  }
599
+ const errorPayload = {
600
+ error: err.message,
601
+ stack: err.stack,
602
+ };
617
603
  console.error('Failed to send prompt', err);
618
604
  if (res.headersSent) {
619
605
  sendEvent(res, {
620
606
  type: 'ERROR_INTERNAL',
621
607
  created: Date.now(),
622
- payload: { error: err.message },
608
+ payload: errorPayload,
623
609
  reason: 'Failed while sending prompt',
624
610
  });
625
611
  }
626
612
  else {
627
- res.status(400).send({ error: err.message });
613
+ res.status(400).send(errorPayload);
628
614
  }
629
615
  }
630
616
  function waitForStormStream(result) {
@@ -24,7 +24,7 @@ export interface UIPagePrompt {
24
24
  description: string;
25
25
  storage_prefix: string;
26
26
  shell_page?: string;
27
- theme: string;
27
+ theme?: string;
28
28
  }
29
29
  export interface UIPageSamplePrompt extends UIPagePrompt {
30
30
  variantId: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kapeta/local-cluster-service",
3
- "version": "0.67.0",
3
+ "version": "0.67.2",
4
4
  "description": "Manages configuration, ports and service discovery for locally running Kapeta systems",
5
5
  "type": "commonjs",
6
6
  "exports": {
@@ -140,6 +140,7 @@ export interface StormEventError {
140
140
  created: number;
141
141
  payload: {
142
142
  error: string;
143
+ stack?: string;
143
144
  };
144
145
  }
145
146
 
@@ -69,6 +69,9 @@ export async function writeImageToDisk(systemId: string, event: StormImage, prom
69
69
  }
70
70
 
71
71
  export function hasPageOnDisk(systemId: string, method: string, path: string) {
72
+ if (!systemId || !method || !path) {
73
+ return false;
74
+ }
72
75
  const baseDir = getSystemBaseDir(systemId);
73
76
  const filePath = getFilePath(method);
74
77
  const fullPath = Path.join(baseDir, normalizePath(path), filePath);
@@ -473,67 +473,53 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
473
473
 
474
474
  router.post('/ui/edit', async (req: KapetaBodyRequest, res: Response) => {
475
475
  try {
476
- const conversationId = req.headers[ConversationIdHeader.toLowerCase()] as string | undefined;
476
+ const systemId = (req.headers[SystemIdHeader.toLowerCase()] ||
477
+ req.headers[ConversationIdHeader.toLowerCase()]) as string | undefined;
477
478
 
478
479
  const aiRequest: StormContextRequest<UIPageEditRequest> = JSON.parse(req.stringBody ?? '{}');
479
-
480
- const pages = aiRequest.prompt.pages
481
- .map((page) => {
482
- const content = readPageFromDiskAsString(conversationId!, page.path, page.method);
483
- if (!content) {
484
- console.warn('Page not found', page);
485
- return undefined;
486
- }
487
-
488
- return {
489
- filename: page.filename,
490
- path: page.path,
491
- method: page.method,
492
- title: page.title,
493
- conversationId: page.conversationId,
494
- prompt: page.prompt,
495
- name: page.name,
496
- description: page.description,
497
- content,
498
- } satisfies Page;
499
- })
500
- .filter((page: Page | undefined) => !!page) as UIPageEditPrompt['pages'];
501
-
502
- const editStream = await stormClient.editPages(
503
- {
504
- prompt: aiRequest.prompt.prompt.prompt,
505
- blockDescription: aiRequest.prompt.blockDescription,
506
- planDescription: aiRequest.prompt.planDescription,
507
- pages,
508
- },
509
- conversationId
510
- );
480
+ const storagePrefix = systemId ? systemId + '_' : 'mock_';
481
+ const queue = new PageQueue(storagePrefix, 5);
511
482
 
512
483
  onRequestAborted(req, res, () => {
513
- editStream.abort();
484
+ queue.cancel();
514
485
  });
515
486
 
516
- res.set('Content-Type', 'application/x-ndjson');
517
- res.set('Access-Control-Expose-Headers', ConversationIdHeader);
518
- res.set(ConversationIdHeader, editStream.getConversationId());
519
-
520
487
  const promises: Promise<void>[] = [];
521
- editStream.on('data', (data: StormEvent) => {
522
- try {
523
- if (data.type === 'PAGE') {
524
- promises.push(sendPageEvent(editStream.getConversationId(), data, res));
525
- } else {
526
- sendEvent(res, data);
527
- }
528
- } catch (e) {
529
- console.error('Failed to process event', e);
488
+
489
+ queue.on('page', (data) => {
490
+ if (systemId) {
491
+ promises.push(sendPageEvent(systemId, data, res));
530
492
  }
531
493
  });
532
494
 
533
- await waitForStormStream(editStream);
534
- if (editStream.isAborted()) {
535
- return;
536
- }
495
+ queue.on('event', (data) => {
496
+ if (data.type === 'FILE_START' || data.type === 'FILE_DONE' || data.type === 'FILE_STATE') {
497
+ sendEvent(res, data);
498
+ }
499
+ });
500
+
501
+ await Promise.allSettled(
502
+ aiRequest.prompt.pages.map((page) => {
503
+ if (page.conversationId) {
504
+ return queue.addPrompt(
505
+ {
506
+ title: page.title,
507
+ name: page.name,
508
+ method: page.method,
509
+ path: page.path,
510
+ description: page.description ?? '',
511
+ filename: page.filename,
512
+ prompt: aiRequest.prompt.prompt.prompt,
513
+ storage_prefix: storagePrefix,
514
+ },
515
+ page.conversationId,
516
+ true
517
+ );
518
+ }
519
+ })
520
+ );
521
+
522
+ await queue.wait();
537
523
  await Promise.all(promises);
538
524
 
539
525
  sendDone(res);
@@ -751,16 +737,23 @@ function sendError(err: Error, res: Response) {
751
737
  if (res.closed) {
752
738
  return;
753
739
  }
740
+
741
+ const errorPayload = {
742
+ error: err.message,
743
+ stack: err.stack,
744
+ };
745
+
754
746
  console.error('Failed to send prompt', err);
747
+
755
748
  if (res.headersSent) {
756
749
  sendEvent(res, {
757
750
  type: 'ERROR_INTERNAL',
758
751
  created: Date.now(),
759
- payload: { error: err.message },
752
+ payload: errorPayload,
760
753
  reason: 'Failed while sending prompt',
761
754
  });
762
755
  } else {
763
- res.status(400).send({ error: err.message });
756
+ res.status(400).send(errorPayload);
764
757
  }
765
758
  }
766
759
 
@@ -44,7 +44,7 @@ export interface UIPagePrompt {
44
44
  storage_prefix: string;
45
45
  shell_page?: string;
46
46
  // contents of theme.css
47
- theme: string;
47
+ theme?: string;
48
48
  }
49
49
 
50
50
  export interface UIPageSamplePrompt extends UIPagePrompt {