@kapeta/local-cluster-service 0.54.2 → 0.54.4
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 +16 -0
- package/dist/cjs/src/storm/codegen.js +38 -19
- package/dist/esm/src/storm/codegen.js +38 -19
- package/package.json +1 -1
- package/src/storm/codegen.ts +42 -24
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
## [0.54.4](https://github.com/kapetacom/local-cluster-service/compare/v0.54.3...v0.54.4) (2024-06-20)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* merge error ([4c29cab](https://github.com/kapetacom/local-cluster-service/commit/4c29cab33c311a3ad21913b974d92207968a15db))
|
7
|
+
* minors ([c36e825](https://github.com/kapetacom/local-cluster-service/commit/c36e8258cf0d7a3fb0915080f64d524ffd925929))
|
8
|
+
* processTemplate will possibly return a list of files for each file when enhancing services ([3f186a3](https://github.com/kapetacom/local-cluster-service/commit/3f186a3afbb6b527c933ff051d2659e07ab39bbd))
|
9
|
+
|
10
|
+
## [0.54.3](https://github.com/kapetacom/local-cluster-service/compare/v0.54.2...v0.54.3) (2024-06-19)
|
11
|
+
|
12
|
+
|
13
|
+
### Bug Fixes
|
14
|
+
|
15
|
+
* emit file done events from codefixer ([#182](https://github.com/kapetacom/local-cluster-service/issues/182)) ([77649dc](https://github.com/kapetacom/local-cluster-service/commit/77649dc1d53a5053f1ff7ed54b8106a3bd410434))
|
16
|
+
|
1
17
|
## [0.54.2](https://github.com/kapetacom/local-cluster-service/compare/v0.54.1...v0.54.2) (2024-06-19)
|
2
18
|
|
3
19
|
|
@@ -117,16 +117,13 @@ class StormCodegen {
|
|
117
117
|
getStream() {
|
118
118
|
return this.out;
|
119
119
|
}
|
120
|
-
handleTemplateFileOutput(blockUri, aiName,
|
120
|
+
handleTemplateFileOutput(blockUri, aiName, data) {
|
121
121
|
if (this.handleFileEvents(blockUri, aiName, data)) {
|
122
122
|
return;
|
123
123
|
}
|
124
124
|
switch (data.type) {
|
125
125
|
case 'FILE_DONE':
|
126
|
-
|
127
|
-
template.content = data.payload.content;
|
128
|
-
this.handleFileDoneOutput(blockUri, aiName, data);
|
129
|
-
break;
|
126
|
+
return this.handleFileDoneOutput(blockUri, aiName, data);
|
130
127
|
}
|
131
128
|
}
|
132
129
|
handleUiOutput(blockUri, blockName, data) {
|
@@ -229,8 +226,10 @@ class StormCodegen {
|
|
229
226
|
if (this.isAborted()) {
|
230
227
|
return;
|
231
228
|
}
|
229
|
+
const kapetaYaml = yaml_1.default.stringify(block.content);
|
230
|
+
const blockDefinition = block.content;
|
232
231
|
// Generate the code for the block using the standard codegen templates
|
233
|
-
const generatedResult = await this.generateBlock(
|
232
|
+
const generatedResult = await this.generateBlock(blockDefinition);
|
234
233
|
if (!generatedResult) {
|
235
234
|
return;
|
236
235
|
}
|
@@ -257,8 +256,8 @@ class StormCodegen {
|
|
257
256
|
if (failedEvents.length > 0) {
|
258
257
|
console.warn('Codegen encountered failed plan events. Plan might be invalid', failedEvents);
|
259
258
|
}
|
259
|
+
// TODO: remove this when we have a better way to handle failed events
|
260
260
|
// Skip failed events - they are not relevant to the materializer
|
261
|
-
// TODO: should the materializer be able to handle failed events?
|
262
261
|
filteredEvents = filteredEvents.filter((event) => !event.type.endsWith('_FAILED') && !event.type.endsWith('ERROR_INTERNAL'));
|
263
262
|
const screenEvents = [];
|
264
263
|
const getScreenEventsFile = () => ({
|
@@ -321,7 +320,7 @@ class StormCodegen {
|
|
321
320
|
if (this.isAborted()) {
|
322
321
|
return;
|
323
322
|
}
|
324
|
-
const basePath = this.getBasePath(
|
323
|
+
const basePath = this.getBasePath(blockDefinition.metadata.name);
|
325
324
|
const screenFilesConverted = screenFiles.map((screenFile) => {
|
326
325
|
return {
|
327
326
|
filename: screenFile.payload.filename,
|
@@ -342,7 +341,7 @@ class StormCodegen {
|
|
342
341
|
};
|
343
342
|
const stream = await stormClient_1.stormClient.generateCode(payload);
|
344
343
|
stream.on('data', (evt) => {
|
345
|
-
this.handleTemplateFileOutput(blockUri, block.aiName,
|
344
|
+
this.handleTemplateFileOutput(blockUri, block.aiName, evt);
|
346
345
|
});
|
347
346
|
this.out.on('aborted', () => {
|
348
347
|
stream.abort();
|
@@ -352,36 +351,37 @@ class StormCodegen {
|
|
352
351
|
// Gather the context files for implementation. These will be all be passed to the AI
|
353
352
|
const contextFiles = relevantFiles.filter((file) => ![codegen_1.AIFileTypes.SERVICE, codegen_1.AIFileTypes.WEB_SCREEN, codegen_1.AIFileTypes.WEB_ROUTER].includes(file.type));
|
354
353
|
// Send the service and UI templates to the AI. These will be sent one-by-one in addition to the context files
|
355
|
-
|
354
|
+
let serviceFiles = allFiles.filter((file) => file.type === codegen_1.AIFileTypes.SERVICE);
|
356
355
|
if (serviceFiles.length > 0) {
|
357
|
-
await this.processTemplates(blockUri, block.aiName, stormClient_1.stormClient.createServiceImplementation.bind(stormClient_1.stormClient), serviceFiles, contextFiles);
|
356
|
+
serviceFiles = await this.processTemplates(blockUri, block.aiName, stormClient_1.stormClient.createServiceImplementation.bind(stormClient_1.stormClient), serviceFiles, contextFiles);
|
358
357
|
}
|
359
358
|
if (this.isAborted()) {
|
360
359
|
return;
|
361
360
|
}
|
361
|
+
for (const contextFile of contextFiles) {
|
362
|
+
const filePath = (0, path_2.join)(basePath, contextFile.filename);
|
363
|
+
await (0, promises_1.writeFile)(filePath, contextFile.content);
|
364
|
+
}
|
362
365
|
for (const screenFile of screenFilesConverted) {
|
363
366
|
const filePath = (0, path_2.join)(basePath, screenFile.filename);
|
364
367
|
await (0, promises_1.writeFile)(filePath, screenFile.content);
|
365
368
|
}
|
369
|
+
// this might decide to overwrite a file that is also a context file
|
366
370
|
for (const serviceFile of serviceFiles) {
|
367
371
|
const filePath = (0, path_2.join)(basePath, serviceFile.filename);
|
368
372
|
await (0, promises_1.writeFile)(filePath, serviceFile.content);
|
369
373
|
}
|
370
|
-
for (const serviceFile of contextFiles) {
|
371
|
-
const filePath = (0, path_2.join)(basePath, serviceFile.filename);
|
372
|
-
await (0, promises_1.writeFile)(filePath, serviceFile.content);
|
373
|
-
}
|
374
374
|
// Write again after modifications
|
375
375
|
for (const webRouterFile of webRouters) {
|
376
376
|
const filePath = (0, path_2.join)(basePath, webRouterFile.filename);
|
377
377
|
await (0, promises_1.writeFile)(filePath, webRouterFile.content);
|
378
378
|
}
|
379
379
|
const kapetaYmlPath = (0, path_2.join)(basePath, 'kapeta.yml');
|
380
|
-
await (0, promises_1.writeFile)(kapetaYmlPath,
|
380
|
+
await (0, promises_1.writeFile)(kapetaYmlPath, kapetaYaml);
|
381
381
|
const blockRef = block.uri;
|
382
382
|
this.emitBlockStatus(blockUri, block.aiName, events_1.StormEventBlockStatusType.QA);
|
383
383
|
const filesToBeFixed = serviceFiles.concat(contextFiles).concat(screenFilesConverted);
|
384
|
-
const codeGenerator = new codegen_1.BlockCodeGenerator(
|
384
|
+
const codeGenerator = new codegen_1.BlockCodeGenerator(blockDefinition);
|
385
385
|
this.emitBlockStatus(blockUri, block.aiName, events_1.StormEventBlockStatusType.BUILDING);
|
386
386
|
await this.verifyAndFixCode(blockUri, block.aiName, codeGenerator, basePath, filesToBeFixed, allFiles);
|
387
387
|
this.out.emit('data', {
|
@@ -587,6 +587,7 @@ class StormCodegen {
|
|
587
587
|
if (this.handleFileEvents(blockUri, blockName, evt)) {
|
588
588
|
return;
|
589
589
|
}
|
590
|
+
this.handleFileDoneOutput(blockUri, blockName, evt);
|
590
591
|
if (evt.type === 'CODE_FIX') {
|
591
592
|
resolved = true;
|
592
593
|
resolve(evt.payload);
|
@@ -675,6 +676,7 @@ class StormCodegen {
|
|
675
676
|
*/
|
676
677
|
async processTemplates(blockUri, aiName, generator, templates, contextFiles) {
|
677
678
|
const promises = templates.map(async (templateFile) => {
|
679
|
+
let changedFiles = [];
|
678
680
|
const stream = await generator({
|
679
681
|
context: contextFiles,
|
680
682
|
template: templateFile,
|
@@ -684,11 +686,28 @@ class StormCodegen {
|
|
684
686
|
stream.abort();
|
685
687
|
});
|
686
688
|
stream.on('data', (evt) => {
|
687
|
-
this.handleTemplateFileOutput(blockUri, aiName,
|
689
|
+
let changedFile = this.handleTemplateFileOutput(blockUri, aiName, evt);
|
690
|
+
if (changedFile) {
|
691
|
+
changedFiles.push(...changedFiles, changedFile);
|
692
|
+
}
|
688
693
|
});
|
689
694
|
await stream.waitForDone();
|
695
|
+
return changedFiles;
|
690
696
|
});
|
691
|
-
await Promise.all(promises);
|
697
|
+
const changedFiles = await Promise.all(promises);
|
698
|
+
let allFiles = templates.concat(contextFiles);
|
699
|
+
let result = [];
|
700
|
+
for (const changedFile of changedFiles.flat()) {
|
701
|
+
const find = allFiles.find((file) => file.filename === changedFile.payload.filename);
|
702
|
+
if (find) {
|
703
|
+
result.push({ type: find.type, filename: find.filename, mode: find.mode, permissions: find.permissions,
|
704
|
+
content: changedFile.payload.content });
|
705
|
+
}
|
706
|
+
else {
|
707
|
+
console.warn("processTemplates: AI changed a file that wasn't in the input [" + changedFile.payload.filename + "]");
|
708
|
+
}
|
709
|
+
}
|
710
|
+
return result;
|
692
711
|
}
|
693
712
|
/**
|
694
713
|
* Converts the generated files to a format that can be sent to the AI
|
@@ -117,16 +117,13 @@ class StormCodegen {
|
|
117
117
|
getStream() {
|
118
118
|
return this.out;
|
119
119
|
}
|
120
|
-
handleTemplateFileOutput(blockUri, aiName,
|
120
|
+
handleTemplateFileOutput(blockUri, aiName, data) {
|
121
121
|
if (this.handleFileEvents(blockUri, aiName, data)) {
|
122
122
|
return;
|
123
123
|
}
|
124
124
|
switch (data.type) {
|
125
125
|
case 'FILE_DONE':
|
126
|
-
|
127
|
-
template.content = data.payload.content;
|
128
|
-
this.handleFileDoneOutput(blockUri, aiName, data);
|
129
|
-
break;
|
126
|
+
return this.handleFileDoneOutput(blockUri, aiName, data);
|
130
127
|
}
|
131
128
|
}
|
132
129
|
handleUiOutput(blockUri, blockName, data) {
|
@@ -229,8 +226,10 @@ class StormCodegen {
|
|
229
226
|
if (this.isAborted()) {
|
230
227
|
return;
|
231
228
|
}
|
229
|
+
const kapetaYaml = yaml_1.default.stringify(block.content);
|
230
|
+
const blockDefinition = block.content;
|
232
231
|
// Generate the code for the block using the standard codegen templates
|
233
|
-
const generatedResult = await this.generateBlock(
|
232
|
+
const generatedResult = await this.generateBlock(blockDefinition);
|
234
233
|
if (!generatedResult) {
|
235
234
|
return;
|
236
235
|
}
|
@@ -257,8 +256,8 @@ class StormCodegen {
|
|
257
256
|
if (failedEvents.length > 0) {
|
258
257
|
console.warn('Codegen encountered failed plan events. Plan might be invalid', failedEvents);
|
259
258
|
}
|
259
|
+
// TODO: remove this when we have a better way to handle failed events
|
260
260
|
// Skip failed events - they are not relevant to the materializer
|
261
|
-
// TODO: should the materializer be able to handle failed events?
|
262
261
|
filteredEvents = filteredEvents.filter((event) => !event.type.endsWith('_FAILED') && !event.type.endsWith('ERROR_INTERNAL'));
|
263
262
|
const screenEvents = [];
|
264
263
|
const getScreenEventsFile = () => ({
|
@@ -321,7 +320,7 @@ class StormCodegen {
|
|
321
320
|
if (this.isAborted()) {
|
322
321
|
return;
|
323
322
|
}
|
324
|
-
const basePath = this.getBasePath(
|
323
|
+
const basePath = this.getBasePath(blockDefinition.metadata.name);
|
325
324
|
const screenFilesConverted = screenFiles.map((screenFile) => {
|
326
325
|
return {
|
327
326
|
filename: screenFile.payload.filename,
|
@@ -342,7 +341,7 @@ class StormCodegen {
|
|
342
341
|
};
|
343
342
|
const stream = await stormClient_1.stormClient.generateCode(payload);
|
344
343
|
stream.on('data', (evt) => {
|
345
|
-
this.handleTemplateFileOutput(blockUri, block.aiName,
|
344
|
+
this.handleTemplateFileOutput(blockUri, block.aiName, evt);
|
346
345
|
});
|
347
346
|
this.out.on('aborted', () => {
|
348
347
|
stream.abort();
|
@@ -352,36 +351,37 @@ class StormCodegen {
|
|
352
351
|
// Gather the context files for implementation. These will be all be passed to the AI
|
353
352
|
const contextFiles = relevantFiles.filter((file) => ![codegen_1.AIFileTypes.SERVICE, codegen_1.AIFileTypes.WEB_SCREEN, codegen_1.AIFileTypes.WEB_ROUTER].includes(file.type));
|
354
353
|
// Send the service and UI templates to the AI. These will be sent one-by-one in addition to the context files
|
355
|
-
|
354
|
+
let serviceFiles = allFiles.filter((file) => file.type === codegen_1.AIFileTypes.SERVICE);
|
356
355
|
if (serviceFiles.length > 0) {
|
357
|
-
await this.processTemplates(blockUri, block.aiName, stormClient_1.stormClient.createServiceImplementation.bind(stormClient_1.stormClient), serviceFiles, contextFiles);
|
356
|
+
serviceFiles = await this.processTemplates(blockUri, block.aiName, stormClient_1.stormClient.createServiceImplementation.bind(stormClient_1.stormClient), serviceFiles, contextFiles);
|
358
357
|
}
|
359
358
|
if (this.isAborted()) {
|
360
359
|
return;
|
361
360
|
}
|
361
|
+
for (const contextFile of contextFiles) {
|
362
|
+
const filePath = (0, path_2.join)(basePath, contextFile.filename);
|
363
|
+
await (0, promises_1.writeFile)(filePath, contextFile.content);
|
364
|
+
}
|
362
365
|
for (const screenFile of screenFilesConverted) {
|
363
366
|
const filePath = (0, path_2.join)(basePath, screenFile.filename);
|
364
367
|
await (0, promises_1.writeFile)(filePath, screenFile.content);
|
365
368
|
}
|
369
|
+
// this might decide to overwrite a file that is also a context file
|
366
370
|
for (const serviceFile of serviceFiles) {
|
367
371
|
const filePath = (0, path_2.join)(basePath, serviceFile.filename);
|
368
372
|
await (0, promises_1.writeFile)(filePath, serviceFile.content);
|
369
373
|
}
|
370
|
-
for (const serviceFile of contextFiles) {
|
371
|
-
const filePath = (0, path_2.join)(basePath, serviceFile.filename);
|
372
|
-
await (0, promises_1.writeFile)(filePath, serviceFile.content);
|
373
|
-
}
|
374
374
|
// Write again after modifications
|
375
375
|
for (const webRouterFile of webRouters) {
|
376
376
|
const filePath = (0, path_2.join)(basePath, webRouterFile.filename);
|
377
377
|
await (0, promises_1.writeFile)(filePath, webRouterFile.content);
|
378
378
|
}
|
379
379
|
const kapetaYmlPath = (0, path_2.join)(basePath, 'kapeta.yml');
|
380
|
-
await (0, promises_1.writeFile)(kapetaYmlPath,
|
380
|
+
await (0, promises_1.writeFile)(kapetaYmlPath, kapetaYaml);
|
381
381
|
const blockRef = block.uri;
|
382
382
|
this.emitBlockStatus(blockUri, block.aiName, events_1.StormEventBlockStatusType.QA);
|
383
383
|
const filesToBeFixed = serviceFiles.concat(contextFiles).concat(screenFilesConverted);
|
384
|
-
const codeGenerator = new codegen_1.BlockCodeGenerator(
|
384
|
+
const codeGenerator = new codegen_1.BlockCodeGenerator(blockDefinition);
|
385
385
|
this.emitBlockStatus(blockUri, block.aiName, events_1.StormEventBlockStatusType.BUILDING);
|
386
386
|
await this.verifyAndFixCode(blockUri, block.aiName, codeGenerator, basePath, filesToBeFixed, allFiles);
|
387
387
|
this.out.emit('data', {
|
@@ -587,6 +587,7 @@ class StormCodegen {
|
|
587
587
|
if (this.handleFileEvents(blockUri, blockName, evt)) {
|
588
588
|
return;
|
589
589
|
}
|
590
|
+
this.handleFileDoneOutput(blockUri, blockName, evt);
|
590
591
|
if (evt.type === 'CODE_FIX') {
|
591
592
|
resolved = true;
|
592
593
|
resolve(evt.payload);
|
@@ -675,6 +676,7 @@ class StormCodegen {
|
|
675
676
|
*/
|
676
677
|
async processTemplates(blockUri, aiName, generator, templates, contextFiles) {
|
677
678
|
const promises = templates.map(async (templateFile) => {
|
679
|
+
let changedFiles = [];
|
678
680
|
const stream = await generator({
|
679
681
|
context: contextFiles,
|
680
682
|
template: templateFile,
|
@@ -684,11 +686,28 @@ class StormCodegen {
|
|
684
686
|
stream.abort();
|
685
687
|
});
|
686
688
|
stream.on('data', (evt) => {
|
687
|
-
this.handleTemplateFileOutput(blockUri, aiName,
|
689
|
+
let changedFile = this.handleTemplateFileOutput(blockUri, aiName, evt);
|
690
|
+
if (changedFile) {
|
691
|
+
changedFiles.push(...changedFiles, changedFile);
|
692
|
+
}
|
688
693
|
});
|
689
694
|
await stream.waitForDone();
|
695
|
+
return changedFiles;
|
690
696
|
});
|
691
|
-
await Promise.all(promises);
|
697
|
+
const changedFiles = await Promise.all(promises);
|
698
|
+
let allFiles = templates.concat(contextFiles);
|
699
|
+
let result = [];
|
700
|
+
for (const changedFile of changedFiles.flat()) {
|
701
|
+
const find = allFiles.find((file) => file.filename === changedFile.payload.filename);
|
702
|
+
if (find) {
|
703
|
+
result.push({ type: find.type, filename: find.filename, mode: find.mode, permissions: find.permissions,
|
704
|
+
content: changedFile.payload.content });
|
705
|
+
}
|
706
|
+
else {
|
707
|
+
console.warn("processTemplates: AI changed a file that wasn't in the input [" + changedFile.payload.filename + "]");
|
708
|
+
}
|
709
|
+
}
|
710
|
+
return result;
|
692
711
|
}
|
693
712
|
/**
|
694
713
|
* Converts the generated files to a format that can be sent to the AI
|
package/package.json
CHANGED
package/src/storm/codegen.ts
CHANGED
@@ -36,7 +36,6 @@ import Path, { join } from 'path';
|
|
36
36
|
import os from 'node:os';
|
37
37
|
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
38
38
|
import YAML from 'yaml';
|
39
|
-
import assert from 'assert';
|
40
39
|
|
41
40
|
type ImplementationGenerator<T = StormFileImplementationPrompt> = (
|
42
41
|
prompt: T,
|
@@ -134,19 +133,15 @@ export class StormCodegen {
|
|
134
133
|
private handleTemplateFileOutput(
|
135
134
|
blockUri: KapetaURI,
|
136
135
|
aiName: string,
|
137
|
-
template: StormFileInfo,
|
138
136
|
data: StormEvent
|
139
|
-
):
|
137
|
+
): StormEventFileDone | undefined {
|
140
138
|
if (this.handleFileEvents(blockUri, aiName, data)) {
|
141
139
|
return;
|
142
140
|
}
|
143
141
|
|
144
142
|
switch (data.type) {
|
145
143
|
case 'FILE_DONE':
|
146
|
-
|
147
|
-
template.content = data.payload.content;
|
148
|
-
this.handleFileDoneOutput(blockUri, aiName, data);
|
149
|
-
break;
|
144
|
+
return this.handleFileDoneOutput(blockUri, aiName, data);
|
150
145
|
}
|
151
146
|
}
|
152
147
|
|
@@ -227,7 +222,7 @@ export class StormCodegen {
|
|
227
222
|
return false;
|
228
223
|
}
|
229
224
|
|
230
|
-
private handleFileDoneOutput(blockUri: KapetaURI, aiName: string, data: StormEvent) {
|
225
|
+
private handleFileDoneOutput(blockUri: KapetaURI, aiName: string, data: StormEvent): StormEventFileDone | undefined {
|
231
226
|
switch (data.type) {
|
232
227
|
case 'FILE_DONE':
|
233
228
|
const ref = blockUri.toNormalizedString();
|
@@ -257,8 +252,12 @@ export class StormCodegen {
|
|
257
252
|
if (this.isAborted()) {
|
258
253
|
return;
|
259
254
|
}
|
255
|
+
|
256
|
+
const kapetaYaml = YAML.stringify(block.content);
|
257
|
+
const blockDefinition = block.content;
|
258
|
+
|
260
259
|
// Generate the code for the block using the standard codegen templates
|
261
|
-
const generatedResult = await this.generateBlock(
|
260
|
+
const generatedResult = await this.generateBlock(blockDefinition);
|
262
261
|
if (!generatedResult) {
|
263
262
|
return;
|
264
263
|
}
|
@@ -295,8 +294,8 @@ export class StormCodegen {
|
|
295
294
|
if (failedEvents.length > 0) {
|
296
295
|
console.warn('Codegen encountered failed plan events. Plan might be invalid', failedEvents);
|
297
296
|
}
|
297
|
+
// TODO: remove this when we have a better way to handle failed events
|
298
298
|
// Skip failed events - they are not relevant to the materializer
|
299
|
-
// TODO: should the materializer be able to handle failed events?
|
300
299
|
filteredEvents = filteredEvents.filter(
|
301
300
|
(event) => !event.type.endsWith('_FAILED') && !event.type.endsWith('ERROR_INTERNAL')
|
302
301
|
);
|
@@ -374,7 +373,7 @@ export class StormCodegen {
|
|
374
373
|
if (this.isAborted()) {
|
375
374
|
return;
|
376
375
|
}
|
377
|
-
const basePath = this.getBasePath(
|
376
|
+
const basePath = this.getBasePath(blockDefinition.metadata.name);
|
378
377
|
|
379
378
|
const screenFilesConverted = screenFiles.map((screenFile) => {
|
380
379
|
return {
|
@@ -400,7 +399,7 @@ export class StormCodegen {
|
|
400
399
|
const stream = await stormClient.generateCode(payload);
|
401
400
|
|
402
401
|
stream.on('data', (evt) => {
|
403
|
-
this.handleTemplateFileOutput(blockUri, block.aiName,
|
402
|
+
this.handleTemplateFileOutput(blockUri, block.aiName, evt);
|
404
403
|
});
|
405
404
|
|
406
405
|
this.out.on('aborted', () => {
|
@@ -417,9 +416,9 @@ export class StormCodegen {
|
|
417
416
|
);
|
418
417
|
|
419
418
|
// Send the service and UI templates to the AI. These will be sent one-by-one in addition to the context files
|
420
|
-
|
419
|
+
let serviceFiles: StormFileInfo[] = allFiles.filter((file) => file.type === AIFileTypes.SERVICE);
|
421
420
|
if (serviceFiles.length > 0) {
|
422
|
-
await this.processTemplates(
|
421
|
+
serviceFiles = await this.processTemplates(
|
423
422
|
blockUri,
|
424
423
|
block.aiName,
|
425
424
|
stormClient.createServiceImplementation.bind(stormClient),
|
@@ -432,21 +431,22 @@ export class StormCodegen {
|
|
432
431
|
return;
|
433
432
|
}
|
434
433
|
|
434
|
+
for (const contextFile of contextFiles) {
|
435
|
+
const filePath = join(basePath, contextFile.filename);
|
436
|
+
await writeFile(filePath, contextFile.content);
|
437
|
+
}
|
438
|
+
|
435
439
|
for (const screenFile of screenFilesConverted) {
|
436
440
|
const filePath = join(basePath, screenFile.filename);
|
437
441
|
await writeFile(filePath, screenFile.content);
|
438
442
|
}
|
439
443
|
|
444
|
+
// this might decide to overwrite a file that is also a context file
|
440
445
|
for (const serviceFile of serviceFiles) {
|
441
446
|
const filePath = join(basePath, serviceFile.filename);
|
442
447
|
await writeFile(filePath, serviceFile.content);
|
443
448
|
}
|
444
449
|
|
445
|
-
for (const serviceFile of contextFiles) {
|
446
|
-
const filePath = join(basePath, serviceFile.filename);
|
447
|
-
await writeFile(filePath, serviceFile.content);
|
448
|
-
}
|
449
|
-
|
450
450
|
// Write again after modifications
|
451
451
|
for (const webRouterFile of webRouters) {
|
452
452
|
const filePath = join(basePath, webRouterFile.filename);
|
@@ -454,14 +454,14 @@ export class StormCodegen {
|
|
454
454
|
}
|
455
455
|
|
456
456
|
const kapetaYmlPath = join(basePath, 'kapeta.yml');
|
457
|
-
await writeFile(kapetaYmlPath,
|
457
|
+
await writeFile(kapetaYmlPath, kapetaYaml);
|
458
458
|
|
459
459
|
const blockRef = block.uri;
|
460
460
|
|
461
461
|
this.emitBlockStatus(blockUri, block.aiName, StormEventBlockStatusType.QA);
|
462
462
|
|
463
463
|
const filesToBeFixed = serviceFiles.concat(contextFiles).concat(screenFilesConverted);
|
464
|
-
const codeGenerator = new BlockCodeGenerator(
|
464
|
+
const codeGenerator = new BlockCodeGenerator(blockDefinition);
|
465
465
|
|
466
466
|
this.emitBlockStatus(blockUri, block.aiName, StormEventBlockStatusType.BUILDING);
|
467
467
|
await this.verifyAndFixCode(blockUri, block.aiName, codeGenerator, basePath, filesToBeFixed, allFiles);
|
@@ -758,6 +758,7 @@ export class StormCodegen {
|
|
758
758
|
if (this.handleFileEvents(blockUri, blockName, evt)) {
|
759
759
|
return;
|
760
760
|
}
|
761
|
+
this.handleFileDoneOutput(blockUri, blockName, evt);
|
761
762
|
|
762
763
|
if (evt.type === 'CODE_FIX') {
|
763
764
|
resolved = true;
|
@@ -863,8 +864,9 @@ export class StormCodegen {
|
|
863
864
|
generator: ImplementationGenerator,
|
864
865
|
templates: StormFileInfo[],
|
865
866
|
contextFiles: StormFileInfo[]
|
866
|
-
): Promise<
|
867
|
+
): Promise<StormFileInfo[]> {
|
867
868
|
const promises = templates.map(async (templateFile) => {
|
869
|
+
let changedFiles: StormEventFileDone[] = [];
|
868
870
|
const stream = await generator({
|
869
871
|
context: contextFiles,
|
870
872
|
template: templateFile,
|
@@ -876,13 +878,29 @@ export class StormCodegen {
|
|
876
878
|
});
|
877
879
|
|
878
880
|
stream.on('data', (evt) => {
|
879
|
-
this.handleTemplateFileOutput(blockUri, aiName,
|
881
|
+
let changedFile = this.handleTemplateFileOutput(blockUri, aiName, evt);
|
882
|
+
if (changedFile) {
|
883
|
+
changedFiles.push(...changedFiles, changedFile);
|
884
|
+
}
|
880
885
|
});
|
881
886
|
|
882
887
|
await stream.waitForDone();
|
888
|
+
return changedFiles;
|
883
889
|
});
|
884
890
|
|
885
|
-
await Promise.all(promises);
|
891
|
+
const changedFiles: StormEventFileDone[][] = await Promise.all(promises);
|
892
|
+
let allFiles = templates.concat(contextFiles);
|
893
|
+
let result: StormFileInfo[] = [];
|
894
|
+
for (const changedFile of changedFiles.flat()) {
|
895
|
+
const find = allFiles.find((file) => file.filename === changedFile.payload.filename);
|
896
|
+
if (find) {
|
897
|
+
result.push({ type: find.type, filename: find.filename, mode: find.mode, permissions: find.permissions,
|
898
|
+
content: changedFile.payload.content });
|
899
|
+
} else {
|
900
|
+
console.warn("processTemplates: AI changed a file that wasn't in the input [" + changedFile.payload.filename + "]");
|
901
|
+
}
|
902
|
+
}
|
903
|
+
return result;
|
886
904
|
}
|
887
905
|
|
888
906
|
/**
|