openAgent
gitlabThe open source UNIVERSAL agent.
clone
https://gitlab.com/bitzzdev/openAgent.gitAIagentautomationopen-sourceuniversal-agent
export * as ToolSandbox from "./sandbox"import { Context, Effect, Layer, Schema } from "effect"import { ToolFailure } from "@opencode-ai/llm"import { ToolDefinitionRecord } from "./metadata"import { CredentialBroker } from "../credential/broker"import { SecretRedactor } from "../credential/redactor"import { makeGlobalNode } from "../effect/app-node"export class SandboxExecutionError extends Schema.TaggedErrorClass<SandboxExecutionError>()("ToolSandbox.ExecutionError",{tool: Schema.String,message: Schema.String,},) {}export interface Interface {readonly execute: (tool: ToolDefinitionRecord,input: unknown,) => Effect.Effect<unknown, ToolFailure>}export class Service extends Context.Service<Service, Interface>()("@openagent/ToolSandbox") {}const layer = Layer.effect(Service,Effect.gen(function* () {const credentials = yield* CredentialBroker.Serviceconst executeHttp = async (tool: ToolDefinitionRecord,input: Record<string, unknown>,creds: Record<string, string>,): Promise<unknown> => {let endpoint = tool.endpoint ?? ""// Interpolate params into endpoint e.g. https://api.linear.app/graphql or https://api.github.com/repos/{owner}/{repo}for (const [k, v] of Object.entries(input)) {if (endpoint.includes(`{${k}}`)) {endpoint = endpoint.replaceAll(`{${k}}`, encodeURIComponent(String(v)))}}// Build headersconst headers: Record<string, string> = {"Content-Type": "application/json",Accept: "application/json","User-Agent": "OpenAgent-UniversalRuntime/1.0",...(tool.headers ?? {}),}// Inject credentials into headers if specified or templatefor (const [k, v] of Object.entries(headers)) {let val = vfor (const [credKey, credVal] of Object.entries(creds)) {val = val.replaceAll(`$${credKey}`, credVal).replaceAll(`{${credKey}}`, credVal)}headers[k] = val}const method = tool.method ?? "POST"let body: string | undefined = undefinedif (method !== "GET") {if (tool.bodyTemplate) {let b = tool.bodyTemplatefor (const [k, v] of Object.entries(input)) {b = b.replaceAll(`{${k}}`, JSON.stringify(v)).replaceAll(`$${k}`, String(v))}body = b} else {body = JSON.stringify(input)}}const controller = new AbortController()const timeout = setTimeout(() => controller.abort(), 30000)try {const response = await fetch(endpoint, {method,headers,body,signal: controller.signal,})clearTimeout(timeout)const text = await response.text()let parsed: unknowntry {parsed = JSON.parse(text)} catch {parsed = { status: response.status, body: text }}if (!response.ok) {throw new Error(`HTTP ${response.status} ${response.statusText}: ${typeof parsed === "object" ? JSON.stringify(parsed) : String(parsed)}`)}return SecretRedactor.redactObject(parsed)} catch (e: any) {clearTimeout(timeout)throw new Error(SecretRedactor.redact(e.message ?? "HTTP request failed"))}}const executeCode = async (tool: ToolDefinitionRecord,input: unknown,creds: Record<string, string>,): Promise<unknown> => {const code = tool.code ?? ""if (!code) {throw new Error(`No code implementation provided for tool "${tool.name}".`)}// Provide controlled sandbox scope with ONLY declared credentialsconst sandboxEnv = Object.freeze({ ...creds })const runner = new Function("input","env","fetch",`"use strict";return (async () => {${code}})();`,)const timeoutPromise = new Promise((_, reject) =>setTimeout(() => reject(new Error(`Tool "${tool.name}" execution timed out after 30s.`)), 30000),)const executionPromise = runner(input, sandboxEnv, fetch)const rawResult = await Promise.race([executionPromise, timeoutPromise])return SecretRedactor.redactObject(rawResult)}return Service.of({execute: Effect.fn("ToolSandbox.execute")(function* (tool: ToolDefinitionRecord, input: unknown) {// Resolve only declared credentialsconst creds = yield* credentials.resolveManyForTool(tool.name, tool.credentials).pipe(Effect.mapError((err) => new ToolFailure({ message: `Credential resolution error: ${err.message}` }),),)return yield* Effect.tryPromise({try: async () => {if (tool.executionMethod === "http" || tool.endpoint) {return await executeHttp(tool, (input ?? {}) as Record<string, unknown>, creds)}return await executeCode(tool, input, creds)},catch: (err: any) =>new ToolFailure({message: SecretRedactor.redact(`Execution failure in "${tool.name}": ${err?.message ?? String(err)}`),}),})}),})}),)export const defaultLayer = layerexport const node = makeGlobalNode({ service: Service, layer, deps: [CredentialBroker.node] })