@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.
Files changed (35) hide show
  1. package/.vscode/launch.json +1 -1
  2. package/CHANGELOG.md +14 -0
  3. package/dist/cjs/src/assetManager.d.ts +1 -1
  4. package/dist/cjs/src/assetManager.js +5 -3
  5. package/dist/cjs/src/filesystem/routes.js +10 -0
  6. package/dist/cjs/src/storm/codegen.d.ts +9 -1
  7. package/dist/cjs/src/storm/codegen.js +111 -8
  8. package/dist/cjs/src/storm/event-parser.d.ts +1 -1
  9. package/dist/cjs/src/storm/event-parser.js +3 -1
  10. package/dist/cjs/src/storm/events.d.ts +32 -1
  11. package/dist/cjs/src/storm/routes.js +26 -1
  12. package/dist/cjs/src/storm/stormClient.d.ts +3 -1
  13. package/dist/cjs/src/storm/stormClient.js +12 -0
  14. package/dist/cjs/src/storm/stream.d.ts +6 -0
  15. package/dist/esm/src/assetManager.d.ts +1 -1
  16. package/dist/esm/src/assetManager.js +5 -3
  17. package/dist/esm/src/filesystem/routes.js +10 -0
  18. package/dist/esm/src/storm/codegen.d.ts +9 -1
  19. package/dist/esm/src/storm/codegen.js +111 -8
  20. package/dist/esm/src/storm/event-parser.d.ts +1 -1
  21. package/dist/esm/src/storm/event-parser.js +3 -1
  22. package/dist/esm/src/storm/events.d.ts +32 -1
  23. package/dist/esm/src/storm/routes.js +26 -1
  24. package/dist/esm/src/storm/stormClient.d.ts +3 -1
  25. package/dist/esm/src/storm/stormClient.js +12 -0
  26. package/dist/esm/src/storm/stream.d.ts +6 -0
  27. package/package.json +1 -1
  28. package/src/assetManager.ts +6 -4
  29. package/src/filesystem/routes.ts +10 -0
  30. package/src/storm/codegen.ts +132 -12
  31. package/src/storm/event-parser.ts +4 -2
  32. package/src/storm/events.ts +38 -1
  33. package/src/storm/routes.ts +39 -2
  34. package/src/storm/stormClient.ts +13 -1
  35. package/src/storm/stream.ts +7 -0
@@ -6,7 +6,7 @@
6
6
  "request": "launch",
7
7
  "name": "Debug Node.js (npm run dev)",
8
8
  "runtimeExecutable": "npm",
9
- "runtimeArgs": ["run-script", "dev"],
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
- await this.maybeGenerateCode(ref, path, yaml);
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
- constructor(userPrompt: string, blocks: BlockDefinitionInfo[], events: StormEvent[]);
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
- constructor(userPrompt, blocks, events) {
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
- for (const block of this.blocks) {
56
- await this.processBlockCode(block);
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: KapetaURI;
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
- await this.maybeGenerateCode(ref, path, yaml);
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
- constructor(userPrompt: string, blocks: BlockDefinitionInfo[], events: StormEvent[]);
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
  */