@kapeta/local-cluster-service 0.44.0 → 0.46.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 (47) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/index.js +2 -0
  3. package/dist/cjs/src/codeGeneratorManager.d.ts +1 -0
  4. package/dist/cjs/src/codeGeneratorManager.js +12 -6
  5. package/dist/cjs/src/middleware/cors.d.ts +1 -0
  6. package/dist/cjs/src/middleware/kapeta.d.ts +1 -0
  7. package/dist/cjs/src/middleware/stringBody.d.ts +1 -0
  8. package/dist/cjs/src/storm/codegen.d.ts +40 -0
  9. package/dist/cjs/src/storm/codegen.js +210 -0
  10. package/dist/cjs/src/storm/event-parser.d.ts +70 -0
  11. package/dist/cjs/src/storm/event-parser.js +522 -0
  12. package/dist/cjs/src/storm/events.d.ts +126 -0
  13. package/dist/cjs/src/storm/events.js +6 -0
  14. package/dist/cjs/src/storm/routes.d.ts +7 -0
  15. package/dist/cjs/src/storm/routes.js +87 -0
  16. package/dist/cjs/src/storm/stormClient.d.ts +13 -0
  17. package/dist/cjs/src/storm/stormClient.js +80 -0
  18. package/dist/cjs/src/storm/stream.d.ts +45 -0
  19. package/dist/cjs/src/storm/stream.js +57 -0
  20. package/dist/esm/index.js +2 -0
  21. package/dist/esm/src/codeGeneratorManager.d.ts +1 -0
  22. package/dist/esm/src/codeGeneratorManager.js +12 -6
  23. package/dist/esm/src/middleware/cors.d.ts +1 -0
  24. package/dist/esm/src/middleware/kapeta.d.ts +1 -0
  25. package/dist/esm/src/middleware/stringBody.d.ts +1 -0
  26. package/dist/esm/src/storm/codegen.d.ts +40 -0
  27. package/dist/esm/src/storm/codegen.js +210 -0
  28. package/dist/esm/src/storm/event-parser.d.ts +70 -0
  29. package/dist/esm/src/storm/event-parser.js +522 -0
  30. package/dist/esm/src/storm/events.d.ts +126 -0
  31. package/dist/esm/src/storm/events.js +6 -0
  32. package/dist/esm/src/storm/routes.d.ts +7 -0
  33. package/dist/esm/src/storm/routes.js +87 -0
  34. package/dist/esm/src/storm/stormClient.d.ts +13 -0
  35. package/dist/esm/src/storm/stormClient.js +80 -0
  36. package/dist/esm/src/storm/stream.d.ts +45 -0
  37. package/dist/esm/src/storm/stream.js +57 -0
  38. package/index.ts +2 -0
  39. package/package.json +3 -3
  40. package/src/codeGeneratorManager.ts +17 -8
  41. package/src/storm/codegen.ts +266 -0
  42. package/src/storm/event-parser.ts +668 -0
  43. package/src/storm/events.ts +168 -0
  44. package/src/storm/routes.ts +111 -0
  45. package/src/storm/stormClient.ts +106 -0
  46. package/src/storm/stream.ts +96 -0
  47. package/src/utils/BlockInstanceRunner.ts +4 -2
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [0.46.0](https://github.com/kapetacom/local-cluster-service/compare/v0.45.0...v0.46.0) (2024-05-28)
2
+
3
+
4
+ ### Features
5
+
6
+ * Change how the UI AI work to get templates from codegen ([#148](https://github.com/kapetacom/local-cluster-service/issues/148)) ([f757ec8](https://github.com/kapetacom/local-cluster-service/commit/f757ec8e4fab0dbb58b96a1b9e25f3e734ddb10f))
7
+
8
+ # [0.45.0](https://github.com/kapetacom/local-cluster-service/compare/v0.44.0...v0.45.0) (2024-05-24)
9
+
10
+
11
+ ### Features
12
+
13
+ * Introduces Storm API ([#147](https://github.com/kapetacom/local-cluster-service/issues/147)) ([5bdb075](https://github.com/kapetacom/local-cluster-service/commit/5bdb075f838c331e3a517778c55f83c32cc5b812))
14
+
1
15
  # [0.44.0](https://github.com/kapetacom/local-cluster-service/compare/v0.43.3...v0.44.0) (2024-05-08)
2
16
 
3
17
 
package/dist/cjs/index.js CHANGED
@@ -50,6 +50,7 @@ const routes_9 = __importDefault(require("./src/attachments/routes"));
50
50
  const routes_10 = __importDefault(require("./src/tasks/routes"));
51
51
  const api_1 = __importDefault(require("./src/api"));
52
52
  const routes_11 = __importDefault(require("./src/ai/routes"));
53
+ const routes_12 = __importDefault(require("./src/storm/routes"));
53
54
  const utils_1 = require("./src/utils/utils");
54
55
  const request_1 = __importDefault(require("request"));
55
56
  const repositoryManager_1 = require("./src/repositoryManager");
@@ -89,6 +90,7 @@ function createServer() {
89
90
  app.use('/tasks', routes_10.default);
90
91
  app.use('/api', api_1.default);
91
92
  app.use('/ai', routes_11.default);
93
+ app.use('/storm', routes_12.default);
92
94
  app.get('/status', async (req, res) => {
93
95
  res.send({
94
96
  ok: true,
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { Definition } from '@kapeta/local-cluster-config';
6
6
  declare class CodeGeneratorManager {
7
+ ensureTarget(targetKind: string): Promise<boolean>;
7
8
  private ensureLanguageTargetInRegistry;
8
9
  reload(): Promise<void>;
9
10
  initialize(): Promise<void>;
@@ -17,6 +17,17 @@ const repositoryManager_1 = require("./repositoryManager");
17
17
  const TARGET_KIND = 'core/language-target';
18
18
  const BLOCK_TYPE_REGEX = /^core\/block-type.*/;
19
19
  class CodeGeneratorManager {
20
+ async ensureTarget(targetKind) {
21
+ const targetRef = (0, nodejs_utils_1.normalizeKapetaUri)(targetKind);
22
+ // Automatically downloads target if not available
23
+ const targetAsset = await assetManager_1.assetManager.getAsset(targetRef);
24
+ if (!targetAsset) {
25
+ console.error('Language target not found: %s', targetKind);
26
+ return false;
27
+ }
28
+ await this.ensureLanguageTargetInRegistry(targetAsset?.path, targetAsset?.version, targetAsset?.data);
29
+ return true;
30
+ }
20
31
  async ensureLanguageTargetInRegistry(path, version, definition) {
21
32
  const key = `${definition.metadata.name}:${version}`;
22
33
  try {
@@ -72,14 +83,9 @@ class CodeGeneratorManager {
72
83
  //Not all block types have targets
73
84
  return;
74
85
  }
75
- const targetRef = (0, nodejs_utils_1.normalizeKapetaUri)(yamlContent.spec.target?.kind);
76
- // Automatically downloads target if not available
77
- const targetAsset = await assetManager_1.assetManager.getAsset(targetRef);
78
- if (!targetAsset) {
79
- console.error('Language target not found: %s', yamlContent.spec.target?.kind);
86
+ if (!(await this.ensureTarget(yamlContent.spec.target?.kind))) {
80
87
  return;
81
88
  }
82
- await this.ensureLanguageTargetInRegistry(targetAsset?.path, targetAsset?.version, targetAsset?.data);
83
89
  const baseDir = path_1.default.dirname(yamlFile);
84
90
  console.log('Generating code for path: %s', baseDir);
85
91
  const codeGenerator = new codegen_1.BlockCodeGenerator(yamlContent);
@@ -2,5 +2,6 @@
2
2
  * Copyright 2023 Kapeta Inc.
3
3
  * SPDX-License-Identifier: BUSL-1.1
4
4
  */
5
+ /// <reference types="cookie-parser" />
5
6
  import { NextFunction, Request, Response } from 'express';
6
7
  export declare function corsHandler(req: Request, res: Response, next: NextFunction): void;
@@ -2,6 +2,7 @@
2
2
  * Copyright 2023 Kapeta Inc.
3
3
  * SPDX-License-Identifier: BUSL-1.1
4
4
  */
5
+ /// <reference types="cookie-parser" />
5
6
  import { NextFunction, Request, Response } from 'express';
6
7
  import { EnvironmentType } from '../types';
7
8
  export interface KapetaRequest extends Request {
@@ -2,6 +2,7 @@
2
2
  * Copyright 2023 Kapeta Inc.
3
3
  * SPDX-License-Identifier: BUSL-1.1
4
4
  */
5
+ /// <reference types="cookie-parser" />
5
6
  import { NextFunction, Request, Response } from 'express';
6
7
  export type StringBodyRequest = Request<any> & {
7
8
  stringBody?: string;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Copyright 2023 Kapeta Inc.
3
+ * SPDX-License-Identifier: BUSL-1.1
4
+ */
5
+ import { StormEvent } from './events';
6
+ import { BlockDefinitionInfo } from './event-parser';
7
+ import { StormStream } from './stream';
8
+ export declare class StormCodegen {
9
+ private readonly userPrompt;
10
+ private readonly blocks;
11
+ private readonly out;
12
+ private readonly events;
13
+ constructor(userPrompt: string, blocks: BlockDefinitionInfo[], events: StormEvent[]);
14
+ process(): Promise<void>;
15
+ getStream(): StormStream;
16
+ private handleTemplateFileOutput;
17
+ private handleUiOutput;
18
+ private handleFileOutput;
19
+ /**
20
+ * Generates the code for a block and sends it to the AI
21
+ */
22
+ private processBlockCode;
23
+ /**
24
+ * Emits the text-based files to the stream
25
+ */
26
+ private emitFiles;
27
+ private emitFile;
28
+ /**
29
+ * Sends the template to the AI and processes the response
30
+ */
31
+ private processTemplates;
32
+ /**
33
+ * Converts the generated files to a format that can be sent to the AI
34
+ */
35
+ private toStormFiles;
36
+ /**
37
+ * Generates the code using codegen for a given block.
38
+ */
39
+ private generateBlock;
40
+ }
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright 2023 Kapeta Inc.
4
+ * SPDX-License-Identifier: BUSL-1.1
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.StormCodegen = void 0;
8
+ const codegen_1 = require("@kapeta/codegen");
9
+ const codeGeneratorManager_1 = require("../codeGeneratorManager");
10
+ const stormClient_1 = require("./stormClient");
11
+ const stream_1 = require("./stream");
12
+ class StormCodegen {
13
+ userPrompt;
14
+ blocks;
15
+ out = new stream_1.StormStream();
16
+ events;
17
+ constructor(userPrompt, blocks, events) {
18
+ this.userPrompt = userPrompt;
19
+ this.blocks = blocks;
20
+ this.events = events;
21
+ }
22
+ async process() {
23
+ for (const block of this.blocks) {
24
+ await this.processBlockCode(block);
25
+ }
26
+ this.out.end();
27
+ }
28
+ getStream() {
29
+ return this.out;
30
+ }
31
+ handleTemplateFileOutput(blockUri, template, data) {
32
+ switch (data.type) {
33
+ case 'FILE':
34
+ template.filename = data.payload.filename;
35
+ template.content = data.payload.content;
36
+ return this.handleFileOutput(blockUri, data);
37
+ }
38
+ }
39
+ handleUiOutput(blockUri, data) {
40
+ switch (data.type) {
41
+ case 'SCREEN':
42
+ this.out.emit('data', {
43
+ type: 'SCREEN',
44
+ reason: data.reason,
45
+ created: Date.now(),
46
+ payload: {
47
+ ...data.payload,
48
+ blockName: blockUri.toNormalizedString(),
49
+ },
50
+ });
51
+ case 'FILE':
52
+ return this.handleFileOutput(blockUri, data);
53
+ }
54
+ }
55
+ handleFileOutput(blockUri, data) {
56
+ switch (data.type) {
57
+ case 'FILE':
58
+ this.emitFile(blockUri, data.payload.filename, data.payload.content, data.reason);
59
+ return {
60
+ type: 'FILE',
61
+ created: Date.now(),
62
+ payload: {
63
+ filename: data.payload.filename,
64
+ content: data.payload.content,
65
+ },
66
+ };
67
+ }
68
+ }
69
+ /**
70
+ * Generates the code for a block and sends it to the AI
71
+ */
72
+ async processBlockCode(block) {
73
+ // Generate the code for the block using the standard codegen templates
74
+ const generatedResult = await this.generateBlock(block.content);
75
+ if (!generatedResult) {
76
+ return;
77
+ }
78
+ const allFiles = this.toStormFiles(generatedResult);
79
+ // Send all the non-ai files to the stream
80
+ this.emitFiles(block.uri, allFiles);
81
+ const relevantFiles = allFiles.filter((file) => file.type !== codegen_1.AIFileTypes.IGNORE && file.type !== codegen_1.AIFileTypes.WEB_SCREEN);
82
+ const uiTemplates = allFiles.filter((file) => file.type === codegen_1.AIFileTypes.WEB_SCREEN);
83
+ if (uiTemplates.length > 0) {
84
+ const uiStream = await stormClient_1.stormClient.createUIImplementation({
85
+ events: this.events,
86
+ templates: uiTemplates,
87
+ context: relevantFiles,
88
+ blockName: block.aiName,
89
+ prompt: this.userPrompt,
90
+ });
91
+ uiStream.on('data', (evt) => {
92
+ this.handleUiOutput(block.uri, evt);
93
+ });
94
+ await uiStream.waitForDone();
95
+ }
96
+ // Gather the context files for implementation. These will be all be passed to the AI
97
+ const contextFiles = relevantFiles.filter((file) => ![codegen_1.AIFileTypes.SERVICE, codegen_1.AIFileTypes.WEB_SCREEN].includes(file.type));
98
+ // Send the service and UI templates to the AI. These will be send one-by-one in addition to the context files
99
+ const serviceFiles = allFiles.filter((file) => file.type === codegen_1.AIFileTypes.SERVICE);
100
+ if (serviceFiles.length > 0) {
101
+ await this.processTemplates(block.uri, stormClient_1.stormClient.createServiceImplementation.bind(stormClient_1.stormClient), serviceFiles, contextFiles);
102
+ }
103
+ }
104
+ /**
105
+ * Emits the text-based files to the stream
106
+ */
107
+ emitFiles(uri, files) {
108
+ files.forEach((file) => {
109
+ if (!file.content || typeof file.content !== 'string') {
110
+ return;
111
+ }
112
+ if (file.type === codegen_1.AIFileTypes.SERVICE) {
113
+ // Don't send the service files to the stream yet
114
+ // They will need to be implemented by the AI
115
+ return;
116
+ }
117
+ if (file.type === codegen_1.AIFileTypes.WEB_SCREEN) {
118
+ // Don't send the web screen files to the stream yet
119
+ // They will need to be implemented by the AI
120
+ return;
121
+ }
122
+ this.emitFile(uri, file.filename, file.content);
123
+ });
124
+ }
125
+ emitFile(uri, filename, content, reason = 'File generated') {
126
+ this.out.emit('data', {
127
+ type: 'FILE',
128
+ reason,
129
+ created: Date.now(),
130
+ payload: {
131
+ filename: filename,
132
+ content: content,
133
+ blockRef: uri.toNormalizedString(),
134
+ },
135
+ });
136
+ }
137
+ /**
138
+ * Sends the template to the AI and processes the response
139
+ */
140
+ async processTemplates(blockUri, generator, templates, contextFiles) {
141
+ const promises = templates.map(async (templateFile) => {
142
+ const stream = await generator({
143
+ context: contextFiles,
144
+ template: templateFile,
145
+ prompt: this.userPrompt,
146
+ });
147
+ const files = [];
148
+ stream.on('data', (evt) => {
149
+ const file = this.handleTemplateFileOutput(blockUri, templateFile, evt);
150
+ if (file) {
151
+ files.push(file);
152
+ }
153
+ });
154
+ await stream.waitForDone();
155
+ return files;
156
+ });
157
+ const fileChunks = await Promise.all(promises);
158
+ return fileChunks.flat();
159
+ }
160
+ /**
161
+ * Converts the generated files to a format that can be sent to the AI
162
+ */
163
+ toStormFiles(generatedResult) {
164
+ const allFiles = generatedResult.files.map((file) => {
165
+ if (!file.content) {
166
+ return {
167
+ ...file,
168
+ type: codegen_1.AIFileTypes.IGNORE,
169
+ };
170
+ }
171
+ if (typeof file.content !== 'string') {
172
+ return {
173
+ ...file,
174
+ type: codegen_1.AIFileTypes.IGNORE,
175
+ };
176
+ }
177
+ const rx = /\/\/AI-TYPE:([a-z0-9- _]+)\n/gi;
178
+ const match = rx.exec(file.content);
179
+ if (!match) {
180
+ return {
181
+ ...file,
182
+ type: codegen_1.AIFileTypes.IGNORE,
183
+ };
184
+ }
185
+ const type = match[1].trim();
186
+ file.content = file.content.replace(rx, '');
187
+ return {
188
+ ...file,
189
+ type,
190
+ };
191
+ });
192
+ return allFiles;
193
+ }
194
+ /**
195
+ * Generates the code using codegen for a given block.
196
+ */
197
+ async generateBlock(yamlContent) {
198
+ if (!yamlContent.spec.target?.kind) {
199
+ //Not all block types have targets
200
+ return;
201
+ }
202
+ if (!(await codeGeneratorManager_1.codeGeneratorManager.ensureTarget(yamlContent.spec.target?.kind))) {
203
+ return;
204
+ }
205
+ const codeGenerator = new codegen_1.BlockCodeGenerator(yamlContent);
206
+ codeGenerator.withOption('AIContext', stormClient_1.STORM_ID);
207
+ return codeGenerator.generate();
208
+ }
209
+ }
210
+ exports.StormCodegen = StormCodegen;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Copyright 2023 Kapeta Inc.
3
+ * SPDX-License-Identifier: BUSL-1.1
4
+ */
5
+ import { StormEvent } from './events';
6
+ import { BlockDefinition, Plan } from '@kapeta/schemas';
7
+ import { KapetaURI } from '@kapeta/nodejs-utils';
8
+ export interface BlockDefinitionInfo {
9
+ uri: KapetaURI;
10
+ content: BlockDefinition;
11
+ aiName: string;
12
+ }
13
+ export interface ParsedResult {
14
+ plan: Plan;
15
+ blocks: BlockDefinitionInfo[];
16
+ }
17
+ export interface StormOptions {
18
+ databaseKind: string;
19
+ apiKind: string;
20
+ clientKind: string;
21
+ webPageKind: string;
22
+ webFragmentKind: string;
23
+ mqKind: string;
24
+ serviceKind: string;
25
+ serviceLanguage: string;
26
+ frontendKind: string;
27
+ frontendLanguage: string;
28
+ exchangeKind: string;
29
+ queueKind: string;
30
+ publisherKind: string;
31
+ subscriberKind: string;
32
+ jwtProviderKind: string;
33
+ jwtConsumerKind: string;
34
+ smtpKind: string;
35
+ externalApiKind: string;
36
+ cliKind: string;
37
+ cliLanguage: string;
38
+ desktopKind: string;
39
+ desktopLanguage: string;
40
+ gatewayKind: string;
41
+ }
42
+ export declare function resolveOptions(): Promise<StormOptions>;
43
+ export declare class StormEventParser {
44
+ private events;
45
+ private planName;
46
+ private planDescription;
47
+ private blocks;
48
+ private connections;
49
+ private failed;
50
+ private error;
51
+ private options;
52
+ constructor(options: StormOptions);
53
+ private reset;
54
+ addEvent(evt: StormEvent): void;
55
+ getEvents(): StormEvent[];
56
+ isValid(): boolean;
57
+ getError(): string;
58
+ private applyLayoutToBlocks;
59
+ toResult(handle: string): ParsedResult;
60
+ private toSafeName;
61
+ private toRef;
62
+ toBlockDefinitions(handle: string): {
63
+ [key: string]: BlockDefinitionInfo;
64
+ };
65
+ private toResourceKind;
66
+ private toBlockKind;
67
+ private toPortType;
68
+ private toBlockTarget;
69
+ private toBlockTargetKind;
70
+ }