222 lines · 11.5 KB · TypeScript
RawDownload
/** * Build the ES module source that runs inside the dynamic Worker. * * The module exports a `WorkerEntrypoint` subclass with an `evaluate` * method that: * 1. Captures console output into `__logs`. * 2. Accumulates user-visible output from `text`, `file`, and `image`. * 3. Creates a recursive `tools` Proxy that dispatches calls via RPC. * 4. Executes the normalised user code with a `Promise.race` timeout. * 5. Returns `{ result, output?, error?, logs }`. * * Tool args cross the dispatcher boundary via Workers RPC, not JSON, so * `Uint8Array` / `ArrayBuffer` survive intact. `Blob` / `File` aren't on * RPC's structured-clone allow-list, so we encode them to a tagged * envelope (`__encodeBinary`) and the host rehydrates them. This is * required for any tool whose upstream wants `multipart/form-data` * (OpenAI Files API, etc.). */export const buildExecutorModule = (body: string, timeoutMs: number): string =>  [    'import { WorkerEntrypoint } from "cloudflare:workers";',    "",    "export default class CodeExecutor extends WorkerEntrypoint {",    "  async evaluate(__dispatcher) {",    "    const __logs = [];",    "    const __outputs = [];",    '    console.log = (...a) => { __logs.push(a.map(String).join(" ")); };',    '    console.warn = (...a) => { __logs.push("[warn] " + a.map(String).join(" ")); };',    '    console.error = (...a) => { __logs.push("[error] " + a.map(String).join(" ")); };',    "    const __serializeErrorValue = (value) => {",    "      if (value instanceof Error) {",    "        return {",    "          __type: 'Error',",    "          name: value.name,",    "          message: value.message,",    "        };",    "      }",    "      if (value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {",    "        return value;",    "      }",    "      try {",    "        return JSON.parse(JSON.stringify(value));",    "      } catch {",    "        return String(value);",    "      }",    "    };",    "    const __renderMessage = (value) => {",    "      if (typeof value === 'string') return value;",    "      if (value && typeof value === 'object' && value.__type === 'Error' && typeof value.message === 'string') {",    "        return value.message;",    "      }",    "      if (value instanceof Error) return value.message;",    "      if (value && typeof value === 'object' && typeof value.message === 'string') return value.message;",    "      if (typeof value === 'undefined') return 'Unknown error';",    "      if (value && typeof value === 'object') {",    "        try {",    "          return JSON.stringify(value);",    "        } catch {",    "          return String(value);",    "        }",    "      }",    "      return String(value);",    "    };",    "    const __formatOutputText = (value) => {",    "      if (typeof value === 'undefined') return 'undefined';",    "      if (value === null) return 'null';",    "      if (typeof value === 'string') return value;",    "      try {",    "        return JSON.stringify(value);",    "      } catch {",    "        return String(value);",    "      }",    "    };",    "    const __isToolFile = (value) => value && typeof value === 'object' && value._tag === 'ToolFile' && typeof value.mimeType === 'string' && value.encoding === 'base64' && typeof value.data === 'string' && typeof value.byteLength === 'number';",    "    const __isMcpTextContentBlock = (value) => value && typeof value === 'object' && value.type === 'text' && typeof value.text === 'string';",    "    const __isMcpImageContentBlock = (value) => value && typeof value === 'object' && value.type === 'image' && typeof value.data === 'string' && typeof value.mimeType === 'string';",    "    const __isMcpAudioContentBlock = (value) => value && typeof value === 'object' && value.type === 'audio' && typeof value.data === 'string' && typeof value.mimeType === 'string';",    "    const __isMcpResourceContentBlock = (value) => value && typeof value === 'object' && value.type === 'resource' && value.resource && typeof value.resource === 'object' && typeof value.resource.uri === 'string' && (typeof value.resource.text === 'string' || typeof value.resource.blob === 'string');",    "    const __isMcpResourceLinkContentBlock = (value) => value && typeof value === 'object' && value.type === 'resource_link' && typeof value.uri === 'string' && typeof value.name === 'string';",    "    const __isMcpContentBlock = (value) => __isMcpTextContentBlock(value) || __isMcpImageContentBlock(value) || __isMcpAudioContentBlock(value) || __isMcpResourceContentBlock(value) || __isMcpResourceLinkContentBlock(value);",    "    const emit = (value) => {",    "      if (__isToolFile(value)) {",    "        __outputs.push({ type: 'file', file: value });",    "        return;",    "      }",    "      if (__isMcpContentBlock(value)) {",    "        __outputs.push({ type: 'content', content: value });",    "        return;",    "      }",    "      __outputs.push({ type: 'content', content: { type: 'text', text: __formatOutputText(value) } });",    "    };",    "    const __serializeThrownError = (err) => {",    "      const primary = __serializeErrorValue(err);",    "      return {",    "        kind: 'unknown',",    "        message: __renderMessage(primary),",    "        primary,",    "        failures: [],",    "        defects: [],",    "        interrupted: false,",    "      };",    "    };",    "",    // Blob/File aren't on Workers RPC's structured-clone allow-list, so    // we encode them to a tagged ArrayBuffer envelope before the call and    // the host rehydrates them — and symmetrically the host encodes    // Blob/File results which we rehydrate here. ArrayBuffer / typed    // arrays cross natively.    "    const __isPlainObject = (v) => {",    "      const p = Object.getPrototypeOf(v);",    "      return p === Object.prototype || p === null;",    "    };",    "    const __isBinaryEnvelope = (v) =>",    "      v && typeof v === 'object' && v.__executorBinary === 1 &&",    "      v.buffer instanceof ArrayBuffer && typeof v.type === 'string';",    "    const __encodeBinary = async (value, seen = new WeakSet()) => {",    "      if (value === null || typeof value !== 'object') return value;",    "      if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) return value;",    "      if (seen.has(value)) throw new Error('Tool RPC payload contains a circular reference');",    "      seen.add(value);",    "      if (typeof File !== 'undefined' && value instanceof File) {",    "        const out = {",    "          __executorBinary: 1,",    "          kind: 'file',",    "          type: value.type,",    "          name: value.name,",    "          lastModified: value.lastModified,",    "          buffer: await value.arrayBuffer(),",    "        };",    "        seen.delete(value);",    "        return out;",    "      }",    "      if (value instanceof Blob) {",    "        const out = { __executorBinary: 1, kind: 'blob', type: value.type, buffer: await value.arrayBuffer() };",    "        seen.delete(value);",    "        return out;",    "      }",    "      if (Array.isArray(value)) {",    "        const out = await Promise.all(value.map((item) => __encodeBinary(item, seen)));",    "        seen.delete(value);",    "        return out;",    "      }",    "      if (!__isPlainObject(value)) { seen.delete(value); return value; }",    "      const out = {};",    "      for (const [k, v] of Object.entries(value)) out[k] = await __encodeBinary(v, seen);",    "      seen.delete(value);",    "      return out;",    "    };",    "    const __decodeBinary = (value, seen = new WeakSet()) => {",    "      if (value === null || typeof value !== 'object') return value;",    "      if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) return value;",    "      if (seen.has(value)) throw new Error('Tool RPC payload contains a circular reference');",    "      seen.add(value);",    "      if (__isBinaryEnvelope(value)) {",    "        seen.delete(value);",    "        if (value.kind === 'file' && typeof value.name === 'string') {",    "          return new File([value.buffer], value.name, {",    "            type: value.type,",    "            ...(typeof value.lastModified === 'number' ? { lastModified: value.lastModified } : {}),",    "          });",    "        }",    "        return new Blob([value.buffer], { type: value.type });",    "      }",    "      if (Array.isArray(value)) { const out = value.map((item) => __decodeBinary(item, seen)); seen.delete(value); return out; }",    "      if (!__isPlainObject(value)) { seen.delete(value); return value; }",    "      const out = {};",    "      for (const [k, v] of Object.entries(value)) out[k] = __decodeBinary(v, seen);",    "      seen.delete(value);",    "      return out;",    "    };",    "    const __publicToolErrorMessage = (error) => {",    "      const values = [error && error.primary, ...(Array.isArray(error && error.failures) ? error.failures : [])];",    "      for (const value of values) {",    "        if (value && value.__type === 'Error' && value.name === 'ExecutionToolError' && typeof value.message === 'string') return value.message;",    "      }",    "      if (error && typeof error.message === 'string' && error.message.startsWith('Internal tool error')) return error.message;",    "      return null;",    "    };",    "    const __toolsEnumerationError = (path) => new Error(",    "      (path.length === 0 ? 'tools' : 'tools.' + path.join('.')) +",    '        \' is a lazy proxy and cannot be enumerated. Use tools.search({ query: "..." }) to find tools, tools.search({ namespace: "<integration>", query: "" }) to list every tool in an integration, or tools.executor.coreTools.connections.list({}) to list saved connections.\',',    "    );",    "    const __makeToolsProxy = (path = []) => new Proxy(() => undefined, {",    "      get(_target, prop) {",    "        if (prop === 'then' || typeof prop === 'symbol') return undefined;",    "        return __makeToolsProxy([...path, String(prop)]);",    "      },",    "      ownKeys() {",    "        throw __toolsEnumerationError(path);",    "      },",    "      getOwnPropertyDescriptor() {",    "        throw __toolsEnumerationError(path);",    "      },",    "      apply(_target, _thisArg, args) {",    "        const toolPath = path.join('.');",    "        if (!toolPath) throw new Error('Tool path missing in invocation');",    "        return (async () => {",    "          const encoded = await __encodeBinary(args[0]);",    "          const data = await __dispatcher.call(toolPath, encoded);",    "          if (!data.ok) throw new Error(__publicToolErrorMessage(data.error) || 'Internal tool error');",    "          return __decodeBinary(data.result);",    "        })();",    "      },",    "    });",    "    const tools = __makeToolsProxy();",    "",    "    try {",    "      const result = await Promise.race([",    "        (async () => {",    body,    "        })(),",    "        new Promise((_, reject) =>",    `          setTimeout(() => reject(new Error("Execution timed out after ${timeoutMs}ms")), ${timeoutMs})`,    "        ),",    "      ]);",    "      return { result, output: __outputs.length > 0 ? __outputs : undefined, logs: __logs };",    "    } catch (err) {",    "      return { result: undefined, output: __outputs.length > 0 ? __outputs : undefined, error: __serializeThrownError(err), logs: __logs };",    "    }",    "  }",    "}",  ].join("\n");