@kapeta/local-cluster-service 0.47.4 → 0.48.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [0.48.1](https://github.com/kapetacom/local-cluster-service/compare/v0.48.0...v0.48.1) (2024-06-03)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Enrich events with block refs and instance ids ([#156](https://github.com/kapetacom/local-cluster-service/issues/156)) ([c45d5d7](https://github.com/kapetacom/local-cluster-service/commit/c45d5d776760a51fb786582d74cb4d049e27f0db))
7
+
8
+ # [0.48.0](https://github.com/kapetacom/local-cluster-service/compare/v0.47.4...v0.48.0) (2024-06-03)
9
+
10
+
11
+ ### Features
12
+
13
+ * write files to disk ([8cb2d28](https://github.com/kapetacom/local-cluster-service/commit/8cb2d28a3bc41fd8dc81f52f77054ccc7a9ac294))
14
+
1
15
  ## [0.47.4](https://github.com/kapetacom/local-cluster-service/compare/v0.47.3...v0.47.4) (2024-05-31)
2
16
 
3
17
 
@@ -3,12 +3,41 @@
3
3
  * Copyright 2023 Kapeta Inc.
4
4
  * SPDX-License-Identifier: BUSL-1.1
5
5
  */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || function (mod) {
23
+ if (mod && mod.__esModule) return mod;
24
+ var result = {};
25
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
26
+ __setModuleDefault(result, mod);
27
+ return result;
28
+ };
29
+ var __importDefault = (this && this.__importDefault) || function (mod) {
30
+ return (mod && mod.__esModule) ? mod : { "default": mod };
31
+ };
6
32
  Object.defineProperty(exports, "__esModule", { value: true });
7
33
  exports.StormCodegen = void 0;
8
34
  const codegen_1 = require("@kapeta/codegen");
9
35
  const codeGeneratorManager_1 = require("../codeGeneratorManager");
10
36
  const stormClient_1 = require("./stormClient");
11
37
  const stream_1 = require("./stream");
38
+ const promises_1 = require("fs/promises");
39
+ const path_1 = __importStar(require("path"));
40
+ const node_os_1 = __importDefault(require("node:os"));
12
41
  class StormCodegen {
13
42
  userPrompt;
14
43
  blocks;
@@ -100,6 +129,19 @@ class StormCodegen {
100
129
  if (serviceFiles.length > 0) {
101
130
  await this.processTemplates(block.uri, block.aiName, stormClient_1.stormClient.createServiceImplementation.bind(stormClient_1.stormClient), serviceFiles, contextFiles);
102
131
  }
132
+ const basePath = path_1.default.join(node_os_1.default.tmpdir(), block.content.metadata.name);
133
+ for (const serviceFile of serviceFiles) {
134
+ const filePath = (0, path_1.join)(basePath, serviceFile.filename);
135
+ await (0, promises_1.writeFile)(filePath, serviceFile.content);
136
+ }
137
+ for (const serviceFile of contextFiles) {
138
+ const filePath = (0, path_1.join)(basePath, serviceFile.filename);
139
+ await (0, promises_1.writeFile)(filePath, serviceFile.content);
140
+ }
141
+ for (const uiFile of uiTemplates) {
142
+ const filePath = (0, path_1.join)(basePath, uiFile.filename);
143
+ await (0, promises_1.writeFile)(filePath, uiFile.content);
144
+ }
103
145
  }
104
146
  /**
105
147
  * Emits the text-based files to the stream
@@ -203,9 +245,12 @@ class StormCodegen {
203
245
  if (!(await codeGeneratorManager_1.codeGeneratorManager.ensureTarget(yamlContent.spec.target?.kind))) {
204
246
  return;
205
247
  }
248
+ const basePath = path_1.default.join(node_os_1.default.tmpdir(), yamlContent.metadata.name);
206
249
  const codeGenerator = new codegen_1.BlockCodeGenerator(yamlContent);
207
250
  codeGenerator.withOption('AIContext', stormClient_1.STORM_ID);
208
- return codeGenerator.generate();
251
+ const generatedResult = await codeGenerator.generate();
252
+ new codegen_1.CodeWriter(basePath).write(generatedResult);
253
+ return generatedResult;
209
254
  }
210
255
  }
211
256
  exports.StormCodegen = StormCodegen;
@@ -51,10 +51,15 @@ export declare class StormEventParser {
51
51
  private options;
52
52
  constructor(options: StormOptions);
53
53
  private reset;
54
- addEvent(handle: string, evt: StormEvent): StormDefinitions;
54
+ /**
55
+ * Builds plan and block definitions - and enriches events with relevant refs and ids
56
+ */
57
+ processEvent(handle: string, evt: StormEvent): StormDefinitions;
55
58
  getEvents(): StormEvent[];
56
59
  isValid(): boolean;
57
60
  getError(): string;
61
+ private toInstanceId;
62
+ private toInstanceIdFromRef;
58
63
  toResult(handle: string): StormDefinitions;
59
64
  private toSafeName;
60
65
  private toRef;
@@ -122,9 +122,11 @@ class StormEventParser {
122
122
  this.blocks = {};
123
123
  this.connections = [];
124
124
  }
125
- addEvent(handle, evt) {
125
+ /**
126
+ * Builds plan and block definitions - and enriches events with relevant refs and ids
127
+ */
128
+ processEvent(handle, evt) {
126
129
  this.events.push(evt);
127
- console.log('evt', evt);
128
130
  switch (evt.type) {
129
131
  case 'CREATE_PLAN_PROPERTIES':
130
132
  this.planName = evt.payload.name;
@@ -137,6 +139,8 @@ class StormEventParser {
137
139
  models: [],
138
140
  types: [],
139
141
  };
142
+ evt.payload.blockRef = this.toRef(handle, evt.payload.name).toNormalizedString();
143
+ evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
140
144
  break;
141
145
  case 'PLAN_RETRY':
142
146
  this.reset();
@@ -147,14 +151,22 @@ class StormEventParser {
147
151
  break;
148
152
  case 'CREATE_API':
149
153
  this.blocks[evt.payload.blockName].apis.push(prettifyKaplang(evt.payload.content));
154
+ evt.payload.blockRef = this.toRef(handle, evt.payload.blockName).toNormalizedString();
155
+ evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
150
156
  break;
151
157
  case 'CREATE_TYPE':
152
158
  this.blocks[evt.payload.blockName].types.push(prettifyKaplang(evt.payload.content));
159
+ evt.payload.blockRef = this.toRef(handle, evt.payload.blockName).toNormalizedString();
160
+ evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
153
161
  break;
154
162
  case 'CREATE_MODEL':
155
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);
156
166
  break;
157
167
  case 'CREATE_CONNECTION':
168
+ evt.payload.fromBlockId = this.toInstanceId(handle, evt.payload.fromComponent);
169
+ evt.payload.toBlockId = this.toInstanceId(handle, evt.payload.toComponent);
158
170
  this.connections.push(evt.payload);
159
171
  break;
160
172
  default:
@@ -176,13 +188,20 @@ class StormEventParser {
176
188
  getError() {
177
189
  return this.error;
178
190
  }
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
+ }
179
198
  toResult(handle) {
180
199
  const planRef = this.toRef(handle, this.planName ?? 'undefined');
181
200
  const blockDefinitions = this.toBlockDefinitions(handle);
182
201
  const refIdMap = {};
183
202
  const blocks = Object.entries(blockDefinitions).map(([ref, block]) => {
184
203
  // Create a deterministic uuid
185
- const id = (0, uuid_1.v5)(ref, uuid_1.v5.URL);
204
+ const id = this.toInstanceIdFromRef(ref);
186
205
  refIdMap[ref] = id;
187
206
  return {
188
207
  id,
@@ -14,6 +14,8 @@ export interface StormBlockInfo {
14
14
  name: string;
15
15
  description: string;
16
16
  }[];
17
+ blockRef?: string;
18
+ instanceId?: string;
17
19
  }
18
20
  export interface StormBlockInfoFilled extends StormBlockInfo {
19
21
  apis: string[];
@@ -28,9 +30,11 @@ export interface StormEventCreateBlock {
28
30
  }
29
31
  export interface StormConnection {
30
32
  fromComponent: string;
33
+ fromBlockId?: string;
31
34
  fromResource: string;
32
35
  fromResourceType: StormResourceType;
33
36
  toComponent: string;
37
+ toBlockId?: string;
34
38
  toResource: string;
35
39
  toResourceType: StormResourceType;
36
40
  }
@@ -72,6 +76,8 @@ export interface StormEventCreateDSL {
72
76
  payload: {
73
77
  blockName: string;
74
78
  content: string;
79
+ blockRef?: string;
80
+ instanceId?: string;
75
81
  };
76
82
  }
77
83
  export interface StormEventError {
@@ -27,12 +27,12 @@ router.post('/:handle/all', async (req, res) => {
27
27
  res.set('Content-Type', 'application/x-ndjson');
28
28
  res.set('Access-Control-Expose-Headers', stormClient_1.ConversationIdHeader);
29
29
  res.set(stormClient_1.ConversationIdHeader, metaStream.getConversationId());
30
- console.log('metaStream.getConversationId()', metaStream.getConversationId());
31
30
  metaStream.on('data', (data) => {
32
- const result = eventParser.addEvent(req.params.handle, data);
31
+ const result = eventParser.processEvent(req.params.handle, data);
32
+ sendEvent(res, data);
33
33
  sendDefinitions(res, result);
34
34
  });
35
- await streamStormPartialResponse(metaStream, res);
35
+ await waitForStormStream(metaStream);
36
36
  if (!eventParser.isValid()) {
37
37
  // We can't continue if the meta stream is invalid
38
38
  sendEvent(res, {
@@ -88,6 +88,16 @@ function sendError(err, res) {
88
88
  res.status(400).send({ error: err.message });
89
89
  }
90
90
  }
91
+ function waitForStormStream(result) {
92
+ return new Promise((resolve, reject) => {
93
+ result.on('error', (err) => {
94
+ reject(err);
95
+ });
96
+ result.on('end', () => {
97
+ resolve();
98
+ });
99
+ });
100
+ }
91
101
  function streamStormPartialResponse(result, res) {
92
102
  return new Promise((resolve, reject) => {
93
103
  result.on('data', (data) => {
@@ -127,7 +127,7 @@ const events = [
127
127
  describe('event-parser', () => {
128
128
  it('it can parse events into a plan and blocks with proper layout', () => {
129
129
  const parser = new event_parser_1.StormEventParser(parserOptions);
130
- events.forEach((event) => parser.addEvent('kapeta', event));
130
+ events.forEach((event) => parser.processEvent('kapeta', event));
131
131
  const result = parser.toResult('kapeta');
132
132
  expect(result.plan.metadata.name).toBe('kapeta/my-plan');
133
133
  expect(result.plan.metadata.description).toBe('my plan description');
@@ -3,12 +3,41 @@
3
3
  * Copyright 2023 Kapeta Inc.
4
4
  * SPDX-License-Identifier: BUSL-1.1
5
5
  */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || function (mod) {
23
+ if (mod && mod.__esModule) return mod;
24
+ var result = {};
25
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
26
+ __setModuleDefault(result, mod);
27
+ return result;
28
+ };
29
+ var __importDefault = (this && this.__importDefault) || function (mod) {
30
+ return (mod && mod.__esModule) ? mod : { "default": mod };
31
+ };
6
32
  Object.defineProperty(exports, "__esModule", { value: true });
7
33
  exports.StormCodegen = void 0;
8
34
  const codegen_1 = require("@kapeta/codegen");
9
35
  const codeGeneratorManager_1 = require("../codeGeneratorManager");
10
36
  const stormClient_1 = require("./stormClient");
11
37
  const stream_1 = require("./stream");
38
+ const promises_1 = require("fs/promises");
39
+ const path_1 = __importStar(require("path"));
40
+ const node_os_1 = __importDefault(require("node:os"));
12
41
  class StormCodegen {
13
42
  userPrompt;
14
43
  blocks;
@@ -100,6 +129,19 @@ class StormCodegen {
100
129
  if (serviceFiles.length > 0) {
101
130
  await this.processTemplates(block.uri, block.aiName, stormClient_1.stormClient.createServiceImplementation.bind(stormClient_1.stormClient), serviceFiles, contextFiles);
102
131
  }
132
+ const basePath = path_1.default.join(node_os_1.default.tmpdir(), block.content.metadata.name);
133
+ for (const serviceFile of serviceFiles) {
134
+ const filePath = (0, path_1.join)(basePath, serviceFile.filename);
135
+ await (0, promises_1.writeFile)(filePath, serviceFile.content);
136
+ }
137
+ for (const serviceFile of contextFiles) {
138
+ const filePath = (0, path_1.join)(basePath, serviceFile.filename);
139
+ await (0, promises_1.writeFile)(filePath, serviceFile.content);
140
+ }
141
+ for (const uiFile of uiTemplates) {
142
+ const filePath = (0, path_1.join)(basePath, uiFile.filename);
143
+ await (0, promises_1.writeFile)(filePath, uiFile.content);
144
+ }
103
145
  }
104
146
  /**
105
147
  * Emits the text-based files to the stream
@@ -203,9 +245,12 @@ class StormCodegen {
203
245
  if (!(await codeGeneratorManager_1.codeGeneratorManager.ensureTarget(yamlContent.spec.target?.kind))) {
204
246
  return;
205
247
  }
248
+ const basePath = path_1.default.join(node_os_1.default.tmpdir(), yamlContent.metadata.name);
206
249
  const codeGenerator = new codegen_1.BlockCodeGenerator(yamlContent);
207
250
  codeGenerator.withOption('AIContext', stormClient_1.STORM_ID);
208
- return codeGenerator.generate();
251
+ const generatedResult = await codeGenerator.generate();
252
+ new codegen_1.CodeWriter(basePath).write(generatedResult);
253
+ return generatedResult;
209
254
  }
210
255
  }
211
256
  exports.StormCodegen = StormCodegen;
@@ -51,10 +51,15 @@ export declare class StormEventParser {
51
51
  private options;
52
52
  constructor(options: StormOptions);
53
53
  private reset;
54
- addEvent(handle: string, evt: StormEvent): StormDefinitions;
54
+ /**
55
+ * Builds plan and block definitions - and enriches events with relevant refs and ids
56
+ */
57
+ processEvent(handle: string, evt: StormEvent): StormDefinitions;
55
58
  getEvents(): StormEvent[];
56
59
  isValid(): boolean;
57
60
  getError(): string;
61
+ private toInstanceId;
62
+ private toInstanceIdFromRef;
58
63
  toResult(handle: string): StormDefinitions;
59
64
  private toSafeName;
60
65
  private toRef;
@@ -122,9 +122,11 @@ class StormEventParser {
122
122
  this.blocks = {};
123
123
  this.connections = [];
124
124
  }
125
- addEvent(handle, evt) {
125
+ /**
126
+ * Builds plan and block definitions - and enriches events with relevant refs and ids
127
+ */
128
+ processEvent(handle, evt) {
126
129
  this.events.push(evt);
127
- console.log('evt', evt);
128
130
  switch (evt.type) {
129
131
  case 'CREATE_PLAN_PROPERTIES':
130
132
  this.planName = evt.payload.name;
@@ -137,6 +139,8 @@ class StormEventParser {
137
139
  models: [],
138
140
  types: [],
139
141
  };
142
+ evt.payload.blockRef = this.toRef(handle, evt.payload.name).toNormalizedString();
143
+ evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
140
144
  break;
141
145
  case 'PLAN_RETRY':
142
146
  this.reset();
@@ -147,14 +151,22 @@ class StormEventParser {
147
151
  break;
148
152
  case 'CREATE_API':
149
153
  this.blocks[evt.payload.blockName].apis.push(prettifyKaplang(evt.payload.content));
154
+ evt.payload.blockRef = this.toRef(handle, evt.payload.blockName).toNormalizedString();
155
+ evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
150
156
  break;
151
157
  case 'CREATE_TYPE':
152
158
  this.blocks[evt.payload.blockName].types.push(prettifyKaplang(evt.payload.content));
159
+ evt.payload.blockRef = this.toRef(handle, evt.payload.blockName).toNormalizedString();
160
+ evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
153
161
  break;
154
162
  case 'CREATE_MODEL':
155
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);
156
166
  break;
157
167
  case 'CREATE_CONNECTION':
168
+ evt.payload.fromBlockId = this.toInstanceId(handle, evt.payload.fromComponent);
169
+ evt.payload.toBlockId = this.toInstanceId(handle, evt.payload.toComponent);
158
170
  this.connections.push(evt.payload);
159
171
  break;
160
172
  default:
@@ -176,13 +188,20 @@ class StormEventParser {
176
188
  getError() {
177
189
  return this.error;
178
190
  }
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
+ }
179
198
  toResult(handle) {
180
199
  const planRef = this.toRef(handle, this.planName ?? 'undefined');
181
200
  const blockDefinitions = this.toBlockDefinitions(handle);
182
201
  const refIdMap = {};
183
202
  const blocks = Object.entries(blockDefinitions).map(([ref, block]) => {
184
203
  // Create a deterministic uuid
185
- const id = (0, uuid_1.v5)(ref, uuid_1.v5.URL);
204
+ const id = this.toInstanceIdFromRef(ref);
186
205
  refIdMap[ref] = id;
187
206
  return {
188
207
  id,
@@ -14,6 +14,8 @@ export interface StormBlockInfo {
14
14
  name: string;
15
15
  description: string;
16
16
  }[];
17
+ blockRef?: string;
18
+ instanceId?: string;
17
19
  }
18
20
  export interface StormBlockInfoFilled extends StormBlockInfo {
19
21
  apis: string[];
@@ -28,9 +30,11 @@ export interface StormEventCreateBlock {
28
30
  }
29
31
  export interface StormConnection {
30
32
  fromComponent: string;
33
+ fromBlockId?: string;
31
34
  fromResource: string;
32
35
  fromResourceType: StormResourceType;
33
36
  toComponent: string;
37
+ toBlockId?: string;
34
38
  toResource: string;
35
39
  toResourceType: StormResourceType;
36
40
  }
@@ -72,6 +76,8 @@ export interface StormEventCreateDSL {
72
76
  payload: {
73
77
  blockName: string;
74
78
  content: string;
79
+ blockRef?: string;
80
+ instanceId?: string;
75
81
  };
76
82
  }
77
83
  export interface StormEventError {
@@ -27,12 +27,12 @@ router.post('/:handle/all', async (req, res) => {
27
27
  res.set('Content-Type', 'application/x-ndjson');
28
28
  res.set('Access-Control-Expose-Headers', stormClient_1.ConversationIdHeader);
29
29
  res.set(stormClient_1.ConversationIdHeader, metaStream.getConversationId());
30
- console.log('metaStream.getConversationId()', metaStream.getConversationId());
31
30
  metaStream.on('data', (data) => {
32
- const result = eventParser.addEvent(req.params.handle, data);
31
+ const result = eventParser.processEvent(req.params.handle, data);
32
+ sendEvent(res, data);
33
33
  sendDefinitions(res, result);
34
34
  });
35
- await streamStormPartialResponse(metaStream, res);
35
+ await waitForStormStream(metaStream);
36
36
  if (!eventParser.isValid()) {
37
37
  // We can't continue if the meta stream is invalid
38
38
  sendEvent(res, {
@@ -88,6 +88,16 @@ function sendError(err, res) {
88
88
  res.status(400).send({ error: err.message });
89
89
  }
90
90
  }
91
+ function waitForStormStream(result) {
92
+ return new Promise((resolve, reject) => {
93
+ result.on('error', (err) => {
94
+ reject(err);
95
+ });
96
+ result.on('end', () => {
97
+ resolve();
98
+ });
99
+ });
100
+ }
91
101
  function streamStormPartialResponse(result, res) {
92
102
  return new Promise((resolve, reject) => {
93
103
  result.on('data', (data) => {
@@ -127,7 +127,7 @@ const events = [
127
127
  describe('event-parser', () => {
128
128
  it('it can parse events into a plan and blocks with proper layout', () => {
129
129
  const parser = new event_parser_1.StormEventParser(parserOptions);
130
- events.forEach((event) => parser.addEvent('kapeta', event));
130
+ events.forEach((event) => parser.processEvent('kapeta', event));
131
131
  const result = parser.toResult('kapeta');
132
132
  expect(result.plan.metadata.name).toBe('kapeta/my-plan');
133
133
  expect(result.plan.metadata.description).toBe('my plan description');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kapeta/local-cluster-service",
3
- "version": "0.47.4",
3
+ "version": "0.48.1",
4
4
  "description": "Manages configuration, ports and service discovery for locally running Kapeta systems",
5
5
  "type": "commonjs",
6
6
  "exports": {
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { Definition } from '@kapeta/local-cluster-config';
7
- import { AIFileTypes, BlockCodeGenerator, GeneratedFile, GeneratedResult } from '@kapeta/codegen';
7
+ import { AIFileTypes, BlockCodeGenerator, CodeWriter, GeneratedFile, GeneratedResult } from '@kapeta/codegen';
8
8
  import { BlockDefinition } from '@kapeta/schemas';
9
9
  import { codeGeneratorManager } from '../codeGeneratorManager';
10
10
  import { STORM_ID, stormClient } from './stormClient';
@@ -12,6 +12,9 @@ import { StormEvent, StormEventFile } from './events';
12
12
  import { BlockDefinitionInfo } from './event-parser';
13
13
  import { ConversationItem, StormFileImplementationPrompt, StormFileInfo, StormStream } from './stream';
14
14
  import { KapetaURI } from '@kapeta/nodejs-utils';
15
+ import { writeFile } from 'fs/promises';
16
+ import path, { join } from 'path';
17
+ import os from 'node:os';
15
18
 
16
19
  type ImplementationGenerator = (prompt: StormFileImplementationPrompt, conversationId?: string) => Promise<StormStream>;
17
20
 
@@ -131,6 +134,23 @@ export class StormCodegen {
131
134
  contextFiles
132
135
  );
133
136
  }
137
+
138
+ const basePath = path.join(os.tmpdir(), block.content.metadata.name);
139
+
140
+ for (const serviceFile of serviceFiles) {
141
+ const filePath = join(basePath, serviceFile.filename);
142
+ await writeFile(filePath, serviceFile.content);
143
+ }
144
+
145
+ for (const serviceFile of contextFiles) {
146
+ const filePath = join(basePath, serviceFile.filename);
147
+ await writeFile(filePath, serviceFile.content);
148
+ }
149
+
150
+ for (const uiFile of uiTemplates) {
151
+ const filePath = join(basePath, uiFile.filename);
152
+ await writeFile(filePath, uiFile.content);
153
+ }
134
154
  }
135
155
 
136
156
  /**
@@ -263,10 +283,12 @@ export class StormCodegen {
263
283
  if (!(await codeGeneratorManager.ensureTarget(yamlContent.spec.target?.kind))) {
264
284
  return;
265
285
  }
286
+ const basePath = path.join(os.tmpdir(), yamlContent.metadata.name);
266
287
 
267
288
  const codeGenerator = new BlockCodeGenerator(yamlContent as BlockDefinition);
268
289
  codeGenerator.withOption('AIContext', STORM_ID);
269
-
270
- return codeGenerator.generate();
290
+ const generatedResult = await codeGenerator.generate();
291
+ new CodeWriter(basePath).write(generatedResult);
292
+ return generatedResult;
271
293
  }
272
294
  }
@@ -221,9 +221,11 @@ export class StormEventParser {
221
221
  this.connections = [];
222
222
  }
223
223
 
224
- public addEvent(handle: string, evt: StormEvent): StormDefinitions {
224
+ /**
225
+ * Builds plan and block definitions - and enriches events with relevant refs and ids
226
+ */
227
+ public processEvent(handle: string, evt: StormEvent): StormDefinitions {
225
228
  this.events.push(evt);
226
- console.log('evt', evt);
227
229
  switch (evt.type) {
228
230
  case 'CREATE_PLAN_PROPERTIES':
229
231
  this.planName = evt.payload.name;
@@ -236,6 +238,8 @@ export class StormEventParser {
236
238
  models: [],
237
239
  types: [],
238
240
  };
241
+ evt.payload.blockRef = this.toRef(handle, evt.payload.name).toNormalizedString();
242
+ evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
239
243
  break;
240
244
  case 'PLAN_RETRY':
241
245
  this.reset();
@@ -246,14 +250,22 @@ export class StormEventParser {
246
250
  break;
247
251
  case 'CREATE_API':
248
252
  this.blocks[evt.payload.blockName].apis.push(prettifyKaplang(evt.payload.content));
253
+ evt.payload.blockRef = this.toRef(handle, evt.payload.blockName).toNormalizedString();
254
+ evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
249
255
  break;
250
256
  case 'CREATE_TYPE':
251
257
  this.blocks[evt.payload.blockName].types.push(prettifyKaplang(evt.payload.content));
258
+ evt.payload.blockRef = this.toRef(handle, evt.payload.blockName).toNormalizedString();
259
+ evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
252
260
  break;
253
261
  case 'CREATE_MODEL':
254
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);
255
265
  break;
256
266
  case 'CREATE_CONNECTION':
267
+ evt.payload.fromBlockId = this.toInstanceId(handle, evt.payload.fromComponent);
268
+ evt.payload.toBlockId = this.toInstanceId(handle, evt.payload.toComponent);
257
269
  this.connections.push(evt.payload);
258
270
  break;
259
271
 
@@ -281,13 +293,22 @@ export class StormEventParser {
281
293
  return this.error;
282
294
  }
283
295
 
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
+
284
305
  public toResult(handle: string): StormDefinitions {
285
306
  const planRef = this.toRef(handle, this.planName ?? 'undefined');
286
307
  const blockDefinitions = this.toBlockDefinitions(handle);
287
308
  const refIdMap: { [key: string]: string } = {};
288
309
  const blocks = Object.entries(blockDefinitions).map(([ref, block]) => {
289
310
  // Create a deterministic uuid
290
- const id = uuid(ref, uuid.URL);
311
+ const id = this.toInstanceIdFromRef(ref);
291
312
  refIdMap[ref] = id;
292
313
  return {
293
314
  id,
@@ -30,6 +30,8 @@ export interface StormBlockInfo {
30
30
  name: string;
31
31
  description: string;
32
32
  }[];
33
+ blockRef?: string;
34
+ instanceId?: string;
33
35
  }
34
36
 
35
37
  export interface StormBlockInfoFilled extends StormBlockInfo {
@@ -47,9 +49,11 @@ export interface StormEventCreateBlock {
47
49
 
48
50
  export interface StormConnection {
49
51
  fromComponent: string;
52
+ fromBlockId?: string;
50
53
  fromResource: string;
51
54
  fromResourceType: StormResourceType;
52
55
  toComponent: string;
56
+ toBlockId?: string;
53
57
  toResource: string;
54
58
  toResourceType: StormResourceType;
55
59
  }
@@ -96,6 +100,8 @@ export interface StormEventCreateDSL {
96
100
  payload: {
97
101
  blockName: string;
98
102
  content: string;
103
+ blockRef?: string;
104
+ instanceId?: string;
99
105
  };
100
106
  }
101
107
 
@@ -35,15 +35,15 @@ router.post('/:handle/all', async (req: KapetaBodyRequest, res: Response) => {
35
35
  res.set('Content-Type', 'application/x-ndjson');
36
36
  res.set('Access-Control-Expose-Headers', ConversationIdHeader);
37
37
  res.set(ConversationIdHeader, metaStream.getConversationId());
38
- console.log('metaStream.getConversationId()', metaStream.getConversationId());
39
38
 
40
39
  metaStream.on('data', (data: StormEvent) => {
41
- const result = eventParser.addEvent(req.params.handle, data);
40
+ const result = eventParser.processEvent(req.params.handle, data);
42
41
 
42
+ sendEvent(res, data);
43
43
  sendDefinitions(res, result);
44
44
  });
45
45
 
46
- await streamStormPartialResponse(metaStream, res);
46
+ await waitForStormStream(metaStream);
47
47
 
48
48
  if (!eventParser.isValid()) {
49
49
  // We can't continue if the meta stream is invalid
@@ -109,6 +109,17 @@ function sendError(err: Error, res: Response) {
109
109
  res.status(400).send({ error: err.message });
110
110
  }
111
111
  }
112
+ function waitForStormStream(result: StormStream) {
113
+ return new Promise<void>((resolve, reject) => {
114
+ result.on('error', (err) => {
115
+ reject(err);
116
+ });
117
+
118
+ result.on('end', () => {
119
+ resolve();
120
+ });
121
+ });
122
+ }
112
123
 
113
124
  function streamStormPartialResponse(result: StormStream, res: Response) {
114
125
  return new Promise<void>((resolve, reject) => {
@@ -139,7 +139,7 @@ const events: StormEvent[] = [
139
139
  describe('event-parser', () => {
140
140
  it('it can parse events into a plan and blocks with proper layout', () => {
141
141
  const parser = new StormEventParser(parserOptions);
142
- events.forEach((event) => parser.addEvent('kapeta', event));
142
+ events.forEach((event) => parser.processEvent('kapeta', event));
143
143
 
144
144
  const result = parser.toResult('kapeta');
145
145