@kapeta/local-cluster-service 0.43.3 → 0.45.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.
- package/CHANGELOG.md +20 -0
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/src/codeGeneratorManager.d.ts +1 -0
- package/dist/cjs/src/codeGeneratorManager.js +12 -6
- package/dist/cjs/src/middleware/cors.d.ts +1 -0
- package/dist/cjs/src/middleware/kapeta.d.ts +1 -0
- package/dist/cjs/src/middleware/stringBody.d.ts +1 -0
- package/dist/cjs/src/proxy/routes.js +19 -19
- package/dist/cjs/src/proxy/types/rest.d.ts +2 -4
- package/dist/cjs/src/proxy/types/rest.js +45 -6
- package/dist/cjs/src/proxy/types/web.d.ts +2 -4
- package/dist/cjs/src/proxy/types/web.js +2 -2
- package/dist/cjs/src/storm/codegen.d.ts +36 -0
- package/dist/cjs/src/storm/codegen.js +160 -0
- package/dist/cjs/src/storm/event-parser.d.ts +70 -0
- package/dist/cjs/src/storm/event-parser.js +543 -0
- package/dist/cjs/src/storm/events.d.ts +127 -0
- package/dist/cjs/src/storm/events.js +6 -0
- package/dist/cjs/src/storm/routes.d.ts +7 -0
- package/dist/cjs/src/storm/routes.js +109 -0
- package/dist/cjs/src/storm/stormClient.d.ts +13 -0
- package/dist/cjs/src/storm/stormClient.js +87 -0
- package/dist/cjs/src/storm/stream.d.ts +38 -0
- package/dist/cjs/src/storm/stream.js +57 -0
- package/dist/cjs/src/types.d.ts +5 -2
- package/dist/esm/index.js +2 -0
- package/dist/esm/src/codeGeneratorManager.d.ts +1 -0
- package/dist/esm/src/codeGeneratorManager.js +12 -6
- package/dist/esm/src/middleware/cors.d.ts +1 -0
- package/dist/esm/src/middleware/kapeta.d.ts +1 -0
- package/dist/esm/src/middleware/stringBody.d.ts +1 -0
- package/dist/esm/src/proxy/routes.js +19 -19
- package/dist/esm/src/proxy/types/rest.d.ts +2 -4
- package/dist/esm/src/proxy/types/rest.js +45 -6
- package/dist/esm/src/proxy/types/web.d.ts +2 -4
- package/dist/esm/src/proxy/types/web.js +2 -2
- package/dist/esm/src/storm/codegen.d.ts +36 -0
- package/dist/esm/src/storm/codegen.js +160 -0
- package/dist/esm/src/storm/event-parser.d.ts +70 -0
- package/dist/esm/src/storm/event-parser.js +543 -0
- package/dist/esm/src/storm/events.d.ts +127 -0
- package/dist/esm/src/storm/events.js +6 -0
- package/dist/esm/src/storm/routes.d.ts +7 -0
- package/dist/esm/src/storm/routes.js +109 -0
- package/dist/esm/src/storm/stormClient.d.ts +13 -0
- package/dist/esm/src/storm/stormClient.js +87 -0
- package/dist/esm/src/storm/stream.d.ts +38 -0
- package/dist/esm/src/storm/stream.js +57 -0
- package/dist/esm/src/types.d.ts +5 -2
- package/index.ts +2 -0
- package/package.json +6 -3
- package/src/codeGeneratorManager.ts +17 -8
- package/src/proxy/routes.ts +26 -20
- package/src/proxy/types/rest.ts +73 -11
- package/src/proxy/types/web.ts +3 -5
- package/src/storm/codegen.ts +210 -0
- package/src/storm/event-parser.ts +688 -0
- package/src/storm/events.ts +169 -0
- package/src/storm/routes.ts +143 -0
- package/src/storm/stormClient.ts +114 -0
- package/src/storm/stream.ts +88 -0
- package/src/types.ts +5 -2
- package/src/utils/BlockInstanceRunner.ts +4 -2
@@ -0,0 +1,109 @@
|
|
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 stormClient_1 = require("./stormClient");
|
14
|
+
const event_parser_1 = require("./event-parser");
|
15
|
+
const codegen_1 = require("./codegen");
|
16
|
+
const router = (0, express_promise_router_1.default)();
|
17
|
+
router.use('/', cors_1.corsHandler);
|
18
|
+
router.use('/', stringBody_1.stringBody);
|
19
|
+
router.post('/:handle/all', async (req, res) => {
|
20
|
+
const handle = req.params.handle;
|
21
|
+
try {
|
22
|
+
const stormOptions = await (0, event_parser_1.resolveOptions)();
|
23
|
+
const eventParser = new event_parser_1.StormEventParser(stormOptions);
|
24
|
+
console.log('Got prompt', req.stringBody);
|
25
|
+
const aiRequest = JSON.parse(req.stringBody ?? '{}');
|
26
|
+
const metaStream = await stormClient_1.stormClient.createMetadata(aiRequest.prompt, aiRequest.history);
|
27
|
+
res.set('Content-Type', 'application/x-ndjson');
|
28
|
+
metaStream.on('data', (data) => {
|
29
|
+
eventParser.addEvent(data);
|
30
|
+
});
|
31
|
+
await streamStormPartialResponse(metaStream, res);
|
32
|
+
if (!eventParser.isValid()) {
|
33
|
+
// We can't continue if the meta stream is invalid
|
34
|
+
res.write({
|
35
|
+
type: 'ERROR_INTERNAL',
|
36
|
+
payload: { error: eventParser.getError() },
|
37
|
+
reason: 'Failed to generate system',
|
38
|
+
created: Date.now(),
|
39
|
+
});
|
40
|
+
res.end();
|
41
|
+
return;
|
42
|
+
}
|
43
|
+
const result = eventParser.toResult(handle);
|
44
|
+
console.log('RESULT\n', JSON.stringify(result, null, 2));
|
45
|
+
const stormCodegen = new codegen_1.StormCodegen(aiRequest.prompt, result.blocks);
|
46
|
+
const codegenStream = streamStormPartialResponse(stormCodegen.getStream(), res);
|
47
|
+
await stormCodegen.process();
|
48
|
+
await codegenStream;
|
49
|
+
sendDone(res);
|
50
|
+
}
|
51
|
+
catch (err) {
|
52
|
+
sendError(err, res);
|
53
|
+
}
|
54
|
+
});
|
55
|
+
router.post('/metadata', async (req, res) => {
|
56
|
+
const aiRequest = JSON.parse(req.stringBody ?? '{}');
|
57
|
+
const result = await stormClient_1.stormClient.createMetadata(aiRequest.prompt, aiRequest.history);
|
58
|
+
await streamStormResponse(result, res);
|
59
|
+
});
|
60
|
+
router.post('/services/implement', async (req, res) => {
|
61
|
+
const aiRequest = JSON.parse(req.stringBody ?? '{}');
|
62
|
+
const result = await stormClient_1.stormClient.createServiceImplementation(aiRequest.prompt, aiRequest.history);
|
63
|
+
await streamStormResponse(result, res);
|
64
|
+
});
|
65
|
+
router.post('/ui/implement', async (req, res) => {
|
66
|
+
const aiRequest = JSON.parse(req.stringBody ?? '{}');
|
67
|
+
const result = await stormClient_1.stormClient.createUIImplementation(aiRequest.prompt, aiRequest.history);
|
68
|
+
await streamStormResponse(result, res);
|
69
|
+
});
|
70
|
+
async function streamStormResponse(result, res) {
|
71
|
+
res.set('Content-Type', 'application/x-ndjson');
|
72
|
+
await streamStormPartialResponse(result, res);
|
73
|
+
sendDone(res);
|
74
|
+
}
|
75
|
+
function sendDone(res) {
|
76
|
+
res.write(JSON.stringify({
|
77
|
+
type: 'DONE',
|
78
|
+
created: Date.now(),
|
79
|
+
}) + '\n');
|
80
|
+
res.end();
|
81
|
+
}
|
82
|
+
function sendError(err, res) {
|
83
|
+
console.error('Failed to send prompt', err);
|
84
|
+
if (res.headersSent) {
|
85
|
+
res.write(JSON.stringify({
|
86
|
+
type: 'ERROR_INTERNAL',
|
87
|
+
created: Date.now(),
|
88
|
+
payload: { error: err.message },
|
89
|
+
reason: 'Failed while sending prompt',
|
90
|
+
}) + '\n');
|
91
|
+
}
|
92
|
+
else {
|
93
|
+
res.status(400).send({ error: err.message });
|
94
|
+
}
|
95
|
+
}
|
96
|
+
function streamStormPartialResponse(result, res) {
|
97
|
+
return new Promise((resolve, reject) => {
|
98
|
+
result.on('data', (data) => {
|
99
|
+
res.write(JSON.stringify(data) + '\n');
|
100
|
+
});
|
101
|
+
result.on('error', (err) => {
|
102
|
+
reject(err);
|
103
|
+
});
|
104
|
+
result.on('end', () => {
|
105
|
+
resolve();
|
106
|
+
});
|
107
|
+
});
|
108
|
+
}
|
109
|
+
exports.default = router;
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import { ConversationItem, StormFileImplementationPrompt, StormStream } from './stream';
|
2
|
+
export declare const STORM_ID = "storm";
|
3
|
+
declare class StormClient {
|
4
|
+
private readonly _baseUrl;
|
5
|
+
constructor();
|
6
|
+
private createOptions;
|
7
|
+
private send;
|
8
|
+
createMetadata(prompt: string, history?: ConversationItem[]): Promise<StormStream>;
|
9
|
+
createUIImplementation(prompt: StormFileImplementationPrompt, history?: ConversationItem[]): Promise<StormStream>;
|
10
|
+
createServiceImplementation(prompt: StormFileImplementationPrompt, history?: ConversationItem[]): Promise<StormStream>;
|
11
|
+
}
|
12
|
+
export declare const stormClient: StormClient;
|
13
|
+
export {};
|
@@ -0,0 +1,87 @@
|
|
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.stormClient = exports.STORM_ID = void 0;
|
7
|
+
/**
|
8
|
+
* Copyright 2023 Kapeta Inc.
|
9
|
+
* SPDX-License-Identifier: BUSL-1.1
|
10
|
+
*/
|
11
|
+
const nodejs_api_client_1 = require("@kapeta/nodejs-api-client");
|
12
|
+
const utils_1 = require("../utils/utils");
|
13
|
+
const promises_1 = __importDefault(require("node:readline/promises"));
|
14
|
+
const node_stream_1 = require("node:stream");
|
15
|
+
const stream_1 = require("./stream");
|
16
|
+
exports.STORM_ID = 'storm';
|
17
|
+
class StormClient {
|
18
|
+
_baseUrl;
|
19
|
+
constructor() {
|
20
|
+
this._baseUrl = (0, utils_1.getRemoteUrl)('ai-service', 'https://ai.kapeta.com');
|
21
|
+
}
|
22
|
+
createOptions(path, method, body) {
|
23
|
+
const url = `${this._baseUrl}${path}`;
|
24
|
+
const headers = {
|
25
|
+
'Content-Type': 'application/json',
|
26
|
+
};
|
27
|
+
const api = new nodejs_api_client_1.KapetaAPI();
|
28
|
+
if (api.hasToken()) {
|
29
|
+
//headers['Authorization'] = `Bearer ${api.getAccessToken()}`; //TODO: Enable authentication
|
30
|
+
}
|
31
|
+
return {
|
32
|
+
url,
|
33
|
+
method: method,
|
34
|
+
body: JSON.stringify(body),
|
35
|
+
headers,
|
36
|
+
};
|
37
|
+
}
|
38
|
+
async send(path, body, method = 'POST') {
|
39
|
+
const stringPrompt = typeof body.prompt === 'string' ? body.prompt : JSON.stringify(body.prompt);
|
40
|
+
const options = this.createOptions(path, method, {
|
41
|
+
prompt: stringPrompt,
|
42
|
+
history: body.history,
|
43
|
+
});
|
44
|
+
const out = new stream_1.StormStream(stringPrompt, body.history);
|
45
|
+
const response = await fetch(options.url, options);
|
46
|
+
if (response.status !== 200) {
|
47
|
+
throw new Error(`Got error response from ${options.url}: ${response.status}\nContent: ${await response.text()}`);
|
48
|
+
}
|
49
|
+
const jsonLStream = promises_1.default.createInterface(node_stream_1.Readable.fromWeb(response.body));
|
50
|
+
jsonLStream.on('line', (line) => {
|
51
|
+
out.addJSONLine(line);
|
52
|
+
});
|
53
|
+
jsonLStream.on('error', (error) => {
|
54
|
+
out.emit('error', error);
|
55
|
+
});
|
56
|
+
jsonLStream.on('pause', () => {
|
57
|
+
console.log('paused');
|
58
|
+
});
|
59
|
+
jsonLStream.on('resume', () => {
|
60
|
+
console.log('resumed');
|
61
|
+
});
|
62
|
+
jsonLStream.on('close', () => {
|
63
|
+
out.end();
|
64
|
+
});
|
65
|
+
return out;
|
66
|
+
}
|
67
|
+
createMetadata(prompt, history) {
|
68
|
+
return this.send('/v2/all', {
|
69
|
+
history: history ?? [],
|
70
|
+
prompt: prompt,
|
71
|
+
});
|
72
|
+
}
|
73
|
+
createUIImplementation(prompt, history) {
|
74
|
+
return this.send('/v2/ui/merge', {
|
75
|
+
history: history ?? [],
|
76
|
+
prompt,
|
77
|
+
});
|
78
|
+
}
|
79
|
+
createServiceImplementation(prompt, history) {
|
80
|
+
console.log('SENDING SERVICE PROMPT', JSON.stringify(prompt, null, 2));
|
81
|
+
return this.send('/v2/services/merge', {
|
82
|
+
history: history ?? [],
|
83
|
+
prompt,
|
84
|
+
});
|
85
|
+
}
|
86
|
+
}
|
87
|
+
exports.stormClient = new StormClient();
|
@@ -0,0 +1,38 @@
|
|
1
|
+
/// <reference types="node" />
|
2
|
+
/**
|
3
|
+
* Copyright 2023 Kapeta Inc.
|
4
|
+
* SPDX-License-Identifier: BUSL-1.1
|
5
|
+
*/
|
6
|
+
import { EventEmitter } from 'node:events';
|
7
|
+
import { StormEvent } from './events';
|
8
|
+
import { AIFileTypes, GeneratedFile } from '@kapeta/codegen';
|
9
|
+
export declare class StormStream extends EventEmitter {
|
10
|
+
private history;
|
11
|
+
private lines;
|
12
|
+
constructor(prompt?: string, history?: ConversationItem[]);
|
13
|
+
addJSONLine(line: string): void;
|
14
|
+
end(): void;
|
15
|
+
on(event: 'end', listener: () => void): this;
|
16
|
+
on(event: 'error', listener: (e: Error) => void): this;
|
17
|
+
on(event: 'data', listener: (data: StormEvent) => void): this;
|
18
|
+
emit(event: 'end'): boolean;
|
19
|
+
emit(event: 'error', e: Error): boolean;
|
20
|
+
emit(event: 'data', data: StormEvent): boolean;
|
21
|
+
waitForDone(): Promise<void>;
|
22
|
+
}
|
23
|
+
export interface ConversationItem {
|
24
|
+
role: 'user' | 'model';
|
25
|
+
content: string;
|
26
|
+
}
|
27
|
+
export interface StormContextRequest<T = string> {
|
28
|
+
history?: ConversationItem[];
|
29
|
+
prompt: T;
|
30
|
+
}
|
31
|
+
export interface StormFileInfo extends GeneratedFile {
|
32
|
+
type: AIFileTypes;
|
33
|
+
}
|
34
|
+
export interface StormFileImplementationPrompt {
|
35
|
+
context: StormFileInfo[];
|
36
|
+
template: StormFileInfo;
|
37
|
+
prompt: string;
|
38
|
+
}
|
@@ -0,0 +1,57 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.StormStream = void 0;
|
4
|
+
/**
|
5
|
+
* Copyright 2023 Kapeta Inc.
|
6
|
+
* SPDX-License-Identifier: BUSL-1.1
|
7
|
+
*/
|
8
|
+
const node_events_1 = require("node:events");
|
9
|
+
class StormStream extends node_events_1.EventEmitter {
|
10
|
+
history;
|
11
|
+
lines = [];
|
12
|
+
constructor(prompt = '', history) {
|
13
|
+
super();
|
14
|
+
this.history = history ?? [];
|
15
|
+
this.history.push({
|
16
|
+
role: 'user',
|
17
|
+
content: prompt,
|
18
|
+
});
|
19
|
+
}
|
20
|
+
addJSONLine(line) {
|
21
|
+
try {
|
22
|
+
this.lines.push(line);
|
23
|
+
const event = JSON.parse(line);
|
24
|
+
if (!event.created) {
|
25
|
+
event.created = Date.now();
|
26
|
+
}
|
27
|
+
this.emit('data', event);
|
28
|
+
}
|
29
|
+
catch (e) {
|
30
|
+
this.emit('error', e);
|
31
|
+
}
|
32
|
+
}
|
33
|
+
end() {
|
34
|
+
this.history.push({
|
35
|
+
role: 'model',
|
36
|
+
content: this.lines.join('\n'),
|
37
|
+
});
|
38
|
+
this.emit('end');
|
39
|
+
}
|
40
|
+
on(event, listener) {
|
41
|
+
return super.on(event, listener);
|
42
|
+
}
|
43
|
+
emit(eventName, ...args) {
|
44
|
+
return super.emit(eventName, ...args);
|
45
|
+
}
|
46
|
+
waitForDone() {
|
47
|
+
return new Promise((resolve, reject) => {
|
48
|
+
this.on('error', (err) => {
|
49
|
+
reject(err);
|
50
|
+
});
|
51
|
+
this.on('end', () => {
|
52
|
+
resolve();
|
53
|
+
});
|
54
|
+
});
|
55
|
+
}
|
56
|
+
}
|
57
|
+
exports.StormStream = StormStream;
|
package/dist/cjs/src/types.d.ts
CHANGED
@@ -6,6 +6,7 @@ import express from 'express';
|
|
6
6
|
import { Connection, Resource } from '@kapeta/schemas';
|
7
7
|
import { StringBodyRequest } from './middleware/stringBody';
|
8
8
|
import { KapetaRequest } from './middleware/kapeta';
|
9
|
+
import { EnrichedAsset } from './assetManager';
|
9
10
|
export declare const KIND_RESOURCE_OPERATOR = "core/resource-type-operator";
|
10
11
|
export declare const KIND_BLOCK_TYPE = "core/block-type";
|
11
12
|
export declare const KIND_BLOCK_TYPE_OPERATOR = "core/block-type-operator";
|
@@ -80,9 +81,11 @@ export type ProxyRequestHandler = (req: StringBodyRequest, res: express.Response
|
|
80
81
|
export interface ProxyRequestInfo {
|
81
82
|
address: string;
|
82
83
|
connection: Connection;
|
83
|
-
providerResource: Resource;
|
84
|
-
consumerResource: Resource;
|
85
84
|
consumerPath: string;
|
85
|
+
consumerResource: Resource;
|
86
|
+
consumerBlockAsset: EnrichedAsset;
|
87
|
+
providerResource: Resource;
|
88
|
+
providerBlockAsset: EnrichedAsset;
|
86
89
|
}
|
87
90
|
export interface SimpleResponse {
|
88
91
|
code: number;
|
package/dist/esm/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,
|
@@ -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
|
-
|
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,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 {
|
@@ -49,19 +49,19 @@ router.all('/:systemId/:consumerInstanceId/:consumerResourceName/:type/*', async
|
|
49
49
|
});
|
50
50
|
return;
|
51
51
|
}
|
52
|
-
const
|
52
|
+
const consumerBlockInstance = lodash_1.default.find(plan.spec.blocks, (blockInstance) => {
|
53
53
|
return blockInstance.id.toLowerCase() === connection.consumer.blockId.toLowerCase();
|
54
54
|
});
|
55
|
-
if (!
|
55
|
+
if (!consumerBlockInstance) {
|
56
56
|
res.status(401).send({ error: `Block instance not found "${req.params.consumerInstanceId}` });
|
57
57
|
return;
|
58
58
|
}
|
59
|
-
const
|
60
|
-
if (!
|
61
|
-
res.status(401).send({ error: `Block asset not found "${
|
59
|
+
const consumerBlockAsset = await assetManager_1.assetManager.getAsset(consumerBlockInstance.block.ref);
|
60
|
+
if (!consumerBlockAsset) {
|
61
|
+
res.status(401).send({ error: `Block asset not found "${consumerBlockInstance.block.ref}` });
|
62
62
|
return;
|
63
63
|
}
|
64
|
-
const consumerResource = getResource(
|
64
|
+
const consumerResource = getResource(consumerBlockAsset.data.spec.consumers, req.params.consumerResourceName);
|
65
65
|
if (!consumerResource) {
|
66
66
|
res.status(401).send({
|
67
67
|
error: `Block resource not found "${req.params.consumerInstanceId}::${req.params.consumerResourceName}`,
|
@@ -69,19 +69,19 @@ router.all('/:systemId/:consumerInstanceId/:consumerResourceName/:type/*', async
|
|
69
69
|
return;
|
70
70
|
}
|
71
71
|
const basePath = clusterService_1.clusterService.getProxyPath(req.params.systemId, req.params.consumerInstanceId, req.params.consumerResourceName, req.params.type);
|
72
|
-
const
|
72
|
+
const providerBlockInstance = lodash_1.default.find(plan.spec.blocks, (blockInstance) => {
|
73
73
|
return blockInstance.id.toLowerCase() === connection.provider.blockId.toLowerCase();
|
74
74
|
});
|
75
|
-
if (!
|
75
|
+
if (!providerBlockInstance) {
|
76
76
|
res.status(401).send({ error: `Block instance not found "${connection.provider.blockId}` });
|
77
77
|
return;
|
78
78
|
}
|
79
|
-
const
|
80
|
-
if (!
|
81
|
-
res.status(401).send({ error: `Block asset not found "${
|
79
|
+
const providerBlockAsset = await assetManager_1.assetManager.getAsset(providerBlockInstance.block.ref);
|
80
|
+
if (!providerBlockAsset) {
|
81
|
+
res.status(401).send({ error: `Block asset not found "${providerBlockInstance.block.ref}` });
|
82
82
|
return;
|
83
83
|
}
|
84
|
-
const providerResource = getResource(
|
84
|
+
const providerResource = getResource(providerBlockAsset.data.spec.providers, connection.provider.resourceName);
|
85
85
|
if (!providerResource) {
|
86
86
|
res.status(401).send({
|
87
87
|
error: `Block resource not found "${connection.provider.blockId}::${connection.provider.resourceName}`,
|
@@ -93,18 +93,18 @@ router.all('/:systemId/:consumerInstanceId/:consumerResourceName/:type/*', async
|
|
93
93
|
while (address.endsWith('/')) {
|
94
94
|
address = address.substring(0, address.length - 1);
|
95
95
|
}
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
that is being called and identify the destination path from the connection.
|
100
|
-
*/
|
96
|
+
// Get the path the consumer requested. Note that this might not match the path the
|
97
|
+
// destination is expecting so we need to identify the method that is being called and
|
98
|
+
// identify the destination path from the connection.
|
101
99
|
const consumerPath = req.originalUrl.substring(basePath.length - 1);
|
102
100
|
typeHandler(req, res, {
|
103
|
-
consumerPath,
|
104
101
|
address,
|
102
|
+
connection,
|
103
|
+
consumerPath,
|
105
104
|
consumerResource,
|
105
|
+
consumerBlockAsset,
|
106
106
|
providerResource,
|
107
|
-
|
107
|
+
providerBlockAsset,
|
108
108
|
});
|
109
109
|
}
|
110
110
|
catch (err) {
|
@@ -2,9 +2,7 @@
|
|
2
2
|
* Copyright 2023 Kapeta Inc.
|
3
3
|
* SPDX-License-Identifier: BUSL-1.1
|
4
4
|
*/
|
5
|
-
import {
|
6
|
-
import { ProxyRequestInfo } from '../../types';
|
7
|
-
import { StringBodyRequest } from '../../middleware/stringBody';
|
5
|
+
import { ProxyRequestHandler } from '../../types';
|
8
6
|
import { Resource } from '@kapeta/schemas';
|
9
7
|
export declare function getRestMethodId(restResource: Resource, httpMethod: string, httpPath: string): string | undefined;
|
10
|
-
export declare
|
8
|
+
export declare const proxyRestRequest: ProxyRequestHandler;
|
@@ -16,6 +16,7 @@ const networkManager_1 = require("../../networkManager");
|
|
16
16
|
const socketManager_1 = require("../../socketManager");
|
17
17
|
const qs_1 = require("qs");
|
18
18
|
const web_1 = require("./web");
|
19
|
+
const kaplang_core_1 = require("@kapeta/kaplang-core");
|
19
20
|
function getRestMethodId(restResource, httpMethod, httpPath) {
|
20
21
|
return lodash_1.default.findKey(restResource.spec.methods, (method) => {
|
21
22
|
let methodType = method.method ? method.method.toUpperCase() : 'GET';
|
@@ -63,12 +64,49 @@ function resolveMethods(req, opts) {
|
|
63
64
|
providerMethod,
|
64
65
|
};
|
65
66
|
}
|
66
|
-
function
|
67
|
+
function resolveEntitiesSource(blockAsset) {
|
68
|
+
return blockAsset.data.spec?.entities?.source?.value || '';
|
69
|
+
}
|
70
|
+
const MASK_STRING = '*******';
|
71
|
+
function maskSimpleRequest(req, consumerMethod, entitiesSource) {
|
72
|
+
const maskedRequest = lodash_1.default.cloneDeep(req);
|
73
|
+
Object.entries(consumerMethod.arguments || {}).forEach(([key, value]) => {
|
74
|
+
if (value.transport === kaplang_core_1.HTTPTransport.BODY && typeof maskedRequest.body === 'string') {
|
75
|
+
try {
|
76
|
+
maskedRequest.body = (0, kaplang_core_1.maskSensitiveData)(maskedRequest.body, kaplang_core_1.DSLConverters.fromSchemaType(value), entitiesSource, MASK_STRING);
|
77
|
+
}
|
78
|
+
catch (error) {
|
79
|
+
// Ignore errors masking the request body
|
80
|
+
}
|
81
|
+
}
|
82
|
+
// TODO: Mask HEADER
|
83
|
+
// TODO: Mask QUERY
|
84
|
+
});
|
85
|
+
return maskedRequest;
|
86
|
+
}
|
87
|
+
function maskSimpleResponse(res, consumerMethod, entitiesSource) {
|
88
|
+
const maskedResponse = lodash_1.default.cloneDeep(res);
|
89
|
+
Object.entries(consumerMethod.arguments || {}).forEach(([key, value]) => {
|
90
|
+
// TODO: Mask HEADER
|
91
|
+
});
|
92
|
+
try {
|
93
|
+
if (typeof maskedResponse.body === 'string') {
|
94
|
+
maskedResponse.body = (0, kaplang_core_1.maskSensitiveData)(maskedResponse.body, kaplang_core_1.DSLConverters.fromSchemaType(consumerMethod.responseType), entitiesSource, MASK_STRING);
|
95
|
+
}
|
96
|
+
}
|
97
|
+
catch (error) {
|
98
|
+
// Ignore errors masking the response body
|
99
|
+
}
|
100
|
+
return maskedResponse;
|
101
|
+
}
|
102
|
+
const proxyRestRequest = (req, res, opts) => {
|
67
103
|
if (lodash_1.default.isEmpty(opts.consumerResource.spec.methods) && lodash_1.default.isEmpty(opts.providerResource.spec.methods)) {
|
68
104
|
// If there are no methods defined, we assume the user controls the path and we just proxy the raw request
|
69
105
|
return (0, web_1.proxyHttpRequest)(req, res, opts);
|
70
106
|
}
|
71
|
-
|
107
|
+
const { consumerMethod, providerMethod } = resolveMethods(req, opts);
|
108
|
+
const consumerEntitiesSource = resolveEntitiesSource(opts.consumerBlockAsset);
|
109
|
+
const providerEntitiesSource = resolveEntitiesSource(opts.providerBlockAsset);
|
72
110
|
const consumerPathTemplate = (0, pathTemplateParser_1.pathTemplateParser)(consumerMethod.path);
|
73
111
|
const providerPathTemplate = (0, pathTemplateParser_1.pathTemplateParser)(providerMethod.path);
|
74
112
|
const pathVariables = consumerPathTemplate.parse(opts.consumerPath);
|
@@ -98,7 +136,7 @@ function proxyRestRequest(req, res, opts) {
|
|
98
136
|
body: req.stringBody,
|
99
137
|
headers: requestHeaders,
|
100
138
|
};
|
101
|
-
const traffic = networkManager_1.networkManager.addRequest(req.params.systemId, opts.connection, reqOpts, consumerMethod.id, providerMethod.id);
|
139
|
+
const traffic = networkManager_1.networkManager.addRequest(req.params.systemId, opts.connection, maskSimpleRequest(reqOpts, consumerMethod, consumerEntitiesSource), consumerMethod.id, providerMethod.id);
|
102
140
|
socketManager_1.socketManager.emit(traffic.connectionId, 'traffic_start', traffic);
|
103
141
|
(0, request_1.default)(reqOpts, function (err, response, responseBody) {
|
104
142
|
if (err) {
|
@@ -113,16 +151,17 @@ function proxyRestRequest(req, res, opts) {
|
|
113
151
|
delete responseHeaders['connection'];
|
114
152
|
res.set(responseHeaders);
|
115
153
|
res.status(response.statusCode);
|
116
|
-
|
154
|
+
const simpleResponse = {
|
117
155
|
code: response.statusCode,
|
118
156
|
headers: response.headers,
|
119
157
|
body: responseBody,
|
120
|
-
}
|
158
|
+
};
|
159
|
+
traffic.withResponse(maskSimpleResponse(simpleResponse, consumerMethod, consumerEntitiesSource));
|
121
160
|
socketManager_1.socketManager.emit(traffic.connectionId, 'traffic_end', traffic);
|
122
161
|
if (responseBody) {
|
123
162
|
res.write(responseBody);
|
124
163
|
}
|
125
164
|
res.end();
|
126
165
|
});
|
127
|
-
}
|
166
|
+
};
|
128
167
|
exports.proxyRestRequest = proxyRestRequest;
|
@@ -2,7 +2,5 @@
|
|
2
2
|
* Copyright 2023 Kapeta Inc.
|
3
3
|
* SPDX-License-Identifier: BUSL-1.1
|
4
4
|
*/
|
5
|
-
import {
|
6
|
-
|
7
|
-
import { StringBodyRequest } from '../../middleware/stringBody';
|
8
|
-
export declare function proxyHttpRequest(req: StringBodyRequest, res: Response, opts: ProxyRequestInfo): void;
|
5
|
+
import { ProxyRequestHandler } from '../../types';
|
6
|
+
export declare const proxyHttpRequest: ProxyRequestHandler;
|
@@ -13,7 +13,7 @@ const lodash_1 = __importDefault(require("lodash"));
|
|
13
13
|
const networkManager_1 = require("../../networkManager");
|
14
14
|
const socketManager_1 = require("../../socketManager");
|
15
15
|
const qs_1 = require("qs");
|
16
|
-
|
16
|
+
const proxyHttpRequest = (req, res, opts) => {
|
17
17
|
const requestHeaders = lodash_1.default.clone(req.headers);
|
18
18
|
delete requestHeaders['content-length'];
|
19
19
|
delete requestHeaders['content-encoding'];
|
@@ -57,5 +57,5 @@ function proxyHttpRequest(req, res, opts) {
|
|
57
57
|
});
|
58
58
|
//We need to pipe the proxy response to the client response to handle sockets and event streams
|
59
59
|
proxyReq.pipe(res);
|
60
|
-
}
|
60
|
+
};
|
61
61
|
exports.proxyHttpRequest = proxyHttpRequest;
|
@@ -0,0 +1,36 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright 2023 Kapeta Inc.
|
3
|
+
* SPDX-License-Identifier: BUSL-1.1
|
4
|
+
*/
|
5
|
+
import { BlockDefinitionInfo } from './event-parser';
|
6
|
+
import { StormStream } from './stream';
|
7
|
+
export declare class StormCodegen {
|
8
|
+
private readonly userPrompt;
|
9
|
+
private readonly blocks;
|
10
|
+
private readonly out;
|
11
|
+
constructor(userPrompt: string, blocks: BlockDefinitionInfo[]);
|
12
|
+
process(): Promise<void>;
|
13
|
+
getStream(): StormStream;
|
14
|
+
private handleFileOutput;
|
15
|
+
/**
|
16
|
+
* Generates the code for a block and sends it to the AI
|
17
|
+
*/
|
18
|
+
private processBlockCode;
|
19
|
+
/**
|
20
|
+
* Emits the text-based files to the stream
|
21
|
+
*/
|
22
|
+
private emitFiles;
|
23
|
+
private emitFile;
|
24
|
+
/**
|
25
|
+
* Sends the template to the AI and processes the response
|
26
|
+
*/
|
27
|
+
private processTemplates;
|
28
|
+
/**
|
29
|
+
* Converts the generated files to a format that can be sent to the AI
|
30
|
+
*/
|
31
|
+
private toStormFiles;
|
32
|
+
/**
|
33
|
+
* Generates the code using codegen for a given block.
|
34
|
+
*/
|
35
|
+
private generateBlock;
|
36
|
+
}
|