@kapeta/local-cluster-service 0.70.7 → 0.70.9

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 (35) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/src/storm/codegen.d.ts +2 -1
  3. package/dist/cjs/src/storm/codegen.js +7 -1
  4. package/dist/cjs/src/storm/event-parser.d.ts +3 -1
  5. package/dist/cjs/src/storm/event-parser.js +5 -2
  6. package/dist/cjs/src/storm/page-utils.d.ts +1 -0
  7. package/dist/cjs/src/storm/page-utils.js +5 -1
  8. package/dist/cjs/src/storm/routes.js +18 -8
  9. package/dist/cjs/src/storm/stormClient.d.ts +1 -0
  10. package/dist/cjs/src/storm/stormClient.js +10 -0
  11. package/dist/cjs/src/storm/utils.d.ts +1 -0
  12. package/dist/cjs/src/storm/utils.js +21 -1
  13. package/dist/cjs/test/storm/event-parser.test.d.ts +1 -0
  14. package/dist/cjs/test/storm/event-parser.test.js +1 -0
  15. package/dist/esm/src/storm/codegen.d.ts +2 -1
  16. package/dist/esm/src/storm/codegen.js +7 -1
  17. package/dist/esm/src/storm/event-parser.d.ts +3 -1
  18. package/dist/esm/src/storm/event-parser.js +5 -2
  19. package/dist/esm/src/storm/page-utils.d.ts +1 -0
  20. package/dist/esm/src/storm/page-utils.js +5 -1
  21. package/dist/esm/src/storm/routes.js +18 -8
  22. package/dist/esm/src/storm/stormClient.d.ts +1 -0
  23. package/dist/esm/src/storm/stormClient.js +10 -0
  24. package/dist/esm/src/storm/utils.d.ts +1 -0
  25. package/dist/esm/src/storm/utils.js +21 -1
  26. package/dist/esm/test/storm/event-parser.test.d.ts +1 -0
  27. package/dist/esm/test/storm/event-parser.test.js +1 -0
  28. package/package.json +1 -1
  29. package/src/storm/codegen.ts +16 -3
  30. package/src/storm/event-parser.ts +10 -3
  31. package/src/storm/page-utils.ts +4 -0
  32. package/src/storm/routes.ts +24 -9
  33. package/src/storm/stormClient.ts +11 -0
  34. package/src/storm/utils.ts +23 -0
  35. package/test/storm/event-parser.test.ts +1 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [0.70.9](https://github.com/kapetacom/local-cluster-service/compare/v0.70.8...v0.70.9) (2024-09-12)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * converted pages are turned into a prompt and this is feed into bottoms up ([85e3c44](https://github.com/kapetacom/local-cluster-service/commit/85e3c448f3bab1b78ec4972572219b8f334d7345))
7
+
8
+ ## [0.70.8](https://github.com/kapetacom/local-cluster-service/compare/v0.70.7...v0.70.8) (2024-09-12)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * use language-target-html if pre-existing systemId is specified ([0a38e0c](https://github.com/kapetacom/local-cluster-service/commit/0a38e0cb384a0954269c1b00ee1ef6660f979e42))
14
+
1
15
  ## [0.70.7](https://github.com/kapetacom/local-cluster-service/compare/v0.70.6...v0.70.7) (2024-09-12)
2
16
 
3
17
 
@@ -12,7 +12,8 @@ export declare class StormCodegen {
12
12
  private readonly events;
13
13
  private tmpDir;
14
14
  private readonly conversationId;
15
- constructor(conversationId: string, userPrompt: string, blocks: BlockDefinitionInfo[], events: StormEvent[]);
15
+ private readonly uiSystemId?;
16
+ constructor(conversationId: string, userPrompt: string, blocks: BlockDefinitionInfo[], events: StormEvent[], uiSystemId?: string);
16
17
  setTmpDir(tmpDir: string): void;
17
18
  process(): Promise<void>;
18
19
  isAborted(): boolean;
@@ -47,6 +47,7 @@ const yaml_1 = __importDefault(require("yaml"));
47
47
  const predefined_1 = require("./predefined");
48
48
  const archetype_1 = require("./archetype");
49
49
  const lodash_1 = __importDefault(require("lodash"));
50
+ const page_utils_1 = require("./page-utils");
50
51
  const SIMULATED_DELAY = 1000;
51
52
  const ENABLE_SIMULATED_DELAY = false;
52
53
  class SimulatedFileDelay {
@@ -100,12 +101,14 @@ class StormCodegen {
100
101
  events;
101
102
  tmpDir;
102
103
  conversationId;
103
- constructor(conversationId, userPrompt, blocks, events) {
104
+ uiSystemId;
105
+ constructor(conversationId, userPrompt, blocks, events, uiSystemId) {
104
106
  this.userPrompt = userPrompt;
105
107
  this.blocks = blocks;
106
108
  this.events = events;
107
109
  this.tmpDir = path_2.default.join(node_os_1.default.tmpdir(), conversationId);
108
110
  this.conversationId = conversationId;
111
+ this.uiSystemId = uiSystemId;
109
112
  }
110
113
  setTmpDir(tmpDir) {
111
114
  this.tmpDir = tmpDir;
@@ -794,6 +797,9 @@ class StormCodegen {
794
797
  const basePath = this.getBasePath(yamlContent.metadata.name);
795
798
  const codeGenerator = new codegen_1.BlockCodeGenerator(yamlContent);
796
799
  codeGenerator.withOption('AIContext', stormClient_1.STORM_ID);
800
+ if (this.uiSystemId) {
801
+ codeGenerator.withOption('AIStaticFiles', (0, page_utils_1.getSystemBaseImplDir)(this.uiSystemId));
802
+ }
797
803
  const generatedResult = await codeGenerator.generate();
798
804
  new codegen_1.CodeWriter(basePath).write(generatedResult);
799
805
  return generatedResult;
@@ -26,6 +26,7 @@ export interface StormOptions {
26
26
  serviceLanguage: string;
27
27
  frontendKind: string;
28
28
  frontendLanguage: string;
29
+ htmlLanguage: string;
29
30
  exchangeKind: string;
30
31
  queueKind: string;
31
32
  publisherKind: string;
@@ -39,7 +40,8 @@ export interface StormOptions {
39
40
  desktopKind: string;
40
41
  desktopLanguage: string;
41
42
  gatewayKind: string;
42
- [key: string]: string;
43
+ systemId?: string;
44
+ [key: string]: string | undefined;
43
45
  }
44
46
  export declare function createPhaseStartEvent(type: StormEventPhaseType): StormEventPhases;
45
47
  export declare function createPhaseEndEvent(type: StormEventPhaseType): StormEventPhases;
@@ -67,6 +67,7 @@ async function resolveOptions() {
67
67
  const javaLanguage = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/language-target-java-spring-boot');
68
68
  const reactLanguage = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/language-target-react-ts');
69
69
  const nodejsLanguage = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/language-target-nodejs');
70
+ const htmlLanguage = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/language-target-html');
70
71
  const blockTypePubsub = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/block-type-pubsub');
71
72
  const resourceTypePubsubSubscriber = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/resource-type-pubsub-subscriber');
72
73
  const resourceTypePubsubSubscription = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/resource-type-pubsub-subscription');
@@ -83,6 +84,7 @@ async function resolveOptions() {
83
84
  !postgresResource ||
84
85
  !javaLanguage ||
85
86
  !reactLanguage ||
87
+ !htmlLanguage ||
86
88
  !webPageResource ||
87
89
  !restApiResource ||
88
90
  !restClientResource ||
@@ -105,6 +107,7 @@ async function resolveOptions() {
105
107
  serviceLanguage: (0, nodejs_utils_1.normalizeKapetaUri)(`${javaLanguage.definition.metadata.name}:${javaLanguage.version}`),
106
108
  frontendKind: (0, nodejs_utils_1.normalizeKapetaUri)(`${blockTypeFrontend.definition.metadata.name}:${blockTypeFrontend.version}`),
107
109
  frontendLanguage: (0, nodejs_utils_1.normalizeKapetaUri)(`${reactLanguage.definition.metadata.name}:${reactLanguage.version}`),
110
+ htmlLanguage: (0, nodejs_utils_1.normalizeKapetaUri)(`${htmlLanguage.definition.metadata.name}:${htmlLanguage.version}`),
108
111
  cliKind: (0, nodejs_utils_1.normalizeKapetaUri)(`${blockTypeCli.definition.metadata.name}:${blockTypeCli.version}`),
109
112
  cliLanguage: (0, nodejs_utils_1.normalizeKapetaUri)(`${nodejsLanguage.definition.metadata.name}:${nodejsLanguage.version}`),
110
113
  desktopKind: (0, nodejs_utils_1.normalizeKapetaUri)(`${blockTypeDesktop.definition.metadata.name}:${blockTypeDesktop.version}`),
@@ -770,7 +773,7 @@ class StormEventParser {
770
773
  case 'CLI':
771
774
  return this.options.cliLanguage;
772
775
  case 'FRONTEND':
773
- return this.options.frontendLanguage;
776
+ return this.options.systemId ? this.options.htmlLanguage : this.options.frontendLanguage;
774
777
  case 'DESKTOP':
775
778
  return this.options.desktopLanguage;
776
779
  }
@@ -791,7 +794,7 @@ class StormEventParser {
791
794
  for (const prop in options) {
792
795
  if (options.hasOwnProperty(prop)) {
793
796
  const value = options[prop];
794
- if (value.indexOf(kind) > 0) {
797
+ if (typeof value === 'string' && value.indexOf(kind) > 0) {
795
798
  return value;
796
799
  }
797
800
  }
@@ -19,6 +19,7 @@ export declare function writeImageToDisk(systemId: string, event: StormImage, pr
19
19
  }>;
20
20
  export declare function hasPageOnDisk(systemId: string, method: string, path: string): boolean;
21
21
  export declare function getSystemBaseDir(systemId: string): string;
22
+ export declare function getSystemBaseImplDir(systemId: string): string;
22
23
  export declare function resolveReadPath(systemId: string, path: string, method: string): string | null;
23
24
  export declare function readPageFromDiskAsString(systemId: string, path: string, method: string): string | null;
24
25
  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.getSystemBaseDir = exports.hasPageOnDisk = exports.writeImageToDisk = exports.writeAssetToDisk = exports.writePageToDisk = exports.normalizePath = exports.SystemIdHeader = void 0;
6
+ exports.writeConversationToFile = exports.readConversationFromFile = exports.readPageFromDisk = exports.readPageFromDiskAsString = exports.resolveReadPath = exports.getSystemBaseImplDir = exports.getSystemBaseDir = exports.hasPageOnDisk = exports.writeImageToDisk = exports.writeAssetToDisk = exports.writePageToDisk = exports.normalizePath = 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"));
@@ -63,6 +63,10 @@ function getSystemBaseDir(systemId) {
63
63
  return path_1.default.join(node_os_1.default.tmpdir(), 'ai-systems', systemId);
64
64
  }
65
65
  exports.getSystemBaseDir = getSystemBaseDir;
66
+ function getSystemBaseImplDir(systemId) {
67
+ return path_1.default.join(node_os_1.default.tmpdir(), 'ai-systems-impl', systemId);
68
+ }
69
+ exports.getSystemBaseImplDir = getSystemBaseImplDir;
66
70
  function getFilePath(method) {
67
71
  // For HEAD requests, we assume we're serving looking for a GET resource
68
72
  return path_1.default.join(method === 'HEAD' ? 'get' : method.toLowerCase(), 'index.html');
@@ -10,7 +10,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  const express_promise_router_1 = __importDefault(require("express-promise-router"));
11
11
  const fs_extra_1 = __importDefault(require("fs-extra"));
12
12
  const path_1 = __importDefault(require("path"));
13
- const node_os_1 = __importDefault(require("node:os"));
14
13
  const lodash_1 = __importDefault(require("lodash"));
15
14
  const cors_1 = require("../middleware/cors");
16
15
  const stringBody_1 = require("../middleware/stringBody");
@@ -69,10 +68,10 @@ router.post('/ui/serve/:systemId', async (req, res) => {
69
68
  }
70
69
  res.status(200).send({ status: 'running', url: svr.getUrl() });
71
70
  });
72
- router.post('/ui/create-system/:systemId', async (req, res) => {
71
+ router.post('/ui/create-system/:handle/:systemId', async (req, res) => {
73
72
  const systemId = req.params.systemId;
74
73
  const srcDir = (0, page_utils_1.getSystemBaseDir)(systemId);
75
- const destDir = path_1.default.join(node_os_1.default.tmpdir(), 'ai-systems-impl', systemId);
74
+ const destDir = (0, page_utils_1.getSystemBaseImplDir)(systemId);
76
75
  await (0, utils_1.copyDirectory)(srcDir, destDir, async (fileName, content) => {
77
76
  const result = await stormClient_1.stormClient.implementAPIClients({
78
77
  content: content,
@@ -80,8 +79,15 @@ router.post('/ui/create-system/:systemId', async (req, res) => {
80
79
  });
81
80
  return result;
82
81
  });
83
- res.end();
84
- return;
82
+ const pages = (0, utils_1.readPages)(destDir);
83
+ const prompt = await stormClient_1.stormClient.generatePrompt(pages);
84
+ req.query.systemId = systemId;
85
+ const promptRequest = {
86
+ prompt: prompt,
87
+ skipImprovement: true,
88
+ };
89
+ req.stringBody = JSON.stringify(promptRequest);
90
+ await handleAll(req, res);
85
91
  });
86
92
  router.delete('/ui/serve/:systemId', async (req, res) => {
87
93
  const systemId = req.params.systemId;
@@ -500,9 +506,13 @@ router.post('/ui/get-vote', async (req, res) => {
500
506
  }
501
507
  });
502
508
  router.post('/:handle/all', async (req, res) => {
509
+ await handleAll(req, res);
510
+ });
511
+ async function handleAll(req, res) {
503
512
  const handle = req.params.handle;
513
+ const systemId = req.query.systemId ?? undefined;
504
514
  try {
505
- const stormOptions = await (0, event_parser_1.resolveOptions)();
515
+ const stormOptions = { ...(await (0, event_parser_1.resolveOptions)()), systemId: systemId };
506
516
  const eventParser = new event_parser_1.StormEventParser(stormOptions);
507
517
  const conversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()];
508
518
  const aiRequest = JSON.parse(req.stringBody ?? '{}');
@@ -571,7 +581,7 @@ router.post('/:handle/all', async (req, res) => {
571
581
  if (!req.query.skipCodegen) {
572
582
  try {
573
583
  sendEvent(res, (0, event_parser_1.createPhaseStartEvent)(events_1.StormEventPhaseType.IMPLEMENTATION));
574
- const stormCodegen = new codegen_1.StormCodegen(metaStream.getConversationId(), aiRequest.prompt, result.blocks, eventParser.getEvents());
584
+ const stormCodegen = new codegen_1.StormCodegen(metaStream.getConversationId(), aiRequest.prompt, result.blocks, eventParser.getEvents(), systemId);
575
585
  onRequestAborted(req, res, () => {
576
586
  stormCodegen.abort();
577
587
  });
@@ -593,7 +603,7 @@ router.post('/:handle/all', async (req, res) => {
593
603
  res.end();
594
604
  }
595
605
  }
596
- });
606
+ }
597
607
  router.post('/block/create', async (req, res) => {
598
608
  const createRequest = JSON.parse(req.stringBody ?? '{}');
599
609
  try {
@@ -70,6 +70,7 @@ declare class StormClient {
70
70
  vote: -1 | 0 | 1;
71
71
  }>;
72
72
  implementAPIClients(prompt: ImplementAPIClientsRequest): Promise<string>;
73
+ generatePrompt(pages: string[]): Promise<string>;
73
74
  classifyUIReferences(prompt: string, conversationId?: string): Promise<StormStream>;
74
75
  editPages(prompt: UIPageEditPrompt, conversationId?: string): Promise<StormStream>;
75
76
  listScreens(prompt: StormUIListPrompt, conversationId?: string): Promise<StormStream>;
@@ -160,6 +160,16 @@ class StormClient {
160
160
  const data = await response.text();
161
161
  return data;
162
162
  }
163
+ async generatePrompt(pages) {
164
+ const u = `${this._baseUrl}/v2/ui/prompt`;
165
+ const response = await (0, undici_1.fetch)(u, {
166
+ method: 'POST',
167
+ body: JSON.stringify({
168
+ pages: pages,
169
+ }),
170
+ });
171
+ return await response.text();
172
+ }
163
173
  classifyUIReferences(prompt, conversationId) {
164
174
  return this.send('/v2/ui/references', {
165
175
  prompt: prompt,
@@ -1,4 +1,5 @@
1
1
  export declare function copyDirectory(src: string, dest: string, modifyHtml: (fileName: string, content: string) => Promise<string>): Promise<void>;
2
+ export declare function readPages(directoryPath: string): string[];
2
3
  export declare function createFuture<T = void>(): {
3
4
  promise: Promise<T>;
4
5
  resolve: (value: T | PromiseLike<T>) => 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.createFuture = exports.copyDirectory = void 0;
6
+ exports.createFuture = exports.readPages = exports.copyDirectory = void 0;
7
7
  /**
8
8
  * Copyright 2023 Kapeta Inc.
9
9
  * SPDX-License-Identifier: BUSL-1.1
@@ -29,6 +29,26 @@ async function copyDirectory(src, dest, modifyHtml) {
29
29
  }
30
30
  }
31
31
  exports.copyDirectory = copyDirectory;
32
+ function readPages(directoryPath) {
33
+ const htmlFiles = [];
34
+ function traverseDirectory(currentPath) {
35
+ const files = fs_extra_1.default.readdirSync(currentPath);
36
+ for (const file of files) {
37
+ const filePath = path_1.default.join(currentPath, file);
38
+ const stats = fs_extra_1.default.statSync(filePath);
39
+ if (stats.isDirectory()) {
40
+ traverseDirectory(filePath);
41
+ }
42
+ else if (stats.isFile() && path_1.default.extname(filePath) === '.html') {
43
+ const content = fs_extra_1.default.readFileSync(filePath, 'utf8');
44
+ htmlFiles.push(content);
45
+ }
46
+ }
47
+ }
48
+ traverseDirectory(directoryPath);
49
+ return htmlFiles;
50
+ }
51
+ exports.readPages = readPages;
32
52
  function createFuture() {
33
53
  let resolve = () => { };
34
54
  let reject = () => { };
@@ -7,6 +7,7 @@ export declare const parserOptions: {
7
7
  serviceLanguage: string;
8
8
  frontendKind: string;
9
9
  frontendLanguage: string;
10
+ htmlLanguage: string;
10
11
  cliKind: string;
11
12
  cliLanguage: string;
12
13
  desktopKind: string;
@@ -19,6 +19,7 @@ exports.parserOptions = {
19
19
  serviceLanguage: 'kapeta/language-target-java-spring-boot:local',
20
20
  frontendKind: 'kapeta/block-type-frontend:local',
21
21
  frontendLanguage: 'kapeta/language-target-react-ts:local',
22
+ htmlLanguage: 'kapeta/language-target-html:local',
22
23
  cliKind: 'kapeta/block-type-cli:local',
23
24
  cliLanguage: 'kapeta/language-target-nodejs-ts:local',
24
25
  desktopKind: 'kapeta/block-type-desktop:local',
@@ -12,7 +12,8 @@ export declare class StormCodegen {
12
12
  private readonly events;
13
13
  private tmpDir;
14
14
  private readonly conversationId;
15
- constructor(conversationId: string, userPrompt: string, blocks: BlockDefinitionInfo[], events: StormEvent[]);
15
+ private readonly uiSystemId?;
16
+ constructor(conversationId: string, userPrompt: string, blocks: BlockDefinitionInfo[], events: StormEvent[], uiSystemId?: string);
16
17
  setTmpDir(tmpDir: string): void;
17
18
  process(): Promise<void>;
18
19
  isAborted(): boolean;
@@ -47,6 +47,7 @@ const yaml_1 = __importDefault(require("yaml"));
47
47
  const predefined_1 = require("./predefined");
48
48
  const archetype_1 = require("./archetype");
49
49
  const lodash_1 = __importDefault(require("lodash"));
50
+ const page_utils_1 = require("./page-utils");
50
51
  const SIMULATED_DELAY = 1000;
51
52
  const ENABLE_SIMULATED_DELAY = false;
52
53
  class SimulatedFileDelay {
@@ -100,12 +101,14 @@ class StormCodegen {
100
101
  events;
101
102
  tmpDir;
102
103
  conversationId;
103
- constructor(conversationId, userPrompt, blocks, events) {
104
+ uiSystemId;
105
+ constructor(conversationId, userPrompt, blocks, events, uiSystemId) {
104
106
  this.userPrompt = userPrompt;
105
107
  this.blocks = blocks;
106
108
  this.events = events;
107
109
  this.tmpDir = path_2.default.join(node_os_1.default.tmpdir(), conversationId);
108
110
  this.conversationId = conversationId;
111
+ this.uiSystemId = uiSystemId;
109
112
  }
110
113
  setTmpDir(tmpDir) {
111
114
  this.tmpDir = tmpDir;
@@ -794,6 +797,9 @@ class StormCodegen {
794
797
  const basePath = this.getBasePath(yamlContent.metadata.name);
795
798
  const codeGenerator = new codegen_1.BlockCodeGenerator(yamlContent);
796
799
  codeGenerator.withOption('AIContext', stormClient_1.STORM_ID);
800
+ if (this.uiSystemId) {
801
+ codeGenerator.withOption('AIStaticFiles', (0, page_utils_1.getSystemBaseImplDir)(this.uiSystemId));
802
+ }
797
803
  const generatedResult = await codeGenerator.generate();
798
804
  new codegen_1.CodeWriter(basePath).write(generatedResult);
799
805
  return generatedResult;
@@ -26,6 +26,7 @@ export interface StormOptions {
26
26
  serviceLanguage: string;
27
27
  frontendKind: string;
28
28
  frontendLanguage: string;
29
+ htmlLanguage: string;
29
30
  exchangeKind: string;
30
31
  queueKind: string;
31
32
  publisherKind: string;
@@ -39,7 +40,8 @@ export interface StormOptions {
39
40
  desktopKind: string;
40
41
  desktopLanguage: string;
41
42
  gatewayKind: string;
42
- [key: string]: string;
43
+ systemId?: string;
44
+ [key: string]: string | undefined;
43
45
  }
44
46
  export declare function createPhaseStartEvent(type: StormEventPhaseType): StormEventPhases;
45
47
  export declare function createPhaseEndEvent(type: StormEventPhaseType): StormEventPhases;
@@ -67,6 +67,7 @@ async function resolveOptions() {
67
67
  const javaLanguage = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/language-target-java-spring-boot');
68
68
  const reactLanguage = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/language-target-react-ts');
69
69
  const nodejsLanguage = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/language-target-nodejs');
70
+ const htmlLanguage = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/language-target-html');
70
71
  const blockTypePubsub = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/block-type-pubsub');
71
72
  const resourceTypePubsubSubscriber = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/resource-type-pubsub-subscriber');
72
73
  const resourceTypePubsubSubscription = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/resource-type-pubsub-subscription');
@@ -83,6 +84,7 @@ async function resolveOptions() {
83
84
  !postgresResource ||
84
85
  !javaLanguage ||
85
86
  !reactLanguage ||
87
+ !htmlLanguage ||
86
88
  !webPageResource ||
87
89
  !restApiResource ||
88
90
  !restClientResource ||
@@ -105,6 +107,7 @@ async function resolveOptions() {
105
107
  serviceLanguage: (0, nodejs_utils_1.normalizeKapetaUri)(`${javaLanguage.definition.metadata.name}:${javaLanguage.version}`),
106
108
  frontendKind: (0, nodejs_utils_1.normalizeKapetaUri)(`${blockTypeFrontend.definition.metadata.name}:${blockTypeFrontend.version}`),
107
109
  frontendLanguage: (0, nodejs_utils_1.normalizeKapetaUri)(`${reactLanguage.definition.metadata.name}:${reactLanguage.version}`),
110
+ htmlLanguage: (0, nodejs_utils_1.normalizeKapetaUri)(`${htmlLanguage.definition.metadata.name}:${htmlLanguage.version}`),
108
111
  cliKind: (0, nodejs_utils_1.normalizeKapetaUri)(`${blockTypeCli.definition.metadata.name}:${blockTypeCli.version}`),
109
112
  cliLanguage: (0, nodejs_utils_1.normalizeKapetaUri)(`${nodejsLanguage.definition.metadata.name}:${nodejsLanguage.version}`),
110
113
  desktopKind: (0, nodejs_utils_1.normalizeKapetaUri)(`${blockTypeDesktop.definition.metadata.name}:${blockTypeDesktop.version}`),
@@ -770,7 +773,7 @@ class StormEventParser {
770
773
  case 'CLI':
771
774
  return this.options.cliLanguage;
772
775
  case 'FRONTEND':
773
- return this.options.frontendLanguage;
776
+ return this.options.systemId ? this.options.htmlLanguage : this.options.frontendLanguage;
774
777
  case 'DESKTOP':
775
778
  return this.options.desktopLanguage;
776
779
  }
@@ -791,7 +794,7 @@ class StormEventParser {
791
794
  for (const prop in options) {
792
795
  if (options.hasOwnProperty(prop)) {
793
796
  const value = options[prop];
794
- if (value.indexOf(kind) > 0) {
797
+ if (typeof value === 'string' && value.indexOf(kind) > 0) {
795
798
  return value;
796
799
  }
797
800
  }
@@ -19,6 +19,7 @@ export declare function writeImageToDisk(systemId: string, event: StormImage, pr
19
19
  }>;
20
20
  export declare function hasPageOnDisk(systemId: string, method: string, path: string): boolean;
21
21
  export declare function getSystemBaseDir(systemId: string): string;
22
+ export declare function getSystemBaseImplDir(systemId: string): string;
22
23
  export declare function resolveReadPath(systemId: string, path: string, method: string): string | null;
23
24
  export declare function readPageFromDiskAsString(systemId: string, path: string, method: string): string | null;
24
25
  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.getSystemBaseDir = exports.hasPageOnDisk = exports.writeImageToDisk = exports.writeAssetToDisk = exports.writePageToDisk = exports.normalizePath = exports.SystemIdHeader = void 0;
6
+ exports.writeConversationToFile = exports.readConversationFromFile = exports.readPageFromDisk = exports.readPageFromDiskAsString = exports.resolveReadPath = exports.getSystemBaseImplDir = exports.getSystemBaseDir = exports.hasPageOnDisk = exports.writeImageToDisk = exports.writeAssetToDisk = exports.writePageToDisk = exports.normalizePath = 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"));
@@ -63,6 +63,10 @@ function getSystemBaseDir(systemId) {
63
63
  return path_1.default.join(node_os_1.default.tmpdir(), 'ai-systems', systemId);
64
64
  }
65
65
  exports.getSystemBaseDir = getSystemBaseDir;
66
+ function getSystemBaseImplDir(systemId) {
67
+ return path_1.default.join(node_os_1.default.tmpdir(), 'ai-systems-impl', systemId);
68
+ }
69
+ exports.getSystemBaseImplDir = getSystemBaseImplDir;
66
70
  function getFilePath(method) {
67
71
  // For HEAD requests, we assume we're serving looking for a GET resource
68
72
  return path_1.default.join(method === 'HEAD' ? 'get' : method.toLowerCase(), 'index.html');
@@ -10,7 +10,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  const express_promise_router_1 = __importDefault(require("express-promise-router"));
11
11
  const fs_extra_1 = __importDefault(require("fs-extra"));
12
12
  const path_1 = __importDefault(require("path"));
13
- const node_os_1 = __importDefault(require("node:os"));
14
13
  const lodash_1 = __importDefault(require("lodash"));
15
14
  const cors_1 = require("../middleware/cors");
16
15
  const stringBody_1 = require("../middleware/stringBody");
@@ -69,10 +68,10 @@ router.post('/ui/serve/:systemId', async (req, res) => {
69
68
  }
70
69
  res.status(200).send({ status: 'running', url: svr.getUrl() });
71
70
  });
72
- router.post('/ui/create-system/:systemId', async (req, res) => {
71
+ router.post('/ui/create-system/:handle/:systemId', async (req, res) => {
73
72
  const systemId = req.params.systemId;
74
73
  const srcDir = (0, page_utils_1.getSystemBaseDir)(systemId);
75
- const destDir = path_1.default.join(node_os_1.default.tmpdir(), 'ai-systems-impl', systemId);
74
+ const destDir = (0, page_utils_1.getSystemBaseImplDir)(systemId);
76
75
  await (0, utils_1.copyDirectory)(srcDir, destDir, async (fileName, content) => {
77
76
  const result = await stormClient_1.stormClient.implementAPIClients({
78
77
  content: content,
@@ -80,8 +79,15 @@ router.post('/ui/create-system/:systemId', async (req, res) => {
80
79
  });
81
80
  return result;
82
81
  });
83
- res.end();
84
- return;
82
+ const pages = (0, utils_1.readPages)(destDir);
83
+ const prompt = await stormClient_1.stormClient.generatePrompt(pages);
84
+ req.query.systemId = systemId;
85
+ const promptRequest = {
86
+ prompt: prompt,
87
+ skipImprovement: true,
88
+ };
89
+ req.stringBody = JSON.stringify(promptRequest);
90
+ await handleAll(req, res);
85
91
  });
86
92
  router.delete('/ui/serve/:systemId', async (req, res) => {
87
93
  const systemId = req.params.systemId;
@@ -500,9 +506,13 @@ router.post('/ui/get-vote', async (req, res) => {
500
506
  }
501
507
  });
502
508
  router.post('/:handle/all', async (req, res) => {
509
+ await handleAll(req, res);
510
+ });
511
+ async function handleAll(req, res) {
503
512
  const handle = req.params.handle;
513
+ const systemId = req.query.systemId ?? undefined;
504
514
  try {
505
- const stormOptions = await (0, event_parser_1.resolveOptions)();
515
+ const stormOptions = { ...(await (0, event_parser_1.resolveOptions)()), systemId: systemId };
506
516
  const eventParser = new event_parser_1.StormEventParser(stormOptions);
507
517
  const conversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()];
508
518
  const aiRequest = JSON.parse(req.stringBody ?? '{}');
@@ -571,7 +581,7 @@ router.post('/:handle/all', async (req, res) => {
571
581
  if (!req.query.skipCodegen) {
572
582
  try {
573
583
  sendEvent(res, (0, event_parser_1.createPhaseStartEvent)(events_1.StormEventPhaseType.IMPLEMENTATION));
574
- const stormCodegen = new codegen_1.StormCodegen(metaStream.getConversationId(), aiRequest.prompt, result.blocks, eventParser.getEvents());
584
+ const stormCodegen = new codegen_1.StormCodegen(metaStream.getConversationId(), aiRequest.prompt, result.blocks, eventParser.getEvents(), systemId);
575
585
  onRequestAborted(req, res, () => {
576
586
  stormCodegen.abort();
577
587
  });
@@ -593,7 +603,7 @@ router.post('/:handle/all', async (req, res) => {
593
603
  res.end();
594
604
  }
595
605
  }
596
- });
606
+ }
597
607
  router.post('/block/create', async (req, res) => {
598
608
  const createRequest = JSON.parse(req.stringBody ?? '{}');
599
609
  try {
@@ -70,6 +70,7 @@ declare class StormClient {
70
70
  vote: -1 | 0 | 1;
71
71
  }>;
72
72
  implementAPIClients(prompt: ImplementAPIClientsRequest): Promise<string>;
73
+ generatePrompt(pages: string[]): Promise<string>;
73
74
  classifyUIReferences(prompt: string, conversationId?: string): Promise<StormStream>;
74
75
  editPages(prompt: UIPageEditPrompt, conversationId?: string): Promise<StormStream>;
75
76
  listScreens(prompt: StormUIListPrompt, conversationId?: string): Promise<StormStream>;
@@ -160,6 +160,16 @@ class StormClient {
160
160
  const data = await response.text();
161
161
  return data;
162
162
  }
163
+ async generatePrompt(pages) {
164
+ const u = `${this._baseUrl}/v2/ui/prompt`;
165
+ const response = await (0, undici_1.fetch)(u, {
166
+ method: 'POST',
167
+ body: JSON.stringify({
168
+ pages: pages,
169
+ }),
170
+ });
171
+ return await response.text();
172
+ }
163
173
  classifyUIReferences(prompt, conversationId) {
164
174
  return this.send('/v2/ui/references', {
165
175
  prompt: prompt,
@@ -1,4 +1,5 @@
1
1
  export declare function copyDirectory(src: string, dest: string, modifyHtml: (fileName: string, content: string) => Promise<string>): Promise<void>;
2
+ export declare function readPages(directoryPath: string): string[];
2
3
  export declare function createFuture<T = void>(): {
3
4
  promise: Promise<T>;
4
5
  resolve: (value: T | PromiseLike<T>) => 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.createFuture = exports.copyDirectory = void 0;
6
+ exports.createFuture = exports.readPages = exports.copyDirectory = void 0;
7
7
  /**
8
8
  * Copyright 2023 Kapeta Inc.
9
9
  * SPDX-License-Identifier: BUSL-1.1
@@ -29,6 +29,26 @@ async function copyDirectory(src, dest, modifyHtml) {
29
29
  }
30
30
  }
31
31
  exports.copyDirectory = copyDirectory;
32
+ function readPages(directoryPath) {
33
+ const htmlFiles = [];
34
+ function traverseDirectory(currentPath) {
35
+ const files = fs_extra_1.default.readdirSync(currentPath);
36
+ for (const file of files) {
37
+ const filePath = path_1.default.join(currentPath, file);
38
+ const stats = fs_extra_1.default.statSync(filePath);
39
+ if (stats.isDirectory()) {
40
+ traverseDirectory(filePath);
41
+ }
42
+ else if (stats.isFile() && path_1.default.extname(filePath) === '.html') {
43
+ const content = fs_extra_1.default.readFileSync(filePath, 'utf8');
44
+ htmlFiles.push(content);
45
+ }
46
+ }
47
+ }
48
+ traverseDirectory(directoryPath);
49
+ return htmlFiles;
50
+ }
51
+ exports.readPages = readPages;
32
52
  function createFuture() {
33
53
  let resolve = () => { };
34
54
  let reject = () => { };
@@ -7,6 +7,7 @@ export declare const parserOptions: {
7
7
  serviceLanguage: string;
8
8
  frontendKind: string;
9
9
  frontendLanguage: string;
10
+ htmlLanguage: string;
10
11
  cliKind: string;
11
12
  cliLanguage: string;
12
13
  desktopKind: string;
@@ -19,6 +19,7 @@ exports.parserOptions = {
19
19
  serviceLanguage: 'kapeta/language-target-java-spring-boot:local',
20
20
  frontendKind: 'kapeta/block-type-frontend:local',
21
21
  frontendLanguage: 'kapeta/language-target-react-ts:local',
22
+ htmlLanguage: 'kapeta/language-target-html:local',
22
23
  cliKind: 'kapeta/block-type-cli:local',
23
24
  cliLanguage: 'kapeta/language-target-nodejs-ts:local',
24
25
  desktopKind: 'kapeta/block-type-desktop:local',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kapeta/local-cluster-service",
3
- "version": "0.70.7",
3
+ "version": "0.70.9",
4
4
  "description": "Manages configuration, ports and service discovery for locally running Kapeta systems",
5
5
  "type": "commonjs",
6
6
  "exports": {
@@ -39,6 +39,7 @@ import YAML from 'yaml';
39
39
  import { PREDEFINED_BLOCKS } from './predefined';
40
40
  import { Archetype } from './archetype';
41
41
  import _ from 'lodash';
42
+ import { getSystemBaseImplDir } from './page-utils';
42
43
 
43
44
  type ImplementationGenerator<T = StormFileImplementationPrompt> = (
44
45
  prompt: T,
@@ -108,13 +109,21 @@ export class StormCodegen {
108
109
  private readonly events: StormEvent[];
109
110
  private tmpDir: string;
110
111
  private readonly conversationId: string;
111
-
112
- constructor(conversationId: string, userPrompt: string, blocks: BlockDefinitionInfo[], events: StormEvent[]) {
112
+ private readonly uiSystemId?: string;
113
+
114
+ constructor(
115
+ conversationId: string,
116
+ userPrompt: string,
117
+ blocks: BlockDefinitionInfo[],
118
+ events: StormEvent[],
119
+ uiSystemId?: string
120
+ ) {
113
121
  this.userPrompt = userPrompt;
114
122
  this.blocks = blocks;
115
123
  this.events = events;
116
124
  this.tmpDir = Path.join(os.tmpdir(), conversationId);
117
125
  this.conversationId = conversationId;
126
+ this.uiSystemId = uiSystemId;
118
127
  }
119
128
 
120
129
  public setTmpDir(tmpDir: string) {
@@ -488,7 +497,6 @@ export class StormCodegen {
488
497
  await writeFile(filePath, webRouterFile.content);
489
498
  }
490
499
 
491
-
492
500
  const blockRef = block.uri;
493
501
 
494
502
  this.emitBlockStatus(blockUri, block.aiName, StormEventBlockStatusType.QA);
@@ -1009,6 +1017,11 @@ export class StormCodegen {
1009
1017
 
1010
1018
  const codeGenerator = new BlockCodeGenerator(yamlContent as BlockDefinition);
1011
1019
  codeGenerator.withOption('AIContext', STORM_ID);
1020
+
1021
+ if (this.uiSystemId) {
1022
+ codeGenerator.withOption('AIStaticFiles', getSystemBaseImplDir(this.uiSystemId));
1023
+ }
1024
+
1012
1025
  const generatedResult = await codeGenerator.generate();
1013
1026
  new CodeWriter(basePath).write(generatedResult);
1014
1027
  return generatedResult;
@@ -67,6 +67,7 @@ export interface StormOptions {
67
67
  serviceLanguage: string;
68
68
  frontendKind: string;
69
69
  frontendLanguage: string;
70
+ htmlLanguage: string;
70
71
  exchangeKind: string;
71
72
  queueKind: string;
72
73
  publisherKind: string;
@@ -80,7 +81,10 @@ export interface StormOptions {
80
81
  desktopKind: string;
81
82
  desktopLanguage: string;
82
83
  gatewayKind: string;
83
- [key: string]: string;
84
+
85
+ systemId?: string;
86
+
87
+ [key: string]: string | undefined;
84
88
  }
85
89
 
86
90
  function prettifyKaplang(source: string) {
@@ -143,6 +147,7 @@ export async function resolveOptions(): Promise<StormOptions> {
143
147
  const javaLanguage = await definitionsManager.getLatestDefinition('kapeta/language-target-java-spring-boot');
144
148
  const reactLanguage = await definitionsManager.getLatestDefinition('kapeta/language-target-react-ts');
145
149
  const nodejsLanguage = await definitionsManager.getLatestDefinition('kapeta/language-target-nodejs');
150
+ const htmlLanguage = await definitionsManager.getLatestDefinition('kapeta/language-target-html');
146
151
 
147
152
  const blockTypePubsub = await definitionsManager.getLatestDefinition('kapeta/block-type-pubsub');
148
153
  const resourceTypePubsubSubscriber = await definitionsManager.getLatestDefinition(
@@ -170,6 +175,7 @@ export async function resolveOptions(): Promise<StormOptions> {
170
175
  !postgresResource ||
171
176
  !javaLanguage ||
172
177
  !reactLanguage ||
178
+ !htmlLanguage ||
173
179
  !webPageResource ||
174
180
  !restApiResource ||
175
181
  !restClientResource ||
@@ -195,6 +201,7 @@ export async function resolveOptions(): Promise<StormOptions> {
195
201
 
196
202
  frontendKind: normalizeKapetaUri(`${blockTypeFrontend.definition.metadata.name}:${blockTypeFrontend.version}`),
197
203
  frontendLanguage: normalizeKapetaUri(`${reactLanguage.definition.metadata.name}:${reactLanguage.version}`),
204
+ htmlLanguage: normalizeKapetaUri(`${htmlLanguage.definition.metadata.name}:${htmlLanguage.version}`),
198
205
 
199
206
  cliKind: normalizeKapetaUri(`${blockTypeCli.definition.metadata.name}:${blockTypeCli.version}`),
200
207
  cliLanguage: normalizeKapetaUri(`${nodejsLanguage.definition.metadata.name}:${nodejsLanguage.version}`),
@@ -997,7 +1004,7 @@ export class StormEventParser {
997
1004
  case 'CLI':
998
1005
  return this.options.cliLanguage;
999
1006
  case 'FRONTEND':
1000
- return this.options.frontendLanguage;
1007
+ return this.options.systemId ? this.options.htmlLanguage : this.options.frontendLanguage;
1001
1008
  case 'DESKTOP':
1002
1009
  return this.options.desktopLanguage;
1003
1010
  }
@@ -1027,7 +1034,7 @@ export class StormEventParser {
1027
1034
  for (const prop in options) {
1028
1035
  if (options.hasOwnProperty(prop)) {
1029
1036
  const value = options[prop];
1030
- if (value.indexOf(kind) > 0) {
1037
+ if (typeof value === 'string' && value.indexOf(kind) > 0) {
1031
1038
  return value;
1032
1039
  }
1033
1040
  }
@@ -78,6 +78,10 @@ export function getSystemBaseDir(systemId: string) {
78
78
  return Path.join(os.tmpdir(), 'ai-systems', systemId);
79
79
  }
80
80
 
81
+ export function getSystemBaseImplDir(systemId: string) {
82
+ return Path.join(os.tmpdir(), 'ai-systems-impl', systemId);
83
+ }
84
+
81
85
  function getFilePath(method: string) {
82
86
  // For HEAD requests, we assume we're serving looking for a GET resource
83
87
  return Path.join(method === 'HEAD' ? 'get' : method.toLowerCase(), 'index.html');
@@ -7,7 +7,6 @@ import Router from 'express-promise-router';
7
7
  import FS from 'fs-extra';
8
8
  import { Response } from 'express';
9
9
  import Path from 'path';
10
- import os from 'node:os';
11
10
  import _ from 'lodash';
12
11
  import { corsHandler } from '../middleware/cors';
13
12
  import { stringBody } from '../middleware/stringBody';
@@ -37,6 +36,7 @@ import { assetManager } from '../assetManager';
37
36
  import uuid from 'node-uuid';
38
37
  import {
39
38
  getSystemBaseDir,
39
+ getSystemBaseImplDir,
40
40
  readPageFromDisk,
41
41
  resolveReadPath,
42
42
  SystemIdHeader,
@@ -47,7 +47,7 @@ import {
47
47
  import { UIServer } from './UIServer';
48
48
  import { randomUUID } from 'crypto';
49
49
  import { ImagePrompt, PageQueue } from './PageGenerator';
50
- import { copyDirectory, createFuture } from './utils';
50
+ import { copyDirectory, createFuture, readPages } from './utils';
51
51
 
52
52
  const UI_SERVERS: { [key: string]: UIServer } = {};
53
53
  const router = Router();
@@ -101,10 +101,10 @@ router.post('/ui/serve/:systemId', async (req: KapetaBodyRequest, res: Response)
101
101
  res.status(200).send({ status: 'running', url: svr.getUrl() });
102
102
  });
103
103
 
104
- router.post('/ui/create-system/:systemId', async (req: KapetaBodyRequest, res: Response) => {
104
+ router.post('/ui/create-system/:handle/:systemId', async (req: KapetaBodyRequest, res: Response) => {
105
105
  const systemId = req.params.systemId as string;
106
106
  const srcDir = getSystemBaseDir(systemId);
107
- const destDir = Path.join(os.tmpdir(), 'ai-systems-impl', systemId);
107
+ const destDir = getSystemBaseImplDir(systemId);
108
108
 
109
109
  await copyDirectory(srcDir, destDir, async (fileName, content) => {
110
110
  const result = await stormClient.implementAPIClients({
@@ -114,8 +114,17 @@ router.post('/ui/create-system/:systemId', async (req: KapetaBodyRequest, res: R
114
114
  return result;
115
115
  });
116
116
 
117
- res.end();
118
- return;
117
+ const pages = readPages(destDir);
118
+ const prompt = await stormClient.generatePrompt(pages);
119
+
120
+ req.query.systemId = systemId;
121
+ const promptRequest: BasePromptRequest = {
122
+ prompt: prompt,
123
+ skipImprovement: true,
124
+ };
125
+ req.stringBody = JSON.stringify(promptRequest);
126
+
127
+ await handleAll(req, res);
119
128
  });
120
129
 
121
130
  router.delete('/ui/serve/:systemId', async (req: KapetaBodyRequest, res: Response) => {
@@ -613,10 +622,15 @@ router.post('/ui/get-vote', async (req: KapetaBodyRequest, res: Response) => {
613
622
  });
614
623
 
615
624
  router.post('/:handle/all', async (req: KapetaBodyRequest, res: Response) => {
625
+ await handleAll(req, res);
626
+ });
627
+
628
+ async function handleAll(req: KapetaBodyRequest, res: Response) {
616
629
  const handle = req.params.handle as string;
630
+ const systemId = (req.query.systemId as string) ?? undefined;
617
631
 
618
632
  try {
619
- const stormOptions = await resolveOptions();
633
+ const stormOptions = { ...(await resolveOptions()), systemId: systemId };
620
634
 
621
635
  const eventParser = new StormEventParser(stormOptions);
622
636
 
@@ -703,7 +717,8 @@ router.post('/:handle/all', async (req: KapetaBodyRequest, res: Response) => {
703
717
  metaStream.getConversationId(),
704
718
  aiRequest.prompt,
705
719
  result.blocks,
706
- eventParser.getEvents()
720
+ eventParser.getEvents(),
721
+ systemId
707
722
  );
708
723
 
709
724
  onRequestAborted(req, res, () => {
@@ -729,7 +744,7 @@ router.post('/:handle/all', async (req: KapetaBodyRequest, res: Response) => {
729
744
  res.end();
730
745
  }
731
746
  }
732
- });
747
+ }
733
748
 
734
749
  router.post('/block/create', async (req: KapetaBodyRequest, res: Response) => {
735
750
  const createRequest: StormCreateBlockRequest = JSON.parse(req.stringBody ?? '{}');
@@ -267,6 +267,17 @@ class StormClient {
267
267
  return data;
268
268
  }
269
269
 
270
+ public async generatePrompt(pages: string[]): Promise<string> {
271
+ const u = `${this._baseUrl}/v2/ui/prompt`;
272
+ const response = await fetch(u, {
273
+ method: 'POST',
274
+ body: JSON.stringify({
275
+ pages: pages,
276
+ }),
277
+ });
278
+ return await response.text();
279
+ }
280
+
270
281
  public classifyUIReferences(prompt: string, conversationId?: string) {
271
282
  return this.send('/v2/ui/references', {
272
283
  prompt: prompt,
@@ -31,6 +31,29 @@ export async function copyDirectory(
31
31
  }
32
32
  }
33
33
 
34
+ export function readPages(directoryPath: string): string[] {
35
+ const htmlFiles: string[] = [];
36
+
37
+ function traverseDirectory(currentPath: string) {
38
+ const files = FS.readdirSync(currentPath);
39
+
40
+ for (const file of files) {
41
+ const filePath = Path.join(currentPath, file);
42
+ const stats = FS.statSync(filePath);
43
+
44
+ if (stats.isDirectory()) {
45
+ traverseDirectory(filePath);
46
+ } else if (stats.isFile() && Path.extname(filePath) === '.html') {
47
+ const content = FS.readFileSync(filePath, 'utf8');
48
+ htmlFiles.push(content);
49
+ }
50
+ }
51
+ }
52
+
53
+ traverseDirectory(directoryPath);
54
+ return htmlFiles;
55
+ }
56
+
34
57
  export function createFuture<T = void>() {
35
58
  let resolve: (value: T | PromiseLike<T>) => void = () => {};
36
59
  let reject: (reason?: any) => void = () => {};
@@ -17,6 +17,7 @@ export const parserOptions = {
17
17
 
18
18
  frontendKind: 'kapeta/block-type-frontend:local',
19
19
  frontendLanguage: 'kapeta/language-target-react-ts:local',
20
+ htmlLanguage: 'kapeta/language-target-html:local',
20
21
 
21
22
  cliKind: 'kapeta/block-type-cli:local',
22
23
  cliLanguage: 'kapeta/language-target-nodejs-ts:local',