@kapeta/local-cluster-service 0.68.0 → 0.70.0
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 +15 -0
- package/dist/cjs/src/storm/UIServer.d.ts +3 -1
- package/dist/cjs/src/storm/UIServer.js +7 -1
- package/dist/cjs/src/storm/routes.js +41 -16
- package/dist/cjs/src/storm/stormClient.d.ts +2 -1
- package/dist/cjs/src/storm/stormClient.js +12 -0
- package/dist/cjs/src/storm/stream.d.ts +4 -0
- package/dist/cjs/src/storm/utils.d.ts +1 -0
- package/dist/cjs/src/storm/utils.js +31 -0
- package/dist/esm/src/storm/UIServer.d.ts +3 -1
- package/dist/esm/src/storm/UIServer.js +7 -1
- package/dist/esm/src/storm/routes.js +41 -16
- package/dist/esm/src/storm/stormClient.d.ts +2 -1
- package/dist/esm/src/storm/stormClient.js +12 -0
- package/dist/esm/src/storm/stream.d.ts +4 -0
- package/dist/esm/src/storm/utils.d.ts +1 -0
- package/dist/esm/src/storm/utils.js +31 -0
- package/package.json +1 -1
- package/src/storm/UIServer.ts +9 -1
- package/src/storm/routes.ts +57 -19
- package/src/storm/stormClient.ts +14 -1
- package/src/storm/stream.ts +5 -0
- package/src/storm/utils.ts +28 -0
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
# [0.70.0](https://github.com/kapetacom/local-cluster-service/compare/v0.69.0...v0.70.0) (2024-09-04)
|
2
|
+
|
3
|
+
|
4
|
+
### Features
|
5
|
+
|
6
|
+
* add "serve" endpoint to resume a UI conversation server ([a2eadb2](https://github.com/kapetacom/local-cluster-service/commit/a2eadb25298d0d59aaf47202eba0fc911ba5e2f8))
|
7
|
+
* change endpoint to end conversation to same format as resume ([6161e5c](https://github.com/kapetacom/local-cluster-service/commit/6161e5ce2fda1261bb26abd6a608ea9e41d46099))
|
8
|
+
|
9
|
+
# [0.69.0](https://github.com/kapetacom/local-cluster-service/compare/v0.68.0...v0.69.0) (2024-09-03)
|
10
|
+
|
11
|
+
|
12
|
+
### Features
|
13
|
+
|
14
|
+
* adding create-system api ([d35545a](https://github.com/kapetacom/local-cluster-service/commit/d35545a3f35916116eca2cd116e1e9b923f1a8c2))
|
15
|
+
|
1
16
|
# [0.68.0](https://github.com/kapetacom/local-cluster-service/compare/v0.67.5...v0.68.0) (2024-09-03)
|
2
17
|
|
3
18
|
|
@@ -1,9 +1,11 @@
|
|
1
1
|
import { StormEventPage } from './events';
|
2
2
|
export declare class UIServer {
|
3
3
|
private readonly systemId;
|
4
|
-
private port;
|
5
4
|
private server;
|
5
|
+
private port;
|
6
6
|
constructor(systemId: string);
|
7
|
+
isRunning(): boolean;
|
8
|
+
getUrl(): string;
|
7
9
|
start(): Promise<void>;
|
8
10
|
close(): void;
|
9
11
|
resolveUrl(screenData: StormEventPage): string;
|
@@ -15,11 +15,17 @@ const http_1 = require("http");
|
|
15
15
|
const path_1 = require("path");
|
16
16
|
class UIServer {
|
17
17
|
systemId;
|
18
|
-
port = 50000;
|
19
18
|
server;
|
19
|
+
port = 50000;
|
20
20
|
constructor(systemId) {
|
21
21
|
this.systemId = systemId;
|
22
22
|
}
|
23
|
+
isRunning() {
|
24
|
+
return !!this.server;
|
25
|
+
}
|
26
|
+
getUrl() {
|
27
|
+
return `http://localhost:${this.port}`;
|
28
|
+
}
|
23
29
|
async start() {
|
24
30
|
const app = (0, express_1.default)();
|
25
31
|
app.get('/_reset', (req, res) => {
|
@@ -10,6 +10,7 @@ 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"));
|
13
14
|
const lodash_1 = __importDefault(require("lodash"));
|
14
15
|
const cors_1 = require("../middleware/cors");
|
15
16
|
const stringBody_1 = require("../middleware/stringBody");
|
@@ -24,6 +25,7 @@ const UIServer_1 = require("./UIServer");
|
|
24
25
|
const crypto_1 = require("crypto");
|
25
26
|
const PageGenerator_1 = require("./PageGenerator");
|
26
27
|
const PromiseQueue_1 = require("./PromiseQueue");
|
28
|
+
const utils_1 = require("./utils");
|
27
29
|
const UI_SERVERS = {};
|
28
30
|
const router = (0, express_promise_router_1.default)();
|
29
31
|
router.use('/', cors_1.corsHandler);
|
@@ -48,7 +50,7 @@ function convertPageEvent(screenData, innerConversationId, mainConversationId) {
|
|
48
50
|
description: screenData.payload.description,
|
49
51
|
prompt: screenData.payload.prompt,
|
50
52
|
path: screenData.payload.path,
|
51
|
-
url:
|
53
|
+
url: screenData.payload.content ? screenData.payload.path : '',
|
52
54
|
method: screenData.payload.method,
|
53
55
|
conversationId: innerConversationId,
|
54
56
|
},
|
@@ -56,8 +58,44 @@ function convertPageEvent(screenData, innerConversationId, mainConversationId) {
|
|
56
58
|
}
|
57
59
|
return screenData;
|
58
60
|
}
|
59
|
-
router.
|
60
|
-
|
61
|
+
router.post('/ui/serve/:systemId', async (req, res) => {
|
62
|
+
const systemId = req.params.systemId;
|
63
|
+
if (!systemId) {
|
64
|
+
res.status(404).send({ error: 'Missing "systemId" in URL' });
|
65
|
+
return;
|
66
|
+
}
|
67
|
+
const svr = (UI_SERVERS[systemId] = UI_SERVERS[systemId] || new UIServer_1.UIServer(systemId));
|
68
|
+
if (!svr.isRunning()) {
|
69
|
+
await UI_SERVERS[systemId].start();
|
70
|
+
}
|
71
|
+
res.status(200).send({ status: 'running', url: svr.getUrl() });
|
72
|
+
});
|
73
|
+
router.post('/ui/create-system/:systemId', async (req, res) => {
|
74
|
+
const systemId = req.params.systemId;
|
75
|
+
const srcDir = (0, page_utils_1.getSystemBaseDir)(systemId);
|
76
|
+
const destDir = path_1.default.join(node_os_1.default.tmpdir(), 'ai-systems-impl', systemId);
|
77
|
+
await (0, utils_1.copyDirectory)(srcDir, destDir, async (fileName, content) => {
|
78
|
+
const result = await stormClient_1.stormClient.implementAPIClients({
|
79
|
+
content: content,
|
80
|
+
fileName: fileName,
|
81
|
+
});
|
82
|
+
return result;
|
83
|
+
});
|
84
|
+
res.end();
|
85
|
+
return;
|
86
|
+
});
|
87
|
+
router.delete('/ui/serve/:systemId', async (req, res) => {
|
88
|
+
const systemId = req.params.systemId;
|
89
|
+
if (!systemId) {
|
90
|
+
res.status(404).send({ error: 'Missing "systemId" in URL' });
|
91
|
+
return;
|
92
|
+
}
|
93
|
+
const server = UI_SERVERS[systemId];
|
94
|
+
if (server) {
|
95
|
+
server.close();
|
96
|
+
delete UI_SERVERS[systemId];
|
97
|
+
}
|
98
|
+
res.status(200).json({ status: 'ok' });
|
61
99
|
});
|
62
100
|
router.post('/ui/screen', async (req, res) => {
|
63
101
|
try {
|
@@ -101,19 +139,6 @@ router.post('/ui/screen', async (req, res) => {
|
|
101
139
|
}
|
102
140
|
}
|
103
141
|
});
|
104
|
-
router.delete('/:handle/ui', async (req, res) => {
|
105
|
-
const conversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()];
|
106
|
-
if (!conversationId) {
|
107
|
-
res.status(400).send('Missing conversation id');
|
108
|
-
return;
|
109
|
-
}
|
110
|
-
const server = UI_SERVERS[conversationId];
|
111
|
-
if (server) {
|
112
|
-
server.close();
|
113
|
-
delete UI_SERVERS[conversationId];
|
114
|
-
}
|
115
|
-
res.status(200).json({ status: 'ok' });
|
116
|
-
});
|
117
142
|
router.post('/:handle/ui/iterative', async (req, res) => {
|
118
143
|
const handle = req.params.handle;
|
119
144
|
try {
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/// <reference types="node" />
|
2
|
-
import { ConversationItem, StormFileImplementationPrompt, StormStream, StormUIImplementationPrompt, StormUIListPrompt } from './stream';
|
2
|
+
import { ConversationItem, ImplementAPIClientsRequest, StormFileImplementationPrompt, StormStream, StormUIImplementationPrompt, StormUIListPrompt } from './stream';
|
3
3
|
import { Page, StormEventPageUrl } from './events';
|
4
4
|
export declare const STORM_ID = "storm";
|
5
5
|
export declare const ConversationIdHeader = "Conversation-Id";
|
@@ -70,6 +70,7 @@ declare class StormClient {
|
|
70
70
|
getVoteUIPage(topic: string, conversationId: string, mainConversationId?: string): Promise<{
|
71
71
|
vote: -1 | 0 | 1;
|
72
72
|
}>;
|
73
|
+
implementAPIClients(prompt: ImplementAPIClientsRequest): 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>;
|
@@ -137,6 +137,18 @@ class StormClient {
|
|
137
137
|
});
|
138
138
|
return response.json();
|
139
139
|
}
|
140
|
+
async implementAPIClients(prompt) {
|
141
|
+
const u = `${this._baseUrl}/v2/ui/implement-api-clients`;
|
142
|
+
const response = await fetch(u, {
|
143
|
+
method: 'POST',
|
144
|
+
body: JSON.stringify({
|
145
|
+
fileName: prompt.fileName,
|
146
|
+
content: prompt.content,
|
147
|
+
}),
|
148
|
+
});
|
149
|
+
const data = await response.text();
|
150
|
+
return data;
|
151
|
+
}
|
140
152
|
classifyUIReferences(prompt, conversationId) {
|
141
153
|
return this.send('/v2/ui/references', {
|
142
154
|
prompt: prompt,
|
@@ -0,0 +1 @@
|
|
1
|
+
export declare function copyDirectory(src: string, dest: string, modifyHtml: (fileName: string, content: string) => Promise<string>): Promise<void>;
|
@@ -0,0 +1,31 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.copyDirectory = void 0;
|
7
|
+
/**
|
8
|
+
* Copyright 2023 Kapeta Inc.
|
9
|
+
* SPDX-License-Identifier: BUSL-1.1
|
10
|
+
*/
|
11
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
12
|
+
const path_1 = __importDefault(require("path"));
|
13
|
+
async function copyDirectory(src, dest, modifyHtml) {
|
14
|
+
await fs_extra_1.default.promises.mkdir(dest, { recursive: true });
|
15
|
+
const entries = await fs_extra_1.default.promises.readdir(src, { withFileTypes: true });
|
16
|
+
for (const entry of entries) {
|
17
|
+
const srcPath = path_1.default.join(src, entry.name);
|
18
|
+
const destPath = path_1.default.join(dest, entry.name);
|
19
|
+
if (entry.isDirectory()) {
|
20
|
+
await copyDirectory(srcPath, destPath, modifyHtml);
|
21
|
+
}
|
22
|
+
else if (entry.isFile()) {
|
23
|
+
let content = await fs_extra_1.default.promises.readFile(srcPath, 'utf-8');
|
24
|
+
if (path_1.default.extname(srcPath) === '.html') {
|
25
|
+
content = await modifyHtml(srcPath, content);
|
26
|
+
}
|
27
|
+
await fs_extra_1.default.promises.writeFile(destPath, content, 'utf-8');
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
31
|
+
exports.copyDirectory = copyDirectory;
|
@@ -1,9 +1,11 @@
|
|
1
1
|
import { StormEventPage } from './events';
|
2
2
|
export declare class UIServer {
|
3
3
|
private readonly systemId;
|
4
|
-
private port;
|
5
4
|
private server;
|
5
|
+
private port;
|
6
6
|
constructor(systemId: string);
|
7
|
+
isRunning(): boolean;
|
8
|
+
getUrl(): string;
|
7
9
|
start(): Promise<void>;
|
8
10
|
close(): void;
|
9
11
|
resolveUrl(screenData: StormEventPage): string;
|
@@ -15,11 +15,17 @@ const http_1 = require("http");
|
|
15
15
|
const path_1 = require("path");
|
16
16
|
class UIServer {
|
17
17
|
systemId;
|
18
|
-
port = 50000;
|
19
18
|
server;
|
19
|
+
port = 50000;
|
20
20
|
constructor(systemId) {
|
21
21
|
this.systemId = systemId;
|
22
22
|
}
|
23
|
+
isRunning() {
|
24
|
+
return !!this.server;
|
25
|
+
}
|
26
|
+
getUrl() {
|
27
|
+
return `http://localhost:${this.port}`;
|
28
|
+
}
|
23
29
|
async start() {
|
24
30
|
const app = (0, express_1.default)();
|
25
31
|
app.get('/_reset', (req, res) => {
|
@@ -10,6 +10,7 @@ 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"));
|
13
14
|
const lodash_1 = __importDefault(require("lodash"));
|
14
15
|
const cors_1 = require("../middleware/cors");
|
15
16
|
const stringBody_1 = require("../middleware/stringBody");
|
@@ -24,6 +25,7 @@ const UIServer_1 = require("./UIServer");
|
|
24
25
|
const crypto_1 = require("crypto");
|
25
26
|
const PageGenerator_1 = require("./PageGenerator");
|
26
27
|
const PromiseQueue_1 = require("./PromiseQueue");
|
28
|
+
const utils_1 = require("./utils");
|
27
29
|
const UI_SERVERS = {};
|
28
30
|
const router = (0, express_promise_router_1.default)();
|
29
31
|
router.use('/', cors_1.corsHandler);
|
@@ -48,7 +50,7 @@ function convertPageEvent(screenData, innerConversationId, mainConversationId) {
|
|
48
50
|
description: screenData.payload.description,
|
49
51
|
prompt: screenData.payload.prompt,
|
50
52
|
path: screenData.payload.path,
|
51
|
-
url:
|
53
|
+
url: screenData.payload.content ? screenData.payload.path : '',
|
52
54
|
method: screenData.payload.method,
|
53
55
|
conversationId: innerConversationId,
|
54
56
|
},
|
@@ -56,8 +58,44 @@ function convertPageEvent(screenData, innerConversationId, mainConversationId) {
|
|
56
58
|
}
|
57
59
|
return screenData;
|
58
60
|
}
|
59
|
-
router.
|
60
|
-
|
61
|
+
router.post('/ui/serve/:systemId', async (req, res) => {
|
62
|
+
const systemId = req.params.systemId;
|
63
|
+
if (!systemId) {
|
64
|
+
res.status(404).send({ error: 'Missing "systemId" in URL' });
|
65
|
+
return;
|
66
|
+
}
|
67
|
+
const svr = (UI_SERVERS[systemId] = UI_SERVERS[systemId] || new UIServer_1.UIServer(systemId));
|
68
|
+
if (!svr.isRunning()) {
|
69
|
+
await UI_SERVERS[systemId].start();
|
70
|
+
}
|
71
|
+
res.status(200).send({ status: 'running', url: svr.getUrl() });
|
72
|
+
});
|
73
|
+
router.post('/ui/create-system/:systemId', async (req, res) => {
|
74
|
+
const systemId = req.params.systemId;
|
75
|
+
const srcDir = (0, page_utils_1.getSystemBaseDir)(systemId);
|
76
|
+
const destDir = path_1.default.join(node_os_1.default.tmpdir(), 'ai-systems-impl', systemId);
|
77
|
+
await (0, utils_1.copyDirectory)(srcDir, destDir, async (fileName, content) => {
|
78
|
+
const result = await stormClient_1.stormClient.implementAPIClients({
|
79
|
+
content: content,
|
80
|
+
fileName: fileName,
|
81
|
+
});
|
82
|
+
return result;
|
83
|
+
});
|
84
|
+
res.end();
|
85
|
+
return;
|
86
|
+
});
|
87
|
+
router.delete('/ui/serve/:systemId', async (req, res) => {
|
88
|
+
const systemId = req.params.systemId;
|
89
|
+
if (!systemId) {
|
90
|
+
res.status(404).send({ error: 'Missing "systemId" in URL' });
|
91
|
+
return;
|
92
|
+
}
|
93
|
+
const server = UI_SERVERS[systemId];
|
94
|
+
if (server) {
|
95
|
+
server.close();
|
96
|
+
delete UI_SERVERS[systemId];
|
97
|
+
}
|
98
|
+
res.status(200).json({ status: 'ok' });
|
61
99
|
});
|
62
100
|
router.post('/ui/screen', async (req, res) => {
|
63
101
|
try {
|
@@ -101,19 +139,6 @@ router.post('/ui/screen', async (req, res) => {
|
|
101
139
|
}
|
102
140
|
}
|
103
141
|
});
|
104
|
-
router.delete('/:handle/ui', async (req, res) => {
|
105
|
-
const conversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()];
|
106
|
-
if (!conversationId) {
|
107
|
-
res.status(400).send('Missing conversation id');
|
108
|
-
return;
|
109
|
-
}
|
110
|
-
const server = UI_SERVERS[conversationId];
|
111
|
-
if (server) {
|
112
|
-
server.close();
|
113
|
-
delete UI_SERVERS[conversationId];
|
114
|
-
}
|
115
|
-
res.status(200).json({ status: 'ok' });
|
116
|
-
});
|
117
142
|
router.post('/:handle/ui/iterative', async (req, res) => {
|
118
143
|
const handle = req.params.handle;
|
119
144
|
try {
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/// <reference types="node" />
|
2
|
-
import { ConversationItem, StormFileImplementationPrompt, StormStream, StormUIImplementationPrompt, StormUIListPrompt } from './stream';
|
2
|
+
import { ConversationItem, ImplementAPIClientsRequest, StormFileImplementationPrompt, StormStream, StormUIImplementationPrompt, StormUIListPrompt } from './stream';
|
3
3
|
import { Page, StormEventPageUrl } from './events';
|
4
4
|
export declare const STORM_ID = "storm";
|
5
5
|
export declare const ConversationIdHeader = "Conversation-Id";
|
@@ -70,6 +70,7 @@ declare class StormClient {
|
|
70
70
|
getVoteUIPage(topic: string, conversationId: string, mainConversationId?: string): Promise<{
|
71
71
|
vote: -1 | 0 | 1;
|
72
72
|
}>;
|
73
|
+
implementAPIClients(prompt: ImplementAPIClientsRequest): 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>;
|
@@ -137,6 +137,18 @@ class StormClient {
|
|
137
137
|
});
|
138
138
|
return response.json();
|
139
139
|
}
|
140
|
+
async implementAPIClients(prompt) {
|
141
|
+
const u = `${this._baseUrl}/v2/ui/implement-api-clients`;
|
142
|
+
const response = await fetch(u, {
|
143
|
+
method: 'POST',
|
144
|
+
body: JSON.stringify({
|
145
|
+
fileName: prompt.fileName,
|
146
|
+
content: prompt.content,
|
147
|
+
}),
|
148
|
+
});
|
149
|
+
const data = await response.text();
|
150
|
+
return data;
|
151
|
+
}
|
140
152
|
classifyUIReferences(prompt, conversationId) {
|
141
153
|
return this.send('/v2/ui/references', {
|
142
154
|
prompt: prompt,
|
@@ -0,0 +1 @@
|
|
1
|
+
export declare function copyDirectory(src: string, dest: string, modifyHtml: (fileName: string, content: string) => Promise<string>): Promise<void>;
|
@@ -0,0 +1,31 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.copyDirectory = void 0;
|
7
|
+
/**
|
8
|
+
* Copyright 2023 Kapeta Inc.
|
9
|
+
* SPDX-License-Identifier: BUSL-1.1
|
10
|
+
*/
|
11
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
12
|
+
const path_1 = __importDefault(require("path"));
|
13
|
+
async function copyDirectory(src, dest, modifyHtml) {
|
14
|
+
await fs_extra_1.default.promises.mkdir(dest, { recursive: true });
|
15
|
+
const entries = await fs_extra_1.default.promises.readdir(src, { withFileTypes: true });
|
16
|
+
for (const entry of entries) {
|
17
|
+
const srcPath = path_1.default.join(src, entry.name);
|
18
|
+
const destPath = path_1.default.join(dest, entry.name);
|
19
|
+
if (entry.isDirectory()) {
|
20
|
+
await copyDirectory(srcPath, destPath, modifyHtml);
|
21
|
+
}
|
22
|
+
else if (entry.isFile()) {
|
23
|
+
let content = await fs_extra_1.default.promises.readFile(srcPath, 'utf-8');
|
24
|
+
if (path_1.default.extname(srcPath) === '.html') {
|
25
|
+
content = await modifyHtml(srcPath, content);
|
26
|
+
}
|
27
|
+
await fs_extra_1.default.promises.writeFile(destPath, content, 'utf-8');
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
31
|
+
exports.copyDirectory = copyDirectory;
|
package/package.json
CHANGED
package/src/storm/UIServer.ts
CHANGED
@@ -12,13 +12,21 @@ import { join } from 'path';
|
|
12
12
|
export class UIServer {
|
13
13
|
private readonly systemId: string;
|
14
14
|
|
15
|
-
private port: number = 50000;
|
16
15
|
private server: Server | undefined;
|
16
|
+
private port: number = 50000;
|
17
17
|
|
18
18
|
constructor(systemId: string) {
|
19
19
|
this.systemId = systemId;
|
20
20
|
}
|
21
21
|
|
22
|
+
public isRunning() {
|
23
|
+
return !!this.server;
|
24
|
+
}
|
25
|
+
|
26
|
+
public getUrl() {
|
27
|
+
return `http://localhost:${this.port}`;
|
28
|
+
}
|
29
|
+
|
22
30
|
public async start() {
|
23
31
|
const app = express();
|
24
32
|
app.get('/_reset', (req: Request, res: Response) => {
|
package/src/storm/routes.ts
CHANGED
@@ -7,6 +7,7 @@ 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';
|
10
11
|
import _ from 'lodash';
|
11
12
|
import { corsHandler } from '../middleware/cors';
|
12
13
|
import { stringBody } from '../middleware/stringBody';
|
@@ -34,11 +35,20 @@ import {
|
|
34
35
|
import { StormCodegen } from './codegen';
|
35
36
|
import { assetManager } from '../assetManager';
|
36
37
|
import uuid from 'node-uuid';
|
37
|
-
import {
|
38
|
+
import {
|
39
|
+
getSystemBaseDir,
|
40
|
+
readPageFromDisk,
|
41
|
+
resolveReadPath,
|
42
|
+
SystemIdHeader,
|
43
|
+
writeAssetToDisk,
|
44
|
+
writeImageToDisk,
|
45
|
+
writePageToDisk,
|
46
|
+
} from './page-utils';
|
38
47
|
import { UIServer } from './UIServer';
|
39
48
|
import { randomUUID } from 'crypto';
|
40
49
|
import { ImagePrompt, PageQueue } from './PageGenerator';
|
41
50
|
import { createFuture } from './PromiseQueue';
|
51
|
+
import { copyDirectory } from './utils';
|
42
52
|
|
43
53
|
const UI_SERVERS: { [key: string]: UIServer } = {};
|
44
54
|
const router = Router();
|
@@ -67,7 +77,7 @@ function convertPageEvent(screenData: StormEvent, innerConversationId: string, m
|
|
67
77
|
description: screenData.payload.description,
|
68
78
|
prompt: screenData.payload.prompt,
|
69
79
|
path: screenData.payload.path,
|
70
|
-
url:
|
80
|
+
url: screenData.payload.content ? screenData.payload.path : '',
|
71
81
|
method: screenData.payload.method,
|
72
82
|
conversationId: innerConversationId,
|
73
83
|
},
|
@@ -77,8 +87,51 @@ function convertPageEvent(screenData: StormEvent, innerConversationId: string, m
|
|
77
87
|
return screenData;
|
78
88
|
}
|
79
89
|
|
80
|
-
router.
|
81
|
-
|
90
|
+
router.post('/ui/serve/:systemId', async (req: KapetaBodyRequest, res: Response) => {
|
91
|
+
const systemId = req.params.systemId as string | undefined;
|
92
|
+
if (!systemId) {
|
93
|
+
res.status(404).send({ error: 'Missing "systemId" in URL' });
|
94
|
+
return;
|
95
|
+
}
|
96
|
+
|
97
|
+
const svr = (UI_SERVERS[systemId] = UI_SERVERS[systemId] || new UIServer(systemId));
|
98
|
+
if (!svr.isRunning()) {
|
99
|
+
await UI_SERVERS[systemId].start();
|
100
|
+
}
|
101
|
+
|
102
|
+
res.status(200).send({ status: 'running', url: svr.getUrl() });
|
103
|
+
});
|
104
|
+
|
105
|
+
router.post('/ui/create-system/:systemId', async (req: KapetaBodyRequest, res: Response) => {
|
106
|
+
const systemId = req.params.systemId as string;
|
107
|
+
const srcDir = getSystemBaseDir(systemId);
|
108
|
+
const destDir = Path.join(os.tmpdir(), 'ai-systems-impl', systemId);
|
109
|
+
|
110
|
+
await copyDirectory(srcDir, destDir, async (fileName, content) => {
|
111
|
+
const result = await stormClient.implementAPIClients({
|
112
|
+
content: content,
|
113
|
+
fileName: fileName,
|
114
|
+
});
|
115
|
+
return result;
|
116
|
+
});
|
117
|
+
|
118
|
+
res.end();
|
119
|
+
return;
|
120
|
+
});
|
121
|
+
|
122
|
+
router.delete('/ui/serve/:systemId', async (req: KapetaBodyRequest, res: Response) => {
|
123
|
+
const systemId = req.params.systemId as string | undefined;
|
124
|
+
if (!systemId) {
|
125
|
+
res.status(404).send({ error: 'Missing "systemId" in URL' });
|
126
|
+
return;
|
127
|
+
}
|
128
|
+
|
129
|
+
const server = UI_SERVERS[systemId];
|
130
|
+
if (server) {
|
131
|
+
server.close();
|
132
|
+
delete UI_SERVERS[systemId];
|
133
|
+
}
|
134
|
+
res.status(200).json({ status: 'ok' });
|
82
135
|
});
|
83
136
|
|
84
137
|
router.post('/ui/screen', async (req: KapetaBodyRequest, res: Response) => {
|
@@ -131,21 +184,6 @@ router.post('/ui/screen', async (req: KapetaBodyRequest, res: Response) => {
|
|
131
184
|
}
|
132
185
|
});
|
133
186
|
|
134
|
-
router.delete('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
|
135
|
-
const conversationId = req.headers[ConversationIdHeader.toLowerCase()] as string | undefined;
|
136
|
-
if (!conversationId) {
|
137
|
-
res.status(400).send('Missing conversation id');
|
138
|
-
return;
|
139
|
-
}
|
140
|
-
|
141
|
-
const server = UI_SERVERS[conversationId];
|
142
|
-
if (server) {
|
143
|
-
server.close();
|
144
|
-
delete UI_SERVERS[conversationId];
|
145
|
-
}
|
146
|
-
res.status(200).json({ status: 'ok' });
|
147
|
-
});
|
148
|
-
|
149
187
|
router.post('/:handle/ui/iterative', async (req: KapetaBodyRequest, res: Response) => {
|
150
188
|
const handle = req.params.handle as string;
|
151
189
|
try {
|
package/src/storm/stormClient.ts
CHANGED
@@ -8,13 +8,13 @@ import readLine from 'node:readline/promises';
|
|
8
8
|
import { Readable } from 'node:stream';
|
9
9
|
import {
|
10
10
|
ConversationItem,
|
11
|
+
ImplementAPIClientsRequest,
|
11
12
|
StormContextRequest,
|
12
13
|
StormFileImplementationPrompt,
|
13
14
|
StormStream,
|
14
15
|
StormUIImplementationPrompt,
|
15
16
|
StormUIListPrompt,
|
16
17
|
} from './stream';
|
17
|
-
import { getRawAsset } from 'node:sea';
|
18
18
|
import { Page, StormEventPageUrl } from './events';
|
19
19
|
|
20
20
|
export const STORM_ID = 'storm';
|
@@ -242,6 +242,19 @@ class StormClient {
|
|
242
242
|
return response.json() as Promise<{ vote: -1 | 0 | 1 }>;
|
243
243
|
}
|
244
244
|
|
245
|
+
public async implementAPIClients(prompt: ImplementAPIClientsRequest) {
|
246
|
+
const u = `${this._baseUrl}/v2/ui/implement-api-clients`;
|
247
|
+
const response = await fetch(u, {
|
248
|
+
method: 'POST',
|
249
|
+
body: JSON.stringify({
|
250
|
+
fileName: prompt.fileName,
|
251
|
+
content: prompt.content,
|
252
|
+
}),
|
253
|
+
});
|
254
|
+
const data = await response.text();
|
255
|
+
return data;
|
256
|
+
}
|
257
|
+
|
245
258
|
public classifyUIReferences(prompt: string, conversationId?: string) {
|
246
259
|
return this.send('/v2/ui/references', {
|
247
260
|
prompt: prompt,
|
package/src/storm/stream.ts
CHANGED
@@ -0,0 +1,28 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright 2023 Kapeta Inc.
|
3
|
+
* SPDX-License-Identifier: BUSL-1.1
|
4
|
+
*/
|
5
|
+
import FS from 'fs-extra';
|
6
|
+
import Path from 'path';
|
7
|
+
|
8
|
+
export async function copyDirectory(src: string, dest: string, modifyHtml: (fileName: string, content: string) => Promise<string>): Promise<void> {
|
9
|
+
await FS.promises.mkdir(dest, { recursive: true });
|
10
|
+
const entries = await FS.promises.readdir(src, { withFileTypes: true });
|
11
|
+
|
12
|
+
for (const entry of entries) {
|
13
|
+
const srcPath = Path.join(src, entry.name);
|
14
|
+
const destPath = Path.join(dest, entry.name);
|
15
|
+
|
16
|
+
if (entry.isDirectory()) {
|
17
|
+
await copyDirectory(srcPath, destPath, modifyHtml);
|
18
|
+
} else if (entry.isFile()) {
|
19
|
+
let content = await FS.promises.readFile(srcPath, 'utf-8');
|
20
|
+
|
21
|
+
if (Path.extname(srcPath) === '.html') {
|
22
|
+
content = await modifyHtml(srcPath, content);
|
23
|
+
}
|
24
|
+
|
25
|
+
await FS.promises.writeFile(destPath, content, 'utf-8');
|
26
|
+
}
|
27
|
+
}
|
28
|
+
}
|