@kapeta/local-cluster-service 0.30.1 → 0.32.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 (44) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/index.js +2 -0
  3. package/dist/cjs/src/ai/aiClient.d.ts +20 -0
  4. package/dist/cjs/src/ai/aiClient.js +74 -0
  5. package/dist/cjs/src/ai/routes.d.ts +7 -0
  6. package/dist/cjs/src/ai/routes.js +37 -0
  7. package/dist/cjs/src/ai/transform.d.ts +11 -0
  8. package/dist/cjs/src/ai/transform.js +239 -0
  9. package/dist/cjs/src/ai/types.d.ts +40 -0
  10. package/dist/cjs/src/ai/types.js +2 -0
  11. package/dist/cjs/src/codeGeneratorManager.js +4 -3
  12. package/dist/cjs/src/containerManager.js +7 -1
  13. package/dist/cjs/src/filesystem/routes.js +16 -0
  14. package/dist/cjs/src/filesystemManager.d.ts +4 -0
  15. package/dist/cjs/src/filesystemManager.js +14 -0
  16. package/dist/cjs/src/utils/utils.d.ts +1 -0
  17. package/dist/cjs/src/utils/utils.js +7 -1
  18. package/dist/esm/index.js +2 -0
  19. package/dist/esm/src/ai/aiClient.d.ts +20 -0
  20. package/dist/esm/src/ai/aiClient.js +74 -0
  21. package/dist/esm/src/ai/routes.d.ts +7 -0
  22. package/dist/esm/src/ai/routes.js +37 -0
  23. package/dist/esm/src/ai/transform.d.ts +11 -0
  24. package/dist/esm/src/ai/transform.js +239 -0
  25. package/dist/esm/src/ai/types.d.ts +40 -0
  26. package/dist/esm/src/ai/types.js +2 -0
  27. package/dist/esm/src/codeGeneratorManager.js +4 -3
  28. package/dist/esm/src/containerManager.js +7 -1
  29. package/dist/esm/src/filesystem/routes.js +16 -0
  30. package/dist/esm/src/filesystemManager.d.ts +4 -0
  31. package/dist/esm/src/filesystemManager.js +14 -0
  32. package/dist/esm/src/utils/utils.d.ts +1 -0
  33. package/dist/esm/src/utils/utils.js +7 -1
  34. package/index.ts +2 -0
  35. package/package.json +1 -1
  36. package/src/ai/aiClient.ts +93 -0
  37. package/src/ai/routes.ts +39 -0
  38. package/src/ai/transform.ts +275 -0
  39. package/src/ai/types.ts +45 -0
  40. package/src/codeGeneratorManager.ts +6 -4
  41. package/src/containerManager.ts +7 -1
  42. package/src/filesystem/routes.ts +22 -0
  43. package/src/filesystemManager.ts +18 -0
  44. package/src/utils/utils.ts +6 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [0.32.0](https://github.com/kapetacom/local-cluster-service/compare/v0.31.0...v0.32.0) (2023-12-20)
2
+
3
+
4
+ ### Features
5
+
6
+ * 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))
7
+
8
+ # [0.31.0](https://github.com/kapetacom/local-cluster-service/compare/v0.30.1...v0.31.0) (2023-12-18)
9
+
10
+
11
+ ### Features
12
+
13
+ * Get/set pixel grid settings ([538b63a](https://github.com/kapetacom/local-cluster-service/commit/538b63a7039d29b24a50c2d37063fe90465bad36))
14
+
1
15
  ## [0.30.1](https://github.com/kapetacom/local-cluster-service/compare/v0.30.0...v0.30.1) (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,
@@ -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 });
@@ -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;
@@ -40,6 +40,22 @@ router.post('/release-channel', (req, res) => {
40
40
  filesystemManager_1.filesystemManager.setReleaseChannel(req.stringBody ?? '');
41
41
  res.sendStatus(204);
42
42
  });
43
+ router.get('/show-pixel-grid', (req, res) => {
44
+ res.send(filesystemManager_1.filesystemManager.getShowPixelGrid());
45
+ });
46
+ router.use('/show-pixel-grid', stringBody_1.stringBody);
47
+ router.post('/show-pixel-grid', (req, res) => {
48
+ filesystemManager_1.filesystemManager.setShowPixelGrid(req?.stringBody === 'true' ?? false);
49
+ res.sendStatus(204);
50
+ });
51
+ router.get('/snap-to-pixel-grid', (req, res) => {
52
+ res.send(filesystemManager_1.filesystemManager.getSnapToPixelGrid());
53
+ });
54
+ router.use('/snap-to-pixel-grid', stringBody_1.stringBody);
55
+ router.post('/snap-to-pixel-grid', (req, res) => {
56
+ filesystemManager_1.filesystemManager.setSnapToPixelGrid(req?.stringBody === 'true' ?? false);
57
+ res.sendStatus(204);
58
+ });
43
59
  router.use('/', (req, res, next) => {
44
60
  if (!req.query.path) {
45
61
  res.status(400).send({ error: 'Missing required query parameter "path"' });
@@ -18,6 +18,10 @@ declare class FilesystemManager {
18
18
  setEditor(editor: string): void;
19
19
  getReleaseChannel(): string | undefined;
20
20
  setReleaseChannel(channel: string): void;
21
+ getShowPixelGrid(): boolean | undefined;
22
+ setShowPixelGrid(show: boolean): void;
23
+ getSnapToPixelGrid(): boolean | undefined;
24
+ setSnapToPixelGrid(snap: boolean): void;
21
25
  }
22
26
  export declare const filesystemManager: FilesystemManager;
23
27
  export {};
@@ -16,6 +16,8 @@ const SECTION_ID = 'filesystem';
16
16
  const PROJECT_ROOT = 'project_root';
17
17
  const EDITOR = 'editor';
18
18
  const RELEASE_CHANNEL = 'release_channel';
19
+ const SHOW_PIXEL_GRID = 'show_pixel_grid';
20
+ const SNAP_TO_PIXEL_GRID = 'snap_to_pixel_grid';
19
21
  function isFile(path) {
20
22
  try {
21
23
  return fs_1.default.statSync(path).isFile();
@@ -100,5 +102,17 @@ class FilesystemManager {
100
102
  setReleaseChannel(channel) {
101
103
  storageService_1.storageService.put('app', RELEASE_CHANNEL, channel);
102
104
  }
105
+ getShowPixelGrid() {
106
+ return storageService_1.storageService.get('app', SHOW_PIXEL_GRID, false);
107
+ }
108
+ setShowPixelGrid(show) {
109
+ storageService_1.storageService.put('app', SHOW_PIXEL_GRID, show);
110
+ }
111
+ getSnapToPixelGrid() {
112
+ return storageService_1.storageService.get('app', SNAP_TO_PIXEL_GRID, false);
113
+ }
114
+ setSnapToPixelGrid(snap) {
115
+ storageService_1.storageService.put('app', SNAP_TO_PIXEL_GRID, snap);
116
+ }
103
117
  }
104
118
  exports.filesystemManager = new FilesystemManager();
@@ -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;
@@ -7,15 +7,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
7
7
  return (mod && mod.__esModule) ? mod : { "default": mod };
8
8
  };
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.getResolvedConfiguration = exports.getBindHost = exports.isLinux = exports.isMac = exports.isWindows = exports.readYML = exports.getBlockInstanceContainerName = void 0;
10
+ exports.getResolvedConfiguration = exports.getBindHost = exports.isLinux = exports.isMac = exports.isWindows = exports.readYML = exports.getRemoteUrl = exports.getBlockInstanceContainerName = void 0;
11
11
  const node_fs_1 = __importDefault(require("node:fs"));
12
12
  const yaml_1 = __importDefault(require("yaml"));
13
13
  const md5_1 = __importDefault(require("md5"));
14
14
  const lodash_1 = __importDefault(require("lodash"));
15
+ const local_cluster_config_1 = __importDefault(require("@kapeta/local-cluster-config"));
15
16
  function getBlockInstanceContainerName(systemId, instanceId) {
16
17
  return `kapeta-block-instance-${(0, md5_1.default)(systemId + instanceId)}`;
17
18
  }
18
19
  exports.getBlockInstanceContainerName = getBlockInstanceContainerName;
20
+ function getRemoteUrl(id, defautValue) {
21
+ const remoteConfig = local_cluster_config_1.default.getClusterConfig().remote;
22
+ return remoteConfig?.[id] ?? defautValue;
23
+ }
24
+ exports.getRemoteUrl = getRemoteUrl;
19
25
  function readYML(path) {
20
26
  const rawYaml = node_fs_1.default.readFileSync(path);
21
27
  try {
package/dist/esm/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,
@@ -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 {};