Skip to content

Commit d724557

Browse files
Merge pull request #40 from SmythOS/feat/core/ecma-sandbox-component-implementation
Added Ecma sandbox component implementation
2 parents fafce47 + 15343bd commit d724557

File tree

7 files changed

+143
-2
lines changed

7 files changed

+143
-2
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { IAgent as Agent } from '@sre/types/Agent.types';
2+
import { Component } from './Component.class';
3+
import Joi from 'joi';
4+
import { ConnectorService } from '@sre/Core/ConnectorsService';
5+
import { CodeExecutionResult } from '@sre/ComputeManager/Code.service/CodeConnector';
6+
7+
export class ECMASandbox extends Component {
8+
protected configSchema = Joi.object({
9+
code: Joi.string().max(500000).allow('').label('Code'),
10+
});
11+
constructor() {
12+
super();
13+
}
14+
init() {}
15+
async process(input, config, agent: Agent) {
16+
await super.process(input, config, agent);
17+
18+
const logger = this.createComponentLogger(agent, config);
19+
try {
20+
let Output: any = {};
21+
let _error = undefined;
22+
23+
let codeInputs = {};
24+
for (let fieldName in input) {
25+
const _type = typeof input[fieldName];
26+
switch (_type) {
27+
case 'string':
28+
const b64encoded = Buffer.from(input[fieldName]).toString('base64');
29+
codeInputs[fieldName] = `___internal.b64decode('${b64encoded}')`;
30+
break;
31+
case 'number':
32+
case 'boolean':
33+
codeInputs[fieldName] = input[fieldName];
34+
break;
35+
default:
36+
codeInputs[fieldName] = input[fieldName];
37+
break;
38+
}
39+
}
40+
41+
const inputVarsCode = this.generateInputVarCode(codeInputs);
42+
const code = inputVarsCode + '\n' + config.data.code;
43+
44+
logger.debug(`Running code: \n${code}\n`);
45+
46+
const ecmaCodeConnector = ConnectorService.getCodeConnector('ECMASandbox');
47+
48+
const executionResponse: CodeExecutionResult = await ecmaCodeConnector.agent(agent.id).execute(config.id, { code });
49+
if (executionResponse.success) {
50+
Output = executionResponse.output;
51+
} else {
52+
Output = undefined;
53+
_error = executionResponse.errors;
54+
}
55+
56+
return { Output, _error, _debug: logger.output };
57+
} catch (err: any) {
58+
const _error = err?.response?.data || err?.message || err.toString();
59+
logger.error(`Error running code: \n${_error}\n`);
60+
return { Output: undefined, _error, _debug: logger.output };
61+
}
62+
}
63+
64+
private generateInputVarCode(input: Record<string, any>) {
65+
let input_vars = '';
66+
for (const key in input) {
67+
input_vars += `var ${key} = ${input[key]};\n`;
68+
}
69+
return input_vars;
70+
}
71+
}

packages/core/src/Components/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import { ServerlessCode } from './ServerlessCode.class';
3737
import { ImageGenerator } from './ImageGenerator.class'; // Legacy
3838
import { MCPClient } from './MCPClient.class';
3939
import { OpenAPI } from './OpenAPI.class';
40+
import { ECMASandbox } from './ECMASandbox.class';
4041

4142
const components = {
4243
Component: new Component(),
@@ -82,6 +83,7 @@ const components = {
8283
ImageGenerator: new ImageGenerator(),
8384
MCPClient: new MCPClient(),
8485
OpenAPI: new OpenAPI(),
86+
ECMASandbox: new ECMASandbox(),
8587
};
8688

8789
export const ComponentInstances = components;

packages/core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export * from './Components/ComponentHost.class';
1818
export * from './Components/DataSourceCleaner.class';
1919
export * from './Components/DataSourceIndexer.class';
2020
export * from './Components/DataSourceLookup.class';
21+
export * from './Components/ECMASandbox.class';
2122
export * from './Components/FEncDec.class';
2223
export * from './Components/FHash.class';
2324
export * from './Components/FileStore.class';

packages/sdk/scripts/generate-components.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ const INCLUDED_COMPONENTS = [
3737
'ImageGenerator',
3838
'TavilyWebSearch',
3939
'MCPClient',
40-
'ServerlessCode'
40+
'ServerlessCode',
41+
'ECMASandbox'
4142
]; // Example: ['APICall', 'GenAILLM', 'Image']
4243

4344
/**
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//!!! DO NOT EDIT THIS FILE, IT IS AUTO-GENERATED !!!//
2+
3+
import { Agent } from '../../Agent/Agent.class';
4+
import { createSafeAccessor } from '../utils';
5+
import { ComponentWrapper } from '../ComponentWrapper.class';
6+
import { InputSettings, ComponentInput } from '../../types/SDKTypes';
7+
8+
export interface TECMASandboxSettings {
9+
name?: string;
10+
/** Code */
11+
code?: string;
12+
}
13+
14+
export type TECMASandboxInputs = {
15+
[key: string]: InputSettings;
16+
};
17+
18+
export type TECMASandboxOutputs = {
19+
[key: string]: any;
20+
};
21+
22+
export function ECMASandbox(settings?: TECMASandboxSettings, agent?: Agent) {
23+
const { name, ...settingsWithoutName } = settings || {};
24+
const dataObject: any = {
25+
name: settings?.name || 'ECMASandbox',
26+
settings: {
27+
...settingsWithoutName
28+
}
29+
};
30+
const component = new ComponentWrapper(dataObject, agent);
31+
32+
if (agent) {
33+
(agent.structure.components as ComponentWrapper[]).push(component);
34+
}
35+
36+
const _out: TECMASandboxOutputs = createSafeAccessor({
37+
// No outputs defined
38+
}, component, '');
39+
40+
const _in: { [key: string]: ComponentInput } = {
41+
// No inputs defined
42+
};
43+
44+
dataObject.outputs = _out;
45+
dataObject.inputs = _in;
46+
47+
component.inputs(_in);
48+
49+
const wrapper = {
50+
/** Component outputs - access via .out.OutputName */
51+
out: _out,
52+
53+
/**
54+
* Create or Connect the component inputs
55+
* if the input does not exist, it will be created
56+
* @examples
57+
* - component.in({ Input: source.out.data })
58+
* - component.in({ Input: { type: 'string', source:source.out.data } })
59+
*/
60+
in: component.inputs.bind(component) as (inputs: TECMASandboxInputs) => void,
61+
};
62+
63+
return wrapper;
64+
}

packages/sdk/src/Components/generated/index.generated.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { APICall } from './APICall';
44
import { APIOutput } from './APIOutput';
55
import { Await } from './Await';
66
import { Classifier } from './Classifier';
7+
import { ECMASandbox } from './ECMASandbox';
78
import { GenAILLM } from './GenAILLM';
89
import { HuggingFace } from './HuggingFace';
910
import { ImageGenerator } from './ImageGenerator';
@@ -13,5 +14,5 @@ import { TavilyWebSearch } from './TavilyWebSearch';
1314

1415

1516

16-
const Components = { APICall, APIOutput, Await, Classifier, GenAILLM, HuggingFace, ImageGenerator, MCPClient, ServerlessCode, TavilyWebSearch, };
17+
const Components = { APICall, APIOutput, Await, Classifier, ECMASandbox, GenAILLM, HuggingFace, ImageGenerator, MCPClient, ServerlessCode, TavilyWebSearch, };
1718
export default Components;

packages/sdk/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export * from './Components/generated/APICall';
3737
export * from './Components/generated/APIOutput';
3838
export * from './Components/generated/Await';
3939
export * from './Components/generated/Classifier';
40+
export * from './Components/generated/ECMASandbox';
4041
export * from './Components/generated/GenAILLM';
4142
export * from './Components/generated/HuggingFace';
4243
export * from './Components/generated/ImageGenerator';

0 commit comments

Comments
 (0)