dpml-prompt
Version:
DPML-powered AI prompt framework - Revolutionary AI-First CLI system based on Deepractice Prompt Markup Language. Build sophisticated AI agents with structured prompts, memory systems, and execution frameworks.
724 lines (616 loc) • 22.4 kB
JavaScript
const path = require('path');
const fs = require('fs').promises;
const { spawn } = require('child_process');
const vm = require('vm');
const SandboxIsolationManager = require('./SandboxIsolationManager');
const SandboxErrorManager = require('./SandboxErrorManager');
/**
* ToolSandbox - 工具沙箱环境管理器
*
* 基于现有协议系统的工具执行环境,支持:
* - @tool:// 协议定位工具
* - @user://.promptx/toolbox 沙箱隔离
* - 自动依赖管理
* - 可复用的执行环境
*/
class ToolSandbox {
constructor(toolReference, options = {}) {
this.toolReference = toolReference; // @tool://url-validator
this.resourceManager = null; // ResourceManager实例
this.toolId = null; // 工具ID,如 url-validator
this.toolContent = null; // 工具文件内容
this.toolInstance = null; // 工具实例
this.dependencies = []; // 依赖列表
this.sandboxPath = null; // 沙箱目录路径
this.sandboxContext = null; // VM沙箱上下文
this.isolationManager = null; // 沙箱隔离管理器
this.errorManager = new SandboxErrorManager(); // 智能错误管理器
// 状态标志
this.isAnalyzed = false;
this.isPrepared = false;
// 配置选项
this.options = {
timeout: 30000,
enableDependencyInstall: true,
rebuild: false, // 强制重建沙箱(用于处理异常情况)
...options
};
}
/**
* 设置ResourceManager实例
* @param {ResourceManager} resourceManager
*/
setResourceManager(resourceManager) {
this.resourceManager = resourceManager;
}
/**
* 清理沙箱状态和缓存
* @param {boolean} deleteDirectory - 是否删除沙箱目录
*/
async clearSandbox(deleteDirectory = false) {
console.log(`[ToolSandbox] 清理沙箱状态${deleteDirectory ? '并删除目录' : ''}`);
// 清空所有缓存和状态
this.isAnalyzed = false;
this.isPrepared = false;
this.toolContent = null;
this.toolInstance = null;
this.dependencies = [];
this.sandboxContext = null;
// 如果需要,删除沙箱目录
if (deleteDirectory && this.sandboxPath && await this.sandboxExists()) {
try {
const { rmdir } = require('fs').promises;
await rmdir(this.sandboxPath, { recursive: true });
console.log(`[ToolSandbox] 已删除沙箱目录 ${this.sandboxPath}`);
} catch (error) {
console.log(`[ToolSandbox] 删除沙箱目录时出错(可忽略): ${error.message}`);
}
}
}
/**
* 分析工具:加载工具内容,提取元信息和依赖
* @returns {Promise<Object>} 分析结果
*/
async analyze() {
if (this.isAnalyzed && !this.options.rebuild) {
console.log(`[ToolSandbox] 使用缓存的分析结果,依赖: ${JSON.stringify(this.dependencies)}`);
return this.getAnalysisResult();
}
if (!this.resourceManager) {
throw new Error('ResourceManager not set. Call setResourceManager() first.');
}
try {
// 1. 解析工具引用,提取工具ID
this.toolId = this.extractToolId(this.toolReference);
// 2. 通过协议系统加载工具(forceReinstall时强制重新加载)
const loadOptions = this.options.forceReinstall ? { noCache: true } : {};
console.log(`[ToolSandbox] 加载工具 ${this.toolReference},选项:`, loadOptions);
const toolResult = await this.resourceManager.loadResource(this.toolReference, loadOptions);
if (!toolResult.success) {
// 调试:尝试不同的查找方式
console.log(`🔍 调试:尝试查找工具 ${this.toolReference}`);
const directLookup = this.resourceManager.registryData.findResourceById(`tool:${this.toolId}`, 'tool');
console.log(` - 直接查找 tool:${this.toolId}: ${directLookup ? '找到' : '未找到'}`);
throw new Error(`Failed to load tool: ${toolResult.error.message}`);
}
this.toolContent = toolResult.content;
// 调试:检查加载的工具内容
console.log(`[ToolSandbox] 加载的工具内容前200字符:`, this.toolContent.substring(0, 200));
// 3. 设置沙箱路径(工具专用沙箱)
this.sandboxPath = await this.resolveSandboxPath();
// 4. 在基础沙箱中分析工具
await this.analyzeToolInSandbox();
this.isAnalyzed = true;
return this.getAnalysisResult();
} catch (error) {
throw new Error(`Tool analysis failed: ${error.message}`);
}
}
/**
* 准备依赖:安装依赖,准备执行环境
* @returns {Promise<Object>} 准备结果
*/
async prepareDependencies() {
// 处理rebuild选项
if (this.options.rebuild) {
console.log(`[ToolSandbox] 手动触发重建沙箱`);
await this.clearSandbox(true);
}
// 分析工具(如果需要)
if (!this.isAnalyzed) {
await this.analyze();
}
// 自动检测依赖是否需要更新
if (!this.options.rebuild && await this.checkDependenciesNeedUpdate()) {
console.log(`[ToolSandbox] 检测到依赖变化,自动重建沙箱`);
await this.clearSandbox(true);
// 重新分析以获取最新依赖
await this.analyze();
}
if (this.isPrepared) {
return { success: true, message: 'Dependencies already prepared' };
}
try {
// 1. 确保沙箱目录存在
await this.ensureSandboxDirectory();
// 2. 如果有依赖,安装它们
if (this.dependencies.length > 0) {
await this.installDependencies();
}
// 3. 创建执行沙箱环境
await this.createExecutionSandbox();
this.isPrepared = true;
return {
success: true,
sandboxPath: this.sandboxPath,
dependencies: this.dependencies
};
} catch (error) {
throw new Error(`Dependency preparation failed: ${error.message}`);
}
}
/**
* 执行工具
* @param {Object} parameters - 工具参数
* @returns {Promise<Object>} 执行结果
*/
async execute(parameters = {}) {
if (!this.isPrepared) {
await this.prepareDependencies();
}
try {
// 1. 参数验证
await this.validateParameters(parameters);
// 2. 在沙箱中执行工具
const result = await this.executeInSandbox(parameters);
return {
success: true,
data: result,
metadata: {
toolId: this.toolId,
sandboxPath: this.sandboxPath,
executionTime: Date.now()
}
};
} catch (error) {
// 使用智能错误管理器分析错误
const intelligentError = this.errorManager.analyzeError(error, {
toolId: this.toolId,
dependencies: this.dependencies,
sandboxPath: this.sandboxPath,
phase: 'execute'
});
// 抛出增强的错误对象,供上层处理自动重试
const enhancedError = new Error(intelligentError.formattedMessage);
enhancedError.intelligentError = intelligentError;
enhancedError.originalError = error;
throw enhancedError;
}
}
/**
* 提取工具ID
* @param {string} toolReference - @tool://url-validator
* @returns {string} 工具ID
*/
extractToolId(toolReference) {
const match = toolReference.match(/^ :\/\/(.+)$/);
if (!match) {
throw new Error(`Invalid tool reference format: ${toolReference}`);
}
return match[1];
}
/**
* 解析沙箱路径
* @returns {Promise<string>} 沙箱绝对路径
*/
async resolveSandboxPath() {
// 使用 @user://.promptx/toolbox/{toolId} 作为沙箱路径
const userDataReference = ` ://.promptx/toolbox/${this.toolId}`;
const result = await this.resourceManager.resolveProtocolReference(userDataReference);
if (!result.success) {
throw new Error(`Failed to resolve sandbox path: ${result.error}`);
}
// 通过UserProtocol解析实际路径
const userProtocol = this.resourceManager.protocols.get('user');
const sandboxPath = await userProtocol.resolvePath(
`.promptx/toolbox/${this.toolId}`,
new Map()
);
return sandboxPath;
}
/**
* 在基础沙箱中分析工具
*/
async analyzeToolInSandbox() {
// 创建分析阶段的隔离管理器
this.isolationManager = new SandboxIsolationManager(this.sandboxPath, {
enableDependencyLoading: false,
analysisMode: true
});
const sandbox = this.isolationManager.createIsolatedContext();
// 调试:检查即将执行的代码
console.log(`[ToolSandbox] 即将执行的工具代码中的getDependencies部分:`,
this.toolContent.match(/getDependencies[\s\S]*?return[\s\S]*?\]/)?.[0] || '未找到getDependencies');
const script = new vm.Script(this.toolContent, { filename: `${this.toolId}.js` });
const context = vm.createContext(sandbox);
try {
script.runInContext(context);
} catch (error) {
// 使用智能错误过滤处理require错误
const filteredError = this._filterRequireError(error);
if (filteredError) {
throw filteredError;
}
// 如果是预期的require错误,继续执行
}
const exported = context.module.exports;
if (!exported) {
throw new Error(`Tool does not export anything: ${this.toolId}`);
}
// 创建工具实例
let toolInstance;
if (typeof exported === 'function') {
toolInstance = new exported();
} else if (typeof exported === 'object') {
toolInstance = exported;
} else {
throw new Error(`Invalid tool export format: ${this.toolId}`);
}
// 提取依赖
if (typeof toolInstance.getDependencies === 'function') {
try {
this.dependencies = toolInstance.getDependencies() || [];
console.log(`[ToolSandbox] 提取到的依赖列表: ${JSON.stringify(this.dependencies)}`);
} catch (error) {
console.warn(`[ToolSandbox] Failed to get dependencies for ${this.toolId}: ${error.message}`);
this.dependencies = [];
}
} else {
console.log(`[ToolSandbox] 工具没有 getDependencies 方法`);
this.dependencies = [];
}
this.toolInstance = toolInstance;
}
/**
* 智能过滤require错误
* @param {Error} error - 捕获的错误
* @returns {Error|null} - 如果是真正的错误则返回Error对象,如果是预期的require错误则返回null
* @private
*/
_filterRequireError(error) {
// 检查是否是MODULE_NOT_FOUND错误
if (error.code === 'MODULE_NOT_FOUND') {
const missingModule = this._extractMissingModuleName(error.message);
if (missingModule) {
// 获取已声明的依赖列表
const declaredDependencies = this._extractDeclaredDependencies();
// 检查缺失的模块是否在依赖声明中
if (this._isDeclaredInDependencies(missingModule, declaredDependencies)) {
console.log(`[ToolSandbox] 依赖 ${missingModule} 未安装,将在prepareDependencies阶段安装`);
return null; // 预期的错误,忽略
} else {
return new Error(`未声明的依赖: ${missingModule},请在getDependencies()中添加此依赖`);
}
}
}
// 其他错误直接返回
return error;
}
/**
* 从错误信息中提取缺失的模块名
* @param {string} errorMessage - 错误信息
* @returns {string|null} - 模块名或null
* @private
*/
_extractMissingModuleName(errorMessage) {
// 匹配 "Cannot find module 'moduleName'" 或 "Cannot resolve module 'moduleName'"
const match = errorMessage.match(/Cannot (?:find|resolve) module ['"]([^'"]+)['"]/);
return match ? match[1] : null;
}
/**
* 尝试从工具代码中提取已声明的依赖
* @returns {string[]} - 依赖列表
* @private
*/
_extractDeclaredDependencies() {
try {
// 尝试通过正则表达式从代码中提取getDependencies的返回值
const dependencyMatch = this.toolContent.match(/getDependencies\s*\(\s*\)\s*\{[\s\S]*?return\s*\[([\s\S]*?)\]/);
if (dependencyMatch) {
const dependencyString = dependencyMatch[1];
// 提取字符串字面量
const stringMatches = dependencyString.match(/['"]([^'"]+)['"]/g);
if (stringMatches) {
return stringMatches.map(str => str.slice(1, -1)); // 去掉引号
}
}
} catch (error) {
console.warn(`[ToolSandbox] 无法解析依赖声明: ${error.message}`);
}
return [];
}
/**
* 检查模块是否在依赖声明中
* @param {string} moduleName - 模块名
* @param {string[]} declaredDependencies - 已声明的依赖列表
* @returns {boolean} - 是否已声明
* @private
*/
_isDeclaredInDependencies(moduleName, declaredDependencies) {
return declaredDependencies.some(dep => {
// 支持 "axios@^1.6.0" 格式,提取模块名部分
const depName = dep.split('@')[0];
return depName === moduleName;
});
}
/**
* 检查沙箱目录是否存在
* @returns {Promise<boolean>}
*/
async sandboxExists() {
try {
await fs.access(this.sandboxPath);
return true;
} catch (error) {
return false;
}
}
/**
* 确保沙箱目录存在
*/
async ensureSandboxDirectory() {
try {
await fs.access(this.sandboxPath);
} catch (error) {
if (error.code === 'ENOENT') {
await fs.mkdir(this.sandboxPath, { recursive: true });
} else {
throw error;
}
}
}
/**
* 安装依赖
*/
async installDependencies() {
if (this.dependencies.length === 0) {
return;
}
// 1. 创建package.json
await this.createPackageJson();
// 2. 使用内置pnpm安装依赖
await this.runPnpmInstall();
}
/**
* 检查依赖是否需要更新
* @returns {Promise<boolean>} true表示需要更新
*/
async checkDependenciesNeedUpdate() {
const packageJsonPath = path.join(this.sandboxPath, 'package.json');
try {
// 读取现有的package.json
const existingContent = await fs.readFile(packageJsonPath, 'utf-8');
const existingPackageJson = JSON.parse(existingContent);
const existingDeps = existingPackageJson.dependencies || {};
// 构建新的依赖对象
const newDeps = {};
for (const dep of this.dependencies) {
if (dep.includes('@')) {
const [name, version] = dep.split('@');
newDeps[name] = version;
} else {
newDeps[dep] = 'latest';
}
}
// 比较依赖是否一致
const existingKeys = Object.keys(existingDeps).sort();
const newKeys = Object.keys(newDeps).sort();
// 检查键是否相同
if (existingKeys.length !== newKeys.length ||
!existingKeys.every((key, index) => key === newKeys[index])) {
console.log(`[ToolSandbox] 依赖列表变化 - 旧: ${existingKeys.join(', ')} | 新: ${newKeys.join(', ')}`);
return true;
}
// 检查版本是否相同
for (const key of existingKeys) {
if (existingDeps[key] !== newDeps[key]) {
console.log(`[ToolSandbox] 依赖版本变化 - ${key}: ${existingDeps[key]} -> ${newDeps[key]}`);
return true;
}
}
return false;
} catch (error) {
// 文件不存在或解析失败,需要创建
console.log(`[ToolSandbox] package.json不存在或无效,需要创建`);
return true;
}
}
/**
* 创建package.json
*/
async createPackageJson() {
const packageJsonPath = path.join(this.sandboxPath, 'package.json');
const packageJson = {
name: `toolbox-${this.toolId}`,
version: '1.0.0',
description: `Sandbox for tool: ${this.toolId}`,
private: true,
dependencies: {}
};
// 解析依赖格式 ["validator@^13.11.0", "lodash"]
console.log(`[ToolSandbox] 正在处理依赖列表: ${JSON.stringify(this.dependencies)}`);
for (const dep of this.dependencies) {
if (dep.includes('@')) {
const [name, version] = dep.split('@');
console.log(`[ToolSandbox] 解析依赖 "${dep}" => name="${name}", version="${version}"`);
packageJson.dependencies[name] = version;
} else {
packageJson.dependencies[dep] = 'latest';
}
}
await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
}
/**
* 运行pnpm安装
*/
async runPnpmInstall() {
return new Promise((resolve, reject) => {
// 获取内置pnpm路径 - 直接从node_modules获取
const pnpmModulePath = require.resolve('pnpm');
const pnpmBinPath = path.join(path.dirname(pnpmModulePath), 'bin', 'pnpm.cjs');
const pnpm = spawn('node', [pnpmBinPath, 'install'], {
cwd: this.sandboxPath,
stdio: 'pipe'
});
let stdout = '';
let stderr = '';
pnpm.stdout.on('data', (data) => {
stdout += data.toString();
});
pnpm.stderr.on('data', (data) => {
stderr += data.toString();
});
pnpm.on('close', (code) => {
if (code === 0) {
resolve({ stdout, stderr });
} else {
reject(new Error(`pnpm install failed with code ${code}: ${stderr}`));
}
});
pnpm.on('error', (error) => {
reject(new Error(`Failed to spawn pnpm: ${error.message}`));
});
});
}
/**
* 创建执行沙箱环境
*/
async createExecutionSandbox() {
// 创建执行阶段的隔离管理器
this.isolationManager = new SandboxIsolationManager(this.sandboxPath, {
enableDependencyLoading: true,
analysisMode: false
});
this.sandboxContext = this.isolationManager.createIsolatedContext();
// 在完全隔离的沙箱中重新加载工具
const script = new vm.Script(this.toolContent, { filename: `${this.toolId}.js` });
const context = vm.createContext(this.sandboxContext);
script.runInContext(context);
const exported = context.module.exports;
if (typeof exported === 'function') {
this.toolInstance = new exported();
} else if (typeof exported === 'object') {
this.toolInstance = exported;
}
}
/**
* 解析协议路径(支持@project://等协议)
* @param {string} protocolPath - 协议路径,如@project://.promptx/cwd
* @returns {Promise<string>} 解析后的绝对路径
*/
async resolveProtocolPath(protocolPath) {
// 处理undefined或null的情况
if (!protocolPath) {
throw new Error('protocolPath is required but was undefined');
}
// 🚀 新架构:@project协议直接使用ProjectPathResolver
if (protocolPath.startsWith('@project://')) {
const { getGlobalProjectPathResolver } = require('../utils/ProjectPathResolver');
const pathResolver = getGlobalProjectPathResolver();
try {
// 提取协议路径的相对部分
const relativePath = protocolPath.replace(/^ :\/\//, '');
const resolvedPath = pathResolver.resolvePath(relativePath);
// 确保目录存在
const fs = require('fs').promises;
try {
await fs.access(resolvedPath);
} catch (error) {
if (error.code === 'ENOENT') {
await fs.mkdir(resolvedPath, { recursive: true });
console.log(`[ToolSandbox] 创建统一工作目录: ${resolvedPath}`);
}
}
return resolvedPath;
} catch (error) {
throw new Error(`解析 ://路径失败: ${error.message}`);
}
}
// 其他协议路径使用ResourceManager解析
if (protocolPath.startsWith('@')) {
if (!this.resourceManager) {
throw new Error('ResourceManager not set. Cannot resolve protocol path.');
}
// 其他协议处理逻辑保持不变
throw new Error(`暂不支持的协议路径: ${protocolPath}`);
}
// 普通路径直接返回
return protocolPath;
}
/**
* 参数验证
*/
async validateParameters(parameters) {
if (typeof this.toolInstance.validate === 'function') {
const result = this.toolInstance.validate(parameters);
if (typeof result === 'boolean' && !result) {
throw new Error('Parameter validation failed');
} else if (result && typeof result === 'object' && !result.valid) {
throw new Error(`Parameter validation failed: ${result.errors?.join(', ')}`);
}
}
}
/**
* 在沙箱中执行工具
*/
async executeInSandbox(parameters) {
if (!this.toolInstance || typeof this.toolInstance.execute !== 'function') {
throw new Error(`Tool ${this.toolId} does not have execute method`);
}
return await this.toolInstance.execute(parameters);
}
/**
* 获取分析结果
*/
getAnalysisResult() {
return {
toolId: this.toolId,
dependencies: this.dependencies,
sandboxPath: this.sandboxPath,
hasMetadata: typeof this.toolInstance?.getMetadata === 'function',
hasSchema: typeof this.toolInstance?.getSchema === 'function'
};
}
/**
* 清理沙箱资源
*/
async cleanup() {
// 清理隔离管理器
if (this.isolationManager) {
this.isolationManager.cleanup();
this.isolationManager = null;
}
// 清理其他资源
this.sandboxContext = null;
this.toolInstance = null;
}
/**
* 获取工具元信息
*/
getToolMetadata() {
if (this.toolInstance && typeof this.toolInstance.getMetadata === 'function') {
return this.toolInstance.getMetadata();
}
return null;
}
/**
* 获取工具Schema
*/
getToolSchema() {
if (this.toolInstance && typeof this.toolInstance.getSchema === 'function') {
return this.toolInstance.getSchema();
}
return null;
}
}
module.exports = ToolSandbox;