
PromptX ToolSandbox源码剖析自动依赖安装与CJS/ESM统一加载是如何实现的【免费下载链接】PromptXPromptX · 领先的AI 智能体上下文平台 PromptX · Leading AI Agent Context Platform项目地址: https://gitcode.com/Deepractice/PromptX本文带你拆解 PromptXAI 智能体上下文平台的核心工具执行环境 ToolSandbox看它如何用 npm 官方 Arborist 实现工具依赖的自动安装又如何用 importx 责任链模式把 CommonJS 与 ES Module 统一加载成同一种形态让工具开发者再也不用纠结模块格式。 什么是 ToolSandboxAI 工具的安全执行环境在 PromptX 中AI Agent 角色比如内置的女娲可以调用各种自定义工具完成任务但工具代码来自用户或社区直接运行存在两大问题依赖问题工具依赖的 npm 包谁来装装到哪里版本冲突怎么办模块格式问题CommonJSrequire和 ES Moduleimport在 Node.js 中加载方式完全不同写错就会报错。ToolSandbox 就是为了解决这两个问题而生的它采用四层架构┌─────────────────────────────────────┐ │ Tool Interface │ 工具实现层 ├─────────────────────────────────────┤ │ ToolSandbox │ 沙箱管理层 ├─────────────────────────────────────┤ │ SandboxIsolationManager │ 隔离执行层 ├─────────────────────────────────────┤ │ VM Context Node.js │ 运行时环境 └─────────────────────────────────────┘核心入口类位于 ToolSandbox.js配合官方使用文档 toolsandbox.md 可以完整理解它的能力边界。 生命周期四步走从工具调用到执行完成工具每次运行都经历固定流程理解这四步是理解后续两个主题的前提分析analyze— analyze() 解析工具内容调用工具的getDependencies()提取依赖清单并初始化目录管理器。准备依赖prepareDependencies— prepareDependencies() 检查哪些包需要安装只安装真正缺失的部分。创建沙箱createExecutionSandbox— createExecutionSandbox() 构建隔离的 VM 执行上下文并注入统一的importx/loadModule加载函数。执行execute— execute() 在沙箱中重新加载工具、校验参数、注入api实例最后调用工具的execute(params)。其中第 1、2 步对应自动依赖安装第 3 步的importx注入对应CJS/ESM 统一加载。下面分别深入。 自动依赖安装预装检测 Arborist 双保险依赖隔离每个工具一套独立 node_modulesToolDirectoryManager 为每个工具分配独立的工具箱目录~/.promptx/toolbox/[tool-id]/ ├── package.json # 该工具专属的依赖清单 └── node_modules/ # 该工具专属的依赖目录这意味着工具 A 用lodash4、工具 B 用lodash3也互不干扰版本冲突天然被隔离。第一步预装检测能省则省prepareDependencies() 并不会无脑全量安装而是先调用analyzeToolDependencies()把依赖分成两类preinstalled预装PromptX 发行版已内置的包直接复用跳过安装required需安装真正缺失的包只安装这部分。如果所有依赖都已预装日志会直接提示 All dependencies are preinstalled, skipping installation!实现秒级就绪。第二步Arborist 安装行为等同官方 npm install真正需要安装时PackageInstaller 接管。它没有自己手写安装逻辑而是直接使用 npm 官方的npmcli/arborist调用arb.reify()一次性解决传递依赖自动补全A → B → C整条依赖链这也是修复 issue #332 的关键版本冲突与 peer deps通过legacyPeerDeps: true兼容旧包结果回读安装后通过arb.loadActual()读取真实安装树记录每个包的版本和路径。依赖声明在写入 package.json 前还会经过 normalizeDependencies() 规范化同时支持{lodash: ^4.17.21}对象格式和[lodash^4]数组格式。加分项自动选择最快的 registry 源getOptimalRegistry() 按三级策略选源用户显式配置NPM_REGISTRY环境变量优先检测系统时区识别为国内环境时自动切换到淘宝镜像registry.npmmirror.com其他情况使用 npm 官方源。国内用户无需手动配置镜像即可享受高速下载这是自动依赖安装体验顺滑的重要一环。 CJS/ESM 统一加载ToolModuleImport 三步策略工具代码里统一用await loadModule(chalk)或await importx(axios)加载任何包不需要关心它是 CJS 还是 ESM。背后的实现是 ToolModuleImport它按以下优先级加载第一步缓存优先moduleCache 是一个Map同一工具生命周期内每个模块只真正加载一次后续命中缓存直接返回。第二步预装包 → 沙箱目录两级查找tryPreinstalled() 先查预装依赖管理器找不到才走 loadFromSandbox()await importxFn(moduleName, { parentURL: pathToFileURL(sandboxPath /package.json).href, cache: true, loader: auto // 关键自动识别 CJS / ESM });两个细节决定了一切parentURL指向沙箱自己的 package.json让 importx 从该工具的node_modules中解析依赖保证加载的是本工具安装的版本loader: autoimportx 自动探测模块类型——CJS 走require语义ESM 走动态import()写错了也不会崩溃。第三步责任链模式做出口统一即使加载成功CJS 和 ESM 返回的模块形态依然不同一个直接是函数一个包在{ default: fn }里。PromptX 用责任链模式解决这个问题ModuleNormalizer 把处理器按优先级排序后串成链每个处理器只处理自己擅长的模块形态处理不了就交给下一个。默认的 8 个处理器链在 createDefaultNormalizer() 中定义优先级处理器职责10NullHandler空值直接放行20FunctionHandler模块本身就是函数30ESModuleHandler识别__esModule标记纯 default 时解包35SmartDefaultHandler智能判断 default 是否应被提取40MultiExportHandler多导出对象如 lodash、nodemailer原样保留50SingleExportHandler单一导出解包60DefaultExportHandler兜底的 default 提取100PrimitiveHandler原始类型及最终兜底其中 SmartDefaultHandler 最有意思它内置 4 套启发式策略纯包装型只有default没有别的内容 → 提取 defaultdefault 是主函数识别 express、debug、chalk 等知名包的主函数 辅助属性特征CJS 包装 ESM 模式default 对象与同级方法高度重合 → 视为包装提取 default重复导出所有命名导出都指向 default → 提取 default。判断不了的情况不强行处理交给后面的 MultiExportHandler 兜底。整条链还有失败即返回原模块的保护normalize()确保规范化环节绝不成为故障点。 安全沙箱代码被关在笼子里统一加载的前提是安全隔离。SandboxIsolationManager 用 Node.js 内置vm模块构建隔离上下文核心手法隔离的 requirecreateIsolatedRequire() 用Module.createRequire()把 require 绑定到沙箱的package.json工具只能解析自己工具箱里的依赖内置模块回退沙箱 require 失败时handleRequireFallback() 允许回退到 Node.js 内置模块path、crypto等但child_process会被拦截引导工具改用api.execute()跨平台执行命令危险操作全部封死createIsolatedProcess() 中的process.exit()、process.abort()、process.binding()、dlopen以及eval全部被替换为抛错的假实现恶意代码无法杀掉宿主进程或加载原生模块polyfill 注入为 Electron 环境补齐File/Blob/FormData等全局对象ElectronPolyfills.js桌面端与 CLI 端行为一致。 总结5 个值得抄的设计独立工具箱目录—— 用user://.promptx/toolbox/[tool-id]协议为每个工具隔离依赖版本冲突零烦恼Arborist 而非手搓安装—— 直接复用 npm 官方依赖求解器传递依赖、冲突处理全部白嫖成熟实现预装检测前置—— 能复用发行版内置包就绝不重复安装加载走缓存 → 预装 → 沙箱三级降级—— 失败自动降级而不是直接报错责任链统一 CJS/ESM 出口—— 每种模块形态交给最懂它的处理器判断不了就保守放行。想动手实践的话可以从官方文档 toolsandbox.md 的快速开始入手写一个声明getDependencies()的最小工具观察~/.promptx/toolbox/下自动生成的package.json和node_modules再通读 ToolSandbox.js 的execute()方法就能完整跑通依赖自动安装 CJS/ESM 统一加载的全链路。【免费下载链接】PromptXPromptX · 领先的AI 智能体上下文平台 PromptX · Leading AI Agent Context Platform项目地址: https://gitcode.com/Deepractice/PromptX创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考