@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 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, template, data) {
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
- template.filename = data.payload.filename;
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(block.content);
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(block.content.metadata.name);
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, webRouter, evt);
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
- const serviceFiles = allFiles.filter((file) => file.type === codegen_1.AIFileTypes.SERVICE);
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, yaml_1.default.stringify(block.content));
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(block.content);
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, templateFile, evt);
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, template, data) {
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
- template.filename = data.payload.filename;
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(block.content);
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(block.content.metadata.name);
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, webRouter, evt);
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
- const serviceFiles = allFiles.filter((file) => file.type === codegen_1.AIFileTypes.SERVICE);
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, yaml_1.default.stringify(block.content));
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(block.content);
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, templateFile, evt);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kapeta/local-cluster-service",
3
- "version": "0.54.2",
3
+ "version": "0.54.4",
4
4
  "description": "Manages configuration, ports and service discovery for locally running Kapeta systems",
5
5
  "type": "commonjs",
6
6
  "exports": {
@@ -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
- ): void {
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
- template.filename = data.payload.filename;
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(block.content);
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(block.content.metadata.name);
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, webRouter, evt);
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
- const serviceFiles: StormFileInfo[] = allFiles.filter((file) => file.type === AIFileTypes.SERVICE);
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, YAML.stringify(block.content));
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(block.content);
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<void> {
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, templateFile, evt);
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
  /**