@kapeta/local-cluster-service 0.31.0 → 0.32.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/index.js +2 -0
  3. package/dist/cjs/src/RepositoryWatcher.js +30 -6
  4. package/dist/cjs/src/ai/aiClient.d.ts +20 -0
  5. package/dist/cjs/src/ai/aiClient.js +74 -0
  6. package/dist/cjs/src/ai/routes.d.ts +7 -0
  7. package/dist/cjs/src/ai/routes.js +37 -0
  8. package/dist/cjs/src/ai/transform.d.ts +11 -0
  9. package/dist/cjs/src/ai/transform.js +239 -0
  10. package/dist/cjs/src/ai/types.d.ts +40 -0
  11. package/dist/cjs/src/ai/types.js +2 -0
  12. package/dist/cjs/src/assetManager.js +5 -2
  13. package/dist/cjs/src/codeGeneratorManager.js +4 -3
  14. package/dist/cjs/src/containerManager.js +7 -1
  15. package/dist/cjs/src/definitionsManager.js +2 -2
  16. package/dist/cjs/src/instanceManager.js +2 -2
  17. package/dist/cjs/src/repositoryManager.js +1 -1
  18. package/dist/cjs/src/utils/utils.d.ts +1 -0
  19. package/dist/cjs/src/utils/utils.js +7 -1
  20. package/dist/esm/index.js +2 -0
  21. package/dist/esm/src/RepositoryWatcher.js +30 -6
  22. package/dist/esm/src/ai/aiClient.d.ts +20 -0
  23. package/dist/esm/src/ai/aiClient.js +74 -0
  24. package/dist/esm/src/ai/routes.d.ts +7 -0
  25. package/dist/esm/src/ai/routes.js +37 -0
  26. package/dist/esm/src/ai/transform.d.ts +11 -0
  27. package/dist/esm/src/ai/transform.js +239 -0
  28. package/dist/esm/src/ai/types.d.ts +40 -0
  29. package/dist/esm/src/ai/types.js +2 -0
  30. package/dist/esm/src/assetManager.js +5 -2
  31. package/dist/esm/src/codeGeneratorManager.js +4 -3
  32. package/dist/esm/src/containerManager.js +7 -1
  33. package/dist/esm/src/definitionsManager.js +2 -2
  34. package/dist/esm/src/instanceManager.js +2 -2
  35. package/dist/esm/src/repositoryManager.js +1 -1
  36. package/dist/esm/src/utils/utils.d.ts +1 -0
  37. package/dist/esm/src/utils/utils.js +7 -1
  38. package/index.ts +2 -0
  39. package/package.json +1 -1
  40. package/src/RepositoryWatcher.ts +34 -6
  41. package/src/ai/aiClient.ts +93 -0
  42. package/src/ai/routes.ts +39 -0
  43. package/src/ai/transform.ts +275 -0
  44. package/src/ai/types.ts +45 -0
  45. package/src/assetManager.ts +5 -1
  46. package/src/codeGeneratorManager.ts +6 -4
  47. package/src/containerManager.ts +7 -1
  48. package/src/definitionsManager.ts +2 -2
  49. package/src/instanceManager.ts +2 -2
  50. package/src/repositoryManager.ts +1 -1
  51. package/src/utils/utils.ts +6 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [0.32.1](https://github.com/kapetacom/local-cluster-service/compare/v0.32.0...v0.32.1) (2023-12-23)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Several smaller fixes ([4036345](https://github.com/kapetacom/local-cluster-service/commit/40363457597d36c97481e94e646a6b45d113bb09))
7
+
8
+ # [0.32.0](https://github.com/kapetacom/local-cluster-service/compare/v0.31.0...v0.32.0) (2023-12-20)
9
+
10
+
11
+ ### Features
12
+
13
+ * Adds AI routes for generating plans from prompts ([#102](https://github.com/kapetacom/local-cluster-service/issues/102)) ([db73996](https://github.com/kapetacom/local-cluster-service/commit/db739965cd8c58252651e0522b11c062f00488af))
14
+
1
15
  # [0.31.0](https://github.com/kapetacom/local-cluster-service/compare/v0.30.1...v0.31.0) (2023-12-18)
2
16
 
3
17
 
package/dist/cjs/index.js CHANGED
@@ -49,6 +49,7 @@ const routes_8 = __importDefault(require("./src/providers/routes"));
49
49
  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
+ const routes_11 = __importDefault(require("./src/ai/routes"));
52
53
  const utils_1 = require("./src/utils/utils");
53
54
  const request_1 = __importDefault(require("request"));
54
55
  const repositoryManager_1 = require("./src/repositoryManager");
@@ -87,6 +88,7 @@ function createServer() {
87
88
  app.use('/attachments', routes_9.default);
88
89
  app.use('/tasks', routes_10.default);
89
90
  app.use('/api', api_1.default);
91
+ app.use('/ai', routes_11.default);
90
92
  app.get('/status', async (req, res) => {
91
93
  res.send({
92
94
  ok: true,
@@ -20,6 +20,24 @@ const cacheManager_1 = require("./cacheManager");
20
20
  const node_events_1 = require("node:events");
21
21
  const assetManager_1 = require("./assetManager");
22
22
  const KAPETA_YML_RX = /^kapeta.ya?ml$/;
23
+ let definitions;
24
+ let definitionTimeout;
25
+ function getDefinitionsDebounced() {
26
+ if (definitionTimeout) {
27
+ clearTimeout(definitionTimeout);
28
+ definitionTimeout = undefined;
29
+ }
30
+ if (!definitions) {
31
+ definitions = local_cluster_config_1.default.getDefinitions();
32
+ }
33
+ else {
34
+ definitionTimeout = setTimeout(() => {
35
+ definitionTimeout = undefined;
36
+ definitions = undefined;
37
+ }, 500);
38
+ }
39
+ return definitions;
40
+ }
23
41
  class RepositoryWatcher extends node_events_1.EventEmitter {
24
42
  watcher;
25
43
  disabled = false;
@@ -186,7 +204,7 @@ class RepositoryWatcher extends node_events_1.EventEmitter {
186
204
  }
187
205
  async checkForChange(assetIdentity, sourceOfChange) {
188
206
  const ymlPath = node_path_1.default.join(this.getRepositoryPath(assetIdentity), 'kapeta.yml');
189
- const newDefinitions = local_cluster_config_1.default.getDefinitions();
207
+ const newDefinitions = getDefinitionsDebounced();
190
208
  const newDefinition = newDefinitions.find((d) => d.ymlPath === ymlPath);
191
209
  let currentDefinition = this.allDefinitions.find((d) => d.ymlPath === ymlPath);
192
210
  const ymlExists = await this.exists(ymlPath);
@@ -265,11 +283,17 @@ class RepositoryWatcher extends node_events_1.EventEmitter {
265
283
  }
266
284
  catch (e) { }
267
285
  if (symbolicLink) {
268
- const realPath = node_path_1.default.join(await fs_extra_1.default.realpath(path), 'kapeta.yml');
269
- if (await this.exists(realPath)) {
270
- //console.log('Watching symlink target %s => %s', path, realPath);
271
- this.watcher?.add(realPath);
272
- this.symbolicLinks[path] = realPath;
286
+ try {
287
+ const realPath = node_path_1.default.join(await fs_extra_1.default.realpath(path), 'kapeta.yml');
288
+ if (await this.exists(realPath)) {
289
+ //console.log('Watching symlink target %s => %s', path, realPath);
290
+ this.watcher?.add(realPath);
291
+ this.symbolicLinks[path] = realPath;
292
+ }
293
+ }
294
+ catch (e) {
295
+ // Remove the symlink - it's broken
296
+ await fs_extra_1.default.remove(path);
273
297
  }
274
298
  }
275
299
  }
@@ -0,0 +1,20 @@
1
+ import { PlanContext } from './transform';
2
+ export type PromptResult = {
3
+ explanation: string;
4
+ response: string;
5
+ context?: PlanContext;
6
+ };
7
+ export interface AIMessage {
8
+ content: string;
9
+ role: 'user' | 'assistant';
10
+ }
11
+ export interface AIRequest {
12
+ messages: AIMessage[];
13
+ }
14
+ declare class AIClient {
15
+ private readonly _baseUrl;
16
+ constructor();
17
+ sendPrompt(handle: string, body: AIRequest): Promise<PromptResult>;
18
+ }
19
+ export declare const aiClient: AIClient;
20
+ export {};
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.aiClient = void 0;
7
+ /**
8
+ * Copyright 2023 Kapeta Inc.
9
+ * SPDX-License-Identifier: BUSL-1.1
10
+ */
11
+ const request_1 = __importDefault(require("request"));
12
+ const transform_1 = require("./transform");
13
+ const nodejs_api_client_1 = require("@kapeta/nodejs-api-client");
14
+ const utils_1 = require("../utils/utils");
15
+ class AIClient {
16
+ _baseUrl;
17
+ constructor() {
18
+ this._baseUrl = (0, utils_1.getRemoteUrl)('ai-service', 'https://ai.kapeta.com');
19
+ }
20
+ async sendPrompt(handle, body) {
21
+ const url = `${this._baseUrl}/v1/plan?type=chat`;
22
+ const headers = {};
23
+ const api = new nodejs_api_client_1.KapetaAPI();
24
+ if (api.hasToken()) {
25
+ headers['Authorization'] = `Bearer ${await api.getAccessToken()}`;
26
+ }
27
+ const options = {
28
+ url,
29
+ method: 'POST',
30
+ json: true,
31
+ body,
32
+ headers,
33
+ };
34
+ return new Promise((resolve, reject) => {
35
+ (0, request_1.default)(options, async (error, response, application) => {
36
+ if (error) {
37
+ console.error(error);
38
+ reject(error);
39
+ }
40
+ if (response.statusCode !== 200) {
41
+ console.log('Prompt failed', response.statusCode, response.body);
42
+ reject(new Error(`Invalid response code: ${response.statusCode}`));
43
+ return;
44
+ }
45
+ try {
46
+ if (application?.name) {
47
+ const planContext = await (0, transform_1.transformToPlan)(handle, application);
48
+ resolve({
49
+ explanation: application.explanation,
50
+ response: application.response ?? application.explanation ?? 'Plan was generated',
51
+ context: planContext,
52
+ });
53
+ }
54
+ else {
55
+ resolve({
56
+ explanation: application.explanation,
57
+ response: application.response ??
58
+ application.explanation ??
59
+ 'I did not understand your request. Please rephrase.',
60
+ });
61
+ }
62
+ }
63
+ catch (err) {
64
+ console.error(err);
65
+ resolve({
66
+ explanation: '',
67
+ response: 'I did not understand your request. Please rephrase.',
68
+ });
69
+ }
70
+ });
71
+ });
72
+ }
73
+ }
74
+ exports.aiClient = new AIClient();
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Copyright 2023 Kapeta Inc.
3
+ * SPDX-License-Identifier: BUSL-1.1
4
+ */
5
+ /// <reference types="express" />
6
+ declare const router: import("express").Router;
7
+ export default router;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright 2023 Kapeta Inc.
4
+ * SPDX-License-Identifier: BUSL-1.1
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ const express_promise_router_1 = __importDefault(require("express-promise-router"));
11
+ const cors_1 = require("../middleware/cors");
12
+ const stringBody_1 = require("../middleware/stringBody");
13
+ const aiClient_1 = require("./aiClient");
14
+ const yaml_1 = __importDefault(require("yaml"));
15
+ const router = (0, express_promise_router_1.default)();
16
+ router.use('/', cors_1.corsHandler);
17
+ router.use('/', stringBody_1.stringBody);
18
+ router.post('/prompt/:handle', async (req, res) => {
19
+ const handle = req.params.handle;
20
+ try {
21
+ const aiRequest = JSON.parse(req.stringBody ?? '{}');
22
+ const result = await aiClient_1.aiClient.sendPrompt(handle, aiRequest);
23
+ if (req.accepts('application/yaml')) {
24
+ res.set('Content-Type', 'application/yaml');
25
+ res.send(yaml_1.default.stringify(result));
26
+ }
27
+ else {
28
+ res.json(result);
29
+ }
30
+ }
31
+ catch (err) {
32
+ console.error('Failed to send prompt', err);
33
+ res.status(400).send({ error: err.message });
34
+ return;
35
+ }
36
+ });
37
+ exports.default = router;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Copyright 2023 Kapeta Inc.
3
+ * SPDX-License-Identifier: BUSL-1.1
4
+ */
5
+ import { BlockDefinition, Plan } from '@kapeta/schemas';
6
+ import { Application } from './types';
7
+ export type PlanContext = {
8
+ plan: Plan;
9
+ blocks: BlockDefinition[];
10
+ };
11
+ export declare const transformToPlan: (handle: string, application: Application) => Promise<PlanContext>;
@@ -0,0 +1,239 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.transformToPlan = void 0;
7
+ const definitionsManager_1 = require("../definitionsManager");
8
+ const nodejs_utils_1 = require("@kapeta/nodejs-utils");
9
+ const node_uuid_1 = __importDefault(require("node-uuid"));
10
+ async function getFreeName(name) {
11
+ let currentName = name;
12
+ let iteration = 1;
13
+ do {
14
+ const found = await definitionsManager_1.definitionsManager.getLatestDefinition(currentName);
15
+ if (!found) {
16
+ return currentName;
17
+ }
18
+ currentName = name + '_' + iteration++;
19
+ } while (true);
20
+ }
21
+ const transformToPlan = async (handle, application) => {
22
+ const blockTypeService = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/block-type-service');
23
+ const blockTypeFrontend = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/block-type-frontend');
24
+ const mongoDbResource = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/resource-type-mongodb');
25
+ const postgresResource = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/resource-type-postgresql');
26
+ const webPageResource = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/resource-type-web-page');
27
+ const webFragmentResource = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/resource-type-web-fragment');
28
+ const restApiResource = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/resource-type-rest-api');
29
+ const restClientResource = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/resource-type-rest-client');
30
+ const javaLanguage = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/language-target-java-spring-boot');
31
+ const nodejsLanguage = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/language-target-nodejs');
32
+ const reactLanguage = await definitionsManager_1.definitionsManager.getLatestDefinition('kapeta/language-target-react-ts');
33
+ if (!blockTypeService ||
34
+ !blockTypeFrontend ||
35
+ !mongoDbResource ||
36
+ !postgresResource ||
37
+ !javaLanguage ||
38
+ !nodejsLanguage ||
39
+ !reactLanguage ||
40
+ !webPageResource ||
41
+ !restApiResource ||
42
+ !restClientResource ||
43
+ !webFragmentResource) {
44
+ throw new Error('Missing definitions');
45
+ }
46
+ const plan = {
47
+ kind: 'core/plan',
48
+ metadata: {
49
+ name: await getFreeName(`${handle}/${application.name}`),
50
+ title: application.title,
51
+ description: application.description,
52
+ visibility: 'private',
53
+ },
54
+ spec: {
55
+ blocks: [],
56
+ connections: [],
57
+ },
58
+ };
59
+ const blocks = [];
60
+ const addToPlan = (ref, name) => {
61
+ const top = 100 + Math.floor(plan.spec.blocks.length / 3) * 300;
62
+ const left = 200 + (plan.spec.blocks.length % 3) * 450;
63
+ plan.spec.blocks.push({
64
+ block: {
65
+ ref,
66
+ },
67
+ name,
68
+ id: node_uuid_1.default.v4(),
69
+ dimensions: {
70
+ top,
71
+ left,
72
+ width: -1,
73
+ height: -1,
74
+ },
75
+ });
76
+ };
77
+ const nameMapper = new Map();
78
+ for (const backend of application.backends) {
79
+ const blockName = `${handle}/${backend.name}`;
80
+ const blockRealName = await getFreeName(blockName);
81
+ nameMapper.set(blockName, blockRealName);
82
+ const language = backend.targetLanguage === 'java' ? javaLanguage : nodejsLanguage;
83
+ const databaseInfo = backend.databases?.[0];
84
+ const database = databaseInfo?.type === 'mongodb' ? mongoDbResource : postgresResource;
85
+ const blockRef = (0, nodejs_utils_1.normalizeKapetaUri)(blockRealName + ':local');
86
+ let targetOptions = {};
87
+ if (backend.targetLanguage === 'java') {
88
+ targetOptions = {
89
+ basePackage: `${handle}.${application.name}`.toLowerCase().replace(/-/g, '_'),
90
+ groupId: `${handle}.${application.name}`.toLowerCase().replace(/-/g, '_'),
91
+ artifactId: backend.name.toLowerCase().replace(/-/g, '_'),
92
+ };
93
+ }
94
+ blocks.push({
95
+ kind: (0, nodejs_utils_1.normalizeKapetaUri)(`${blockTypeService.definition.metadata.name}:${blockTypeService.version}`),
96
+ metadata: {
97
+ name: blockRealName,
98
+ title: backend.title,
99
+ description: backend.description,
100
+ visibility: 'private',
101
+ },
102
+ spec: {
103
+ target: {
104
+ kind: (0, nodejs_utils_1.normalizeKapetaUri)(`${language.definition.metadata.name}:${language.version}`),
105
+ options: targetOptions,
106
+ },
107
+ consumers: [
108
+ {
109
+ kind: (0, nodejs_utils_1.normalizeKapetaUri)(`${database.definition.metadata.name}:${database.version}`),
110
+ metadata: {
111
+ name: (databaseInfo?.name ?? 'main').replace(/-/g, ''),
112
+ },
113
+ spec: {
114
+ port: {
115
+ type: database.definition.spec.ports[0].type,
116
+ },
117
+ },
118
+ },
119
+ ],
120
+ providers: [
121
+ {
122
+ kind: (0, nodejs_utils_1.normalizeKapetaUri)(`${restApiResource.definition.metadata.name}:${restApiResource.version}`),
123
+ metadata: {
124
+ name: 'main',
125
+ },
126
+ spec: {
127
+ port: {
128
+ type: restApiResource.definition.spec.ports[0].type,
129
+ },
130
+ },
131
+ },
132
+ ],
133
+ },
134
+ });
135
+ addToPlan(blockRef, backend.name);
136
+ }
137
+ for (const frontend of application.frontends) {
138
+ const blockName = `${handle}/${frontend.name}`;
139
+ const blockRealName = await getFreeName(blockName);
140
+ nameMapper.set(blockName, blockRealName);
141
+ const language = reactLanguage;
142
+ const blockRef = (0, nodejs_utils_1.normalizeKapetaUri)(blockRealName + ':local');
143
+ blocks.push({
144
+ kind: (0, nodejs_utils_1.normalizeKapetaUri)(`${blockTypeFrontend.definition.metadata.name}:${blockTypeFrontend.version}`),
145
+ metadata: {
146
+ name: blockRealName,
147
+ title: frontend.title,
148
+ description: frontend.description,
149
+ visibility: 'private',
150
+ },
151
+ spec: {
152
+ target: {
153
+ kind: (0, nodejs_utils_1.normalizeKapetaUri)(`${language.definition.metadata.name}:${language.version}`),
154
+ options: {},
155
+ },
156
+ consumers: [],
157
+ providers: [
158
+ {
159
+ kind: (0, nodejs_utils_1.normalizeKapetaUri)(`${webPageResource.definition.metadata.name}:${webPageResource.version}`),
160
+ metadata: {
161
+ name: 'main',
162
+ },
163
+ spec: {
164
+ port: {
165
+ type: webPageResource.definition.spec.ports[0].type,
166
+ },
167
+ },
168
+ },
169
+ ],
170
+ },
171
+ });
172
+ addToPlan(blockRef, frontend.name);
173
+ }
174
+ application.connections?.forEach((connection) => {
175
+ const fullProviderName = nameMapper.get(`${handle}/${connection.provider.name}`);
176
+ const fullConsumerName = nameMapper.get(`${handle}/${connection.consumer.name}`);
177
+ const consumerResourceName = connection.provider.name;
178
+ const providerRef = (0, nodejs_utils_1.normalizeKapetaUri)(`${fullProviderName}:local`);
179
+ const consumerRef = (0, nodejs_utils_1.normalizeKapetaUri)(`${fullConsumerName}:local`);
180
+ const instanceProvider = plan.spec.blocks.find((b) => b.block.ref === providerRef);
181
+ const instanceConsumer = plan.spec.blocks.find((b) => b.block.ref === consumerRef);
182
+ const consumerBlock = blocks.find((block) => block.metadata.name === fullConsumerName);
183
+ const providerBlock = blocks.find((block) => block.metadata.name === fullProviderName);
184
+ if (!consumerBlock) {
185
+ throw new Error('Missing consumer block: ' + fullConsumerName);
186
+ }
187
+ if (!providerBlock) {
188
+ throw new Error('Missing provider block: ' + fullProviderName);
189
+ }
190
+ const portType = (0, nodejs_utils_1.parseKapetaUri)(providerBlock.kind).fullName === 'kapeta/block-type-service' ? 'rest' : 'web';
191
+ if (portType === 'rest') {
192
+ consumerBlock.spec.consumers.push({
193
+ kind: (0, nodejs_utils_1.normalizeKapetaUri)(`${restClientResource.definition.metadata.name}:${restClientResource.version}`),
194
+ metadata: {
195
+ name: consumerResourceName,
196
+ },
197
+ spec: {
198
+ port: {
199
+ type: 'rest',
200
+ },
201
+ },
202
+ });
203
+ }
204
+ else {
205
+ consumerBlock.spec.consumers.push({
206
+ kind: (0, nodejs_utils_1.normalizeKapetaUri)(`${webFragmentResource.definition.metadata.name}:${webFragmentResource.version}`),
207
+ metadata: {
208
+ name: consumerResourceName,
209
+ },
210
+ spec: {
211
+ port: {
212
+ type: 'web',
213
+ },
214
+ },
215
+ });
216
+ }
217
+ plan.spec.connections.push({
218
+ provider: {
219
+ blockId: instanceProvider.id,
220
+ resourceName: 'main',
221
+ port: {
222
+ type: portType,
223
+ },
224
+ },
225
+ consumer: {
226
+ blockId: instanceConsumer.id,
227
+ resourceName: consumerResourceName,
228
+ port: {
229
+ type: portType,
230
+ },
231
+ },
232
+ });
233
+ });
234
+ return {
235
+ plan,
236
+ blocks,
237
+ };
238
+ };
239
+ exports.transformToPlan = transformToPlan;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Copyright 2023 Kapeta Inc.
3
+ * SPDX-License-Identifier: BUSL-1.1
4
+ */
5
+ export interface Database {
6
+ name: string;
7
+ type: 'mongodb' | 'postgres';
8
+ }
9
+ export interface BackendService {
10
+ name: string;
11
+ title: string;
12
+ description: string;
13
+ targetLanguage: 'java' | 'node';
14
+ databases: Database[] | null;
15
+ }
16
+ export interface FrontendService {
17
+ name: string;
18
+ title: string;
19
+ description: string;
20
+ targetLanguage: 'react';
21
+ }
22
+ export interface Endpoint {
23
+ type: 'backend' | 'frontend';
24
+ name: string;
25
+ }
26
+ export interface Connection {
27
+ provider: Endpoint;
28
+ consumer: Endpoint;
29
+ }
30
+ export interface Application {
31
+ kind: 'core/plan';
32
+ name: string;
33
+ title: string;
34
+ description: string;
35
+ backends: BackendService[];
36
+ frontends: FrontendService[];
37
+ connections: Connection[];
38
+ explanation: string;
39
+ response: string;
40
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -24,10 +24,13 @@ const node_os_1 = __importDefault(require("node:os"));
24
24
  const CACHE_TTL = 60 * 60 * 1000; // 1 hour
25
25
  const UPGRADE_CHECK_INTERVAL = 10 * 60 * 1000; // 10 minutes
26
26
  const toKey = (ref) => `assetManager:asset:${ref}`;
27
+ function filterExists(asset) {
28
+ return fs_extra_1.default.existsSync(asset.path);
29
+ }
27
30
  function enrichAsset(asset) {
28
31
  return {
29
32
  ref: `kapeta://${asset.definition.metadata.name}:${asset.version}`,
30
- editable: asset.version === 'local',
33
+ editable: asset.version === 'local', //Only local versions are editable
31
34
  exists: true,
32
35
  version: asset.version,
33
36
  kind: asset.definition.kind,
@@ -81,7 +84,7 @@ class AssetManager {
81
84
  assetKinds.push('core/plan');
82
85
  }
83
86
  const assets = await definitionsManager_1.definitionsManager.getDefinitions(assetKinds);
84
- return assets.map(enrichAsset);
87
+ return assets.filter(filterExists).map(enrichAsset);
85
88
  }
86
89
  async getPlans() {
87
90
  return this.getAssets(['core/plan']);
@@ -58,13 +58,14 @@ class CodeGeneratorManager {
58
58
  });
59
59
  }
60
60
  async canGenerateCode(yamlContent) {
61
- if (!yamlContent.spec.target?.kind) {
61
+ if (!yamlContent.spec?.target?.kind || !yamlContent.kind) {
62
62
  //Not all block types have targets
63
63
  return false;
64
64
  }
65
+ const kindUri = (0, nodejs_utils_1.parseKapetaUri)(yamlContent.kind);
65
66
  const blockTypes = await definitionsManager_1.definitionsManager.getDefinitions(BLOCK_TYPE_KIND);
66
- const blockTypeKinds = blockTypes.map((blockType) => blockType.definition.metadata.name.toLowerCase() + ':' + blockType.version);
67
- return !!(yamlContent && yamlContent.kind && blockTypeKinds.indexOf(yamlContent.kind.toLowerCase()) > -1);
67
+ const blockTypeKinds = blockTypes.map((blockType) => (0, nodejs_utils_1.parseKapetaUri)(blockType.definition.metadata.name).fullName);
68
+ return blockTypeKinds.includes(kindUri.fullName);
68
69
  }
69
70
  async generate(yamlFile, yamlContent) {
70
71
  if (!yamlContent.spec.target?.kind) {
@@ -98,11 +98,17 @@ class ContainerManager {
98
98
  ];
99
99
  for (const opts of connectOptions) {
100
100
  try {
101
+ const testClient = new dockerode_1.default({
102
+ ...opts,
103
+ timeout: 1000, // 1 secs should be enough for a ping
104
+ });
105
+ await testClient.ping();
106
+ // If we get here - we have a working connection
107
+ // Now create a client with a longer timeout for all other operations
101
108
  const client = new dockerode_1.default({
102
109
  ...opts,
103
110
  timeout: 15 * 60 * 1000, //15 minutes should be enough for any operation
104
111
  });
105
- await client.ping();
106
112
  this._docker = client;
107
113
  const versionInfo = await client.version();
108
114
  this._version = versionInfo.Server?.Version ?? versionInfo.Version;
@@ -131,9 +131,9 @@ class DefinitionsManager {
131
131
  const definitions = await this.getDefinitions();
132
132
  return definitions.find((d) => {
133
133
  if (!uri.version) {
134
- return d.definition.metadata.name === uri.fullName;
134
+ return d.definition.metadata.name.toLowerCase() === uri.fullName.toLowerCase();
135
135
  }
136
- return (0, nodejs_utils_1.parseKapetaUri)(`${d.definition.metadata.name}:${d.version}`).id === uri.id;
136
+ return (0, nodejs_utils_1.parseKapetaUri)(`${d.definition.metadata.name}:${d.version}`).equals(uri);
137
137
  });
138
138
  }
139
139
  async getLatestDefinition(name) {
@@ -25,7 +25,7 @@ const nodejs_utils_1 = require("@kapeta/nodejs-utils");
25
25
  const definitionsManager_1 = require("./definitionsManager");
26
26
  const taskManager_1 = require("./taskManager");
27
27
  const CHECK_INTERVAL = 5000;
28
- const DEFAULT_HEALTH_PORT_TYPE = 'rest';
28
+ const DEFAULT_HEALTH_PORT_TYPE = 'http';
29
29
  const MIN_TIME_RUNNING = 30000; //If something didnt run for more than 30 secs - it failed
30
30
  class InstanceManager {
31
31
  _interval = undefined;
@@ -198,7 +198,7 @@ class InstanceManager {
198
198
  }
199
199
  getHealthUrl(info, address) {
200
200
  let healthUrl = null;
201
- let health = info.health;
201
+ let health = info.health ?? '/.kapeta/health';
202
202
  if (health) {
203
203
  if (health.startsWith('/')) {
204
204
  health = health.substring(1);
@@ -183,7 +183,7 @@ class RepositoryManager extends node_events_1.EventEmitter {
183
183
  if (await definitionsManager_1.definitionsManager.exists(ref)) {
184
184
  return;
185
185
  }
186
- throw new Error(`Failed to install asset: ${ref}`);
186
+ throw new Error(`Failed to find asset after installation: ${ref}. Please try again`);
187
187
  };
188
188
  };
189
189
  const tasks = [];
@@ -5,6 +5,7 @@
5
5
  import { EntityList } from '@kapeta/schemas';
6
6
  import { AnyMap } from '../types';
7
7
  export declare function getBlockInstanceContainerName(systemId: string, instanceId: string): string;
8
+ export declare function getRemoteUrl(id: string, defautValue: string): any;
8
9
  export declare function readYML(path: string): any;
9
10
  export declare function isWindows(): boolean;
10
11
  export declare function isMac(): boolean;