@kapeta/local-cluster-service 0.48.1 → 0.48.3
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 +14 -0
- package/dist/cjs/src/storm/codegen.d.ts +2 -0
- package/dist/cjs/src/storm/codegen.js +19 -3
- package/dist/cjs/src/storm/event-parser.d.ts +4 -4
- package/dist/cjs/src/storm/event-parser.js +45 -37
- package/dist/cjs/src/storm/events.d.ts +18 -2
- package/dist/esm/src/storm/codegen.d.ts +2 -0
- package/dist/esm/src/storm/codegen.js +19 -3
- package/dist/esm/src/storm/event-parser.d.ts +4 -4
- package/dist/esm/src/storm/event-parser.js +45 -37
- package/dist/esm/src/storm/events.d.ts +18 -2
- package/package.json +1 -1
- package/src/storm/codegen.ts +20 -4
- package/src/storm/event-parser.ts +52 -41
- package/src/storm/events.ts +19 -1
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## [0.48.3](https://github.com/kapetacom/local-cluster-service/compare/v0.48.2...v0.48.3) (2024-06-03)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* Added missing instance and block refs ([dc77a7c](https://github.com/kapetacom/local-cluster-service/commit/dc77a7c480d4ed70e1dc8dc5c1b13093d739e20c))
|
7
|
+
|
8
|
+
## [0.48.2](https://github.com/kapetacom/local-cluster-service/compare/v0.48.1...v0.48.2) (2024-06-03)
|
9
|
+
|
10
|
+
|
11
|
+
### Bug Fixes
|
12
|
+
|
13
|
+
* Include resource name for api and model ([#157](https://github.com/kapetacom/local-cluster-service/issues/157)) ([b79a704](https://github.com/kapetacom/local-cluster-service/commit/b79a704b74d11cb0ec0fc8346e29ed754b39fb12))
|
14
|
+
|
1
15
|
## [0.48.1](https://github.com/kapetacom/local-cluster-service/compare/v0.48.0...v0.48.1) (2024-06-03)
|
2
16
|
|
3
17
|
|
@@ -10,12 +10,14 @@ export declare class StormCodegen {
|
|
10
10
|
private readonly blocks;
|
11
11
|
private readonly out;
|
12
12
|
private readonly events;
|
13
|
+
private readonly tmpDir;
|
13
14
|
constructor(userPrompt: string, blocks: BlockDefinitionInfo[], events: StormEvent[]);
|
14
15
|
process(): Promise<void>;
|
15
16
|
getStream(): StormStream;
|
16
17
|
private handleTemplateFileOutput;
|
17
18
|
private handleUiOutput;
|
18
19
|
private handleFileOutput;
|
20
|
+
private getBasePath;
|
19
21
|
/**
|
20
22
|
* Generates the code for a block and sends it to the AI
|
21
23
|
*/
|
@@ -34,6 +34,7 @@ exports.StormCodegen = void 0;
|
|
34
34
|
const codegen_1 = require("@kapeta/codegen");
|
35
35
|
const codeGeneratorManager_1 = require("../codeGeneratorManager");
|
36
36
|
const stormClient_1 = require("./stormClient");
|
37
|
+
const event_parser_1 = require("./event-parser");
|
37
38
|
const stream_1 = require("./stream");
|
38
39
|
const promises_1 = require("fs/promises");
|
39
40
|
const path_1 = __importStar(require("path"));
|
@@ -43,10 +44,12 @@ class StormCodegen {
|
|
43
44
|
blocks;
|
44
45
|
out = new stream_1.StormStream();
|
45
46
|
events;
|
47
|
+
tmpDir;
|
46
48
|
constructor(userPrompt, blocks, events) {
|
47
49
|
this.userPrompt = userPrompt;
|
48
50
|
this.blocks = blocks;
|
49
51
|
this.events = events;
|
52
|
+
this.tmpDir = node_os_1.default.tmpdir();
|
50
53
|
}
|
51
54
|
async process() {
|
52
55
|
for (const block of this.blocks) {
|
@@ -68,6 +71,7 @@ class StormCodegen {
|
|
68
71
|
handleUiOutput(blockUri, aiName, data) {
|
69
72
|
switch (data.type) {
|
70
73
|
case 'SCREEN':
|
74
|
+
const ref = blockUri.toNormalizedString();
|
71
75
|
this.out.emit('data', {
|
72
76
|
type: 'SCREEN',
|
73
77
|
reason: data.reason,
|
@@ -75,6 +79,8 @@ class StormCodegen {
|
|
75
79
|
payload: {
|
76
80
|
...data.payload,
|
77
81
|
blockName: aiName,
|
82
|
+
blockRef: ref,
|
83
|
+
instanceId: event_parser_1.StormEventParser.toInstanceIdFromRef(ref),
|
78
84
|
},
|
79
85
|
});
|
80
86
|
case 'FILE':
|
@@ -84,6 +90,7 @@ class StormCodegen {
|
|
84
90
|
handleFileOutput(blockUri, aiName, data) {
|
85
91
|
switch (data.type) {
|
86
92
|
case 'FILE':
|
93
|
+
const ref = blockUri.toNormalizedString();
|
87
94
|
this.emitFile(blockUri, aiName, data.payload.filename, data.payload.content, data.reason);
|
88
95
|
return {
|
89
96
|
type: 'FILE',
|
@@ -91,10 +98,15 @@ class StormCodegen {
|
|
91
98
|
payload: {
|
92
99
|
filename: data.payload.filename,
|
93
100
|
content: data.payload.content,
|
101
|
+
blockRef: ref,
|
102
|
+
instanceId: event_parser_1.StormEventParser.toInstanceIdFromRef(ref),
|
94
103
|
},
|
95
104
|
};
|
96
105
|
}
|
97
106
|
}
|
107
|
+
getBasePath(blockName) {
|
108
|
+
return path_1.default.join(this.tmpDir, blockName);
|
109
|
+
}
|
98
110
|
/**
|
99
111
|
* Generates the code for a block and sends it to the AI
|
100
112
|
*/
|
@@ -129,7 +141,7 @@ class StormCodegen {
|
|
129
141
|
if (serviceFiles.length > 0) {
|
130
142
|
await this.processTemplates(block.uri, block.aiName, stormClient_1.stormClient.createServiceImplementation.bind(stormClient_1.stormClient), serviceFiles, contextFiles);
|
131
143
|
}
|
132
|
-
const basePath =
|
144
|
+
const basePath = this.getBasePath(block.content.metadata.name);
|
133
145
|
for (const serviceFile of serviceFiles) {
|
134
146
|
const filePath = (0, path_1.join)(basePath, serviceFile.filename);
|
135
147
|
await (0, promises_1.writeFile)(filePath, serviceFile.content);
|
@@ -165,15 +177,19 @@ class StormCodegen {
|
|
165
177
|
});
|
166
178
|
}
|
167
179
|
emitFile(uri, blockName, filename, content, reason = 'File generated') {
|
180
|
+
const basePath = this.getBasePath(uri.fullName);
|
181
|
+
const ref = uri.toNormalizedString();
|
168
182
|
this.out.emit('data', {
|
169
183
|
type: 'FILE',
|
170
184
|
reason,
|
171
185
|
created: Date.now(),
|
172
186
|
payload: {
|
173
187
|
filename: filename,
|
188
|
+
path: (0, path_1.join)(basePath, filename),
|
174
189
|
content: content,
|
175
190
|
blockName,
|
176
|
-
blockRef:
|
191
|
+
blockRef: ref,
|
192
|
+
instanceId: event_parser_1.StormEventParser.toInstanceIdFromRef(ref),
|
177
193
|
},
|
178
194
|
});
|
179
195
|
}
|
@@ -245,7 +261,7 @@ class StormCodegen {
|
|
245
261
|
if (!(await codeGeneratorManager_1.codeGeneratorManager.ensureTarget(yamlContent.spec.target?.kind))) {
|
246
262
|
return;
|
247
263
|
}
|
248
|
-
const basePath =
|
264
|
+
const basePath = this.getBasePath(yamlContent.metadata.name);
|
249
265
|
const codeGenerator = new codegen_1.BlockCodeGenerator(yamlContent);
|
250
266
|
codeGenerator.withOption('AIContext', stormClient_1.STORM_ID);
|
251
267
|
const generatedResult = await codeGenerator.generate();
|
@@ -41,6 +41,10 @@ export interface StormOptions {
|
|
41
41
|
}
|
42
42
|
export declare function resolveOptions(): Promise<StormOptions>;
|
43
43
|
export declare class StormEventParser {
|
44
|
+
static toInstanceId(handle: string, blockName: string): string;
|
45
|
+
static toInstanceIdFromRef(ref: string): string;
|
46
|
+
static toSafeName(name: string): string;
|
47
|
+
static toRef(handle: string, name: string): KapetaURI;
|
44
48
|
private events;
|
45
49
|
private planName;
|
46
50
|
private planDescription;
|
@@ -58,11 +62,7 @@ export declare class StormEventParser {
|
|
58
62
|
getEvents(): StormEvent[];
|
59
63
|
isValid(): boolean;
|
60
64
|
getError(): string;
|
61
|
-
private toInstanceId;
|
62
|
-
private toInstanceIdFromRef;
|
63
65
|
toResult(handle: string): StormDefinitions;
|
64
|
-
private toSafeName;
|
65
|
-
private toRef;
|
66
66
|
toBlockDefinitions(handle: string): {
|
67
67
|
[key: string]: BlockDefinitionInfo;
|
68
68
|
};
|
@@ -105,6 +105,19 @@ async function resolveOptions() {
|
|
105
105
|
}
|
106
106
|
exports.resolveOptions = resolveOptions;
|
107
107
|
class StormEventParser {
|
108
|
+
static toInstanceId(handle, blockName) {
|
109
|
+
const ref = this.toRef(handle, blockName);
|
110
|
+
return this.toInstanceIdFromRef(ref.toNormalizedString());
|
111
|
+
}
|
112
|
+
static toInstanceIdFromRef(ref) {
|
113
|
+
return (0, uuid_1.v5)((0, nodejs_utils_1.normalizeKapetaUri)(ref), uuid_1.v5.URL);
|
114
|
+
}
|
115
|
+
static toSafeName(name) {
|
116
|
+
return name.toLowerCase().replace(/[^0-9a-z-]/gi, '');
|
117
|
+
}
|
118
|
+
static toRef(handle, name) {
|
119
|
+
return (0, nodejs_utils_1.parseKapetaUri)(handle + '/' + this.toSafeName(name) + ':local');
|
120
|
+
}
|
108
121
|
events = [];
|
109
122
|
planName = '';
|
110
123
|
planDescription = '';
|
@@ -126,7 +139,9 @@ class StormEventParser {
|
|
126
139
|
* Builds plan and block definitions - and enriches events with relevant refs and ids
|
127
140
|
*/
|
128
141
|
processEvent(handle, evt) {
|
142
|
+
let blockInfo;
|
129
143
|
this.events.push(evt);
|
144
|
+
console.log('Processing event: %s', evt.type);
|
130
145
|
switch (evt.type) {
|
131
146
|
case 'CREATE_PLAN_PROPERTIES':
|
132
147
|
this.planName = evt.payload.name;
|
@@ -139,8 +154,8 @@ class StormEventParser {
|
|
139
154
|
models: [],
|
140
155
|
types: [],
|
141
156
|
};
|
142
|
-
evt.payload.blockRef =
|
143
|
-
evt.payload.instanceId =
|
157
|
+
evt.payload.blockRef = StormEventParser.toRef(handle, evt.payload.name).toNormalizedString();
|
158
|
+
evt.payload.instanceId = StormEventParser.toInstanceIdFromRef(evt.payload.blockRef);
|
144
159
|
break;
|
145
160
|
case 'PLAN_RETRY':
|
146
161
|
this.reset();
|
@@ -150,23 +165,29 @@ class StormEventParser {
|
|
150
165
|
this.error = evt.payload.error;
|
151
166
|
break;
|
152
167
|
case 'CREATE_API':
|
153
|
-
this.blocks[evt.payload.blockName]
|
154
|
-
|
155
|
-
evt.payload.
|
168
|
+
blockInfo = this.blocks[evt.payload.blockName];
|
169
|
+
blockInfo.apis.push(prettifyKaplang(evt.payload.content));
|
170
|
+
evt.payload.blockRef = StormEventParser.toRef(handle, evt.payload.blockName).toNormalizedString();
|
171
|
+
evt.payload.instanceId = StormEventParser.toInstanceIdFromRef(evt.payload.blockRef);
|
172
|
+
const api = blockInfo.resources.find((r) => r.type == 'API');
|
173
|
+
evt.payload.resourceName = api?.name;
|
174
|
+
break;
|
175
|
+
case 'CREATE_MODEL':
|
176
|
+
blockInfo = this.blocks[evt.payload.blockName];
|
177
|
+
blockInfo.models.push(prettifyKaplang(evt.payload.content));
|
178
|
+
evt.payload.blockRef = StormEventParser.toRef(handle, evt.payload.blockName).toNormalizedString();
|
179
|
+
evt.payload.instanceId = StormEventParser.toInstanceIdFromRef(evt.payload.blockRef);
|
180
|
+
const database = blockInfo.resources.find((r) => r.type == 'DATABASE');
|
181
|
+
evt.payload.resourceName = database?.name;
|
156
182
|
break;
|
157
183
|
case 'CREATE_TYPE':
|
158
184
|
this.blocks[evt.payload.blockName].types.push(prettifyKaplang(evt.payload.content));
|
159
|
-
evt.payload.blockRef =
|
160
|
-
evt.payload.instanceId =
|
161
|
-
break;
|
162
|
-
case 'CREATE_MODEL':
|
163
|
-
this.blocks[evt.payload.blockName].models.push(prettifyKaplang(evt.payload.content));
|
164
|
-
evt.payload.blockRef = this.toRef(handle, evt.payload.blockName).toNormalizedString();
|
165
|
-
evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
|
185
|
+
evt.payload.blockRef = StormEventParser.toRef(handle, evt.payload.blockName).toNormalizedString();
|
186
|
+
evt.payload.instanceId = StormEventParser.toInstanceIdFromRef(evt.payload.blockRef);
|
166
187
|
break;
|
167
188
|
case 'CREATE_CONNECTION':
|
168
|
-
evt.payload.fromBlockId =
|
169
|
-
evt.payload.toBlockId =
|
189
|
+
evt.payload.fromBlockId = StormEventParser.toInstanceId(handle, evt.payload.fromComponent);
|
190
|
+
evt.payload.toBlockId = StormEventParser.toInstanceId(handle, evt.payload.toComponent);
|
170
191
|
this.connections.push(evt.payload);
|
171
192
|
break;
|
172
193
|
default:
|
@@ -188,20 +209,13 @@ class StormEventParser {
|
|
188
209
|
getError() {
|
189
210
|
return this.error;
|
190
211
|
}
|
191
|
-
toInstanceId(handle, blockName) {
|
192
|
-
const ref = this.toRef(handle, blockName);
|
193
|
-
return this.toInstanceIdFromRef(ref.toNormalizedString());
|
194
|
-
}
|
195
|
-
toInstanceIdFromRef(ref) {
|
196
|
-
return (0, uuid_1.v5)((0, nodejs_utils_1.normalizeKapetaUri)(ref), uuid_1.v5.URL);
|
197
|
-
}
|
198
212
|
toResult(handle) {
|
199
|
-
const planRef =
|
213
|
+
const planRef = StormEventParser.toRef(handle, this.planName ?? 'undefined');
|
200
214
|
const blockDefinitions = this.toBlockDefinitions(handle);
|
201
215
|
const refIdMap = {};
|
202
216
|
const blocks = Object.entries(blockDefinitions).map(([ref, block]) => {
|
203
217
|
// Create a deterministic uuid
|
204
|
-
const id =
|
218
|
+
const id = StormEventParser.toInstanceIdFromRef(ref);
|
205
219
|
refIdMap[ref] = id;
|
206
220
|
return {
|
207
221
|
id,
|
@@ -221,8 +235,8 @@ class StormEventParser {
|
|
221
235
|
this.connections
|
222
236
|
.filter((connection) => connection.fromResourceType === 'API' && connection.toResourceType === 'CLIENT')
|
223
237
|
.forEach((apiConnection) => {
|
224
|
-
const apiProviderRef =
|
225
|
-
const clientConsumerRef =
|
238
|
+
const apiProviderRef = StormEventParser.toRef(handle, apiConnection.fromComponent);
|
239
|
+
const clientConsumerRef = StormEventParser.toRef(handle, apiConnection.toComponent);
|
226
240
|
const apiProviderBlock = blockDefinitions[apiProviderRef.toNormalizedString()];
|
227
241
|
if (!apiProviderBlock) {
|
228
242
|
console.warn('API provider not found: %s', apiConnection.fromComponent, apiConnection);
|
@@ -275,8 +289,8 @@ class StormEventParser {
|
|
275
289
|
clientResource.spec.source = apiResource.spec.source;
|
276
290
|
});
|
277
291
|
const connections = this.connections.map((connection) => {
|
278
|
-
const fromRef =
|
279
|
-
const toRef =
|
292
|
+
const fromRef = StormEventParser.toRef(handle, connection.fromComponent);
|
293
|
+
const toRef = StormEventParser.toRef(handle, connection.toComponent);
|
280
294
|
return {
|
281
295
|
port: {
|
282
296
|
type: this.toPortType(connection.fromResourceType),
|
@@ -309,16 +323,10 @@ class StormEventParser {
|
|
309
323
|
blocks: Object.values(blockDefinitions),
|
310
324
|
};
|
311
325
|
}
|
312
|
-
toSafeName(name) {
|
313
|
-
return name.toLowerCase().replace(/[^0-9a-z-]/gi, '');
|
314
|
-
}
|
315
|
-
toRef(handle, name) {
|
316
|
-
return (0, nodejs_utils_1.parseKapetaUri)(handle + '/' + this.toSafeName(name) + ':local');
|
317
|
-
}
|
318
326
|
toBlockDefinitions(handle) {
|
319
327
|
const result = {};
|
320
328
|
Object.entries(this.blocks).forEach(([, blockInfo]) => {
|
321
|
-
const blockRef =
|
329
|
+
const blockRef = StormEventParser.toRef(handle, blockInfo.name);
|
322
330
|
const blockDefinitionInfo = {
|
323
331
|
uri: blockRef,
|
324
332
|
aiName: blockInfo.name,
|
@@ -522,7 +530,7 @@ class StormEventParser {
|
|
522
530
|
if (connection.fromResourceType !== 'API') {
|
523
531
|
return;
|
524
532
|
}
|
525
|
-
const fromRef =
|
533
|
+
const fromRef = StormEventParser.toRef(handle, connection.fromComponent);
|
526
534
|
const apiProviderBlock = blockDefinitions[fromRef.toNormalizedString()];
|
527
535
|
if (!apiProviderBlock) {
|
528
536
|
console.warn('Provider block not found: %s', connection.fromComponent, connection);
|
@@ -577,8 +585,8 @@ class StormEventParser {
|
|
577
585
|
}
|
578
586
|
let options = {};
|
579
587
|
if (kind.includes('java')) {
|
580
|
-
const groupId = `ai.${
|
581
|
-
const artifactId =
|
588
|
+
const groupId = `ai.${StormEventParser.toSafeName(handle)}`;
|
589
|
+
const artifactId = StormEventParser.toSafeName(this.planName);
|
582
590
|
options = {
|
583
591
|
groupId,
|
584
592
|
artifactId,
|
@@ -70,7 +70,7 @@ export interface StormEventPlanRetry {
|
|
70
70
|
};
|
71
71
|
}
|
72
72
|
export interface StormEventCreateDSL {
|
73
|
-
type: '
|
73
|
+
type: 'CREATE_TYPE';
|
74
74
|
reason: string;
|
75
75
|
created: number;
|
76
76
|
payload: {
|
@@ -80,6 +80,18 @@ export interface StormEventCreateDSL {
|
|
80
80
|
instanceId?: string;
|
81
81
|
};
|
82
82
|
}
|
83
|
+
export interface StormEventCreateDSLResource extends Omit<StormEventCreateDSL, 'type'> {
|
84
|
+
type: 'CREATE_API' | 'CREATE_MODEL';
|
85
|
+
reason: string;
|
86
|
+
created: number;
|
87
|
+
payload: {
|
88
|
+
blockName: string;
|
89
|
+
content: string;
|
90
|
+
blockRef?: string;
|
91
|
+
instanceId?: string;
|
92
|
+
resourceName?: string;
|
93
|
+
};
|
94
|
+
}
|
83
95
|
export interface StormEventError {
|
84
96
|
type: 'INVALID_RESPONSE' | 'ERROR_INTERNAL';
|
85
97
|
reason: string;
|
@@ -100,6 +112,8 @@ export interface StormEventScreen {
|
|
100
112
|
created: number;
|
101
113
|
payload: {
|
102
114
|
blockName: string;
|
115
|
+
blockRef?: string;
|
116
|
+
instanceId?: string;
|
103
117
|
name: string;
|
104
118
|
template: string;
|
105
119
|
description: string;
|
@@ -122,9 +136,11 @@ export interface StormEventFile {
|
|
122
136
|
created: number;
|
123
137
|
payload: {
|
124
138
|
filename: string;
|
139
|
+
path: string;
|
125
140
|
content: string;
|
126
141
|
blockName: string;
|
127
142
|
blockRef: string;
|
143
|
+
instanceId: string;
|
128
144
|
};
|
129
145
|
}
|
130
146
|
export interface StormEventDone {
|
@@ -137,4 +153,4 @@ export interface StormEventDefinitionChange {
|
|
137
153
|
created: number;
|
138
154
|
payload: StormDefinitions;
|
139
155
|
}
|
140
|
-
export type StormEvent = StormEventCreateBlock | StormEventCreateConnection | StormEventCreatePlanProperties | StormEventInvalidResponse | StormEventPlanRetry | StormEventCreateDSL | StormEventError | StormEventScreen | StormEventScreenCandidate | StormEventFile | StormEventDone | StormEventDefinitionChange;
|
156
|
+
export type StormEvent = StormEventCreateBlock | StormEventCreateConnection | StormEventCreatePlanProperties | StormEventInvalidResponse | StormEventPlanRetry | StormEventCreateDSL | StormEventCreateDSLResource | StormEventError | StormEventScreen | StormEventScreenCandidate | StormEventFile | StormEventDone | StormEventDefinitionChange;
|
@@ -10,12 +10,14 @@ export declare class StormCodegen {
|
|
10
10
|
private readonly blocks;
|
11
11
|
private readonly out;
|
12
12
|
private readonly events;
|
13
|
+
private readonly tmpDir;
|
13
14
|
constructor(userPrompt: string, blocks: BlockDefinitionInfo[], events: StormEvent[]);
|
14
15
|
process(): Promise<void>;
|
15
16
|
getStream(): StormStream;
|
16
17
|
private handleTemplateFileOutput;
|
17
18
|
private handleUiOutput;
|
18
19
|
private handleFileOutput;
|
20
|
+
private getBasePath;
|
19
21
|
/**
|
20
22
|
* Generates the code for a block and sends it to the AI
|
21
23
|
*/
|
@@ -34,6 +34,7 @@ exports.StormCodegen = void 0;
|
|
34
34
|
const codegen_1 = require("@kapeta/codegen");
|
35
35
|
const codeGeneratorManager_1 = require("../codeGeneratorManager");
|
36
36
|
const stormClient_1 = require("./stormClient");
|
37
|
+
const event_parser_1 = require("./event-parser");
|
37
38
|
const stream_1 = require("./stream");
|
38
39
|
const promises_1 = require("fs/promises");
|
39
40
|
const path_1 = __importStar(require("path"));
|
@@ -43,10 +44,12 @@ class StormCodegen {
|
|
43
44
|
blocks;
|
44
45
|
out = new stream_1.StormStream();
|
45
46
|
events;
|
47
|
+
tmpDir;
|
46
48
|
constructor(userPrompt, blocks, events) {
|
47
49
|
this.userPrompt = userPrompt;
|
48
50
|
this.blocks = blocks;
|
49
51
|
this.events = events;
|
52
|
+
this.tmpDir = node_os_1.default.tmpdir();
|
50
53
|
}
|
51
54
|
async process() {
|
52
55
|
for (const block of this.blocks) {
|
@@ -68,6 +71,7 @@ class StormCodegen {
|
|
68
71
|
handleUiOutput(blockUri, aiName, data) {
|
69
72
|
switch (data.type) {
|
70
73
|
case 'SCREEN':
|
74
|
+
const ref = blockUri.toNormalizedString();
|
71
75
|
this.out.emit('data', {
|
72
76
|
type: 'SCREEN',
|
73
77
|
reason: data.reason,
|
@@ -75,6 +79,8 @@ class StormCodegen {
|
|
75
79
|
payload: {
|
76
80
|
...data.payload,
|
77
81
|
blockName: aiName,
|
82
|
+
blockRef: ref,
|
83
|
+
instanceId: event_parser_1.StormEventParser.toInstanceIdFromRef(ref),
|
78
84
|
},
|
79
85
|
});
|
80
86
|
case 'FILE':
|
@@ -84,6 +90,7 @@ class StormCodegen {
|
|
84
90
|
handleFileOutput(blockUri, aiName, data) {
|
85
91
|
switch (data.type) {
|
86
92
|
case 'FILE':
|
93
|
+
const ref = blockUri.toNormalizedString();
|
87
94
|
this.emitFile(blockUri, aiName, data.payload.filename, data.payload.content, data.reason);
|
88
95
|
return {
|
89
96
|
type: 'FILE',
|
@@ -91,10 +98,15 @@ class StormCodegen {
|
|
91
98
|
payload: {
|
92
99
|
filename: data.payload.filename,
|
93
100
|
content: data.payload.content,
|
101
|
+
blockRef: ref,
|
102
|
+
instanceId: event_parser_1.StormEventParser.toInstanceIdFromRef(ref),
|
94
103
|
},
|
95
104
|
};
|
96
105
|
}
|
97
106
|
}
|
107
|
+
getBasePath(blockName) {
|
108
|
+
return path_1.default.join(this.tmpDir, blockName);
|
109
|
+
}
|
98
110
|
/**
|
99
111
|
* Generates the code for a block and sends it to the AI
|
100
112
|
*/
|
@@ -129,7 +141,7 @@ class StormCodegen {
|
|
129
141
|
if (serviceFiles.length > 0) {
|
130
142
|
await this.processTemplates(block.uri, block.aiName, stormClient_1.stormClient.createServiceImplementation.bind(stormClient_1.stormClient), serviceFiles, contextFiles);
|
131
143
|
}
|
132
|
-
const basePath =
|
144
|
+
const basePath = this.getBasePath(block.content.metadata.name);
|
133
145
|
for (const serviceFile of serviceFiles) {
|
134
146
|
const filePath = (0, path_1.join)(basePath, serviceFile.filename);
|
135
147
|
await (0, promises_1.writeFile)(filePath, serviceFile.content);
|
@@ -165,15 +177,19 @@ class StormCodegen {
|
|
165
177
|
});
|
166
178
|
}
|
167
179
|
emitFile(uri, blockName, filename, content, reason = 'File generated') {
|
180
|
+
const basePath = this.getBasePath(uri.fullName);
|
181
|
+
const ref = uri.toNormalizedString();
|
168
182
|
this.out.emit('data', {
|
169
183
|
type: 'FILE',
|
170
184
|
reason,
|
171
185
|
created: Date.now(),
|
172
186
|
payload: {
|
173
187
|
filename: filename,
|
188
|
+
path: (0, path_1.join)(basePath, filename),
|
174
189
|
content: content,
|
175
190
|
blockName,
|
176
|
-
blockRef:
|
191
|
+
blockRef: ref,
|
192
|
+
instanceId: event_parser_1.StormEventParser.toInstanceIdFromRef(ref),
|
177
193
|
},
|
178
194
|
});
|
179
195
|
}
|
@@ -245,7 +261,7 @@ class StormCodegen {
|
|
245
261
|
if (!(await codeGeneratorManager_1.codeGeneratorManager.ensureTarget(yamlContent.spec.target?.kind))) {
|
246
262
|
return;
|
247
263
|
}
|
248
|
-
const basePath =
|
264
|
+
const basePath = this.getBasePath(yamlContent.metadata.name);
|
249
265
|
const codeGenerator = new codegen_1.BlockCodeGenerator(yamlContent);
|
250
266
|
codeGenerator.withOption('AIContext', stormClient_1.STORM_ID);
|
251
267
|
const generatedResult = await codeGenerator.generate();
|
@@ -41,6 +41,10 @@ export interface StormOptions {
|
|
41
41
|
}
|
42
42
|
export declare function resolveOptions(): Promise<StormOptions>;
|
43
43
|
export declare class StormEventParser {
|
44
|
+
static toInstanceId(handle: string, blockName: string): string;
|
45
|
+
static toInstanceIdFromRef(ref: string): string;
|
46
|
+
static toSafeName(name: string): string;
|
47
|
+
static toRef(handle: string, name: string): KapetaURI;
|
44
48
|
private events;
|
45
49
|
private planName;
|
46
50
|
private planDescription;
|
@@ -58,11 +62,7 @@ export declare class StormEventParser {
|
|
58
62
|
getEvents(): StormEvent[];
|
59
63
|
isValid(): boolean;
|
60
64
|
getError(): string;
|
61
|
-
private toInstanceId;
|
62
|
-
private toInstanceIdFromRef;
|
63
65
|
toResult(handle: string): StormDefinitions;
|
64
|
-
private toSafeName;
|
65
|
-
private toRef;
|
66
66
|
toBlockDefinitions(handle: string): {
|
67
67
|
[key: string]: BlockDefinitionInfo;
|
68
68
|
};
|
@@ -105,6 +105,19 @@ async function resolveOptions() {
|
|
105
105
|
}
|
106
106
|
exports.resolveOptions = resolveOptions;
|
107
107
|
class StormEventParser {
|
108
|
+
static toInstanceId(handle, blockName) {
|
109
|
+
const ref = this.toRef(handle, blockName);
|
110
|
+
return this.toInstanceIdFromRef(ref.toNormalizedString());
|
111
|
+
}
|
112
|
+
static toInstanceIdFromRef(ref) {
|
113
|
+
return (0, uuid_1.v5)((0, nodejs_utils_1.normalizeKapetaUri)(ref), uuid_1.v5.URL);
|
114
|
+
}
|
115
|
+
static toSafeName(name) {
|
116
|
+
return name.toLowerCase().replace(/[^0-9a-z-]/gi, '');
|
117
|
+
}
|
118
|
+
static toRef(handle, name) {
|
119
|
+
return (0, nodejs_utils_1.parseKapetaUri)(handle + '/' + this.toSafeName(name) + ':local');
|
120
|
+
}
|
108
121
|
events = [];
|
109
122
|
planName = '';
|
110
123
|
planDescription = '';
|
@@ -126,7 +139,9 @@ class StormEventParser {
|
|
126
139
|
* Builds plan and block definitions - and enriches events with relevant refs and ids
|
127
140
|
*/
|
128
141
|
processEvent(handle, evt) {
|
142
|
+
let blockInfo;
|
129
143
|
this.events.push(evt);
|
144
|
+
console.log('Processing event: %s', evt.type);
|
130
145
|
switch (evt.type) {
|
131
146
|
case 'CREATE_PLAN_PROPERTIES':
|
132
147
|
this.planName = evt.payload.name;
|
@@ -139,8 +154,8 @@ class StormEventParser {
|
|
139
154
|
models: [],
|
140
155
|
types: [],
|
141
156
|
};
|
142
|
-
evt.payload.blockRef =
|
143
|
-
evt.payload.instanceId =
|
157
|
+
evt.payload.blockRef = StormEventParser.toRef(handle, evt.payload.name).toNormalizedString();
|
158
|
+
evt.payload.instanceId = StormEventParser.toInstanceIdFromRef(evt.payload.blockRef);
|
144
159
|
break;
|
145
160
|
case 'PLAN_RETRY':
|
146
161
|
this.reset();
|
@@ -150,23 +165,29 @@ class StormEventParser {
|
|
150
165
|
this.error = evt.payload.error;
|
151
166
|
break;
|
152
167
|
case 'CREATE_API':
|
153
|
-
this.blocks[evt.payload.blockName]
|
154
|
-
|
155
|
-
evt.payload.
|
168
|
+
blockInfo = this.blocks[evt.payload.blockName];
|
169
|
+
blockInfo.apis.push(prettifyKaplang(evt.payload.content));
|
170
|
+
evt.payload.blockRef = StormEventParser.toRef(handle, evt.payload.blockName).toNormalizedString();
|
171
|
+
evt.payload.instanceId = StormEventParser.toInstanceIdFromRef(evt.payload.blockRef);
|
172
|
+
const api = blockInfo.resources.find((r) => r.type == 'API');
|
173
|
+
evt.payload.resourceName = api?.name;
|
174
|
+
break;
|
175
|
+
case 'CREATE_MODEL':
|
176
|
+
blockInfo = this.blocks[evt.payload.blockName];
|
177
|
+
blockInfo.models.push(prettifyKaplang(evt.payload.content));
|
178
|
+
evt.payload.blockRef = StormEventParser.toRef(handle, evt.payload.blockName).toNormalizedString();
|
179
|
+
evt.payload.instanceId = StormEventParser.toInstanceIdFromRef(evt.payload.blockRef);
|
180
|
+
const database = blockInfo.resources.find((r) => r.type == 'DATABASE');
|
181
|
+
evt.payload.resourceName = database?.name;
|
156
182
|
break;
|
157
183
|
case 'CREATE_TYPE':
|
158
184
|
this.blocks[evt.payload.blockName].types.push(prettifyKaplang(evt.payload.content));
|
159
|
-
evt.payload.blockRef =
|
160
|
-
evt.payload.instanceId =
|
161
|
-
break;
|
162
|
-
case 'CREATE_MODEL':
|
163
|
-
this.blocks[evt.payload.blockName].models.push(prettifyKaplang(evt.payload.content));
|
164
|
-
evt.payload.blockRef = this.toRef(handle, evt.payload.blockName).toNormalizedString();
|
165
|
-
evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
|
185
|
+
evt.payload.blockRef = StormEventParser.toRef(handle, evt.payload.blockName).toNormalizedString();
|
186
|
+
evt.payload.instanceId = StormEventParser.toInstanceIdFromRef(evt.payload.blockRef);
|
166
187
|
break;
|
167
188
|
case 'CREATE_CONNECTION':
|
168
|
-
evt.payload.fromBlockId =
|
169
|
-
evt.payload.toBlockId =
|
189
|
+
evt.payload.fromBlockId = StormEventParser.toInstanceId(handle, evt.payload.fromComponent);
|
190
|
+
evt.payload.toBlockId = StormEventParser.toInstanceId(handle, evt.payload.toComponent);
|
170
191
|
this.connections.push(evt.payload);
|
171
192
|
break;
|
172
193
|
default:
|
@@ -188,20 +209,13 @@ class StormEventParser {
|
|
188
209
|
getError() {
|
189
210
|
return this.error;
|
190
211
|
}
|
191
|
-
toInstanceId(handle, blockName) {
|
192
|
-
const ref = this.toRef(handle, blockName);
|
193
|
-
return this.toInstanceIdFromRef(ref.toNormalizedString());
|
194
|
-
}
|
195
|
-
toInstanceIdFromRef(ref) {
|
196
|
-
return (0, uuid_1.v5)((0, nodejs_utils_1.normalizeKapetaUri)(ref), uuid_1.v5.URL);
|
197
|
-
}
|
198
212
|
toResult(handle) {
|
199
|
-
const planRef =
|
213
|
+
const planRef = StormEventParser.toRef(handle, this.planName ?? 'undefined');
|
200
214
|
const blockDefinitions = this.toBlockDefinitions(handle);
|
201
215
|
const refIdMap = {};
|
202
216
|
const blocks = Object.entries(blockDefinitions).map(([ref, block]) => {
|
203
217
|
// Create a deterministic uuid
|
204
|
-
const id =
|
218
|
+
const id = StormEventParser.toInstanceIdFromRef(ref);
|
205
219
|
refIdMap[ref] = id;
|
206
220
|
return {
|
207
221
|
id,
|
@@ -221,8 +235,8 @@ class StormEventParser {
|
|
221
235
|
this.connections
|
222
236
|
.filter((connection) => connection.fromResourceType === 'API' && connection.toResourceType === 'CLIENT')
|
223
237
|
.forEach((apiConnection) => {
|
224
|
-
const apiProviderRef =
|
225
|
-
const clientConsumerRef =
|
238
|
+
const apiProviderRef = StormEventParser.toRef(handle, apiConnection.fromComponent);
|
239
|
+
const clientConsumerRef = StormEventParser.toRef(handle, apiConnection.toComponent);
|
226
240
|
const apiProviderBlock = blockDefinitions[apiProviderRef.toNormalizedString()];
|
227
241
|
if (!apiProviderBlock) {
|
228
242
|
console.warn('API provider not found: %s', apiConnection.fromComponent, apiConnection);
|
@@ -275,8 +289,8 @@ class StormEventParser {
|
|
275
289
|
clientResource.spec.source = apiResource.spec.source;
|
276
290
|
});
|
277
291
|
const connections = this.connections.map((connection) => {
|
278
|
-
const fromRef =
|
279
|
-
const toRef =
|
292
|
+
const fromRef = StormEventParser.toRef(handle, connection.fromComponent);
|
293
|
+
const toRef = StormEventParser.toRef(handle, connection.toComponent);
|
280
294
|
return {
|
281
295
|
port: {
|
282
296
|
type: this.toPortType(connection.fromResourceType),
|
@@ -309,16 +323,10 @@ class StormEventParser {
|
|
309
323
|
blocks: Object.values(blockDefinitions),
|
310
324
|
};
|
311
325
|
}
|
312
|
-
toSafeName(name) {
|
313
|
-
return name.toLowerCase().replace(/[^0-9a-z-]/gi, '');
|
314
|
-
}
|
315
|
-
toRef(handle, name) {
|
316
|
-
return (0, nodejs_utils_1.parseKapetaUri)(handle + '/' + this.toSafeName(name) + ':local');
|
317
|
-
}
|
318
326
|
toBlockDefinitions(handle) {
|
319
327
|
const result = {};
|
320
328
|
Object.entries(this.blocks).forEach(([, blockInfo]) => {
|
321
|
-
const blockRef =
|
329
|
+
const blockRef = StormEventParser.toRef(handle, blockInfo.name);
|
322
330
|
const blockDefinitionInfo = {
|
323
331
|
uri: blockRef,
|
324
332
|
aiName: blockInfo.name,
|
@@ -522,7 +530,7 @@ class StormEventParser {
|
|
522
530
|
if (connection.fromResourceType !== 'API') {
|
523
531
|
return;
|
524
532
|
}
|
525
|
-
const fromRef =
|
533
|
+
const fromRef = StormEventParser.toRef(handle, connection.fromComponent);
|
526
534
|
const apiProviderBlock = blockDefinitions[fromRef.toNormalizedString()];
|
527
535
|
if (!apiProviderBlock) {
|
528
536
|
console.warn('Provider block not found: %s', connection.fromComponent, connection);
|
@@ -577,8 +585,8 @@ class StormEventParser {
|
|
577
585
|
}
|
578
586
|
let options = {};
|
579
587
|
if (kind.includes('java')) {
|
580
|
-
const groupId = `ai.${
|
581
|
-
const artifactId =
|
588
|
+
const groupId = `ai.${StormEventParser.toSafeName(handle)}`;
|
589
|
+
const artifactId = StormEventParser.toSafeName(this.planName);
|
582
590
|
options = {
|
583
591
|
groupId,
|
584
592
|
artifactId,
|
@@ -70,7 +70,7 @@ export interface StormEventPlanRetry {
|
|
70
70
|
};
|
71
71
|
}
|
72
72
|
export interface StormEventCreateDSL {
|
73
|
-
type: '
|
73
|
+
type: 'CREATE_TYPE';
|
74
74
|
reason: string;
|
75
75
|
created: number;
|
76
76
|
payload: {
|
@@ -80,6 +80,18 @@ export interface StormEventCreateDSL {
|
|
80
80
|
instanceId?: string;
|
81
81
|
};
|
82
82
|
}
|
83
|
+
export interface StormEventCreateDSLResource extends Omit<StormEventCreateDSL, 'type'> {
|
84
|
+
type: 'CREATE_API' | 'CREATE_MODEL';
|
85
|
+
reason: string;
|
86
|
+
created: number;
|
87
|
+
payload: {
|
88
|
+
blockName: string;
|
89
|
+
content: string;
|
90
|
+
blockRef?: string;
|
91
|
+
instanceId?: string;
|
92
|
+
resourceName?: string;
|
93
|
+
};
|
94
|
+
}
|
83
95
|
export interface StormEventError {
|
84
96
|
type: 'INVALID_RESPONSE' | 'ERROR_INTERNAL';
|
85
97
|
reason: string;
|
@@ -100,6 +112,8 @@ export interface StormEventScreen {
|
|
100
112
|
created: number;
|
101
113
|
payload: {
|
102
114
|
blockName: string;
|
115
|
+
blockRef?: string;
|
116
|
+
instanceId?: string;
|
103
117
|
name: string;
|
104
118
|
template: string;
|
105
119
|
description: string;
|
@@ -122,9 +136,11 @@ export interface StormEventFile {
|
|
122
136
|
created: number;
|
123
137
|
payload: {
|
124
138
|
filename: string;
|
139
|
+
path: string;
|
125
140
|
content: string;
|
126
141
|
blockName: string;
|
127
142
|
blockRef: string;
|
143
|
+
instanceId: string;
|
128
144
|
};
|
129
145
|
}
|
130
146
|
export interface StormEventDone {
|
@@ -137,4 +153,4 @@ export interface StormEventDefinitionChange {
|
|
137
153
|
created: number;
|
138
154
|
payload: StormDefinitions;
|
139
155
|
}
|
140
|
-
export type StormEvent = StormEventCreateBlock | StormEventCreateConnection | StormEventCreatePlanProperties | StormEventInvalidResponse | StormEventPlanRetry | StormEventCreateDSL | StormEventError | StormEventScreen | StormEventScreenCandidate | StormEventFile | StormEventDone | StormEventDefinitionChange;
|
156
|
+
export type StormEvent = StormEventCreateBlock | StormEventCreateConnection | StormEventCreatePlanProperties | StormEventInvalidResponse | StormEventPlanRetry | StormEventCreateDSL | StormEventCreateDSLResource | StormEventError | StormEventScreen | StormEventScreenCandidate | StormEventFile | StormEventDone | StormEventDefinitionChange;
|
package/package.json
CHANGED
package/src/storm/codegen.ts
CHANGED
@@ -9,7 +9,7 @@ import { BlockDefinition } from '@kapeta/schemas';
|
|
9
9
|
import { codeGeneratorManager } from '../codeGeneratorManager';
|
10
10
|
import { STORM_ID, stormClient } from './stormClient';
|
11
11
|
import { StormEvent, StormEventFile } from './events';
|
12
|
-
import { BlockDefinitionInfo } from './event-parser';
|
12
|
+
import { BlockDefinitionInfo, StormEventParser } from './event-parser';
|
13
13
|
import { ConversationItem, StormFileImplementationPrompt, StormFileInfo, StormStream } from './stream';
|
14
14
|
import { KapetaURI } from '@kapeta/nodejs-utils';
|
15
15
|
import { writeFile } from 'fs/promises';
|
@@ -23,11 +23,13 @@ export class StormCodegen {
|
|
23
23
|
private readonly blocks: BlockDefinitionInfo[];
|
24
24
|
private readonly out = new StormStream();
|
25
25
|
private readonly events: StormEvent[];
|
26
|
+
private readonly tmpDir: string;
|
26
27
|
|
27
28
|
constructor(userPrompt: string, blocks: BlockDefinitionInfo[], events: StormEvent[]) {
|
28
29
|
this.userPrompt = userPrompt;
|
29
30
|
this.blocks = blocks;
|
30
31
|
this.events = events;
|
32
|
+
this.tmpDir = os.tmpdir();
|
31
33
|
}
|
32
34
|
|
33
35
|
public async process() {
|
@@ -54,6 +56,7 @@ export class StormCodegen {
|
|
54
56
|
private handleUiOutput(blockUri: KapetaURI, aiName: string, data: StormEvent) {
|
55
57
|
switch (data.type) {
|
56
58
|
case 'SCREEN':
|
59
|
+
const ref = blockUri.toNormalizedString();
|
57
60
|
this.out.emit('data', {
|
58
61
|
type: 'SCREEN',
|
59
62
|
reason: data.reason,
|
@@ -61,6 +64,8 @@ export class StormCodegen {
|
|
61
64
|
payload: {
|
62
65
|
...data.payload,
|
63
66
|
blockName: aiName,
|
67
|
+
blockRef: ref,
|
68
|
+
instanceId: StormEventParser.toInstanceIdFromRef(ref),
|
64
69
|
},
|
65
70
|
});
|
66
71
|
case 'FILE':
|
@@ -71,6 +76,7 @@ export class StormCodegen {
|
|
71
76
|
private handleFileOutput(blockUri: KapetaURI, aiName: string, data: StormEvent) {
|
72
77
|
switch (data.type) {
|
73
78
|
case 'FILE':
|
79
|
+
const ref = blockUri.toNormalizedString();
|
74
80
|
this.emitFile(blockUri, aiName, data.payload.filename, data.payload.content, data.reason);
|
75
81
|
return {
|
76
82
|
type: 'FILE',
|
@@ -78,11 +84,17 @@ export class StormCodegen {
|
|
78
84
|
payload: {
|
79
85
|
filename: data.payload.filename,
|
80
86
|
content: data.payload.content,
|
87
|
+
blockRef: ref,
|
88
|
+
instanceId: StormEventParser.toInstanceIdFromRef(ref),
|
81
89
|
},
|
82
90
|
} as StormEventFile;
|
83
91
|
}
|
84
92
|
}
|
85
93
|
|
94
|
+
private getBasePath(blockName: string) {
|
95
|
+
return path.join(this.tmpDir, blockName);
|
96
|
+
}
|
97
|
+
|
86
98
|
/**
|
87
99
|
* Generates the code for a block and sends it to the AI
|
88
100
|
*/
|
@@ -135,7 +147,7 @@ export class StormCodegen {
|
|
135
147
|
);
|
136
148
|
}
|
137
149
|
|
138
|
-
const basePath =
|
150
|
+
const basePath = this.getBasePath(block.content.metadata.name);
|
139
151
|
|
140
152
|
for (const serviceFile of serviceFiles) {
|
141
153
|
const filePath = join(basePath, serviceFile.filename);
|
@@ -185,15 +197,19 @@ export class StormCodegen {
|
|
185
197
|
content: string,
|
186
198
|
reason: string = 'File generated'
|
187
199
|
) {
|
200
|
+
const basePath = this.getBasePath(uri.fullName);
|
201
|
+
const ref = uri.toNormalizedString();
|
188
202
|
this.out.emit('data', {
|
189
203
|
type: 'FILE',
|
190
204
|
reason,
|
191
205
|
created: Date.now(),
|
192
206
|
payload: {
|
193
207
|
filename: filename,
|
208
|
+
path: join(basePath, filename),
|
194
209
|
content: content,
|
195
210
|
blockName,
|
196
|
-
blockRef:
|
211
|
+
blockRef: ref,
|
212
|
+
instanceId: StormEventParser.toInstanceIdFromRef(ref),
|
197
213
|
},
|
198
214
|
} satisfies StormEventFile);
|
199
215
|
}
|
@@ -283,7 +299,7 @@ export class StormCodegen {
|
|
283
299
|
if (!(await codeGeneratorManager.ensureTarget(yamlContent.spec.target?.kind))) {
|
284
300
|
return;
|
285
301
|
}
|
286
|
-
const basePath =
|
302
|
+
const basePath = this.getBasePath(yamlContent.metadata.name);
|
287
303
|
|
288
304
|
const codeGenerator = new BlockCodeGenerator(yamlContent as BlockDefinition);
|
289
305
|
codeGenerator.withOption('AIContext', STORM_ID);
|
@@ -201,6 +201,23 @@ export async function resolveOptions(): Promise<StormOptions> {
|
|
201
201
|
}
|
202
202
|
|
203
203
|
export class StormEventParser {
|
204
|
+
public static toInstanceId(handle: string, blockName: string) {
|
205
|
+
const ref = this.toRef(handle, blockName);
|
206
|
+
return this.toInstanceIdFromRef(ref.toNormalizedString());
|
207
|
+
}
|
208
|
+
|
209
|
+
public static toInstanceIdFromRef(ref: string) {
|
210
|
+
return uuid(normalizeKapetaUri(ref), uuid.URL);
|
211
|
+
}
|
212
|
+
|
213
|
+
public static toSafeName(name: string): string {
|
214
|
+
return name.toLowerCase().replace(/[^0-9a-z-]/gi, '');
|
215
|
+
}
|
216
|
+
|
217
|
+
public static toRef(handle: string, name: string) {
|
218
|
+
return parseKapetaUri(handle + '/' + this.toSafeName(name) + ':local');
|
219
|
+
}
|
220
|
+
|
204
221
|
private events: StormEvent[] = [];
|
205
222
|
private planName: string = '';
|
206
223
|
private planDescription: string = '';
|
@@ -225,7 +242,9 @@ export class StormEventParser {
|
|
225
242
|
* Builds plan and block definitions - and enriches events with relevant refs and ids
|
226
243
|
*/
|
227
244
|
public processEvent(handle: string, evt: StormEvent): StormDefinitions {
|
245
|
+
let blockInfo;
|
228
246
|
this.events.push(evt);
|
247
|
+
console.log('Processing event: %s', evt.type);
|
229
248
|
switch (evt.type) {
|
230
249
|
case 'CREATE_PLAN_PROPERTIES':
|
231
250
|
this.planName = evt.payload.name;
|
@@ -238,8 +257,8 @@ export class StormEventParser {
|
|
238
257
|
models: [],
|
239
258
|
types: [],
|
240
259
|
};
|
241
|
-
evt.payload.blockRef =
|
242
|
-
evt.payload.instanceId =
|
260
|
+
evt.payload.blockRef = StormEventParser.toRef(handle, evt.payload.name).toNormalizedString();
|
261
|
+
evt.payload.instanceId = StormEventParser.toInstanceIdFromRef(evt.payload.blockRef);
|
243
262
|
break;
|
244
263
|
case 'PLAN_RETRY':
|
245
264
|
this.reset();
|
@@ -249,23 +268,32 @@ export class StormEventParser {
|
|
249
268
|
this.error = evt.payload.error;
|
250
269
|
break;
|
251
270
|
case 'CREATE_API':
|
252
|
-
this.blocks[evt.payload.blockName]
|
253
|
-
|
254
|
-
evt.payload.
|
271
|
+
blockInfo = this.blocks[evt.payload.blockName];
|
272
|
+
blockInfo.apis.push(prettifyKaplang(evt.payload.content));
|
273
|
+
evt.payload.blockRef = StormEventParser.toRef(handle, evt.payload.blockName).toNormalizedString();
|
274
|
+
evt.payload.instanceId = StormEventParser.toInstanceIdFromRef(evt.payload.blockRef);
|
275
|
+
|
276
|
+
const api = blockInfo.resources.find((r) => r.type == 'API');
|
277
|
+
evt.payload.resourceName = api?.name;
|
278
|
+
break;
|
279
|
+
case 'CREATE_MODEL':
|
280
|
+
blockInfo = this.blocks[evt.payload.blockName];
|
281
|
+
blockInfo.models.push(prettifyKaplang(evt.payload.content));
|
282
|
+
evt.payload.blockRef = StormEventParser.toRef(handle, evt.payload.blockName).toNormalizedString();
|
283
|
+
evt.payload.instanceId = StormEventParser.toInstanceIdFromRef(evt.payload.blockRef);
|
284
|
+
|
285
|
+
const database = blockInfo.resources.find((r) => r.type == 'DATABASE');
|
286
|
+
evt.payload.resourceName = database?.name;
|
255
287
|
break;
|
288
|
+
|
256
289
|
case 'CREATE_TYPE':
|
257
290
|
this.blocks[evt.payload.blockName].types.push(prettifyKaplang(evt.payload.content));
|
258
|
-
evt.payload.blockRef =
|
259
|
-
evt.payload.instanceId =
|
260
|
-
break;
|
261
|
-
case 'CREATE_MODEL':
|
262
|
-
this.blocks[evt.payload.blockName].models.push(prettifyKaplang(evt.payload.content));
|
263
|
-
evt.payload.blockRef = this.toRef(handle, evt.payload.blockName).toNormalizedString();
|
264
|
-
evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
|
291
|
+
evt.payload.blockRef = StormEventParser.toRef(handle, evt.payload.blockName).toNormalizedString();
|
292
|
+
evt.payload.instanceId = StormEventParser.toInstanceIdFromRef(evt.payload.blockRef);
|
265
293
|
break;
|
266
294
|
case 'CREATE_CONNECTION':
|
267
|
-
evt.payload.fromBlockId =
|
268
|
-
evt.payload.toBlockId =
|
295
|
+
evt.payload.fromBlockId = StormEventParser.toInstanceId(handle, evt.payload.fromComponent);
|
296
|
+
evt.payload.toBlockId = StormEventParser.toInstanceId(handle, evt.payload.toComponent);
|
269
297
|
this.connections.push(evt.payload);
|
270
298
|
break;
|
271
299
|
|
@@ -293,22 +321,13 @@ export class StormEventParser {
|
|
293
321
|
return this.error;
|
294
322
|
}
|
295
323
|
|
296
|
-
private toInstanceId(handle: string, blockName: string) {
|
297
|
-
const ref = this.toRef(handle, blockName);
|
298
|
-
return this.toInstanceIdFromRef(ref.toNormalizedString());
|
299
|
-
}
|
300
|
-
|
301
|
-
private toInstanceIdFromRef(ref: string) {
|
302
|
-
return uuid(normalizeKapetaUri(ref), uuid.URL);
|
303
|
-
}
|
304
|
-
|
305
324
|
public toResult(handle: string): StormDefinitions {
|
306
|
-
const planRef =
|
325
|
+
const planRef = StormEventParser.toRef(handle, this.planName ?? 'undefined');
|
307
326
|
const blockDefinitions = this.toBlockDefinitions(handle);
|
308
327
|
const refIdMap: { [key: string]: string } = {};
|
309
328
|
const blocks = Object.entries(blockDefinitions).map(([ref, block]) => {
|
310
329
|
// Create a deterministic uuid
|
311
|
-
const id =
|
330
|
+
const id = StormEventParser.toInstanceIdFromRef(ref);
|
312
331
|
refIdMap[ref] = id;
|
313
332
|
return {
|
314
333
|
id,
|
@@ -329,8 +348,8 @@ export class StormEventParser {
|
|
329
348
|
this.connections
|
330
349
|
.filter((connection) => connection.fromResourceType === 'API' && connection.toResourceType === 'CLIENT')
|
331
350
|
.forEach((apiConnection) => {
|
332
|
-
const apiProviderRef =
|
333
|
-
const clientConsumerRef =
|
351
|
+
const apiProviderRef = StormEventParser.toRef(handle, apiConnection.fromComponent);
|
352
|
+
const clientConsumerRef = StormEventParser.toRef(handle, apiConnection.toComponent);
|
334
353
|
const apiProviderBlock = blockDefinitions[apiProviderRef.toNormalizedString()];
|
335
354
|
if (!apiProviderBlock) {
|
336
355
|
console.warn('API provider not found: %s', apiConnection.fromComponent, apiConnection);
|
@@ -407,8 +426,8 @@ export class StormEventParser {
|
|
407
426
|
});
|
408
427
|
|
409
428
|
const connections: Connection[] = this.connections.map((connection) => {
|
410
|
-
const fromRef =
|
411
|
-
const toRef =
|
429
|
+
const fromRef = StormEventParser.toRef(handle, connection.fromComponent);
|
430
|
+
const toRef = StormEventParser.toRef(handle, connection.toComponent);
|
412
431
|
|
413
432
|
return {
|
414
433
|
port: {
|
@@ -445,19 +464,11 @@ export class StormEventParser {
|
|
445
464
|
};
|
446
465
|
}
|
447
466
|
|
448
|
-
private toSafeName(name: string): string {
|
449
|
-
return name.toLowerCase().replace(/[^0-9a-z-]/gi, '');
|
450
|
-
}
|
451
|
-
|
452
|
-
private toRef(handle: string, name: string) {
|
453
|
-
return parseKapetaUri(handle + '/' + this.toSafeName(name) + ':local');
|
454
|
-
}
|
455
|
-
|
456
467
|
public toBlockDefinitions(handle: string): { [key: string]: BlockDefinitionInfo } {
|
457
468
|
const result: { [key: string]: BlockDefinitionInfo } = {};
|
458
469
|
|
459
470
|
Object.entries(this.blocks).forEach(([, blockInfo]) => {
|
460
|
-
const blockRef =
|
471
|
+
const blockRef = StormEventParser.toRef(handle, blockInfo.name);
|
461
472
|
|
462
473
|
const blockDefinitionInfo: BlockDefinitionInfo = {
|
463
474
|
uri: blockRef,
|
@@ -679,7 +690,7 @@ export class StormEventParser {
|
|
679
690
|
return;
|
680
691
|
}
|
681
692
|
|
682
|
-
const fromRef =
|
693
|
+
const fromRef = StormEventParser.toRef(handle, connection.fromComponent);
|
683
694
|
|
684
695
|
const apiProviderBlock = blockDefinitions[fromRef.toNormalizedString()];
|
685
696
|
if (!apiProviderBlock) {
|
@@ -760,8 +771,8 @@ export class StormEventParser {
|
|
760
771
|
let options: { [key: string]: any } = {};
|
761
772
|
|
762
773
|
if (kind.includes('java')) {
|
763
|
-
const groupId = `ai.${
|
764
|
-
const artifactId =
|
774
|
+
const groupId = `ai.${StormEventParser.toSafeName(handle)}`;
|
775
|
+
const artifactId = StormEventParser.toSafeName(this.planName);
|
765
776
|
options = {
|
766
777
|
groupId,
|
767
778
|
artifactId,
|
package/src/storm/events.ts
CHANGED
@@ -94,7 +94,7 @@ export interface StormEventPlanRetry {
|
|
94
94
|
}
|
95
95
|
|
96
96
|
export interface StormEventCreateDSL {
|
97
|
-
type: '
|
97
|
+
type: 'CREATE_TYPE';
|
98
98
|
reason: string;
|
99
99
|
created: number;
|
100
100
|
payload: {
|
@@ -105,6 +105,19 @@ export interface StormEventCreateDSL {
|
|
105
105
|
};
|
106
106
|
}
|
107
107
|
|
108
|
+
export interface StormEventCreateDSLResource extends Omit<StormEventCreateDSL, 'type'> {
|
109
|
+
type: 'CREATE_API' | 'CREATE_MODEL';
|
110
|
+
reason: string;
|
111
|
+
created: number;
|
112
|
+
payload: {
|
113
|
+
blockName: string;
|
114
|
+
content: string;
|
115
|
+
blockRef?: string;
|
116
|
+
instanceId?: string;
|
117
|
+
resourceName?: string;
|
118
|
+
};
|
119
|
+
}
|
120
|
+
|
108
121
|
export interface StormEventError {
|
109
122
|
type: 'INVALID_RESPONSE' | 'ERROR_INTERNAL';
|
110
123
|
reason: string;
|
@@ -127,6 +140,8 @@ export interface StormEventScreen {
|
|
127
140
|
created: number;
|
128
141
|
payload: {
|
129
142
|
blockName: string;
|
143
|
+
blockRef?: string;
|
144
|
+
instanceId?: string;
|
130
145
|
name: string;
|
131
146
|
template: string;
|
132
147
|
description: string;
|
@@ -151,9 +166,11 @@ export interface StormEventFile {
|
|
151
166
|
created: number;
|
152
167
|
payload: {
|
153
168
|
filename: string;
|
169
|
+
path: string;
|
154
170
|
content: string;
|
155
171
|
blockName: string;
|
156
172
|
blockRef: string;
|
173
|
+
instanceId: string;
|
157
174
|
};
|
158
175
|
}
|
159
176
|
|
@@ -176,6 +193,7 @@ export type StormEvent =
|
|
176
193
|
| StormEventInvalidResponse
|
177
194
|
| StormEventPlanRetry
|
178
195
|
| StormEventCreateDSL
|
196
|
+
| StormEventCreateDSLResource
|
179
197
|
| StormEventError
|
180
198
|
| StormEventScreen
|
181
199
|
| StormEventScreenCandidate
|