@kapeta/local-cluster-service 0.70.8 → 0.70.10

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,17 @@
1
+ ## [0.70.10](https://github.com/kapetacom/local-cluster-service/compare/v0.70.9...v0.70.10) (2024-09-12)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Bug in argument ordering ([#248](https://github.com/kapetacom/local-cluster-service/issues/248)) ([a092a4c](https://github.com/kapetacom/local-cluster-service/commit/a092a4cdd727631943b3d81a552e55e68ca44c88))
7
+
8
+ ## [0.70.9](https://github.com/kapetacom/local-cluster-service/compare/v0.70.8...v0.70.9) (2024-09-12)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * converted pages are turned into a prompt and this is feed into bottoms up ([85e3c44](https://github.com/kapetacom/local-cluster-service/commit/85e3c448f3bab1b78ec4972572219b8f334d7345))
14
+
1
15
  ## [0.70.8](https://github.com/kapetacom/local-cluster-service/compare/v0.70.7...v0.70.8) (2024-09-12)
2
16
 
3
17
 
@@ -78,7 +78,7 @@ class PageQueue extends node_events_1.EventEmitter {
78
78
  //console.log('Ignoring duplicate prompt', initialPrompt.path);
79
79
  return true;
80
80
  }
81
- if ((0, page_utils_1.hasPageOnDisk)(this.systemId, 'GET', path)) {
81
+ if ((0, page_utils_1.hasPageOnDisk)(this.systemId, path, 'GET')) {
82
82
  //console.log('Ignoring prompt with existing page', initialPrompt.path);
83
83
  return true;
84
84
  }
@@ -798,7 +798,7 @@ class StormCodegen {
798
798
  const codeGenerator = new codegen_1.BlockCodeGenerator(yamlContent);
799
799
  codeGenerator.withOption('AIContext', stormClient_1.STORM_ID);
800
800
  if (this.uiSystemId) {
801
- codeGenerator.withOption('AIStaticFiles', (0, page_utils_1.getSystemBaseDir)(this.uiSystemId));
801
+ codeGenerator.withOption('AIStaticFiles', (0, page_utils_1.getSystemBaseImplDir)(this.uiSystemId));
802
802
  }
803
803
  const generatedResult = await codeGenerator.generate();
804
804
  new codegen_1.CodeWriter(basePath).write(generatedResult);
@@ -17,8 +17,9 @@ export declare function writeAssetToDisk(systemId: string, event: StormEventFile
17
17
  export declare function writeImageToDisk(systemId: string, event: StormImage, prompt: ImagePrompt): Promise<{
18
18
  path: string;
19
19
  }>;
20
- export declare function hasPageOnDisk(systemId: string, method: string, path: string): boolean;
20
+ export declare function hasPageOnDisk(systemId: string, path: string, method: 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"));
@@ -54,8 +54,9 @@ async function writeImageToDisk(systemId, event, prompt) {
54
54
  };
55
55
  }
56
56
  exports.writeImageToDisk = writeImageToDisk;
57
- function hasPageOnDisk(systemId, method, path) {
58
- const fullPath = resolveReadPath(systemId, method, path);
57
+ function hasPageOnDisk(systemId, path, method) {
58
+ const fullPath = resolveReadPath(systemId, path, method);
59
+ console.log('Checking for page on disk:', fullPath);
59
60
  return !!fullPath && fs_extra_1.default.existsSync(fullPath);
60
61
  }
61
62
  exports.hasPageOnDisk = hasPageOnDisk;
@@ -63,6 +64,10 @@ function getSystemBaseDir(systemId) {
63
64
  return path_1.default.join(node_os_1.default.tmpdir(), 'ai-systems', systemId);
64
65
  }
65
66
  exports.getSystemBaseDir = getSystemBaseDir;
67
+ function getSystemBaseImplDir(systemId) {
68
+ return path_1.default.join(node_os_1.default.tmpdir(), 'ai-systems-impl', systemId);
69
+ }
70
+ exports.getSystemBaseImplDir = getSystemBaseImplDir;
66
71
  function getFilePath(method) {
67
72
  // For HEAD requests, we assume we're serving looking for a GET resource
68
73
  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,6 +506,9 @@ 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;
504
513
  const systemId = req.query.systemId ?? undefined;
505
514
  try {
@@ -594,7 +603,7 @@ router.post('/:handle/all', async (req, res) => {
594
603
  res.end();
595
604
  }
596
605
  }
597
- });
606
+ }
598
607
  router.post('/block/create', async (req, res) => {
599
608
  const createRequest = JSON.parse(req.stringBody ?? '{}');
600
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 = () => { };
@@ -78,7 +78,7 @@ class PageQueue extends node_events_1.EventEmitter {
78
78
  //console.log('Ignoring duplicate prompt', initialPrompt.path);
79
79
  return true;
80
80
  }
81
- if ((0, page_utils_1.hasPageOnDisk)(this.systemId, 'GET', path)) {
81
+ if ((0, page_utils_1.hasPageOnDisk)(this.systemId, path, 'GET')) {
82
82
  //console.log('Ignoring prompt with existing page', initialPrompt.path);
83
83
  return true;
84
84
  }
@@ -798,7 +798,7 @@ class StormCodegen {
798
798
  const codeGenerator = new codegen_1.BlockCodeGenerator(yamlContent);
799
799
  codeGenerator.withOption('AIContext', stormClient_1.STORM_ID);
800
800
  if (this.uiSystemId) {
801
- codeGenerator.withOption('AIStaticFiles', (0, page_utils_1.getSystemBaseDir)(this.uiSystemId));
801
+ codeGenerator.withOption('AIStaticFiles', (0, page_utils_1.getSystemBaseImplDir)(this.uiSystemId));
802
802
  }
803
803
  const generatedResult = await codeGenerator.generate();
804
804
  new codegen_1.CodeWriter(basePath).write(generatedResult);
@@ -17,8 +17,9 @@ export declare function writeAssetToDisk(systemId: string, event: StormEventFile
17
17
  export declare function writeImageToDisk(systemId: string, event: StormImage, prompt: ImagePrompt): Promise<{
18
18
  path: string;
19
19
  }>;
20
- export declare function hasPageOnDisk(systemId: string, method: string, path: string): boolean;
20
+ export declare function hasPageOnDisk(systemId: string, path: string, method: 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"));
@@ -54,8 +54,9 @@ async function writeImageToDisk(systemId, event, prompt) {
54
54
  };
55
55
  }
56
56
  exports.writeImageToDisk = writeImageToDisk;
57
- function hasPageOnDisk(systemId, method, path) {
58
- const fullPath = resolveReadPath(systemId, method, path);
57
+ function hasPageOnDisk(systemId, path, method) {
58
+ const fullPath = resolveReadPath(systemId, path, method);
59
+ console.log('Checking for page on disk:', fullPath);
59
60
  return !!fullPath && fs_extra_1.default.existsSync(fullPath);
60
61
  }
61
62
  exports.hasPageOnDisk = hasPageOnDisk;
@@ -63,6 +64,10 @@ function getSystemBaseDir(systemId) {
63
64
  return path_1.default.join(node_os_1.default.tmpdir(), 'ai-systems', systemId);
64
65
  }
65
66
  exports.getSystemBaseDir = getSystemBaseDir;
67
+ function getSystemBaseImplDir(systemId) {
68
+ return path_1.default.join(node_os_1.default.tmpdir(), 'ai-systems-impl', systemId);
69
+ }
70
+ exports.getSystemBaseImplDir = getSystemBaseImplDir;
66
71
  function getFilePath(method) {
67
72
  // For HEAD requests, we assume we're serving looking for a GET resource
68
73
  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,6 +506,9 @@ 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;
504
513
  const systemId = req.query.systemId ?? undefined;
505
514
  try {
@@ -594,7 +603,7 @@ router.post('/:handle/all', async (req, res) => {
594
603
  res.end();
595
604
  }
596
605
  }
597
- });
606
+ }
598
607
  router.post('/block/create', async (req, res) => {
599
608
  const createRequest = JSON.parse(req.stringBody ?? '{}');
600
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 = () => { };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kapeta/local-cluster-service",
3
- "version": "0.70.8",
3
+ "version": "0.70.10",
4
4
  "description": "Manages configuration, ports and service discovery for locally running Kapeta systems",
5
5
  "type": "commonjs",
6
6
  "exports": {
@@ -81,7 +81,7 @@ export class PageQueue extends EventEmitter {
81
81
  return true;
82
82
  }
83
83
 
84
- if (hasPageOnDisk(this.systemId, 'GET', path)) {
84
+ if (hasPageOnDisk(this.systemId, path, 'GET')) {
85
85
  //console.log('Ignoring prompt with existing page', initialPrompt.path);
86
86
  return true;
87
87
  }
@@ -39,7 +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 { getSystemBaseDir } from './page-utils';
42
+ import { getSystemBaseImplDir } from './page-utils';
43
43
 
44
44
  type ImplementationGenerator<T = StormFileImplementationPrompt> = (
45
45
  prompt: T,
@@ -1019,7 +1019,7 @@ export class StormCodegen {
1019
1019
  codeGenerator.withOption('AIContext', STORM_ID);
1020
1020
 
1021
1021
  if (this.uiSystemId) {
1022
- codeGenerator.withOption('AIStaticFiles', getSystemBaseDir(this.uiSystemId));
1022
+ codeGenerator.withOption('AIStaticFiles', getSystemBaseImplDir(this.uiSystemId));
1023
1023
  }
1024
1024
 
1025
1025
  const generatedResult = await codeGenerator.generate();
@@ -69,8 +69,9 @@ export async function writeImageToDisk(systemId: string, event: StormImage, prom
69
69
  };
70
70
  }
71
71
 
72
- export function hasPageOnDisk(systemId: string, method: string, path: string) {
73
- const fullPath = resolveReadPath(systemId, method, path);
72
+ export function hasPageOnDisk(systemId: string, path: string, method: string) {
73
+ const fullPath = resolveReadPath(systemId, path, method);
74
+ console.log('Checking for page on disk:', fullPath);
74
75
  return !!fullPath && FS.existsSync(fullPath);
75
76
  }
76
77
 
@@ -78,6 +79,10 @@ export function getSystemBaseDir(systemId: string) {
78
79
  return Path.join(os.tmpdir(), 'ai-systems', systemId);
79
80
  }
80
81
 
82
+ export function getSystemBaseImplDir(systemId: string) {
83
+ return Path.join(os.tmpdir(), 'ai-systems-impl', systemId);
84
+ }
85
+
81
86
  function getFilePath(method: string) {
82
87
  // For HEAD requests, we assume we're serving looking for a GET resource
83
88
  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,6 +622,10 @@ 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;
617
630
  const systemId = (req.query.systemId as string) ?? undefined;
618
631
 
@@ -731,7 +744,7 @@ router.post('/:handle/all', async (req: KapetaBodyRequest, res: Response) => {
731
744
  res.end();
732
745
  }
733
746
  }
734
- });
747
+ }
735
748
 
736
749
  router.post('/block/create', async (req: KapetaBodyRequest, res: Response) => {
737
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 = () => {};