@kapeta/local-cluster-service 0.48.4 → 0.49.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/.vscode/launch.json +1 -1
- package/CHANGELOG.md +14 -0
- package/dist/cjs/src/assetManager.d.ts +1 -1
- package/dist/cjs/src/assetManager.js +5 -3
- package/dist/cjs/src/filesystem/routes.js +10 -0
- package/dist/cjs/src/storm/codegen.d.ts +9 -1
- package/dist/cjs/src/storm/codegen.js +111 -8
- package/dist/cjs/src/storm/event-parser.d.ts +1 -1
- package/dist/cjs/src/storm/event-parser.js +3 -1
- package/dist/cjs/src/storm/events.d.ts +32 -1
- package/dist/cjs/src/storm/routes.js +26 -1
- package/dist/cjs/src/storm/stormClient.d.ts +3 -1
- package/dist/cjs/src/storm/stormClient.js +12 -0
- package/dist/cjs/src/storm/stream.d.ts +6 -0
- package/dist/esm/src/assetManager.d.ts +1 -1
- package/dist/esm/src/assetManager.js +5 -3
- package/dist/esm/src/filesystem/routes.js +10 -0
- package/dist/esm/src/storm/codegen.d.ts +9 -1
- package/dist/esm/src/storm/codegen.js +111 -8
- package/dist/esm/src/storm/event-parser.d.ts +1 -1
- package/dist/esm/src/storm/event-parser.js +3 -1
- package/dist/esm/src/storm/events.d.ts +32 -1
- package/dist/esm/src/storm/routes.js +26 -1
- package/dist/esm/src/storm/stormClient.d.ts +3 -1
- package/dist/esm/src/storm/stormClient.js +12 -0
- package/dist/esm/src/storm/stream.d.ts +6 -0
- package/package.json +1 -1
- package/src/assetManager.ts +6 -4
- package/src/filesystem/routes.ts +10 -0
- package/src/storm/codegen.ts +132 -12
- package/src/storm/event-parser.ts +4 -2
- package/src/storm/events.ts +38 -1
- package/src/storm/routes.ts +39 -2
- package/src/storm/stormClient.ts +13 -1
- package/src/storm/stream.ts +7 -0
package/.vscode/launch.json
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
"request": "launch",
|
7
7
|
"name": "Debug Node.js (npm run dev)",
|
8
8
|
"runtimeExecutable": "npm",
|
9
|
-
"runtimeArgs": ["run
|
9
|
+
"runtimeArgs": ["run", "start:dev"],
|
10
10
|
"restart": true,
|
11
11
|
"console": "integratedTerminal",
|
12
12
|
"internalConsoleOptions": "neverOpen",
|
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
# [0.49.0](https://github.com/kapetacom/local-cluster-service/compare/v0.48.5...v0.49.0) (2024-06-05)
|
2
|
+
|
3
|
+
|
4
|
+
### Features
|
5
|
+
|
6
|
+
* Implement ability to save ai generated plan ([#161](https://github.com/kapetacom/local-cluster-service/issues/161)) ([8223757](https://github.com/kapetacom/local-cluster-service/commit/8223757ff90ee6b5e1b7f171fc724d9877051df2))
|
7
|
+
|
8
|
+
## [0.48.5](https://github.com/kapetacom/local-cluster-service/compare/v0.48.4...v0.48.5) (2024-06-04)
|
9
|
+
|
10
|
+
|
11
|
+
### Bug Fixes
|
12
|
+
|
13
|
+
* we should read the file previously written ([fe0b78e](https://github.com/kapetacom/local-cluster-service/commit/fe0b78efd5018be28308170a0c76b2619425a158))
|
14
|
+
|
1
15
|
## [0.48.4](https://github.com/kapetacom/local-cluster-service/compare/v0.48.3...v0.48.4) (2024-06-03)
|
2
16
|
|
3
17
|
|
@@ -28,7 +28,7 @@ declare class AssetManager {
|
|
28
28
|
getPlan(ref: string, noCache?: boolean): Promise<Plan>;
|
29
29
|
getBlockInstance(systemId: string, instanceId: string): Promise<BlockInstance>;
|
30
30
|
getAsset(ref: string, noCache?: boolean, autoFetch?: boolean): Promise<EnrichedAsset | undefined>;
|
31
|
-
createAsset(path: string, yaml: BlockDefinition, sourceOfChange?: SourceOfChange): Promise<EnrichedAsset[]>;
|
31
|
+
createAsset(path: string, yaml: BlockDefinition, sourceOfChange?: SourceOfChange, codegen?: boolean): Promise<EnrichedAsset[]>;
|
32
32
|
updateAsset(ref: string, yaml: Definition, sourceOfChange?: SourceOfChange): Promise<void>;
|
33
33
|
importFile(filePath: string): Promise<EnrichedAsset[]>;
|
34
34
|
unregisterAsset(ref: string): Promise<void>;
|
@@ -142,7 +142,7 @@ class AssetManager {
|
|
142
142
|
}
|
143
143
|
return undefined;
|
144
144
|
}
|
145
|
-
async createAsset(path, yaml, sourceOfChange = 'filesystem') {
|
145
|
+
async createAsset(path, yaml, sourceOfChange = 'filesystem', codegen = true) {
|
146
146
|
if (await fs_extra_1.default.pathExists(path)) {
|
147
147
|
throw new Error('File already exists: ' + path);
|
148
148
|
}
|
@@ -159,9 +159,11 @@ class AssetManager {
|
|
159
159
|
cacheManager_1.cacheManager.set(key, a, CACHE_TTL);
|
160
160
|
});
|
161
161
|
definitionsManager_1.definitionsManager.clearCache();
|
162
|
-
console.log(`Created asset at: ${path}`);
|
163
162
|
const ref = `kapeta://${yaml.metadata.name}:local`;
|
164
|
-
|
163
|
+
console.log(`Created asset ${ref} at: ${path}`);
|
164
|
+
if (codegen) {
|
165
|
+
await this.maybeGenerateCode(ref, path, yaml);
|
166
|
+
}
|
165
167
|
return asset;
|
166
168
|
}
|
167
169
|
async updateAsset(ref, yaml, sourceOfChange = 'filesystem') {
|
@@ -11,6 +11,7 @@ const express_promise_router_1 = __importDefault(require("express-promise-router
|
|
11
11
|
const stringBody_1 = require("../middleware/stringBody");
|
12
12
|
const filesystemManager_1 = require("../filesystemManager");
|
13
13
|
const cors_1 = require("../middleware/cors");
|
14
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
14
15
|
let router = (0, express_promise_router_1.default)();
|
15
16
|
router.use('/', cors_1.corsHandler);
|
16
17
|
router.get('/root', (req, res) => {
|
@@ -81,6 +82,15 @@ router.get('/readfile', async (req, res) => {
|
|
81
82
|
res.status(400).send({ error: '' + err });
|
82
83
|
}
|
83
84
|
});
|
85
|
+
router.get('/exists', async (req, res) => {
|
86
|
+
let pathArg = req.query.path;
|
87
|
+
try {
|
88
|
+
res.send(await fs_extra_1.default.pathExists(pathArg));
|
89
|
+
}
|
90
|
+
catch (err) {
|
91
|
+
res.status(400).send({ error: '' + err });
|
92
|
+
}
|
93
|
+
});
|
84
94
|
router.put('/mkdir', async (req, res) => {
|
85
95
|
let pathArg = req.query.path;
|
86
96
|
try {
|
@@ -11,7 +11,8 @@ export declare class StormCodegen {
|
|
11
11
|
private readonly out;
|
12
12
|
private readonly events;
|
13
13
|
private readonly tmpDir;
|
14
|
-
|
14
|
+
private readonly conversationId;
|
15
|
+
constructor(conversationId: string, userPrompt: string, blocks: BlockDefinitionInfo[], events: StormEvent[]);
|
15
16
|
process(): Promise<void>;
|
16
17
|
getStream(): StormStream;
|
17
18
|
private handleTemplateFileOutput;
|
@@ -22,6 +23,13 @@ export declare class StormCodegen {
|
|
22
23
|
* Generates the code for a block and sends it to the AI
|
23
24
|
*/
|
24
25
|
private processBlockCode;
|
26
|
+
private verifyAndFixCode;
|
27
|
+
removePrefix(prefix: string, str: string): string;
|
28
|
+
writeToFile(fileName: string, code: Promise<string>): Promise<void>;
|
29
|
+
/**
|
30
|
+
* Sends the code to the AI for a fix
|
31
|
+
*/
|
32
|
+
private codeFix;
|
25
33
|
/**
|
26
34
|
* Emits the text-based files to the stream
|
27
35
|
*/
|
@@ -36,25 +36,31 @@ const codeGeneratorManager_1 = require("../codeGeneratorManager");
|
|
36
36
|
const stormClient_1 = require("./stormClient");
|
37
37
|
const event_parser_1 = require("./event-parser");
|
38
38
|
const stream_1 = require("./stream");
|
39
|
+
const nodejs_utils_1 = require("@kapeta/nodejs-utils");
|
39
40
|
const promises_1 = require("fs/promises");
|
40
41
|
const path_1 = __importStar(require("path"));
|
41
42
|
const node_os_1 = __importDefault(require("node:os"));
|
43
|
+
const fs_1 = require("fs");
|
44
|
+
const path_2 = __importDefault(require("path"));
|
42
45
|
class StormCodegen {
|
43
46
|
userPrompt;
|
44
47
|
blocks;
|
45
48
|
out = new stream_1.StormStream();
|
46
49
|
events;
|
47
50
|
tmpDir;
|
48
|
-
|
51
|
+
conversationId;
|
52
|
+
constructor(conversationId, userPrompt, blocks, events) {
|
49
53
|
this.userPrompt = userPrompt;
|
50
54
|
this.blocks = blocks;
|
51
55
|
this.events = events;
|
52
|
-
this.tmpDir = node_os_1.default.tmpdir();
|
56
|
+
this.tmpDir = path_2.default.join(node_os_1.default.tmpdir(), conversationId);
|
57
|
+
this.conversationId = conversationId;
|
53
58
|
}
|
54
59
|
async process() {
|
55
|
-
|
56
|
-
|
57
|
-
}
|
60
|
+
const promises = this.blocks.map((block) => {
|
61
|
+
return this.processBlockCode(block);
|
62
|
+
});
|
63
|
+
await Promise.all(promises);
|
58
64
|
this.out.end();
|
59
65
|
}
|
60
66
|
getStream() {
|
@@ -118,7 +124,7 @@ class StormCodegen {
|
|
118
124
|
}
|
119
125
|
const allFiles = this.toStormFiles(generatedResult);
|
120
126
|
// Send all the non-ai files to the stream
|
121
|
-
this.emitFiles(block.uri, block.aiName, allFiles);
|
127
|
+
this.emitFiles((0, nodejs_utils_1.parseKapetaUri)(block.uri), block.aiName, allFiles);
|
122
128
|
const relevantFiles = allFiles.filter((file) => file.type !== codegen_1.AIFileTypes.IGNORE && file.type !== codegen_1.AIFileTypes.WEB_SCREEN);
|
123
129
|
const uiTemplates = allFiles.filter((file) => file.type === codegen_1.AIFileTypes.WEB_SCREEN);
|
124
130
|
if (uiTemplates.length > 0) {
|
@@ -130,7 +136,7 @@ class StormCodegen {
|
|
130
136
|
prompt: this.userPrompt,
|
131
137
|
});
|
132
138
|
uiStream.on('data', (evt) => {
|
133
|
-
this.handleUiOutput(block.uri, block.aiName, evt);
|
139
|
+
this.handleUiOutput((0, nodejs_utils_1.parseKapetaUri)(block.uri), block.aiName, evt);
|
134
140
|
});
|
135
141
|
await uiStream.waitForDone();
|
136
142
|
}
|
@@ -139,7 +145,7 @@ class StormCodegen {
|
|
139
145
|
// Send the service and UI templates to the AI. These will be send one-by-one in addition to the context files
|
140
146
|
const serviceFiles = allFiles.filter((file) => file.type === codegen_1.AIFileTypes.SERVICE);
|
141
147
|
if (serviceFiles.length > 0) {
|
142
|
-
await this.processTemplates(block.uri, block.aiName, stormClient_1.stormClient.createServiceImplementation.bind(stormClient_1.stormClient), serviceFiles, contextFiles);
|
148
|
+
await this.processTemplates((0, nodejs_utils_1.parseKapetaUri)(block.uri), block.aiName, stormClient_1.stormClient.createServiceImplementation.bind(stormClient_1.stormClient), serviceFiles, contextFiles);
|
143
149
|
}
|
144
150
|
const basePath = this.getBasePath(block.content.metadata.name);
|
145
151
|
for (const serviceFile of serviceFiles) {
|
@@ -154,6 +160,103 @@ class StormCodegen {
|
|
154
160
|
const filePath = (0, path_1.join)(basePath, uiFile.filename);
|
155
161
|
await (0, promises_1.writeFile)(filePath, uiFile.content);
|
156
162
|
}
|
163
|
+
const filesToBeFixed = serviceFiles.concat(contextFiles);
|
164
|
+
const codeGenerator = new codegen_1.BlockCodeGenerator(block.content);
|
165
|
+
await this.verifyAndFixCode(codeGenerator, basePath, filesToBeFixed, allFiles);
|
166
|
+
const blockRef = block.uri;
|
167
|
+
this.out.emit('data', {
|
168
|
+
type: 'BLOCK_READY',
|
169
|
+
reason: 'Block ready',
|
170
|
+
created: Date.now(),
|
171
|
+
payload: {
|
172
|
+
path: basePath,
|
173
|
+
blockName: block.aiName,
|
174
|
+
blockRef,
|
175
|
+
instanceId: event_parser_1.StormEventParser.toInstanceIdFromRef(blockRef),
|
176
|
+
},
|
177
|
+
});
|
178
|
+
}
|
179
|
+
async verifyAndFixCode(codeGenerator, basePath, filesToBeFixed, knownFiles) {
|
180
|
+
let attempts = 0;
|
181
|
+
let validCode = false;
|
182
|
+
for (let i = 0; i <= 3; i++) {
|
183
|
+
attempts++;
|
184
|
+
try {
|
185
|
+
console.log(`Validating the code in ${basePath} attempt #${attempts}`);
|
186
|
+
const result = await codeGenerator.validateForTarget(basePath);
|
187
|
+
if (result && result.valid) {
|
188
|
+
validCode = true;
|
189
|
+
break;
|
190
|
+
}
|
191
|
+
if (result && !result.valid) {
|
192
|
+
console.debug('Validation error:', result);
|
193
|
+
const errorStream = await stormClient_1.stormClient.createErrorClassification(result.error, []);
|
194
|
+
const fixes = new Map();
|
195
|
+
errorStream.on('data', (evt) => {
|
196
|
+
if (evt.type === 'ERROR_CLASSIFIER') {
|
197
|
+
// find the file that caused the error
|
198
|
+
// strip base path from event file name, if it exists sometimes the AI sends the full path
|
199
|
+
const eventFileName = this.removePrefix(basePath + '/', evt.payload.filename);
|
200
|
+
const file = filesToBeFixed.find((f) => f.filename === eventFileName);
|
201
|
+
if (!file) {
|
202
|
+
console.log(`Could not find the file ${eventFileName} in the list of files to be fixed, Henrik might wanna create a new file for this fix`);
|
203
|
+
}
|
204
|
+
// read the content of the file
|
205
|
+
const content = (0, fs_1.readFileSync)((0, path_1.join)(basePath, eventFileName), 'utf8');
|
206
|
+
const fix = `${evt.payload.potentialFix}\n---\n${knownFiles
|
207
|
+
.map((e) => e.filename)
|
208
|
+
.join('\n')}\n---\n${content}`;
|
209
|
+
console.log(`trying to fix the code in ${eventFileName}`);
|
210
|
+
console.debug(`with the fix:\n${fix}`);
|
211
|
+
const code = this.codeFix(fix);
|
212
|
+
fixes.set((0, path_1.join)(basePath, eventFileName), code);
|
213
|
+
}
|
214
|
+
});
|
215
|
+
await errorStream.waitForDone();
|
216
|
+
for (const [filename, codePromise] of fixes) {
|
217
|
+
const code = await codePromise;
|
218
|
+
(0, fs_1.writeFileSync)(filename, code);
|
219
|
+
}
|
220
|
+
}
|
221
|
+
}
|
222
|
+
catch (e) {
|
223
|
+
console.error('Error:', e);
|
224
|
+
}
|
225
|
+
}
|
226
|
+
if (validCode) {
|
227
|
+
console.log(`Validation successful after ${attempts} attempts`);
|
228
|
+
}
|
229
|
+
else {
|
230
|
+
console.error(`Validation failed for ${basePath} after ${attempts} attempts`);
|
231
|
+
}
|
232
|
+
}
|
233
|
+
removePrefix(prefix, str) {
|
234
|
+
if (str.startsWith(prefix)) {
|
235
|
+
return str.slice(prefix.length);
|
236
|
+
}
|
237
|
+
return str;
|
238
|
+
}
|
239
|
+
async writeToFile(fileName, code) {
|
240
|
+
console.log(`writing the fixed code to ${fileName}`);
|
241
|
+
const resolvedCode = await code;
|
242
|
+
(0, fs_1.writeFileSync)(fileName, resolvedCode);
|
243
|
+
}
|
244
|
+
/**
|
245
|
+
* Sends the code to the AI for a fix
|
246
|
+
*/
|
247
|
+
async codeFix(fix) {
|
248
|
+
return new Promise(async (resolve, reject) => {
|
249
|
+
const fixStream = await stormClient_1.stormClient.createCodeFix(fix, []);
|
250
|
+
fixStream.on('data', (evt) => {
|
251
|
+
if (evt.type === 'CODE_FIX') {
|
252
|
+
resolve(evt.payload.content);
|
253
|
+
}
|
254
|
+
});
|
255
|
+
fixStream.on('error', (err) => {
|
256
|
+
reject(err);
|
257
|
+
});
|
258
|
+
await fixStream.waitForDone();
|
259
|
+
});
|
157
260
|
}
|
158
261
|
/**
|
159
262
|
* Emits the text-based files to the stream
|
@@ -6,7 +6,7 @@ import { StormEvent } from './events';
|
|
6
6
|
import { BlockDefinition, Plan } from '@kapeta/schemas';
|
7
7
|
import { KapetaURI } from '@kapeta/nodejs-utils';
|
8
8
|
export interface BlockDefinitionInfo {
|
9
|
-
uri:
|
9
|
+
uri: string;
|
10
10
|
content: BlockDefinition;
|
11
11
|
aiName: string;
|
12
12
|
}
|
@@ -315,6 +315,8 @@ class StormEventParser {
|
|
315
315
|
name: planRef.fullName,
|
316
316
|
title: this.planName,
|
317
317
|
description: this.planDescription,
|
318
|
+
structure: 'mono',
|
319
|
+
visibility: 'private',
|
318
320
|
},
|
319
321
|
spec: {
|
320
322
|
blocks,
|
@@ -331,7 +333,7 @@ class StormEventParser {
|
|
331
333
|
Object.entries(this.blocks).forEach(([, blockInfo]) => {
|
332
334
|
const blockRef = StormEventParser.toRef(handle, blockInfo.name);
|
333
335
|
const blockDefinitionInfo = {
|
334
|
-
uri: blockRef,
|
336
|
+
uri: blockRef.toNormalizedString(),
|
335
337
|
aiName: blockInfo.name,
|
336
338
|
content: {
|
337
339
|
kind: this.toBlockKind(blockInfo.type),
|
@@ -100,6 +100,26 @@ export interface StormEventError {
|
|
100
100
|
error: string;
|
101
101
|
};
|
102
102
|
}
|
103
|
+
export interface StormEventErrorClassifier {
|
104
|
+
type: 'ERROR_CLASSIFIER';
|
105
|
+
reason: string;
|
106
|
+
created: number;
|
107
|
+
payload: StormEventErrorClassifierInfo;
|
108
|
+
}
|
109
|
+
export interface StormEventCodeFix {
|
110
|
+
type: 'CODE_FIX';
|
111
|
+
reason: string;
|
112
|
+
created: number;
|
113
|
+
payload: {
|
114
|
+
filename: string;
|
115
|
+
content: string;
|
116
|
+
};
|
117
|
+
}
|
118
|
+
export interface StormEventErrorClassifierInfo {
|
119
|
+
error: string;
|
120
|
+
filename: string;
|
121
|
+
potentialFix: string;
|
122
|
+
}
|
103
123
|
export interface ScreenTemplate {
|
104
124
|
name: string;
|
105
125
|
template: string;
|
@@ -143,6 +163,17 @@ export interface StormEventFile {
|
|
143
163
|
instanceId: string;
|
144
164
|
};
|
145
165
|
}
|
166
|
+
export interface StormEventBlockReady {
|
167
|
+
type: 'BLOCK_READY';
|
168
|
+
reason: string;
|
169
|
+
created: number;
|
170
|
+
payload: {
|
171
|
+
path: string;
|
172
|
+
blockName: string;
|
173
|
+
blockRef: string;
|
174
|
+
instanceId: string;
|
175
|
+
};
|
176
|
+
}
|
146
177
|
export interface StormEventDone {
|
147
178
|
type: 'DONE';
|
148
179
|
created: number;
|
@@ -153,4 +184,4 @@ export interface StormEventDefinitionChange {
|
|
153
184
|
created: number;
|
154
185
|
payload: StormDefinitions;
|
155
186
|
}
|
156
|
-
export type StormEvent = StormEventCreateBlock | StormEventCreateConnection | StormEventCreatePlanProperties | StormEventInvalidResponse | StormEventPlanRetry | StormEventCreateDSL | StormEventCreateDSLResource | StormEventError | StormEventScreen | StormEventScreenCandidate | StormEventFile | StormEventDone | StormEventDefinitionChange;
|
187
|
+
export type StormEvent = StormEventCreateBlock | StormEventCreateConnection | StormEventCreatePlanProperties | StormEventInvalidResponse | StormEventPlanRetry | StormEventCreateDSL | StormEventCreateDSLResource | StormEventError | StormEventScreen | StormEventScreenCandidate | StormEventFile | StormEventDone | StormEventDefinitionChange | StormEventErrorClassifier | StormEventCodeFix | StormEventBlockReady;
|
@@ -8,11 +8,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
8
|
};
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
10
10
|
const express_promise_router_1 = __importDefault(require("express-promise-router"));
|
11
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
11
12
|
const cors_1 = require("../middleware/cors");
|
12
13
|
const stringBody_1 = require("../middleware/stringBody");
|
13
14
|
const stormClient_1 = require("./stormClient");
|
14
15
|
const event_parser_1 = require("./event-parser");
|
15
16
|
const codegen_1 = require("./codegen");
|
17
|
+
const assetManager_1 = require("../assetManager");
|
18
|
+
const path_1 = __importDefault(require("path"));
|
16
19
|
const router = (0, express_promise_router_1.default)();
|
17
20
|
router.use('/', cors_1.corsHandler);
|
18
21
|
router.use('/', stringBody_1.stringBody);
|
@@ -47,7 +50,7 @@ router.post('/:handle/all', async (req, res) => {
|
|
47
50
|
const result = eventParser.toResult(handle);
|
48
51
|
sendDefinitions(res, result);
|
49
52
|
if (!req.query.skipCodegen) {
|
50
|
-
const stormCodegen = new codegen_1.StormCodegen(aiRequest.prompt, result.blocks, eventParser.getEvents());
|
53
|
+
const stormCodegen = new codegen_1.StormCodegen(metaStream.getConversationId(), aiRequest.prompt, result.blocks, eventParser.getEvents());
|
51
54
|
const codegenPromise = streamStormPartialResponse(stormCodegen.getStream(), res);
|
52
55
|
await stormCodegen.process();
|
53
56
|
await codegenPromise;
|
@@ -59,6 +62,28 @@ router.post('/:handle/all', async (req, res) => {
|
|
59
62
|
res.end();
|
60
63
|
}
|
61
64
|
});
|
65
|
+
router.post('/block/create', async (req, res) => {
|
66
|
+
const createRequest = JSON.parse(req.stringBody ?? '{}');
|
67
|
+
try {
|
68
|
+
const ymlPath = path_1.default.join(createRequest.newPath, 'kapeta.yml');
|
69
|
+
console.log('Creating block at', ymlPath);
|
70
|
+
const [asset] = await assetManager_1.assetManager.createAsset(ymlPath, createRequest.definition);
|
71
|
+
if (await fs_extra_1.default.pathExists(createRequest.tmpPath)) {
|
72
|
+
console.log('Moving block from', createRequest.tmpPath, 'to', createRequest.newPath);
|
73
|
+
await fs_extra_1.default.move(createRequest.tmpPath, createRequest.newPath, {
|
74
|
+
overwrite: true,
|
75
|
+
});
|
76
|
+
console.log('Updating asset', asset.ref);
|
77
|
+
res.send(await assetManager_1.assetManager.updateAsset(asset.ref, createRequest.definition));
|
78
|
+
}
|
79
|
+
else {
|
80
|
+
res.send(asset);
|
81
|
+
}
|
82
|
+
}
|
83
|
+
catch (err) {
|
84
|
+
res.status(500).send({ error: err.message });
|
85
|
+
}
|
86
|
+
});
|
62
87
|
function sendDefinitions(res, result) {
|
63
88
|
sendEvent(res, {
|
64
89
|
type: 'DEFINITION_CHANGE',
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { StormFileImplementationPrompt, StormStream, StormUIImplementationPrompt } from './stream';
|
1
|
+
import { ConversationItem, StormFileImplementationPrompt, StormStream, StormUIImplementationPrompt } from './stream';
|
2
2
|
export declare const STORM_ID = "storm";
|
3
3
|
export declare const ConversationIdHeader = "Conversation-Id";
|
4
4
|
declare class StormClient {
|
@@ -9,6 +9,8 @@ declare class StormClient {
|
|
9
9
|
createMetadata(prompt: string, conversationId?: string): Promise<StormStream>;
|
10
10
|
createUIImplementation(prompt: StormUIImplementationPrompt, conversationId?: string): Promise<StormStream>;
|
11
11
|
createServiceImplementation(prompt: StormFileImplementationPrompt, conversationId?: string): Promise<StormStream>;
|
12
|
+
createErrorClassification(prompt: string, history?: ConversationItem[], conversationId?: string): Promise<StormStream>;
|
13
|
+
createCodeFix(prompt: string, history?: ConversationItem[], conversationId?: string): Promise<StormStream>;
|
12
14
|
}
|
13
15
|
export declare const stormClient: StormClient;
|
14
16
|
export {};
|
@@ -83,5 +83,17 @@ class StormClient {
|
|
83
83
|
conversationId,
|
84
84
|
});
|
85
85
|
}
|
86
|
+
createErrorClassification(prompt, history, conversationId) {
|
87
|
+
return this.send('/v2/code/errorclassifier', {
|
88
|
+
conversationId: conversationId,
|
89
|
+
prompt,
|
90
|
+
});
|
91
|
+
}
|
92
|
+
createCodeFix(prompt, history, conversationId) {
|
93
|
+
return this.send('/v2/code/fix', {
|
94
|
+
conversationId: conversationId,
|
95
|
+
prompt,
|
96
|
+
});
|
97
|
+
}
|
86
98
|
}
|
87
99
|
exports.stormClient = new StormClient();
|
@@ -6,6 +6,7 @@
|
|
6
6
|
import { EventEmitter } from 'node:events';
|
7
7
|
import { StormEvent } from './events';
|
8
8
|
import { AIFileTypes, GeneratedFile } from '@kapeta/codegen';
|
9
|
+
import { BlockDefinition } from '@kapeta/schemas';
|
9
10
|
export declare class StormStream extends EventEmitter {
|
10
11
|
private conversationId;
|
11
12
|
private lines;
|
@@ -29,6 +30,11 @@ export interface StormContextRequest<T = string> {
|
|
29
30
|
conversationId?: string;
|
30
31
|
prompt: T;
|
31
32
|
}
|
33
|
+
export interface StormCreateBlockRequest {
|
34
|
+
definition: BlockDefinition;
|
35
|
+
tmpPath: string;
|
36
|
+
newPath: string;
|
37
|
+
}
|
32
38
|
export interface StormFileInfo extends GeneratedFile {
|
33
39
|
type: AIFileTypes;
|
34
40
|
}
|
@@ -28,7 +28,7 @@ declare class AssetManager {
|
|
28
28
|
getPlan(ref: string, noCache?: boolean): Promise<Plan>;
|
29
29
|
getBlockInstance(systemId: string, instanceId: string): Promise<BlockInstance>;
|
30
30
|
getAsset(ref: string, noCache?: boolean, autoFetch?: boolean): Promise<EnrichedAsset | undefined>;
|
31
|
-
createAsset(path: string, yaml: BlockDefinition, sourceOfChange?: SourceOfChange): Promise<EnrichedAsset[]>;
|
31
|
+
createAsset(path: string, yaml: BlockDefinition, sourceOfChange?: SourceOfChange, codegen?: boolean): Promise<EnrichedAsset[]>;
|
32
32
|
updateAsset(ref: string, yaml: Definition, sourceOfChange?: SourceOfChange): Promise<void>;
|
33
33
|
importFile(filePath: string): Promise<EnrichedAsset[]>;
|
34
34
|
unregisterAsset(ref: string): Promise<void>;
|
@@ -142,7 +142,7 @@ class AssetManager {
|
|
142
142
|
}
|
143
143
|
return undefined;
|
144
144
|
}
|
145
|
-
async createAsset(path, yaml, sourceOfChange = 'filesystem') {
|
145
|
+
async createAsset(path, yaml, sourceOfChange = 'filesystem', codegen = true) {
|
146
146
|
if (await fs_extra_1.default.pathExists(path)) {
|
147
147
|
throw new Error('File already exists: ' + path);
|
148
148
|
}
|
@@ -159,9 +159,11 @@ class AssetManager {
|
|
159
159
|
cacheManager_1.cacheManager.set(key, a, CACHE_TTL);
|
160
160
|
});
|
161
161
|
definitionsManager_1.definitionsManager.clearCache();
|
162
|
-
console.log(`Created asset at: ${path}`);
|
163
162
|
const ref = `kapeta://${yaml.metadata.name}:local`;
|
164
|
-
|
163
|
+
console.log(`Created asset ${ref} at: ${path}`);
|
164
|
+
if (codegen) {
|
165
|
+
await this.maybeGenerateCode(ref, path, yaml);
|
166
|
+
}
|
165
167
|
return asset;
|
166
168
|
}
|
167
169
|
async updateAsset(ref, yaml, sourceOfChange = 'filesystem') {
|
@@ -11,6 +11,7 @@ const express_promise_router_1 = __importDefault(require("express-promise-router
|
|
11
11
|
const stringBody_1 = require("../middleware/stringBody");
|
12
12
|
const filesystemManager_1 = require("../filesystemManager");
|
13
13
|
const cors_1 = require("../middleware/cors");
|
14
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
14
15
|
let router = (0, express_promise_router_1.default)();
|
15
16
|
router.use('/', cors_1.corsHandler);
|
16
17
|
router.get('/root', (req, res) => {
|
@@ -81,6 +82,15 @@ router.get('/readfile', async (req, res) => {
|
|
81
82
|
res.status(400).send({ error: '' + err });
|
82
83
|
}
|
83
84
|
});
|
85
|
+
router.get('/exists', async (req, res) => {
|
86
|
+
let pathArg = req.query.path;
|
87
|
+
try {
|
88
|
+
res.send(await fs_extra_1.default.pathExists(pathArg));
|
89
|
+
}
|
90
|
+
catch (err) {
|
91
|
+
res.status(400).send({ error: '' + err });
|
92
|
+
}
|
93
|
+
});
|
84
94
|
router.put('/mkdir', async (req, res) => {
|
85
95
|
let pathArg = req.query.path;
|
86
96
|
try {
|
@@ -11,7 +11,8 @@ export declare class StormCodegen {
|
|
11
11
|
private readonly out;
|
12
12
|
private readonly events;
|
13
13
|
private readonly tmpDir;
|
14
|
-
|
14
|
+
private readonly conversationId;
|
15
|
+
constructor(conversationId: string, userPrompt: string, blocks: BlockDefinitionInfo[], events: StormEvent[]);
|
15
16
|
process(): Promise<void>;
|
16
17
|
getStream(): StormStream;
|
17
18
|
private handleTemplateFileOutput;
|
@@ -22,6 +23,13 @@ export declare class StormCodegen {
|
|
22
23
|
* Generates the code for a block and sends it to the AI
|
23
24
|
*/
|
24
25
|
private processBlockCode;
|
26
|
+
private verifyAndFixCode;
|
27
|
+
removePrefix(prefix: string, str: string): string;
|
28
|
+
writeToFile(fileName: string, code: Promise<string>): Promise<void>;
|
29
|
+
/**
|
30
|
+
* Sends the code to the AI for a fix
|
31
|
+
*/
|
32
|
+
private codeFix;
|
25
33
|
/**
|
26
34
|
* Emits the text-based files to the stream
|
27
35
|
*/
|