748 lines · 29.4 KB · TypeScript
RawDownload
import {  recoverExecutionBody,  stripTypeScript,  type CodeExecutor,  type CodeExecutionOptions,  type ExecuteOutputItem,  type ExecuteResult,  type SandboxToolInvoker,} from "@executor-js/codemode-core";import * as Data from "effect/Data";import * as Effect from "effect/Effect";import {  getQuickJS,  shouldInterruptAfterDeadline,  type QuickJSContext,  type QuickJSDeferredPromise,  type QuickJSHandle,  type QuickJSRuntime,  type QuickJSWASMModule,} from "quickjs-emscripten"; export type QuickJsExecutorOptions = {  timeoutMs?: number;  memoryLimitBytes?: number;  maxStackSizeBytes?: number;}; // Allow pre-loading a QuickJS module (e.g. with custom WASM bytes for compiled binaries)let preloadedModule: QuickJSWASMModule | null = null; export const setQuickJSModule = (mod: QuickJSWASMModule) => {  preloadedModule = mod;}; const resolveQuickJS = (): Promise<QuickJSWASMModule> =>  preloadedModule ? Promise.resolve(preloadedModule) : getQuickJS(); class QuickJsExecutionError extends Data.TaggedError("QuickJsExecutionError")<{  readonly message: string;}> {} // Large OpenAPI specs can take longer to parse inside QuickJS, so keep the// default execution budget at five minutes unless a caller opts into less.const DEFAULT_TIMEOUT_MS = 5 * 60_000;const DEFAULT_MEMORY_LIMIT_BYTES = 64 * 1024 * 1024;const DEFAULT_MAX_STACK_SIZE_BYTES = 1 * 1024 * 1024;const EXECUTION_FILENAME = "executor-quickjs-runtime.js"; const toError = (cause: unknown): Error =>  cause instanceof Error ? cause : new Error(String(cause)); // Defect surfaced to the sandbox when a tool dispatch reaches the// reject path. Two sources reach here://   - `ExecutionToolError` emitted by the execution package — message//     is already either the canonical//     `Internal tool error [<corrId>]` opaque generic (plugin defects//     passed through `makeExecutorToolInvoker`'s catchCause) or an//     intentional public string (validators in engine.ts://     "tools.search expects an object: ...").//   - Anything else — assume infra defect and emit a bare opaque//     generic.// `_tag === "ExecutionToolError"` is the in-band signal that the// message was generated by code we control and is safe to pass// through; we never read `.cause` / `.stack`.const sandboxDefectMessage = (cause: unknown): string => {  if (cause !== null && typeof cause === "object") {    const tagged = cause as { readonly _tag?: unknown; readonly message?: unknown };    // oxlint-disable-next-line executor/no-manual-tag-check, executor/no-unknown-error-message -- boundary: QuickJS reject path receives an already-rendered ExecutionToolError; we pass its in-band public message through and otherwise emit the opaque generic    if (tagged._tag === "ExecutionToolError" && typeof tagged.message === "string") {      return tagged.message;    }  }  return "Internal tool error";}; const toErrorMessage = (cause: unknown): string => {  if (typeof cause === "object" && cause !== null) {    const message =      "message" in cause && typeof cause.message === "string" ? cause.message : undefined;     if (message) {      return message;    }     const stack = "stack" in cause && typeof cause.stack === "string" ? cause.stack : undefined;     if (stack) {      return stack;    }  }   const error = toError(cause);  return error.stack ?? error.message;}; const serializeJson = (value: unknown, label: string): string | undefined => {  if (typeof value === "undefined") {    return undefined;  }   try {    return JSON.stringify(value);  } catch (cause) {    throw new Error(`${label} is not JSON serializable: ${toError(cause).message}`);  }}; const looksLikeInterruptedError = (message: string): boolean => /\binterrupted\b/i.test(message); const timeoutMessage = (timeoutMs: number): string =>  `QuickJS execution timed out after ${timeoutMs}ms`; const normalizeExecutionError = (cause: unknown, deadlineMs: number, timeoutMs: number): string => {  const message = toErrorMessage(cause);  return Date.now() >= deadlineMs && looksLikeInterruptedError(message)    ? timeoutMessage(timeoutMs)    : message;}; const buildExecutionSource = (code: string): string => {  // QuickJS evaluates plain JavaScript only; strip any TS type syntax  // first. A parse failure here throws a SyntaxError which the outer  // `Effect.tryPromise` maps to `QuickJsExecutionError` with the  // sucrase-formatted message intact.  const body = stripTypeScript(recoverExecutionBody(code));   return [    '"use strict";',    "const __invokeTool = __executor_invokeTool;",    "const __log = __executor_log;",    "const __output = __executor_output;",    "const __yield = __executor_yield;",    "const __sleep = __executor_sleep;",    "const __cancelSleep = __executor_cancelSleep;",    "try { delete globalThis.__executor_invokeTool; } catch {}",    "try { delete globalThis.__executor_log; } catch {}",    "try { delete globalThis.__executor_output; } catch {}",    "try { delete globalThis.__executor_yield; } catch {}",    "try { delete globalThis.__executor_sleep; } catch {}",    "try { delete globalThis.__executor_cancelSleep; } catch {}",    "const __formatLogArg = (value) => {",    "  if (typeof value === 'string') return value;",    "  try {",    "    return JSON.stringify(value);",    "  } catch {",    "    return String(value);",    "  }",    "};",    "const __formatLogLine = (args) => args.map(__formatLogArg).join(' ');",    "const __outputs = [];",    "globalThis.__executor_outputs = __outputs;",    "const __recordOutput = (item) => { __outputs.push(item); __output(item); };",    "const yield_control = () => __yield();",    "const yieldControl = yield_control;",    "const __timers = new Map();",    "let __nextTimerId = 1;",    "const setTimeout = (callback, delay = 0, ...args) => {",    "  if (typeof callback !== 'function') throw new TypeError('setTimeout callback must be a function');",    "  const id = __nextTimerId++;",    "  const timer = { cancelled: false };",    "  __timers.set(id, timer);",    "  const ms = Math.max(0, Number.isFinite(Number(delay)) ? Math.floor(Number(delay)) : 0);",    "  __sleep(ms, id).then((active) => {",    "    if (active !== 1 || timer.cancelled) return;",    "    __timers.delete(id);",    "    callback(...args);",    "  }, () => {",    "    __timers.delete(id);",    "  });",    "  return id;",    "};",    "const clearTimeout = (id) => {",    "  const key = Number(id);",    "  const timer = __timers.get(key);",    "  if (!timer) return;",    "  timer.cancelled = true;",    "  __timers.delete(key);",    "  __cancelSleep(key);",    "};",    "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 __IMAGE_DETAIL_META_KEY = 'codex/imageDetail';",    "const __DEFAULT_IMAGE_DETAIL = 'high';",    "const __validImageDetails = new Set(['auto', 'low', 'high', 'original']);",    "const __normalizeImageDetail = (detail) => {",    "  if (detail === null || typeof detail === 'undefined') return undefined;",    "  if (typeof detail !== 'string') throw new TypeError('image detail must be a string when provided');",    "  const normalized = detail.toLowerCase();",    "  if (!__validImageDetails.has(normalized)) throw new TypeError('image detail must be one of: auto, low, high, original');",    "  return normalized;",    "};",    "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' || typeof value.mime_type === 'string' || value.data.toLowerCase().startsWith('data:'));",    "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 __parseDataImageUrl = (imageUrl) => {",    "  if (typeof imageUrl !== 'string' || imageUrl.length === 0) throw new TypeError('image expects a non-empty data URI, an object with image_url and optional detail, or a raw MCP image block');",    "  const lower = imageUrl.toLowerCase();",    "  if (lower.startsWith('http://') || lower.startsWith('https://')) throw new TypeError('remote image URLs are not supported in code output; pass a base64 data URI or MCP image block');",    "  const match = /^data:([^;,]+);base64,(.*)$/i.exec(imageUrl);",    "  if (!match) throw new TypeError('image expects a base64 data URI or MCP image block');",    "  return { mimeType: match[1], data: match[2] };",    "};",    "const __imageDetailFromMeta = (value) => {",    "  const meta = value && typeof value === 'object' ? value._meta : undefined;",    "  const detail = meta && typeof meta === 'object' ? meta[__IMAGE_DETAIL_META_KEY] : undefined;",    "  return typeof detail === 'string' && __validImageDetails.has(detail) ? detail : undefined;",    "};",    "const __imageWithDetail = (block, detail) => ({ ...block, _meta: { ...(block._meta && typeof block._meta === 'object' ? block._meta : {}), [__IMAGE_DETAIL_META_KEY]: detail ?? __DEFAULT_IMAGE_DETAIL } });",    "const __normalizeImageBlock = (value, detailOverride) => {",    "  const override = __normalizeImageDetail(detailOverride);",    "  if (typeof value === 'string') return __imageWithDetail({ type: 'image', ...__parseDataImageUrl(value) }, override);",    "  if (!value || typeof value !== 'object' || Array.isArray(value)) throw new TypeError('image expects a non-empty data URI, an object with image_url and optional detail, or a raw MCP image block');",    "  if (typeof value.image_url === 'string') {",    "    return __imageWithDetail({ type: 'image', ...__parseDataImageUrl(value.image_url) }, override ?? __normalizeImageDetail(value.detail));",    "  }",    "  if (value.type === 'image' && typeof value.data === 'string') {",    "    const parsed = value.data.toLowerCase().startsWith('data:') ? __parseDataImageUrl(value.data) : { data: value.data, mimeType: typeof value.mimeType === 'string' ? value.mimeType : typeof value.mime_type === 'string' ? value.mime_type : 'application/octet-stream' };",    "    return __imageWithDetail({ ...value, type: 'image', data: parsed.data, mimeType: parsed.mimeType }, override ?? __imageDetailFromMeta(value));",    "  }",    "  throw new TypeError('image expects a non-empty data URI, an object with image_url and optional detail, or a raw MCP image block');",    "};",    "const text = (value) => { __recordOutput({ type: 'content', content: { type: 'text', text: __formatOutputText(value) } }); };",    "const image = (value, detail) => { __recordOutput({ type: 'content', content: __normalizeImageBlock(value, detail) }); };",    "const audio = (value) => { if (!__isMcpAudioContentBlock(value)) throw new TypeError('audio expects an MCP audio content block'); __recordOutput({ type: 'content', content: value }); };",    "const file = (value) => { if (!__isToolFile(value)) throw new TypeError('file expects a ToolFile value'); __recordOutput({ type: 'file', file: value }); };",    "const resource = (value) => { if (!__isMcpResourceContentBlock(value) && !__isMcpResourceLinkContentBlock(value)) throw new TypeError('resource expects an MCP resource or resource_link content block'); __recordOutput({ type: 'content', content: value }); };",    "const notify = (value) => { const notification = value && typeof value === 'object' && typeof value.message === 'string' ? { message: value.message, ...(Object.prototype.hasOwnProperty.call(value, 'data') ? { data: value.data } : {}) } : { message: __formatOutputText(value) }; __recordOutput({ type: 'notification', notification }); };",    "const emit = (value) => {",    "  if (__isToolFile(value)) { file(value); return; }",    "  if (__isMcpContentBlock(value)) { __recordOutput({ type: 'content', content: value }); return; }",    "  text(value);",    "};",    "const __builtinToolKeys = { '': ['search', 'describe', 'executor'], 'describe': ['tool'], 'executor': ['sources'], 'executor.sources': ['list'] };",    "const __toolKeysForPath = (path) => __builtinToolKeys[path.join('.')] ?? [];",    "const __makeToolsProxy = (path = []) => new Proxy(() => undefined, {",    "  get(_target, prop) {",    "    if (prop === 'then' || typeof prop === 'symbol') {",    "      return undefined;",    "    }",    "    return __makeToolsProxy([...path, String(prop)]);",    "  },",    "  ownKeys() {",    "    return __toolKeysForPath(path);",    "  },",    "  getOwnPropertyDescriptor(_target, prop) {",    "    return typeof prop === 'string' && __toolKeysForPath(path).includes(prop) ? { enumerable: true, configurable: true } : undefined;",    "  },",    "  apply(_target, _thisArg, args) {",    "    const toolPath = path.join('.');",    "    if (!toolPath) {",    "      throw new Error('Tool path missing in invocation');",    "    }",    "    return Promise.resolve(__invokeTool(toolPath, args[0])).then((raw) => raw === undefined ? undefined : JSON.parse(raw));",    "  },",    "});",    "const tools = __makeToolsProxy();",    "const console = {",    "  log: (...args) => __log('log', __formatLogLine(args)),",    "  warn: (...args) => __log('warn', __formatLogLine(args)),",    "  error: (...args) => __log('error', __formatLogLine(args)),",    "  info: (...args) => __log('info', __formatLogLine(args)),",    "  debug: (...args) => __log('debug', __formatLogLine(args)),",    "};",    "const fetch = (..._args) => {",    "  throw new Error('fetch is disabled in QuickJS executor');",    "};",    "(async () => {",    body,    "})()",  ].join("\n");}; const readPropDump = (context: QuickJSContext, handle: QuickJSHandle, key: string): unknown => {  const prop = context.getProp(handle, key);  try {    return context.dump(prop);  } finally {    prop.dispose();  }}; const readOutputItems = (context: QuickJSContext): ExecuteOutputItem[] | undefined => {  const output = readPropDump(context, context.global, "__executor_outputs");  return Array.isArray(output) && output.length > 0 ? (output as ExecuteOutputItem[]) : undefined;}; const readResultState = (  context: QuickJSContext,  handle: QuickJSHandle,): {  settled: boolean;  value: unknown;  error: unknown;} => ({  settled: readPropDump(context, handle, "settled") === true,  value: readPropDump(context, handle, "v"),  error: readPropDump(context, handle, "e"),}); const createLogBridge = (context: QuickJSContext, logs: string[]): QuickJSHandle =>  context.newFunction("__executor_log", (levelHandle, lineHandle) => {    const level = context.getString(levelHandle);    const line = context.getString(lineHandle);    logs.push(`[${level}] ${line}`);    return context.undefined;  }); const createOutputBridge = (  context: QuickJSContext,  executionOptions: CodeExecutionOptions | undefined,): QuickJSHandle =>  context.newFunction("__executor_output", (itemHandle) => {    const item = context.dump(itemHandle) as ExecuteOutputItem;    void executionOptions?.onOutput?.(item);    return context.undefined;  }); type RunPromise = <A, E>(effect: Effect.Effect<A, E>) => Promise<A>; const createYieldBridge = (  context: QuickJSContext,  pendingDeferreds: Set<QuickJSDeferredPromise>,  executionOptions: CodeExecutionOptions | undefined,): QuickJSHandle =>  context.newFunction("__executor_yield", () => {    const deferred = context.newPromise();    pendingDeferreds.add(deferred);    deferred.settled.finally(() => {      pendingDeferreds.delete(deferred);    });     let yielded: void | Promise<void>;    try {      yielded = executionOptions?.onYield?.();    } catch (cause) {      if (deferred.alive) {        const errorHandle = context.newError(          cause instanceof Error ? cause.message : String(cause),        );        deferred.reject(errorHandle);        errorHandle.dispose();      }      return deferred.handle;    }     void Promise.resolve(yielded).then(      () => {        if (!deferred.alive) return;        deferred.resolve();      },      (cause) => {        if (!deferred.alive) return;        const errorHandle = context.newError(          cause instanceof Error ? cause.message : String(cause),        );        deferred.reject(errorHandle);        errorHandle.dispose();      },    );     return deferred.handle;  }); const createSleepBridge = (  context: QuickJSContext,  pendingDeferreds: Set<QuickJSDeferredPromise>,  pendingTimers: Map<    number,    {      readonly timeout: ReturnType<typeof setTimeout>;      readonly deferred: QuickJSDeferredPromise;    }  >,): QuickJSHandle =>  context.newFunction("__executor_sleep", (delayHandle, idHandle) => {    const rawDelay =      delayHandle === undefined || context.typeof(delayHandle) === "undefined"        ? 0        : context.dump(delayHandle);    const rawId =      idHandle === undefined || context.typeof(idHandle) === "undefined"        ? undefined        : context.dump(idHandle);    const timerId =      typeof rawId === "number" && Number.isFinite(rawId) ? Math.floor(rawId) : undefined;    const delayMs =      typeof rawDelay === "number" && Number.isFinite(rawDelay)        ? Math.max(0, Math.floor(rawDelay))        : 0;    const deferred = context.newPromise();    pendingDeferreds.add(deferred);     const timer = setTimeout(() => {      if (timerId !== undefined) {        const current = pendingTimers.get(timerId);        if (!current || current.deferred !== deferred) return;        pendingTimers.delete(timerId);      }      if (!deferred.alive) return;      const activeHandle = context.newNumber(1);      deferred.resolve(activeHandle);      activeHandle.dispose();    }, delayMs);    if (timerId !== undefined) {      pendingTimers.set(timerId, { timeout: timer, deferred });    }     deferred.settled.finally(() => {      clearTimeout(timer);      if (timerId !== undefined) {        const current = pendingTimers.get(timerId);        if (current?.deferred === deferred) {          pendingTimers.delete(timerId);        }      }      pendingDeferreds.delete(deferred);    });     return deferred.handle;  }); const createCancelSleepBridge = (  context: QuickJSContext,  pendingTimers: Map<    number,    {      readonly timeout: ReturnType<typeof setTimeout>;      readonly deferred: QuickJSDeferredPromise;    }  >,): QuickJSHandle =>  context.newFunction("__executor_cancelSleep", (idHandle) => {    const rawId =      idHandle === undefined || context.typeof(idHandle) === "undefined"        ? undefined        : context.dump(idHandle);    const timerId =      typeof rawId === "number" && Number.isFinite(rawId) ? Math.floor(rawId) : undefined;    if (timerId === undefined) return context.undefined;     const current = pendingTimers.get(timerId);    if (!current) return context.undefined;    clearTimeout(current.timeout);    pendingTimers.delete(timerId);    if (current.deferred.alive) {      const cancelledHandle = context.newNumber(0);      current.deferred.resolve(cancelledHandle);      cancelledHandle.dispose();    }    return context.undefined;  }); const createToolBridge = (  context: QuickJSContext,  toolInvoker: SandboxToolInvoker,  pendingDeferreds: Set<QuickJSDeferredPromise>,  runPromise: RunPromise,): QuickJSHandle =>  context.newFunction("__executor_invokeTool", (pathHandle, argsHandle) => {    const path = context.getString(pathHandle);    const args =      argsHandle === undefined || context.typeof(argsHandle) === "undefined"        ? undefined        : context.dump(argsHandle);    const deferred = context.newPromise();    pendingDeferreds.add(deferred);    deferred.settled.finally(() => {      pendingDeferreds.delete(deferred);    });     void runPromise(toolInvoker.invoke({ path, args })).then(      (value) => {        if (!deferred.alive) {          return;        }         const serialized = serializeJson(value, `Tool result for ${path}`);        if (typeof serialized === "undefined") {          deferred.resolve();          return;        }         const valueHandle = context.newString(serialized);        deferred.resolve(valueHandle);        valueHandle.dispose();      },      (cause) => {        if (!deferred.alive) {

Lines 1–500 of 748.