@kapeta/local-cluster-service 0.49.0 → 0.51.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.
@@ -12,6 +12,7 @@ const fs_extra_1 = __importDefault(require("fs-extra"));
12
12
  const cors_1 = require("../middleware/cors");
13
13
  const stringBody_1 = require("../middleware/stringBody");
14
14
  const stormClient_1 = require("./stormClient");
15
+ const events_1 = require("./events");
15
16
  const event_parser_1 = require("./event-parser");
16
17
  const codegen_1 = require("./codegen");
17
18
  const assetManager_1 = require("../assetManager");
@@ -27,15 +28,41 @@ router.post('/:handle/all', async (req, res) => {
27
28
  const conversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()];
28
29
  const aiRequest = JSON.parse(req.stringBody ?? '{}');
29
30
  const metaStream = await stormClient_1.stormClient.createMetadata(aiRequest.prompt, conversationId);
31
+ onRequestAborted(req, res, () => {
32
+ metaStream.abort();
33
+ });
30
34
  res.set('Content-Type', 'application/x-ndjson');
31
35
  res.set('Access-Control-Expose-Headers', stormClient_1.ConversationIdHeader);
32
36
  res.set(stormClient_1.ConversationIdHeader, metaStream.getConversationId());
37
+ let currentPhase = events_1.StormEventPhaseType.META;
33
38
  metaStream.on('data', (data) => {
34
39
  const result = eventParser.processEvent(req.params.handle, data);
40
+ switch (data.type) {
41
+ case 'CREATE_API':
42
+ case 'CREATE_MODEL':
43
+ case 'CREATE_TYPE':
44
+ if (currentPhase !== events_1.StormEventPhaseType.DEFINITIONS) {
45
+ sendEvent(res, (0, event_parser_1.createPhaseEndEvent)(events_1.StormEventPhaseType.META));
46
+ currentPhase = events_1.StormEventPhaseType.DEFINITIONS;
47
+ sendEvent(res, (0, event_parser_1.createPhaseStartEvent)(events_1.StormEventPhaseType.DEFINITIONS));
48
+ }
49
+ break;
50
+ }
35
51
  sendEvent(res, data);
36
52
  sendDefinitions(res, result);
37
53
  });
38
- await waitForStormStream(metaStream);
54
+ try {
55
+ sendEvent(res, (0, event_parser_1.createPhaseStartEvent)(events_1.StormEventPhaseType.META));
56
+ await waitForStormStream(metaStream);
57
+ }
58
+ finally {
59
+ if (!metaStream.isAborted()) {
60
+ sendEvent(res, (0, event_parser_1.createPhaseEndEvent)(currentPhase));
61
+ }
62
+ }
63
+ if (metaStream.isAborted()) {
64
+ return;
65
+ }
39
66
  if (!eventParser.isValid()) {
40
67
  // We can't continue if the meta stream is invalid
41
68
  sendEvent(res, {
@@ -48,32 +75,45 @@ router.post('/:handle/all', async (req, res) => {
48
75
  return;
49
76
  }
50
77
  const result = eventParser.toResult(handle);
78
+ if (metaStream.isAborted()) {
79
+ return;
80
+ }
51
81
  sendDefinitions(res, result);
52
82
  if (!req.query.skipCodegen) {
53
- const stormCodegen = new codegen_1.StormCodegen(metaStream.getConversationId(), aiRequest.prompt, result.blocks, eventParser.getEvents());
54
- const codegenPromise = streamStormPartialResponse(stormCodegen.getStream(), res);
55
- await stormCodegen.process();
56
- await codegenPromise;
83
+ try {
84
+ sendEvent(res, (0, event_parser_1.createPhaseStartEvent)(events_1.StormEventPhaseType.IMPLEMENTATION));
85
+ const stormCodegen = new codegen_1.StormCodegen(metaStream.getConversationId(), aiRequest.prompt, result.blocks, eventParser.getEvents());
86
+ onRequestAborted(req, res, () => {
87
+ stormCodegen.abort();
88
+ });
89
+ const codegenPromise = streamStormPartialResponse(stormCodegen.getStream(), res);
90
+ await stormCodegen.process();
91
+ await codegenPromise;
92
+ }
93
+ finally {
94
+ if (!metaStream.isAborted()) {
95
+ sendEvent(res, (0, event_parser_1.createPhaseEndEvent)(events_1.StormEventPhaseType.IMPLEMENTATION));
96
+ }
97
+ }
57
98
  }
58
99
  sendDone(res);
59
100
  }
60
101
  catch (err) {
61
102
  sendError(err, res);
62
- res.end();
103
+ if (!res.closed) {
104
+ res.end();
105
+ }
63
106
  }
64
107
  });
65
108
  router.post('/block/create', async (req, res) => {
66
109
  const createRequest = JSON.parse(req.stringBody ?? '{}');
67
110
  try {
68
111
  const ymlPath = path_1.default.join(createRequest.newPath, 'kapeta.yml');
69
- console.log('Creating block at', ymlPath);
70
112
  const [asset] = await assetManager_1.assetManager.createAsset(ymlPath, createRequest.definition);
71
113
  if (await fs_extra_1.default.pathExists(createRequest.tmpPath)) {
72
- console.log('Moving block from', createRequest.tmpPath, 'to', createRequest.newPath);
73
114
  await fs_extra_1.default.move(createRequest.tmpPath, createRequest.newPath, {
74
115
  overwrite: true,
75
116
  });
76
- console.log('Updating asset', asset.ref);
77
117
  res.send(await assetManager_1.assetManager.updateAsset(asset.ref, createRequest.definition));
78
118
  }
79
119
  else {
@@ -93,6 +133,9 @@ function sendDefinitions(res, result) {
93
133
  });
94
134
  }
95
135
  function sendDone(res) {
136
+ if (res.closed) {
137
+ return;
138
+ }
96
139
  sendEvent(res, {
97
140
  type: 'DONE',
98
141
  created: Date.now(),
@@ -100,6 +143,9 @@ function sendDone(res) {
100
143
  res.end();
101
144
  }
102
145
  function sendError(err, res) {
146
+ if (res.closed) {
147
+ return;
148
+ }
103
149
  console.error('Failed to send prompt', err);
104
150
  if (res.headersSent) {
105
151
  sendEvent(res, {
@@ -137,6 +183,17 @@ function streamStormPartialResponse(result, res) {
137
183
  });
138
184
  }
139
185
  function sendEvent(res, evt) {
186
+ if (res.closed) {
187
+ return;
188
+ }
140
189
  res.write(JSON.stringify(evt) + '\n');
141
190
  }
191
+ function onRequestAborted(req, res, onAborted) {
192
+ req.on('close', () => {
193
+ onAborted();
194
+ });
195
+ res.on('close', () => {
196
+ onAborted();
197
+ });
198
+ }
142
199
  exports.default = router;
@@ -46,12 +46,13 @@ class StormClient {
46
46
  prompt: stringPrompt,
47
47
  conversationId: body.conversationId,
48
48
  });
49
+ const abort = new AbortController();
50
+ options.signal = abort.signal;
49
51
  const response = await fetch(options.url, options);
50
52
  if (response.status !== 200) {
51
53
  throw new Error(`Got error response from ${options.url}: ${response.status}\nContent: ${await response.text()}`);
52
54
  }
53
55
  const conversationId = response.headers.get(exports.ConversationIdHeader);
54
- console.log('Received conversationId', conversationId);
55
56
  const out = new stream_1.StormStream(stringPrompt, conversationId);
56
57
  const jsonLStream = promises_1.default.createInterface(node_stream_1.Readable.fromWeb(response.body));
57
58
  jsonLStream.on('line', (line) => {
@@ -63,6 +64,9 @@ class StormClient {
63
64
  jsonLStream.on('close', () => {
64
65
  out.end();
65
66
  });
67
+ out.on('aborted', () => {
68
+ abort.abort();
69
+ });
66
70
  return out;
67
71
  }
68
72
  createMetadata(prompt, conversationId) {
@@ -10,17 +10,22 @@ import { BlockDefinition } from '@kapeta/schemas';
10
10
  export declare class StormStream extends EventEmitter {
11
11
  private conversationId;
12
12
  private lines;
13
+ private aborted;
13
14
  constructor(prompt?: string, conversationId?: string | null);
14
15
  getConversationId(): string;
16
+ isAborted(): boolean;
15
17
  addJSONLine(line: string): void;
16
18
  end(): void;
17
19
  on(event: 'end', listener: () => void): this;
20
+ on(event: 'aborted', listener: () => void): this;
18
21
  on(event: 'error', listener: (e: Error) => void): this;
19
22
  on(event: 'data', listener: (data: StormEvent) => void): this;
20
23
  emit(event: 'end'): boolean;
24
+ emit(event: 'aborted'): void;
21
25
  emit(event: 'error', e: Error): boolean;
22
26
  emit(event: 'data', data: StormEvent): boolean;
23
27
  waitForDone(): Promise<void>;
28
+ abort(): void;
24
29
  }
25
30
  export interface ConversationItem {
26
31
  role: 'user' | 'model';
@@ -9,6 +9,7 @@ const node_events_1 = require("node:events");
9
9
  class StormStream extends node_events_1.EventEmitter {
10
10
  conversationId = '';
11
11
  lines = [];
12
+ aborted = false;
12
13
  constructor(prompt = '', conversationId) {
13
14
  super();
14
15
  this.conversationId = conversationId || '';
@@ -16,6 +17,9 @@ class StormStream extends node_events_1.EventEmitter {
16
17
  getConversationId() {
17
18
  return this.conversationId;
18
19
  }
20
+ isAborted() {
21
+ return this.aborted;
22
+ }
19
23
  addJSONLine(line) {
20
24
  try {
21
25
  this.lines.push(line);
@@ -48,5 +52,12 @@ class StormStream extends node_events_1.EventEmitter {
48
52
  });
49
53
  });
50
54
  }
55
+ abort() {
56
+ if (this.aborted) {
57
+ return;
58
+ }
59
+ this.aborted = true;
60
+ this.emit('aborted');
61
+ }
51
62
  }
52
63
  exports.StormStream = StormStream;
@@ -14,10 +14,12 @@ export declare class StormCodegen {
14
14
  private readonly conversationId;
15
15
  constructor(conversationId: string, userPrompt: string, blocks: BlockDefinitionInfo[], events: StormEvent[]);
16
16
  process(): Promise<void>;
17
+ isAborted(): boolean;
17
18
  getStream(): StormStream;
18
19
  private handleTemplateFileOutput;
19
20
  private handleUiOutput;
20
- private handleFileOutput;
21
+ private handleFileEvents;
22
+ private handleFileDoneOutput;
21
23
  private getBasePath;
22
24
  /**
23
25
  * Generates the code for a block and sends it to the AI
@@ -33,7 +35,7 @@ export declare class StormCodegen {
33
35
  /**
34
36
  * Emits the text-based files to the stream
35
37
  */
36
- private emitFiles;
38
+ private emitStaticFiles;
37
39
  private emitFile;
38
40
  /**
39
41
  * Sends the template to the AI and processes the response
@@ -47,4 +49,5 @@ export declare class StormCodegen {
47
49
  * Generates the code using codegen for a given block.
48
50
  */
49
51
  private generateBlock;
52
+ abort(): void;
50
53
  }
@@ -42,6 +42,49 @@ const path_1 = __importStar(require("path"));
42
42
  const node_os_1 = __importDefault(require("node:os"));
43
43
  const fs_1 = require("fs");
44
44
  const path_2 = __importDefault(require("path"));
45
+ const SIMULATED_DELAY = 1000;
46
+ class SimulatedFileDelay {
47
+ file;
48
+ stream;
49
+ constructor(file, stream) {
50
+ this.file = file;
51
+ this.stream = stream;
52
+ }
53
+ async start() {
54
+ const commonPayload = {
55
+ filename: this.file.payload.filename,
56
+ path: this.file.payload.path,
57
+ blockName: this.file.payload.blockName,
58
+ blockRef: this.file.payload.blockRef,
59
+ instanceId: this.file.payload.instanceId,
60
+ };
61
+ this.stream.emit('data', {
62
+ type: 'FILE_START',
63
+ created: Date.now(),
64
+ reason: 'File start',
65
+ payload: commonPayload,
66
+ });
67
+ const lines = this.file.payload.content.split('\n');
68
+ const delayPerLine = SIMULATED_DELAY / lines.length;
69
+ for (const line of lines) {
70
+ await new Promise((resolve) => {
71
+ setTimeout(() => {
72
+ this.stream.emit('data', {
73
+ type: 'FILE_CHUNK',
74
+ created: Date.now(),
75
+ reason: 'File chunk',
76
+ payload: {
77
+ ...commonPayload,
78
+ content: line,
79
+ },
80
+ });
81
+ resolve();
82
+ }, delayPerLine);
83
+ });
84
+ }
85
+ this.stream.emit('data', this.file);
86
+ }
87
+ }
45
88
  class StormCodegen {
46
89
  userPrompt;
47
90
  blocks;
@@ -63,46 +106,139 @@ class StormCodegen {
63
106
  await Promise.all(promises);
64
107
  this.out.end();
65
108
  }
109
+ isAborted() {
110
+ return this.out.isAborted();
111
+ }
66
112
  getStream() {
67
113
  return this.out;
68
114
  }
69
115
  handleTemplateFileOutput(blockUri, aiName, template, data) {
116
+ if (this.handleFileEvents(blockUri, aiName, data)) {
117
+ return;
118
+ }
70
119
  switch (data.type) {
71
- case 'FILE':
120
+ case 'FILE_DONE':
72
121
  template.filename = data.payload.filename;
73
122
  template.content = data.payload.content;
74
- return this.handleFileOutput(blockUri, aiName, data);
123
+ this.handleFileDoneOutput(blockUri, aiName, data);
124
+ break;
75
125
  }
76
126
  }
77
- handleUiOutput(blockUri, aiName, data) {
127
+ handleUiOutput(blockUri, blockName, data) {
128
+ const blockRef = blockUri.toNormalizedString();
129
+ const instanceId = event_parser_1.StormEventParser.toInstanceIdFromRef(blockRef);
130
+ if (this.handleFileEvents(blockUri, blockName, data)) {
131
+ return;
132
+ }
78
133
  switch (data.type) {
79
134
  case 'SCREEN':
80
- const ref = blockUri.toNormalizedString();
81
135
  this.out.emit('data', {
82
136
  type: 'SCREEN',
83
137
  reason: data.reason,
84
138
  created: Date.now(),
85
139
  payload: {
86
140
  ...data.payload,
87
- blockName: aiName,
88
- blockRef: ref,
89
- instanceId: event_parser_1.StormEventParser.toInstanceIdFromRef(ref),
141
+ blockName,
142
+ blockRef,
143
+ instanceId,
144
+ },
145
+ });
146
+ break;
147
+ case 'FILE_START':
148
+ case 'FILE_CHUNK_RESET':
149
+ this.out.emit('data', {
150
+ ...data,
151
+ payload: {
152
+ ...data.payload,
153
+ blockName,
154
+ blockRef,
155
+ instanceId,
156
+ },
157
+ });
158
+ break;
159
+ case 'FILE_CHUNK':
160
+ this.out.emit('data', {
161
+ ...data,
162
+ payload: {
163
+ ...data.payload,
164
+ blockName,
165
+ blockRef,
166
+ instanceId,
90
167
  },
91
168
  });
92
- case 'FILE':
93
- return this.handleFileOutput(blockUri, aiName, data);
169
+ break;
170
+ case 'FILE_STATE':
171
+ this.out.emit('data', {
172
+ ...data,
173
+ payload: {
174
+ ...data.payload,
175
+ blockName,
176
+ blockRef,
177
+ instanceId,
178
+ },
179
+ });
180
+ break;
181
+ case 'FILE_DONE':
182
+ this.handleFileDoneOutput(blockUri, blockName, data);
183
+ break;
94
184
  }
95
185
  }
96
- handleFileOutput(blockUri, aiName, data) {
186
+ handleFileEvents(blockUri, blockName, data) {
187
+ const blockRef = blockUri.toNormalizedString();
188
+ const instanceId = event_parser_1.StormEventParser.toInstanceIdFromRef(blockRef);
189
+ const basePath = this.getBasePath(blockUri.fullName);
97
190
  switch (data.type) {
98
- case 'FILE':
191
+ case 'FILE_START':
192
+ case 'FILE_CHUNK_RESET':
193
+ this.out.emit('data', {
194
+ ...data,
195
+ payload: {
196
+ ...data.payload,
197
+ path: (0, path_1.join)(basePath, data.payload.filename),
198
+ blockName,
199
+ blockRef,
200
+ instanceId,
201
+ },
202
+ });
203
+ return true;
204
+ case 'FILE_CHUNK':
205
+ this.out.emit('data', {
206
+ ...data,
207
+ payload: {
208
+ ...data.payload,
209
+ path: (0, path_1.join)(basePath, data.payload.filename),
210
+ blockName,
211
+ blockRef,
212
+ instanceId,
213
+ },
214
+ });
215
+ return true;
216
+ case 'FILE_STATE':
217
+ this.out.emit('data', {
218
+ ...data,
219
+ payload: {
220
+ ...data.payload,
221
+ path: (0, path_1.join)(basePath, data.payload.filename),
222
+ blockName,
223
+ blockRef,
224
+ instanceId,
225
+ },
226
+ });
227
+ return true;
228
+ }
229
+ return false;
230
+ }
231
+ handleFileDoneOutput(blockUri, aiName, data) {
232
+ switch (data.type) {
233
+ case 'FILE_DONE':
99
234
  const ref = blockUri.toNormalizedString();
100
235
  this.emitFile(blockUri, aiName, data.payload.filename, data.payload.content, data.reason);
101
236
  return {
102
- type: 'FILE',
237
+ type: 'FILE_DONE',
103
238
  created: Date.now(),
104
239
  payload: {
105
240
  filename: data.payload.filename,
241
+ path: (0, path_1.join)(this.getBasePath(blockUri.fullName), data.payload.filename),
106
242
  content: data.payload.content,
107
243
  blockRef: ref,
108
244
  instanceId: event_parser_1.StormEventParser.toInstanceIdFromRef(ref),
@@ -117,6 +253,9 @@ class StormCodegen {
117
253
  * Generates the code for a block and sends it to the AI
118
254
  */
119
255
  async processBlockCode(block) {
256
+ if (this.isAborted()) {
257
+ return;
258
+ }
120
259
  // Generate the code for the block using the standard codegen templates
121
260
  const generatedResult = await this.generateBlock(block.content);
122
261
  if (!generatedResult) {
@@ -124,7 +263,10 @@ class StormCodegen {
124
263
  }
125
264
  const allFiles = this.toStormFiles(generatedResult);
126
265
  // Send all the non-ai files to the stream
127
- this.emitFiles((0, nodejs_utils_1.parseKapetaUri)(block.uri), block.aiName, allFiles);
266
+ await this.emitStaticFiles((0, nodejs_utils_1.parseKapetaUri)(block.uri), block.aiName, allFiles);
267
+ if (this.isAborted()) {
268
+ return;
269
+ }
128
270
  const relevantFiles = allFiles.filter((file) => file.type !== codegen_1.AIFileTypes.IGNORE && file.type !== codegen_1.AIFileTypes.WEB_SCREEN);
129
271
  const uiTemplates = allFiles.filter((file) => file.type === codegen_1.AIFileTypes.WEB_SCREEN);
130
272
  if (uiTemplates.length > 0) {
@@ -138,8 +280,14 @@ class StormCodegen {
138
280
  uiStream.on('data', (evt) => {
139
281
  this.handleUiOutput((0, nodejs_utils_1.parseKapetaUri)(block.uri), block.aiName, evt);
140
282
  });
283
+ this.out.on('aborted', () => {
284
+ uiStream.abort();
285
+ });
141
286
  await uiStream.waitForDone();
142
287
  }
288
+ if (this.isAborted()) {
289
+ return;
290
+ }
143
291
  // Gather the context files for implementation. These will be all be passed to the AI
144
292
  const contextFiles = relevantFiles.filter((file) => ![codegen_1.AIFileTypes.SERVICE, codegen_1.AIFileTypes.WEB_SCREEN].includes(file.type));
145
293
  // Send the service and UI templates to the AI. These will be send one-by-one in addition to the context files
@@ -148,6 +296,9 @@ class StormCodegen {
148
296
  await this.processTemplates((0, nodejs_utils_1.parseKapetaUri)(block.uri), block.aiName, stormClient_1.stormClient.createServiceImplementation.bind(stormClient_1.stormClient), serviceFiles, contextFiles);
149
297
  }
150
298
  const basePath = this.getBasePath(block.content.metadata.name);
299
+ if (this.isAborted()) {
300
+ return;
301
+ }
151
302
  for (const serviceFile of serviceFiles) {
152
303
  const filePath = (0, path_1.join)(basePath, serviceFile.filename);
153
304
  await (0, promises_1.writeFile)(filePath, serviceFile.content);
@@ -192,6 +343,9 @@ class StormCodegen {
192
343
  console.debug('Validation error:', result);
193
344
  const errorStream = await stormClient_1.stormClient.createErrorClassification(result.error, []);
194
345
  const fixes = new Map();
346
+ this.out.on('aborted', () => {
347
+ errorStream.abort();
348
+ });
195
349
  errorStream.on('data', (evt) => {
196
350
  if (evt.type === 'ERROR_CLASSIFIER') {
197
351
  // find the file that caused the error
@@ -206,8 +360,8 @@ class StormCodegen {
206
360
  const fix = `${evt.payload.potentialFix}\n---\n${knownFiles
207
361
  .map((e) => e.filename)
208
362
  .join('\n')}\n---\n${content}`;
209
- console.log(`trying to fix the code in ${eventFileName}`);
210
- console.debug(`with the fix:\n${fix}`);
363
+ //console.log(`trying to fix the code in ${eventFileName}`);
364
+ //console.debug(`with the fix:\n${fix}`);
211
365
  const code = this.codeFix(fix);
212
366
  fixes.set((0, path_1.join)(basePath, eventFileName), code);
213
367
  }
@@ -252,6 +406,9 @@ class StormCodegen {
252
406
  resolve(evt.payload.content);
253
407
  }
254
408
  });
409
+ this.out.on('aborted', () => {
410
+ fixStream.abort();
411
+ });
255
412
  fixStream.on('error', (err) => {
256
413
  reject(err);
257
414
  });
@@ -261,8 +418,8 @@ class StormCodegen {
261
418
  /**
262
419
  * Emits the text-based files to the stream
263
420
  */
264
- emitFiles(uri, aiName, files) {
265
- files.forEach((file) => {
421
+ async emitStaticFiles(uri, aiName, files) {
422
+ const promises = files.map((file) => {
266
423
  if (!file.content || typeof file.content !== 'string') {
267
424
  return;
268
425
  }
@@ -276,14 +433,30 @@ class StormCodegen {
276
433
  // They will need to be implemented by the AI
277
434
  return;
278
435
  }
279
- this.emitFile(uri, aiName, file.filename, file.content);
436
+ const basePath = this.getBasePath(uri.fullName);
437
+ const ref = uri.toNormalizedString();
438
+ const fileEvent = {
439
+ type: 'FILE_DONE',
440
+ reason: 'File generated',
441
+ created: Date.now(),
442
+ payload: {
443
+ filename: file.filename,
444
+ path: (0, path_1.join)(basePath, file.filename),
445
+ content: file.content,
446
+ blockName: aiName,
447
+ blockRef: ref,
448
+ instanceId: event_parser_1.StormEventParser.toInstanceIdFromRef(ref),
449
+ },
450
+ };
451
+ return new SimulatedFileDelay(fileEvent, this.out).start();
280
452
  });
453
+ return Promise.all(promises);
281
454
  }
282
455
  emitFile(uri, blockName, filename, content, reason = 'File generated') {
283
456
  const basePath = this.getBasePath(uri.fullName);
284
457
  const ref = uri.toNormalizedString();
285
458
  this.out.emit('data', {
286
- type: 'FILE',
459
+ type: 'FILE_DONE',
287
460
  reason,
288
461
  created: Date.now(),
289
462
  payload: {
@@ -306,18 +479,15 @@ class StormCodegen {
306
479
  template: templateFile,
307
480
  prompt: this.userPrompt,
308
481
  });
309
- const files = [];
482
+ this.out.on('aborted', () => {
483
+ stream.abort();
484
+ });
310
485
  stream.on('data', (evt) => {
311
- const file = this.handleTemplateFileOutput(blockUri, aiName, templateFile, evt);
312
- if (file) {
313
- files.push(file);
314
- }
486
+ this.handleTemplateFileOutput(blockUri, aiName, templateFile, evt);
315
487
  });
316
488
  await stream.waitForDone();
317
- return files;
318
489
  });
319
- const fileChunks = await Promise.all(promises);
320
- return fileChunks.flat();
490
+ await Promise.all(promises);
321
491
  }
322
492
  /**
323
493
  * Converts the generated files to a format that can be sent to the AI
@@ -357,6 +527,9 @@ class StormCodegen {
357
527
  * Generates the code using codegen for a given block.
358
528
  */
359
529
  async generateBlock(yamlContent) {
530
+ if (this.isAborted()) {
531
+ return;
532
+ }
360
533
  if (!yamlContent.spec.target?.kind) {
361
534
  //Not all block types have targets
362
535
  return;
@@ -371,5 +544,8 @@ class StormCodegen {
371
544
  new codegen_1.CodeWriter(basePath).write(generatedResult);
372
545
  return generatedResult;
373
546
  }
547
+ abort() {
548
+ this.out.abort();
549
+ }
374
550
  }
375
551
  exports.StormCodegen = StormCodegen;
@@ -2,7 +2,7 @@
2
2
  * Copyright 2023 Kapeta Inc.
3
3
  * SPDX-License-Identifier: BUSL-1.1
4
4
  */
5
- import { StormEvent } from './events';
5
+ import { StormEvent, StormEventPhases, StormEventPhaseType } from './events';
6
6
  import { BlockDefinition, Plan } from '@kapeta/schemas';
7
7
  import { KapetaURI } from '@kapeta/nodejs-utils';
8
8
  export interface BlockDefinitionInfo {
@@ -39,6 +39,9 @@ export interface StormOptions {
39
39
  desktopLanguage: string;
40
40
  gatewayKind: string;
41
41
  }
42
+ export declare function createPhaseStartEvent(type: StormEventPhaseType): StormEventPhases;
43
+ export declare function createPhaseEndEvent(type: StormEventPhaseType): StormEventPhases;
44
+ export declare function createPhaseEvent(start: boolean, type: StormEventPhaseType): StormEventPhases;
42
45
  export declare function resolveOptions(): Promise<StormOptions>;
43
46
  export declare class StormEventParser {
44
47
  static toInstanceId(handle: string, blockName: string): string;