diff --git a/packages/core/src/Components/ECMASandbox.class.ts b/packages/core/src/Components/ECMASandbox.class.ts new file mode 100644 index 00000000..1d13df98 --- /dev/null +++ b/packages/core/src/Components/ECMASandbox.class.ts @@ -0,0 +1,71 @@ +import { IAgent as Agent } from '@sre/types/Agent.types'; +import { Component } from './Component.class'; +import Joi from 'joi'; +import { ConnectorService } from '@sre/Core/ConnectorsService'; +import { CodeExecutionResult } from '@sre/ComputeManager/Code.service/CodeConnector'; + +export class ECMASandbox extends Component { + protected configSchema = Joi.object({ + code: Joi.string().max(500000).allow('').label('Code'), + }); + constructor() { + super(); + } + init() {} + async process(input, config, agent: Agent) { + await super.process(input, config, agent); + + const logger = this.createComponentLogger(agent, config); + try { + let Output: any = {}; + let _error = undefined; + + let codeInputs = {}; + for (let fieldName in input) { + const _type = typeof input[fieldName]; + switch (_type) { + case 'string': + const b64encoded = Buffer.from(input[fieldName]).toString('base64'); + codeInputs[fieldName] = `___internal.b64decode('${b64encoded}')`; + break; + case 'number': + case 'boolean': + codeInputs[fieldName] = input[fieldName]; + break; + default: + codeInputs[fieldName] = input[fieldName]; + break; + } + } + + const inputVarsCode = this.generateInputVarCode(codeInputs); + const code = inputVarsCode + '\n' + config.data.code; + + logger.debug(`Running code: \n${code}\n`); + + const ecmaCodeConnector = ConnectorService.getCodeConnector('ECMASandbox'); + + const executionResponse: CodeExecutionResult = await ecmaCodeConnector.agent(agent.id).execute(config.id, { code }); + if (executionResponse.success) { + Output = executionResponse.output; + } else { + Output = undefined; + _error = executionResponse.errors; + } + + return { Output, _error, _debug: logger.output }; + } catch (err: any) { + const _error = err?.response?.data || err?.message || err.toString(); + logger.error(`Error running code: \n${_error}\n`); + return { Output: undefined, _error, _debug: logger.output }; + } + } + + private generateInputVarCode(input: Record) { + let input_vars = ''; + for (const key in input) { + input_vars += `var ${key} = ${input[key]};\n`; + } + return input_vars; + } +} diff --git a/packages/core/src/Components/index.ts b/packages/core/src/Components/index.ts index d947530f..247b29f9 100644 --- a/packages/core/src/Components/index.ts +++ b/packages/core/src/Components/index.ts @@ -37,6 +37,7 @@ import { ServerlessCode } from './ServerlessCode.class'; import { ImageGenerator } from './ImageGenerator.class'; // Legacy import { MCPClient } from './MCPClient.class'; import { OpenAPI } from './OpenAPI.class'; +import { ECMASandbox } from './ECMASandbox.class'; const components = { Component: new Component(), @@ -82,6 +83,7 @@ const components = { ImageGenerator: new ImageGenerator(), MCPClient: new MCPClient(), OpenAPI: new OpenAPI(), + ECMASandbox: new ECMASandbox(), }; export const ComponentInstances = components; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index f330efc5..89689fdb 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -18,6 +18,7 @@ export * from './Components/ComponentHost.class'; export * from './Components/DataSourceCleaner.class'; export * from './Components/DataSourceIndexer.class'; export * from './Components/DataSourceLookup.class'; +export * from './Components/ECMASandbox.class'; export * from './Components/FEncDec.class'; export * from './Components/FHash.class'; export * from './Components/FileStore.class'; diff --git a/packages/sdk/scripts/generate-components.js b/packages/sdk/scripts/generate-components.js index fd2b15be..768afce9 100644 --- a/packages/sdk/scripts/generate-components.js +++ b/packages/sdk/scripts/generate-components.js @@ -37,7 +37,8 @@ const INCLUDED_COMPONENTS = [ 'ImageGenerator', 'TavilyWebSearch', 'MCPClient', - 'ServerlessCode' + 'ServerlessCode', + 'ECMASandbox' ]; // Example: ['APICall', 'GenAILLM', 'Image'] /** diff --git a/packages/sdk/src/Components/generated/ECMASandbox.ts b/packages/sdk/src/Components/generated/ECMASandbox.ts new file mode 100644 index 00000000..bd7c4ce5 --- /dev/null +++ b/packages/sdk/src/Components/generated/ECMASandbox.ts @@ -0,0 +1,64 @@ +//!!! DO NOT EDIT THIS FILE, IT IS AUTO-GENERATED !!!// + +import { Agent } from '../../Agent/Agent.class'; +import { createSafeAccessor } from '../utils'; +import { ComponentWrapper } from '../ComponentWrapper.class'; +import { InputSettings, ComponentInput } from '../../types/SDKTypes'; + +export interface TECMASandboxSettings { + name?: string; + /** Code */ + code?: string; +} + +export type TECMASandboxInputs = { + [key: string]: InputSettings; +}; + +export type TECMASandboxOutputs = { + [key: string]: any; +}; + +export function ECMASandbox(settings?: TECMASandboxSettings, agent?: Agent) { + const { name, ...settingsWithoutName } = settings || {}; + const dataObject: any = { + name: settings?.name || 'ECMASandbox', + settings: { + ...settingsWithoutName + } + }; + const component = new ComponentWrapper(dataObject, agent); + + if (agent) { + (agent.structure.components as ComponentWrapper[]).push(component); + } + + const _out: TECMASandboxOutputs = createSafeAccessor({ + // No outputs defined + }, component, ''); + + const _in: { [key: string]: ComponentInput } = { + // No inputs defined + }; + + dataObject.outputs = _out; + dataObject.inputs = _in; + + component.inputs(_in); + + const wrapper = { + /** Component outputs - access via .out.OutputName */ + out: _out, + + /** + * Create or Connect the component inputs + * if the input does not exist, it will be created + * @examples + * - component.in({ Input: source.out.data }) + * - component.in({ Input: { type: 'string', source:source.out.data } }) + */ + in: component.inputs.bind(component) as (inputs: TECMASandboxInputs) => void, + }; + + return wrapper; +} diff --git a/packages/sdk/src/Components/generated/index.generated.ts b/packages/sdk/src/Components/generated/index.generated.ts index 744fe36b..dfd689ff 100644 --- a/packages/sdk/src/Components/generated/index.generated.ts +++ b/packages/sdk/src/Components/generated/index.generated.ts @@ -4,6 +4,7 @@ import { APICall } from './APICall'; import { APIOutput } from './APIOutput'; import { Await } from './Await'; import { Classifier } from './Classifier'; +import { ECMASandbox } from './ECMASandbox'; import { GenAILLM } from './GenAILLM'; import { HuggingFace } from './HuggingFace'; import { ImageGenerator } from './ImageGenerator'; @@ -13,5 +14,5 @@ import { TavilyWebSearch } from './TavilyWebSearch'; -const Components = { APICall, APIOutput, Await, Classifier, GenAILLM, HuggingFace, ImageGenerator, MCPClient, ServerlessCode, TavilyWebSearch, }; +const Components = { APICall, APIOutput, Await, Classifier, ECMASandbox, GenAILLM, HuggingFace, ImageGenerator, MCPClient, ServerlessCode, TavilyWebSearch, }; export default Components; \ No newline at end of file diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index 9d268a35..685e5a1e 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -35,6 +35,7 @@ export * from './Components/generated/APICall'; export * from './Components/generated/APIOutput'; export * from './Components/generated/Await'; export * from './Components/generated/Classifier'; +export * from './Components/generated/ECMASandbox'; export * from './Components/generated/GenAILLM'; export * from './Components/generated/HuggingFace'; export * from './Components/generated/ImageGenerator';