
在 Mastra 中用 QuickJS WebAssembly 运行 Code Modemastra/quickjs无原生依赖的进程内隔离执行方案【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastraQuickJsCodeModeTransport是 Mastra 框架中的一种 Code Mode 传输层实现它把模型编写model-authored的 TypeScript 程序放进一个编译为 WebAssembly 的 QuickJS 解释器中执行从而在不依赖任何原生插件、不需要 Node.js 启动标志的前提下获得与宿主进程隔离的执行边界。本指南将带你完整掌握该包的安装、配置、运行原理与安全模型并结合仓库源码解释它如何让 Code Mode 在 serverless 等受限环境中落地。读完你可以直接将mastra/quickjs接入自己的 Agent让模型生成的代码安全地编排你的业务工具。Code Mode 与传输层Transport的定位在 packages/core/src/tools/code-mode/types.ts 中可以看到Code Mode 让 LLM 编写并执行一个TypeScript 程序程序通过external_*函数编排其他 Mastra 工具每个external_*调用都会以 RPC 方式回调宿主host由宿主的真实 Mastra 工具执行保留参数校验、追踪、请求上下文与mastra实例。CodeModeTransport是这一机制的抽象接口run()方法接收程序本体、允许调用的工具 ID 列表allow-list、调度器dispatch、超时与中止信号等参数requiresSandbox?: boolean声明该传输层是否需要在 WorkspaceSandbox 中执行。进程内传输层如 V8 隔离、QuickJS自带执行边界会将其设为false从而让createCodeMode无需配置 sandbox 也能运行run(opts)运行程序并通过dispatch分发external_*调用程序结束后解析为CodeModeToolResult。CodeModeToolResult是统一的执行结果契约包含success、result、logs按顺序捕获的 console 输出与errormessage、name、可选line字段。默认情况下createCodeMode使用基于 WorkspaceSandbox 的 stdio 传输层而mastra/quickjs提供的QuickJsCodeModeTransport则把执行边界内置到进程内部声明requiresSandbox false因此无需任何外部沙箱进程。为什么需要 QuickJS 传输层Code Mode 的进程内执行此前依赖mastra/isolated-vm但它的部署代价较高isolated-vm是原生插件且在 Node 20 上要求宿主进程以--no-node-snapshot启动。这两个条件在大多数 serverless 平台上都不可用导致这类环境只剩下完整工作区沙箱这一种选择。mastra/quickjs的出现填补了这个空白见 CHANGELOG.md 中 0.1.0 的发布说明QuickJS 是编译为 WebAssembly 的解释器无原生二进制、无安装步骤、无 Node 标志甚至可以在浏览器中运行。代价是执行速度WASM 中的解释器比 V8 慢不少但这对 Code Mode 面向的工具编排型负载影响有限——慢的是纯计算密集型代码而大量时间花费在await工具调用上的程序几乎不受影响。安装npm install mastra/quickjs包依赖关系见 package.json运行时依赖仅有quickjs-emscriptenQuickJS 的 WASM 封装与ts-blank-space纯 JavaScript 的 TypeScript 类型擦除器与mastra/core的 peer 依赖范围为1.55.0-0 2.0.0-0并声明engines.node 20.19.0。快速上手接入定价 AgentREADME 中的完整示例展示了最小接入路径——把getPrice工具通过createCodeMode包装成execute_typescript工具并挂到 Agent 上import { Agent } from mastra/core/agent; import { createCodeMode, createTool } from mastra/core/tools; import { QuickJsCodeModeTransport } from mastra/quickjs; import { z } from zod; const getPrice createTool({ id: getPrice, description: Get the price of a product, inputSchema: z.object({ productId: z.string() }), outputSchema: z.object({ price: z.number() }), execute: async ({ productId }) ({ price: productId pro ? 49 : 19 }), }); const { tool, instructions } createCodeMode( { tools: { getPrice } }, new QuickJsCodeModeTransport({ memoryLimitMb: 128 }), ); const agent new Agent({ id: pricing-agent, name: Pricing agent, instructions: [Answer pricing questions., instructions], model: openai/gpt-5.6-sol, tools: { execute_typescript: tool }, });要点说明createCodeMode返回两个东西tool默认 ID 为execute_typescript可经config.id覆盖和instructions注入 Agent 指令引导模型正确编写编排程序模型生成的程序将以return value结尾可用Promise.all批量并发调用external_*工具并在 JS 中做聚合与运算默认超时 30 秒DEFAULT_TIMEOUT见 code-mode.ts可经createCodeMode({ timeout })调整。传输层配置项详解QuickJsCodeModeTransportOptions在 transport.ts 中定义共三个可选参数参数类型默认值说明memoryLimitMbnumber128QuickJS 解释器堆内存上限MiB超出即终止程序maxStackSizeBytesnumber10485761 MiB解释器栈上限字节防止失控递归撑爆栈moduleQuickJSWASMModulegetQuickJS()的共享 release 构建预加载的 QuickJS WASM 模块可固定特定变体关于module的典型用法默认的getQuickJS()解析到共享 release 构建对大多数调用者已足够但你可以传入自定义模块来固定特定变体——例如适配自己打包器的单文件变体或测试时带泄漏检测器的 debug 构建下文测试与质量保障会看到它的实战用法。运行原理WASM 边界与安全模型隔离即边界该传输层的核心设计思想在 transport.ts 的头部注释中阐明解释器本身就是真正的安全边界。QuickJS 以裸全局对象启动客端guest没有任何文件系统、网络、进程、定时器或模块访问能力唯一的能力来源是被注入的external_*函数它们回调宿主分发器且allow-list 在宿主端强制实施。测试用例直接断言了这一隔离性见 transport.test.ts// 客端视角宿主能力全部不可见 const result await run( return { process: typeof process, require: typeof require, fetch: typeof fetch, setTimeout: typeof setTimeout, }; ); // { process: undefined, require: undefined, fetch: undefined, setTimeout: undefined }同时宿主桥接__hostCall/__hostLog在 bootstrap 阶段即被删除importScripts、module、globalThis.process也全部不可见彻底切断模块加载器与宿主引用。JSON 字符串双向跨界宿主调用与客端程序之间通过JSON 字符串双向穿越 WASM 边界客端永远拿不到宿主的对象引用客端调用external_*时参数先经JSON.stringify序列化——不可序列化的参数如BigInt会被拒绝并返回必须是 JSON-serializable的错误测试用例验证了此时调度器根本不会被调用宿主侧rpcHost将结果封入{ ok, result }信封返回若工具抛错则封入{ ok: false, error: { message, name } }客端据此重建保留原始错误名的 Error 对象程序结果同样以 JSON 信封回传非序列化结果如循环引用a.self a会落入错误路径返回TypeError/ not JSON-serializable。宿主端 Allow-List 强制run()一开始就构建externals工具 ID 经sanitizeToolId清洗为合法的external_*名字如my-tool→external_my_tool与allowList new Set(toolIds)。每次 RPC 都会检查if (!allowList.has(tool)) { // 返回 { ok: false, error: { message: Tool ${tool} is not available in Code Mode, name: NotAllowedError } } }只有 allow-list 内的工具才会生成对应的external_*全局函数未被暴露的工具客端连名字都无法调用得到ReferenceError调度器永远不会被触达。TypeScript 剥离为什么选 ts-blank-spaceQuickJS 只运行纯 JavaScript因此宿主需要先把模型生成的 TypeScript 剥离类型。源码注释给出了一个刻意的技术选型使用ts-blank-space而非 esbuild。理由很直白——esbuild 本身是原生二进制引入它就等于重新引入了这个包存在的初衷要避免的依赖。ts-blank-space是纯 JavaScript 的类型擦除器且只擦除类型、不改动其余代码因此真正的语法错误会被完整保留并在evalCode阶段以 QuickJS 自己的诊断信息作为SyntaxError抛出。程序在剥离前被包裹成(async () { ... })使得顶层return/await/const合法化与核心运行器的程序模块行为保持一致。测试确认了类型注解如const double (n: number): number n * 2能被正确剥离并运行。console 捕获bootstrap 阶段向客端注入一个冻结的console对象log/info/warn/error所有输出经__hostLog桥接汇入logs数组并随CodeModeToolResult返回。失败时失败前的日志也会被保留console.log(step 1 done); throw new Error(...)仍会带回[step 1 done]。并发模型为什么刻意避开 asyncify 构建quickjs-emscripten提供了 asyncify 构建其宿主函数可以是async看起来像是自然之选。但 transport.ts 的注释与测试都说明它不可用asyncify 模块一次只能为一个异步调用挂起重复进入已挂起的模块会使其崩溃而 Code Mode 的核心价值恰恰是用Promise.all批量调用工具需要任意数量的调用同时在途in-flight。因此该传输层使用常规同步构建每个external_*调用把 deferred promise 交给客端后立即返回控制权给宿主不挂起模块宿主在真实分发完成时结算 deferred并泵动客端的微任务队列。于是任意数量的调用可以同时在途。测试对此有专门的守护8 个external_calc调用通过Promise.all并行发出断言宿主侧maxInFlight达到 8另一个用例用逆序延迟证明并发调用可以乱序完成并正确归位。运行时控制内存、栈、超时与中止内存与栈限制run()中通过runtime.setMemoryLimit(memoryLimitMb * 1024 * 1024)与runtime.setMaxStackSize(maxStackSizeBytes)落地两个限制。测试用memoryLimitMb: 16运行无限分配程序断言结果是 OOM 错误而非30 秒超时——以此证明内存限制真实生效。超时宿主侧建立deadline Date.now() timeout通过两套机制协同同步死循环while (true) {}依靠 QuickJS 的中断处理器setInterruptHandler解释器在操作间隙回调它返回真值即展开求值栈产生TimeoutError异步挂起await new Promise(() {})或external_*调用永不返回此时没有客端代码在跑中断处理器够不到靠宿主侧的Promise.race([guestPromise, timeoutPromise, abortPromise])兜底。中止abortSignal通过三重路径生效运行前已中止则直接返回AbortError运行中中止且客端正在同步执行则中断处理器立即展开中止发生在客端 await 期间则由 abortPromise 竞速兜底。一个关键测试用例验证了中止必须立即停止失控同步循环信号在分发期间触发客端随后进入死循环断言 5 秒内返回AbortError而非烧 CPU 等到 30 秒超时。资源回收与内存泄漏防护QuickJS 的句柄handle必须手工释放任何一个未释放的句柄都会在 runtime 释放时中止整个 WASM 模块。为此run()维护pendingDeferreds集合与abandonedGuestResult在cleanup()中统一释放所有未结算的 deferred 与弃置的结果句柄对于提前结束超时/中止时残留在作业队列中的客端工作先以drainJobs()50ms 预算、最多 10 轮见DRAIN_BUDGET_MS/MAX_DRAIN_PASSES排空再释放 context 与 runtime。与其他传输层的关系与选型mastra/quickjs与 code-mode/isolated-vm 是可互换的兄弟实现行为契约高度一致——测试注释明确指出该测试套件是逐条从mastra/isolated-vm的传输层测试移植过来的因为两者共享同一份安全进程内传输层行为契约。差异只在部署成本维度mastra/isolated-vmmastra/quickjs执行引擎V8 isolateisolated-vm原生插件QuickJSWASM安装需要原生二进制或源码编译纯 WASM无安装步骤Node 标志Node 20 需--no-node-snapshot缺失时构造器快速失败见 isolated-vm/src/transport.ts 的assertNoNodeSnapshot无需任何标志普通 Node 进程即可运行浏览器不可用可运行执行速度快V8慢WASM 解释器对await密集的工具编排负载影响有限TypeScript 剥离esbuildts-blank-spacemastra/quickjs的测试特意验证了这一点测试进程未携带--no-node-snapshotvitest 配置不传任何execArgv仍能正常构造与运行——这正是该包存在的意义。选型建议需要极致性能且能接受原生依赖时选isolated-vm部署在 serverless、边缘函数、浏览器等受限环境或想完全避免原生二进制时选quickjs。错误处理与结果契约run()的输出统一为CodeModeToolResult调用方只需检查success场景successerror.name说明程序正常返回true—result为返回值logs含 console 输出程序抛异常false原始错误名如TypeError错误信息保留原始message语法错误falseSyntaxError来自 QuickJS 自身诊断结果不可序列化falseTypeError循环引用等超时falseTimeoutError消息含timed out after ${timeout}ms中止falseAbortError信号触发或运行前已中止内存/栈超限false非TimeoutError如out of memory调用未暴露工具false客端捕获NotAllowedError/ReferenceError宿主编排层拦截客端异常会保留原始错误名传回测试用自定义CustomError验证宿主分发的不可序列化结果也会先序列化成功才上报成功事件避免先报成功再失败的不一致观测。onExternalCall/onExternalResult两个 observer 钩子可供追踪且钩子抛错会被吞掉——观察者异常绝不能阻止 RPC 响应否则客端对应 promise 会挂到超时。测试与质量保障测试套件transport.test.ts运行在真实 QuickJS WASM 解释器上无 fake、无进程派生、无网络直接检验隔离边界本身覆盖TypeScript 剥离、console 捕获、Promise.all并发、allow-list 强制、超时/中止/内存限制、日志保留、宿主能力隔离与桥接隐藏。其中句柄释放专项测试使用quickjs-emscripten的debug 构建 泄漏检测器DEBUG_SYNC变体 TestQuickJSWASMModule.assertNoMemoryAllocated()对成功运行、失败运行、分发在途超时、同步循环超时四种路径断言零残留分配——这是对长运行 Agent 进程中难以归因的内存问题的事前防线。此外还有一组端到端用例直接用createCodeModeQuickJsCodeModeTransport不配置任何 sandbox完成external_getTopProducts的调用与聚合证明requiresSandbox: false的真实效果。适用场景与已知限制适用场景serverless / 边缘函数等无法安装原生插件、无法设置 Node 启动标志的平台浏览器端需要执行模型代码的场景WASM 天然可运行希望完全摆脱原生二进制依赖、简化部署与构建链路的环境需要进程内隔离、又不想引入完整 WorkspaceSandbox 的开销时。已知限制解释型执行速度低于 V8纯计算密集的模型程序会有明显性能差距这一点在 CHANGELOG 与源码注释中都有明确交代客端程序必须返回 JSON 可序列化值工具参数亦然客端无模块系统与网络能力——这是隔离设计的必然结果编排能力需通过 allow-list 工具暴露版本 0.1.x 仍处于早期阶段当前 0.1.1API 细节以 CHANGELOG.md 与源码注释为准。接入时只需记住一条原则模型代码永远不可信把一切真实能力都收敛为显式暴露的external_*工具并配合合理的memoryLimitMb、maxStackSizeBytes与timeout即可在受限环境中获得与 WorkspaceSandbox 同等级别的进程内隔离执行。【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考