run402-mcp
Version:
MCP server for Run402 — full-stack backend infrastructure for AI agents: Postgres, auth, storage, serverless functions and atomic deploys. Paid with x402/MPP. Includes $0.03 image generation.
669 lines (661 loc) • 29.7 kB
JavaScript
/**
* The `run` sandbox: a TypeScript snippet executes inside QuickJS compiled to
* WebAssembly (the asyncify build of `quickjs-emscripten`), never inside Node.
*
* A QuickJS runtime can touch the host only through functions the host binds
* into it, so `require`, `process`, `fetch`, `import()`, timers, and every Node
* builtin are absent by construction: there is nothing to deny. What the
* snippet does see is the global inventory below (`SANDBOX_GLOBALS`, pinned by
* `sandbox.test.ts`): the ECMAScript builtins QuickJS ships, the web builtins
* this module adds in plain JavaScript (`URL`, `TextEncoder`, `TextDecoder`,
* `structuredClone`, `crypto.randomUUID`), a captured `console`, and whatever
* an extension installs (the `r` recorder from `./sandbox-proxy.ts`).
*
* Each run gets its own WebAssembly module instance, runtime, and context, so
* nothing survives from one run to the next and a run that ends badly cannot
* poison another. Bounds: a memory limit on the runtime, a stack limit, and a
* deadline enforced two ways — the interrupt handler stops JavaScript that is
* running past it, and the event loop stops waiting on the host past it. An
* async host call already in flight at the deadline is allowed to settle
* before the run returns, because its side effect is real either way.
*
* The source is TypeScript or JavaScript. It is wrapped as the body of an
* async function (so top-level `await` and `return` both work), types are
* stripped on the host with `node:module`'s `stripTypeScriptTypes` (strip-only:
* `enum` and parameter properties are syntax errors, as in Node), the result is
* parsed, and when the body ends in an expression statement that statement is
* rewritten into a `return` from the parse tree, never by string search.
*/
import { randomUUID } from "node:crypto";
import { stripTypeScriptTypes } from "node:module";
import { parse as parseJs } from "acorn";
import { newQuickJSAsyncWASMModule, } from "quickjs-emscripten";
/** 64 MB: the runtime's allocation ceiling. */
export const SANDBOX_MEMORY_LIMIT_BYTES = 64 * 1024 * 1024;
/** The interpreter's own stack ceiling, well inside the WebAssembly stack. */
export const SANDBOX_STACK_LIMIT_BYTES = 256 * 1024;
/** The serialized (compact JSON) value a run may return. */
export const SANDBOX_MAX_VALUE_BYTES = 4 * 1024 * 1024;
/** Console lines retained per run. */
export const SANDBOX_MAX_LOG_LINES = 500;
/** Characters retained per console line. */
export const SANDBOX_MAX_LOG_LINE_CHARS = 2048;
/** The file name QuickJS reports in stack traces. */
const SNIPPET_FILE = "snippet.ts";
/** The wrapper's head sits on line 1, so line numbers match the source and only line 1's columns shift. */
const WRAP_HEAD = "(async function () {";
const WRAP_TAIL = "\n})";
/**
* The snippet's global inventory, sorted. `r` is present when the chain proxy
* is installed (always, in the `run` tool). A new global is a deliberate diff
* of this list and of the snapshot test.
*/
export const SANDBOX_GLOBALS = [
"AggregateError", "Array", "ArrayBuffer", "BigInt", "BigInt64Array", "BigUint64Array", "Boolean",
"DataView", "Date", "Error", "EvalError", "FinalizationRegistry", "Float16Array", "Float32Array",
"Float64Array", "Function", "Infinity", "Int16Array", "Int32Array", "Int8Array", "InternalError",
"Iterator", "JSON", "Map", "Math", "NaN", "Number", "Object", "Promise", "Proxy", "RangeError",
"ReferenceError", "Reflect", "RegExp", "Set", "SharedArrayBuffer", "String", "Symbol", "SyntaxError",
"TextDecoder", "TextEncoder", "TypeError", "URIError", "URL", "Uint16Array", "Uint32Array", "Uint8Array",
"Uint8ClampedArray", "WeakMap", "WeakRef", "WeakSet", "console", "crypto", "decodeURI",
"decodeURIComponent", "encodeURI", "encodeURIComponent", "escape", "eval", "globalThis", "isFinite",
"isNaN", "parseFloat", "parseInt", "r", "structuredClone", "undefined", "unescape",
];
/**
* Wrap, strip types, parse, and rewrite the final expression statement into a
* `return`. The result is the source of one async function expression.
*/
export function prepareSnippet(code) {
const wrapped = `${WRAP_HEAD}${code}${WRAP_TAIL}`;
let stripped;
try {
stripped = stripTypesQuietly(wrapped);
}
catch (err) {
return { ok: false, error: syntaxErrorFromStrip(err) };
}
let program;
try {
program = parseJs(stripped, { ecmaVersion: "latest", sourceType: "script", locations: true });
}
catch (err) {
return { ok: false, error: syntaxErrorFromAcorn(err) };
}
// The whole source must be exactly the wrapper expression: a snippet that
// closes the wrapper's brace early is a syntax error, not a second program.
const only = program.body.length === 1 ? program.body[0] : null;
const fn = only?.type === "ExpressionStatement" ? only.expression : null;
if (!fn || fn.type !== "FunctionExpression" || !fn.async || fn.start !== 1) {
return {
ok: false,
error: { code: "RUN_SYNTAX_ERROR", message: "Unbalanced braces: the snippet closes a block it did not open.", line: 1, column: 1 },
};
}
const statements = fn.body.body;
const last = statements.length > 0 ? statements[statements.length - 1] : null;
if (!last || last.type !== "ExpressionStatement" || !last.expression || last.directive !== undefined) {
return { ok: true, source: stripped };
}
const expr = last.expression;
const source = stripped.slice(0, last.start) +
"return (" + stripped.slice(expr.start, expr.end) + ");" +
stripped.slice(last.end);
return { ok: true, source };
}
function shiftColumn(line, column) {
return line === 1 ? Math.max(1, column - WRAP_HEAD.length) : column;
}
function syntaxErrorFromStrip(err) {
const e = err;
const message = typeof e?.message === "string" ? e.message : String(err);
const stack = typeof e?.stack === "string" ? e.stack.split("\n") : [];
// Node's type-stripping error stack is `:<line>`, the source line(s), then a caret line.
const lineMatch = /^[^\n]*:(\d+)$/.exec(stack[0] ?? "");
const line = lineMatch ? Number(lineMatch[1]) : 1;
const caret = stack.slice(1, 6).find((l) => /^\s*\^+\s*$/.test(l));
const column = caret ? caret.indexOf("^") + 1 : 1;
return { code: "RUN_SYNTAX_ERROR", message, line, column: shiftColumn(line, column) };
}
function syntaxErrorFromAcorn(err) {
const e = err;
const raw = typeof e?.message === "string" ? e.message : String(err);
const line = typeof e?.loc?.line === "number" ? e.loc.line : 1;
const column = typeof e?.loc?.column === "number" ? e.loc.column + 1 : 1;
const message = raw.replace(/\s*\(\d+:\d+\)$/, "");
return { code: "RUN_SYNTAX_ERROR", message, line, column: shiftColumn(line, column) };
}
/** The first `snippet.ts:<line>:<column>` frame of a QuickJS stack, mapped back to the source. */
function locationFromStack(stack) {
if (typeof stack !== "string")
return {};
const m = new RegExp(`${SNIPPET_FILE.replace(".", "\\.")}:(\\d+)(?::(\\d+))?`).exec(stack);
if (!m)
return {};
const line = Number(m[1]);
return m[2] ? { line, column: shiftColumn(line, Number(m[2])) } : { line };
}
// ─── the in-sandbox base: console, web builtins, the driver ──────────────────
/**
* Evaluated inside the sandbox and called with the base host object
* `{ log, settle, uuid, url }`. Defines `console`, `crypto`, `URL`,
* `TextEncoder`, `TextDecoder`, `structuredClone`, and returns the driver that
* runs the snippet function and reports how it settled.
*/
const BASE_INSTALL = String.raw `(function (host) {
"use strict";
const g = globalThis;
const stringify = JSON.stringify;
const define = (name, value) =>
Object.defineProperty(g, name, { value, writable: true, configurable: true, enumerable: false });
const format = (v) => {
if (typeof v === "string") return v;
if (v instanceof Error) return (v.name || "Error") + ": " + v.message;
if ((typeof v === "object" && v !== null) || typeof v === "function") {
try { const s = stringify(v); if (s !== undefined) return s; } catch (e) { /* fall through */ }
}
try { return String(v); } catch (e) { return Object.prototype.toString.call(v); }
};
const makeLevel = (level) => (...args) => { host.log(level, args.map(format).join(" ")); };
define("console", { log: makeLevel("log"), info: makeLevel("info"), warn: makeLevel("warn"), error: makeLevel("error") });
define("crypto", { randomUUID: () => host.uuid() });
const parseUrl = (input, base) => {
const raw = base === undefined ? host.url(String(input)) : host.url(String(input), String(base));
return raw ? JSON.parse(raw) : null;
};
class URL {
#p;
constructor(input, base) {
const p = parseUrl(input, base);
if (!p) throw new TypeError("Invalid URL: " + String(input));
this.#p = p;
}
static canParse(input, base) { return parseUrl(input, base) !== null; }
get href() { return this.#p.href; }
get origin() { return this.#p.origin; }
get protocol() { return this.#p.protocol; }
get username() { return this.#p.username; }
get password() { return this.#p.password; }
get host() { return this.#p.host; }
get hostname() { return this.#p.hostname; }
get port() { return this.#p.port; }
get pathname() { return this.#p.pathname; }
get search() { return this.#p.search; }
get hash() { return this.#p.hash; }
toString() { return this.#p.href; }
toJSON() { return this.#p.href; }
}
define("URL", URL);
class TextEncoder {
get encoding() { return "utf-8"; }
encode(input = "") {
const s = String(input);
const out = [];
for (let i = 0; i < s.length; i++) {
let c = s.charCodeAt(i);
if (c >= 0xd800 && c <= 0xdbff && i + 1 < s.length) {
const d = s.charCodeAt(i + 1);
if (d >= 0xdc00 && d <= 0xdfff) { c = 0x10000 + ((c - 0xd800) << 10) + (d - 0xdc00); i++; } else c = 0xfffd;
} else if (c >= 0xd800 && c <= 0xdfff) c = 0xfffd;
if (c < 0x80) out.push(c);
else if (c < 0x800) out.push(0xc0 | (c >> 6), 0x80 | (c & 63));
else if (c < 0x10000) out.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 63), 0x80 | (c & 63));
else out.push(0xf0 | (c >> 18), 0x80 | ((c >> 12) & 63), 0x80 | ((c >> 6) & 63), 0x80 | (c & 63));
}
return new Uint8Array(out);
}
}
define("TextEncoder", TextEncoder);
class TextDecoder {
constructor(label = "utf-8") {
const l = String(label).trim().toLowerCase();
if (l !== "utf-8" && l !== "utf8" && l !== "unicode-1-1-utf-8") throw new RangeError('The "' + label + '" encoding is not supported');
}
get encoding() { return "utf-8"; }
decode(input) {
if (input === undefined) return "";
const b = input instanceof Uint8Array ? input
: ArrayBuffer.isView(input) ? new Uint8Array(input.buffer, input.byteOffset, input.byteLength)
: new Uint8Array(input);
const cont = (k) => k < b.length && (b[k] & 0xc0) === 0x80;
let out = "";
let chunk = [];
for (let i = 0; i < b.length;) {
const x = b[i];
let cp = 0xfffd, n = 1;
if (x < 0x80) cp = x;
else if (x >= 0xc2 && x < 0xe0 && cont(i + 1)) { cp = ((x & 31) << 6) | (b[i + 1] & 63); n = 2; }
else if (x >= 0xe0 && x < 0xf0 && cont(i + 1) && cont(i + 2)) {
const v = ((x & 15) << 12) | ((b[i + 1] & 63) << 6) | (b[i + 2] & 63);
if (v >= 0x800 && (v < 0xd800 || v > 0xdfff)) { cp = v; n = 3; }
} else if (x >= 0xf0 && x < 0xf5 && cont(i + 1) && cont(i + 2) && cont(i + 3)) {
const v = ((x & 7) << 18) | ((b[i + 1] & 63) << 12) | ((b[i + 2] & 63) << 6) | (b[i + 3] & 63);
if (v >= 0x10000 && v <= 0x10ffff) { cp = v; n = 4; }
}
if (!(i === 0 && cp === 0xfeff)) chunk.push(cp);
i += n;
if (chunk.length >= 4096) { out += String.fromCodePoint(...chunk); chunk = []; }
}
return out + String.fromCodePoint(...chunk);
}
}
define("TextDecoder", TextDecoder);
const cloneError = (what) => {
const e = new Error(what + " could not be cloned.");
e.name = "DataCloneError";
return e;
};
define("structuredClone", function structuredClone(value) {
const seen = new Map();
const walk = (v) => {
if (typeof v === "symbol") throw cloneError("Symbol()");
if (typeof v === "function") throw cloneError("A function");
if (v === null || typeof v !== "object") return v;
if (seen.has(v)) return seen.get(v);
let out;
if (Array.isArray(v)) {
out = [];
seen.set(v, out);
for (let i = 0; i < v.length; i++) out[i] = walk(v[i]);
return out;
}
if (v instanceof Date) { out = new Date(v.getTime()); seen.set(v, out); return out; }
if (v instanceof RegExp) { out = new RegExp(v.source, v.flags); seen.set(v, out); return out; }
if (v instanceof Map) { out = new Map(); seen.set(v, out); for (const [k, x] of v) out.set(walk(k), walk(x)); return out; }
if (v instanceof Set) { out = new Set(); seen.set(v, out); for (const x of v) out.add(walk(x)); return out; }
if (v instanceof ArrayBuffer) { out = v.slice(0); seen.set(v, out); return out; }
if (ArrayBuffer.isView(v)) {
const buffer = walk(v.buffer);
out = v instanceof DataView ? new DataView(buffer, v.byteOffset, v.byteLength) : new v.constructor(buffer, v.byteOffset, v.length);
seen.set(v, out);
return out;
}
if (v instanceof Error) {
out = new Error(String(v.message));
out.name = String(v.name);
seen.set(v, out);
return out;
}
out = {};
seen.set(v, out);
for (const k of Object.keys(v)) out[k] = walk(v[k]);
return out;
};
return walk(value);
});
const describe = (e) => {
if (e !== null && (typeof e === "object" || typeof e === "function")) {
const d = { name: "Error", message: "", stack: "" };
try { d.name = String(e.name === undefined ? "Error" : e.name); } catch (x) { /* keep default */ }
try { d.message = String(e.message === undefined ? "" : e.message); } catch (x) { /* keep default */ }
try { if (typeof e.stack === "string") d.stack = e.stack; } catch (x) { /* keep default */ }
try { if (typeof e.code === "string") d.code = e.code; } catch (x) { /* keep default */ }
try { if (typeof e.error_ref === "string") d.error_ref = e.error_ref; } catch (x) { /* keep default */ }
return d;
}
let message;
try { message = String(e); } catch (x) { message = typeof e; }
return { name: "Error", message, stack: "" };
};
return function start(fn) {
let p;
try { p = fn(); } catch (e) { p = Promise.reject(e); }
p.then(
(v) => {
if (v === undefined) { host.settle("undefined", ""); return; }
let json;
try { json = stringify(v); } catch (e) { host.settle("not_serializable", String(e && e.message ? e.message : e)); return; }
if (json === undefined) {
host.settle("not_serializable", "The value is " + (typeof v === "function" ? "a function or an r handle" : "a " + typeof v) + ", which is not JSON.");
return;
}
host.settle("ok", json);
},
(e) => {
let d;
try { d = stringify(describe(e)); } catch (x) { d = stringify({ name: "Error", message: "The snippet threw a value that could not be described.", stack: "" }); }
host.settle("error", d);
},
);
};
})`;
/** Run one snippet to completion (or to its bound) and describe how it ended. */
export async function runInSandbox(opts) {
const started = Date.now();
const deadline = started + opts.timeoutMs;
const maxValueBytes = opts.maxValueBytes ?? SANDBOX_MAX_VALUE_BYTES;
const logs = [];
let logsDropped = 0;
let interrupted = false;
let settled = null;
const finish = (fields) => ({
logs,
logs_dropped: logsDropped,
timed_out: interrupted,
duration_ms: Date.now() - started,
...fields,
});
const prepared = prepareSnippet(opts.code);
if (!prepared.ok)
return finish({ status: "error", error: prepared.error });
const module = await newQuickJSAsyncWASMModule();
const runtime = module.newRuntime();
runtime.setMemoryLimit(opts.memoryLimitBytes ?? SANDBOX_MEMORY_LIMIT_BYTES);
runtime.setMaxStackSize(SANDBOX_STACK_LIMIT_BYTES);
runtime.setInterruptHandler(() => {
if (Date.now() > deadline)
interrupted = true;
return interrupted;
});
const ctx = runtime.newContext();
// Async host calls in flight; the loop waits on them and never past them.
const inflight = new Set();
let wake = null;
const notify = () => {
const w = wake;
wake = null;
w?.();
};
let alive = true;
const baseHost = {
sync: {
log: (level, line) => {
if (logs.length >= SANDBOX_MAX_LOG_LINES) {
logsDropped++;
return undefined;
}
const text = line ?? "";
logs.push({
level: (level === "info" || level === "warn" || level === "error" ? level : "log"),
line: text.length > SANDBOX_MAX_LOG_LINE_CHARS ? `${text.slice(0, SANDBOX_MAX_LOG_LINE_CHARS)}…` : text,
});
return undefined;
},
settle: (kind, payload) => {
if (settled)
return undefined;
if (kind === "ok")
settled = { kind: "ok", json: payload ?? "null" };
else if (kind === "undefined")
settled = { kind: "undefined" };
else if (kind === "not_serializable")
settled = { kind: "not_serializable", message: payload ?? "" };
else {
let thrown;
try {
thrown = JSON.parse(payload ?? "{}");
}
catch {
thrown = { name: "Error", message: "The snippet threw a value that could not be described." };
}
settled = { kind: "error", thrown };
}
return undefined;
},
uuid: () => randomUUID(),
url: (input, base) => {
try {
const u = base === undefined ? new URL(input ?? "") : new URL(input ?? "", base);
return JSON.stringify({
href: u.href, origin: u.origin, protocol: u.protocol, username: u.username, password: u.password,
host: u.host, hostname: u.hostname, port: u.port, pathname: u.pathname, search: u.search, hash: u.hash,
});
}
catch {
return "";
}
},
},
};
const hostObject = (ext) => {
const obj = ctx.newObject();
for (const [name, impl] of Object.entries(ext.sync ?? {})) {
const fn = ctx.newFunction(name, (...args) => {
const out = impl(...args.map((h) => readString(ctx, h)));
return out === undefined ? undefined : ctx.newString(out);
});
ctx.setProp(obj, name, fn);
fn.dispose();
}
for (const [name, impl] of Object.entries(ext.async ?? {})) {
const fn = ctx.newFunction(name, (...args) => {
const strings = args.map((h) => readString(ctx, h));
const deferred = ctx.newPromise();
const call = Promise.resolve()
.then(() => impl(...strings))
.then((value) => {
if (!alive)
return;
const h = ctx.newString(value);
deferred.resolve(h);
h.dispose();
}, (err) => {
if (!alive)
return;
const h = ctx.newError(err instanceof Error ? err.message : String(err));
deferred.reject(h);
h.dispose();
})
.finally(() => {
deferred.dispose();
inflight.delete(call);
notify();
});
inflight.add(call);
return deferred.handle;
});
ctx.setProp(obj, name, fn);
fn.dispose();
}
return obj;
};
/** Evaluate an install function and call it with its host object. Returns the call's result handle. */
const install = (source, ext) => {
const fnHandle = ctx.unwrapResult(ctx.evalCode(source, "run402-sandbox-install.js"));
const hostHandle = hostObject(ext);
try {
return ctx.unwrapResult(ctx.callFunction(fnHandle, ctx.undefined, hostHandle));
}
finally {
hostHandle.dispose();
fnHandle.dispose();
}
};
let outcome;
// A host-level failure inside the WebAssembly module (its native stack
// overflowing under deep async recursion, say) leaves the module unusable;
// it is private to this run, so it is dropped rather than disposed.
let poisoned = false;
try {
const start = install(BASE_INSTALL, baseHost);
for (const ext of opts.extensions ?? [])
install(ext.install, ext).dispose();
const compiled = ctx.evalCode(prepared.source, SNIPPET_FILE);
if (compiled.error) {
const dumped = dumpError(ctx, compiled.error);
compiled.error.dispose();
start.dispose();
outcome = finish({
status: "error",
error: classify(dumped, { interrupted, phase: "compile" }),
});
}
else {
const called = ctx.callFunction(start, ctx.undefined, compiled.value);
compiled.value.dispose();
start.dispose();
let loopError = null;
if (called.error) {
loopError = classify(dumpError(ctx, called.error), { interrupted, phase: "run" });
called.error.dispose();
}
else {
called.value.dispose();
}
// The event loop: run jobs, then wait for the next host call to settle.
while (!loopError && !settled) {
const jobs = runtime.executePendingJobs();
if (jobs.error) {
loopError = classify(dumpError(ctx, jobs.error), { interrupted, phase: "run" });
jobs.error.dispose();
break;
}
if (settled || interrupted)
break;
if (Date.now() > deadline) {
interrupted = true;
break;
}
if (runtime.hasPendingJob())
continue;
if (inflight.size === 0) {
loopError = {
code: "RUN_EXCEPTION",
message: "The snippet is waiting on a promise that nothing can settle: the sandbox has no timers or I/O of its own, only awaited r calls settle.",
};
break;
}
await new Promise((resolve) => {
const timer = setTimeout(resolve, Math.max(0, deadline - Date.now()) + 1);
wake = () => {
clearTimeout(timer);
resolve();
};
});
}
// A host call in flight at the deadline completes or fails on its own;
// its side effect is real, so the run reports only after it settles.
if (interrupted)
await Promise.allSettled([...inflight]);
outcome = settle(settled, loopError, interrupted, maxValueBytes, finish);
}
}
catch (err) {
poisoned = true;
if (interrupted)
await Promise.allSettled([...inflight]);
const overflow = err instanceof RangeError && /call stack/i.test(err.message);
outcome = settle(null, {
code: "RUN_EXCEPTION",
message: overflow
? "InternalError: stack overflow (the recursion is too deep for the sandbox)."
: `The sandbox stopped: ${err instanceof Error ? err.message : String(err)}`,
}, interrupted, maxValueBytes, finish);
}
finally {
alive = false;
if (!poisoned) {
try {
ctx.dispose();
runtime.dispose();
}
catch {
// The module instance is private to this run and is dropped either way.
}
}
}
return outcome;
}
function settle(settled, loopError, interrupted, maxValueBytes, finish) {
if (interrupted) {
return finish({
status: "error",
error: { code: "RUN_TIMEOUT", message: "The run passed its deadline. Calls that completed before it are listed in calls[]; their side effects happened." },
});
}
if (loopError)
return finish({ status: "error", error: loopError });
if (!settled) {
return finish({ status: "error", error: { code: "RUN_EXCEPTION", message: "The snippet ended without settling." } });
}
switch (settled.kind) {
case "undefined":
return finish({ status: "ok", value_kind: "undefined" });
case "not_serializable":
return finish({ status: "error", error: { code: "RUN_VALUE_NOT_SERIALIZABLE", message: `The result is not JSON: ${settled.message}` } });
case "ok": {
const bytes = Buffer.byteLength(settled.json, "utf8");
if (bytes > maxValueBytes) {
return finish({
status: "error",
error: {
code: "RUN_VALUE_TOO_LARGE",
message: `The result serializes to ${bytes} bytes, over the ${maxValueBytes}-byte cap. Narrow the selection in the snippet (filter, map to the fields you need, or slice).`,
},
});
}
return finish({ status: "ok", value_json: settled.json });
}
case "error":
return finish({ status: "error", error: classify(settled.thrown, { interrupted, phase: "run" }) });
}
}
function classify(e, ctx) {
const location = locationFromStack(e.stack);
if (ctx.interrupted) {
return { code: "RUN_TIMEOUT", message: "The run passed its deadline." };
}
if (/out of memory/i.test(e.message)) {
return { code: "RUN_MEMORY_EXCEEDED", message: `The sandbox reached its ${SANDBOX_MEMORY_LIMIT_BYTES / (1024 * 1024)} MB memory limit.` };
}
if (ctx.phase === "compile" && e.name === "SyntaxError") {
return { code: "RUN_SYNTAX_ERROR", message: e.message, line: location.line ?? 1, column: location.column ?? 1 };
}
const thrown = { name: e.name, message: e.message };
if (e.code)
thrown.code = e.code;
if (e.error_ref)
thrown.error_ref = e.error_ref;
return {
code: "RUN_EXCEPTION",
message: e.name && e.name !== "Error" ? `${e.name}: ${e.message}` : e.message,
...location,
thrown,
};
}
function dumpError(ctx, handle) {
let dumped;
try {
dumped = ctx.dump(handle);
}
catch {
dumped = null;
}
if (dumped && typeof dumped === "object") {
const d = dumped;
return {
name: typeof d.name === "string" ? d.name : "Error",
message: typeof d.message === "string" ? d.message : String(d.message ?? ""),
...(typeof d.stack === "string" ? { stack: d.stack } : {}),
...(typeof d.code === "string" ? { code: d.code } : {}),
...(typeof d.error_ref === "string" ? { error_ref: d.error_ref } : {}),
};
}
return { name: "Error", message: dumped === null || dumped === undefined ? "out of memory" : String(dumped) };
}
function readString(ctx, h) {
return ctx.typeof(h) === "undefined" ? undefined : ctx.getString(h);
}
/**
* `stripTypeScriptTypes` announces itself with a one-time ExperimentalWarning
* on stderr, which an MCP host shows as noise on every server start. Drop
* exactly that warning, for the duration of this call only; every other
* warning still reaches the process.
*/
function stripTypesQuietly(source) {
const emit = process.emitWarning;
process.emitWarning = function quietStripWarning(warning, ...rest) {
const type = typeof rest[0] === "string" ? rest[0] : rest[0]?.type;
const message = typeof warning === "string" ? warning : warning?.message ?? "";
const name = typeof warning === "string" ? undefined : warning?.name;
if ((type === "ExperimentalWarning" || name === "ExperimentalWarning") && /stripTypeScriptTypes|type strip/i.test(message))
return;
return emit.call(process, warning, ...rest);
};
try {
return stripTypeScriptTypes(source, { mode: "strip" });
}
finally {
process.emitWarning = emit;
}
}
//# sourceMappingURL=sandbox.js.map