* feat: 为promptx_tool增加forceReinstall选项支持 解决工具沙箱缓存机制问题,允许强制重新安装工具依赖。 ## 修改内容 ### 1. 工具定义更新 (toolDefinitions.js) - 增加 context.options.forceReinstall 参数支持 - 增加 context.options.timeout 参数支持 - 完善参数描述,说明context用途 - 移除暂时不需要的role_id和session_id参数 ### 2. 执行逻辑优化 (ToolCommand.js) - 支持从context.options提取执行选项 - 将选项传递给ToolSandbox构造函数 - 增加调试日志输出沙箱选项 - 完善JSDoc注释 ## 解决的问题 - Issue #107: PromptX工具沙箱缓存机制不支持工具集更新 - 鲁班等AI角色在调试工具时遇到的缓存问题 - 工具依赖更新后无法自动重新安装的问题 ## 使用方式 ```javascript // 强制重新安装依赖 { tool_resource: "@tool://tool-name", parameters: { /* 工具参数 */ }, context: { options: { forceReinstall: true } } } ``` 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: 简化context参数,移除暂时不需要的role_id和session_id 按照奥卡姆剃刀原则,移除当前没有实际用途的参数: - 移除 context.role_id - 移除 context.session_id - 保留 context.options 用于执行配置 - 简化API接口,降低复杂度 未来如需要可重新添加这些参数。 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: 简化promptx_tool参数结构,移除context层级 ## 主要改进 ### 1. 扁平化参数结构 - 移除复杂的context嵌套,直接使用顶级参数 - forceReinstall 和 timeout 作为可选的顶级参数 - 遵循MCP协议惯例,降低AI理解成本 ### 2. 参数说明优化 - forceReinstall: 明确默认值为false - 详细说明使用场景:工具开发和调试中的缓存问题 - timeout: 明确默认值为30000ms ### 3. 防止盲目调用 - 增加重要提醒:要求AI先了解工具说明再调用 - 避免大模型在不了解工具的情况下盲目执行 ## 新的调用方式 ## 优势 - 符合MCP预训练共识,降低AI学习成本 - 参数结构简洁直观 - 保持向后兼容性 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
308 lines
8.8 KiB
JavaScript
308 lines
8.8 KiB
JavaScript
const BasePouchCommand = require('../BasePouchCommand')
|
||
const { getGlobalResourceManager } = require('../../resource')
|
||
const ToolSandbox = require('../../../tool/ToolSandbox')
|
||
const logger = require('../../../utils/logger')
|
||
|
||
/**
|
||
* Tool命令处理器
|
||
* 实现promptx_tool MCP工具,执行通过@tool协议声明的工具
|
||
*/
|
||
class ToolCommand extends BasePouchCommand {
|
||
constructor() {
|
||
super()
|
||
this.resourceManager = null
|
||
}
|
||
|
||
/**
|
||
* 获取或初始化ResourceManager
|
||
*/
|
||
async getResourceManager() {
|
||
if (!this.resourceManager) {
|
||
this.resourceManager = getGlobalResourceManager()
|
||
// 确保ResourceManager已初始化
|
||
if (!this.resourceManager.initialized) {
|
||
await this.resourceManager.initializeWithNewArchitecture()
|
||
}
|
||
}
|
||
return this.resourceManager
|
||
}
|
||
|
||
// BasePouchCommand的抽象方法实现
|
||
getPurpose() {
|
||
return '执行通过@tool协议声明的JavaScript工具'
|
||
}
|
||
|
||
async getContent(args) {
|
||
try {
|
||
// 处理参数:如果是数组,取第一个元素;否则直接使用
|
||
const toolArgs = Array.isArray(args) ? args[0] : args
|
||
|
||
// 执行工具调用
|
||
const result = await this.executeToolInternal(toolArgs)
|
||
|
||
// 格式化响应
|
||
if (result.success) {
|
||
return `🔧 Tool执行成功
|
||
|
||
📋 工具资源: ${result.tool_resource}
|
||
📊 执行结果:
|
||
${JSON.stringify(result.result, null, 2)}
|
||
|
||
⏱️ 性能指标:
|
||
- 执行时间: ${result.metadata.execution_time_ms}ms
|
||
- 时间戳: ${result.metadata.timestamp}
|
||
- 版本: ${result.metadata.version}`
|
||
} else {
|
||
return `❌ Tool执行失败
|
||
|
||
📋 工具资源: ${result.tool_resource}
|
||
❌ 错误信息: ${result.error.message}
|
||
🏷️ 错误类型: ${result.error.type}
|
||
🔢 错误代码: ${result.error.code}
|
||
|
||
⏱️ 执行时间: ${result.metadata.execution_time_ms}ms`
|
||
}
|
||
} catch (error) {
|
||
return `❌ Tool执行异常
|
||
|
||
错误详情: ${error.message}
|
||
|
||
💡 请检查:
|
||
1. 工具资源引用格式是否正确 (@tool://tool-name)
|
||
2. 工具参数是否有效
|
||
3. 工具文件是否存在并可执行`
|
||
}
|
||
}
|
||
|
||
getPATEOAS(args) {
|
||
return {
|
||
currentState: 'tool_executed',
|
||
nextActions: [
|
||
{
|
||
action: 'execute_another_tool',
|
||
description: '执行其他工具',
|
||
method: 'promptx tool'
|
||
},
|
||
{
|
||
action: 'view_available_tools',
|
||
description: '查看可用工具',
|
||
method: 'promptx welcome'
|
||
}
|
||
]
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 内部工具执行方法 - 使用ToolSandbox三阶段执行流程
|
||
* @param {Object} args - 命令参数
|
||
* @param {string} args.tool_resource - 工具资源引用,格式:@tool://tool-name
|
||
* @param {Object} args.parameters - 传递给工具的参数
|
||
* @param {boolean} args.forceReinstall - 是否强制重新安装工具依赖(默认false)
|
||
* @param {number} args.timeout - 工具执行超时时间(毫秒,默认30000ms)
|
||
* @returns {Promise<Object>} 执行结果
|
||
*/
|
||
async executeToolInternal(args) {
|
||
const startTime = Date.now()
|
||
let sandbox = null
|
||
|
||
try {
|
||
// 1. 参数验证
|
||
this.validateArguments(args)
|
||
|
||
const { tool_resource, parameters, forceReinstall = false, timeout = 30000 } = args
|
||
|
||
logger.debug(`[PromptXTool] 开始执行工具: ${tool_resource}`)
|
||
|
||
// 2. 构建沙箱选项并创建ToolSandbox实例
|
||
const sandboxOptions = { forceReinstall, timeout }
|
||
logger.debug(`[PromptXTool] 沙箱选项:`, sandboxOptions)
|
||
sandbox = new ToolSandbox(tool_resource, sandboxOptions)
|
||
|
||
// 3. 设置ResourceManager
|
||
const resourceManager = await this.getResourceManager()
|
||
sandbox.setResourceManager(resourceManager)
|
||
|
||
// 4. ToolSandbox三阶段执行流程
|
||
logger.debug(`[PromptXTool] Phase 1: 分析工具`)
|
||
const analysisResult = await sandbox.analyze()
|
||
|
||
logger.debug(`[PromptXTool] Phase 2: 准备依赖`, { dependencies: analysisResult.dependencies })
|
||
await sandbox.prepareDependencies()
|
||
|
||
logger.debug(`[PromptXTool] Phase 3: 执行工具`)
|
||
const result = await sandbox.execute(parameters)
|
||
|
||
// 5. 格式化成功结果
|
||
return this.formatSuccessResult(result, tool_resource, startTime)
|
||
|
||
} catch (error) {
|
||
// 6. 格式化错误结果
|
||
logger.error(`[PromptXTool] 工具执行失败: ${error.message}`, error)
|
||
return this.formatErrorResult(error, args.tool_resource, startTime)
|
||
} finally {
|
||
// 7. 清理沙箱资源
|
||
if (sandbox) {
|
||
try {
|
||
await sandbox.cleanup()
|
||
} catch (cleanupError) {
|
||
logger.warn(`[PromptXTool] 沙箱清理失败: ${cleanupError.message}`)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 验证命令参数
|
||
* @param {Object} args - 命令参数
|
||
*/
|
||
validateArguments(args) {
|
||
if (!args) {
|
||
throw new Error('Missing arguments')
|
||
}
|
||
|
||
if (!args.tool_resource) {
|
||
throw new Error('Missing required parameter: tool_resource')
|
||
}
|
||
|
||
if (!args.tool_resource.startsWith('@tool://')) {
|
||
throw new Error('Invalid tool_resource format. Must start with @tool://')
|
||
}
|
||
|
||
if (!args.parameters || typeof args.parameters !== 'object') {
|
||
throw new Error('Missing or invalid parameters. Must be an object')
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 格式化成功结果 - 适配ToolSandbox返回格式
|
||
* @param {*} result - 工具执行结果
|
||
* @param {string} toolResource - 工具资源引用
|
||
* @param {number} startTime - 开始时间
|
||
* @returns {Object} 格式化的成功结果
|
||
*/
|
||
formatSuccessResult(result, toolResource, startTime) {
|
||
const duration = Date.now() - startTime
|
||
|
||
return {
|
||
success: true,
|
||
tool_resource: toolResource,
|
||
result: result, // ToolSandbox直接返回工具结果
|
||
metadata: {
|
||
executor: 'ToolSandbox',
|
||
execution_time_ms: duration,
|
||
timestamp: new Date().toISOString(),
|
||
version: '1.0.0'
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 格式化错误结果 - 适配ToolSandbox错误格式
|
||
* @param {Error} error - 错误对象
|
||
* @param {string} toolResource - 工具资源引用(可能为空)
|
||
* @param {number} startTime - 开始时间
|
||
* @returns {Object} 格式化的错误结果
|
||
*/
|
||
formatErrorResult(error, toolResource, startTime) {
|
||
const duration = Date.now() - startTime
|
||
const executionId = this.generateExecutionId()
|
||
|
||
return {
|
||
success: false,
|
||
tool_resource: toolResource || 'unknown',
|
||
error: {
|
||
code: this.getErrorCode(error),
|
||
message: error.message,
|
||
details: {
|
||
executionId: executionId,
|
||
executionTime: `${duration}ms`,
|
||
stack: error.stack
|
||
}
|
||
},
|
||
metadata: {
|
||
executor: 'ToolSandbox',
|
||
timestamp: new Date().toISOString()
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 根据错误类型获取错误代码 - 增强支持ToolSandbox错误
|
||
* @param {Error} error - 错误对象
|
||
* @returns {string} 错误代码
|
||
*/
|
||
getErrorCode(error) {
|
||
const message = error.message.toLowerCase()
|
||
|
||
// ToolSandbox特有错误
|
||
if (message.includes('analyze') || message.includes('analysis')) {
|
||
return 'ANALYSIS_ERROR'
|
||
}
|
||
if (message.includes('dependencies') || message.includes('pnpm')) {
|
||
return 'DEPENDENCY_ERROR'
|
||
}
|
||
if (message.includes('sandbox') || message.includes('execution')) {
|
||
return 'EXECUTION_ERROR'
|
||
}
|
||
if (message.includes('validation') || message.includes('validate')) {
|
||
return 'VALIDATION_ERROR'
|
||
}
|
||
|
||
// 通用错误
|
||
if (message.includes('not found')) {
|
||
return 'TOOL_NOT_FOUND'
|
||
}
|
||
if (message.includes('invalid tool_resource format')) {
|
||
return 'INVALID_TOOL_RESOURCE'
|
||
}
|
||
if (message.includes('missing')) {
|
||
return 'MISSING_PARAMETER'
|
||
}
|
||
if (message.includes('syntax')) {
|
||
return 'TOOL_SYNTAX_ERROR'
|
||
}
|
||
if (message.includes('timeout')) {
|
||
return 'EXECUTION_TIMEOUT'
|
||
}
|
||
|
||
return 'UNKNOWN_ERROR'
|
||
}
|
||
|
||
/**
|
||
* 生成执行ID
|
||
* @returns {string} 唯一的执行ID
|
||
*/
|
||
generateExecutionId() {
|
||
return `tool_exec_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
||
}
|
||
|
||
/**
|
||
* 获取工具命令的元信息 - ToolSandbox版本
|
||
* @returns {Object} 命令元信息
|
||
*/
|
||
getMetadata() {
|
||
return {
|
||
name: 'promptx_tool',
|
||
description: '使用ToolSandbox执行通过@tool协议声明的工具',
|
||
version: '2.0.0',
|
||
author: 'PromptX Framework',
|
||
executor: 'ToolSandbox',
|
||
supports: {
|
||
protocols: ['@tool://'],
|
||
formats: ['.tool.js'],
|
||
features: [
|
||
'ToolSandbox沙箱执行',
|
||
'自动依赖管理',
|
||
'三阶段执行流程',
|
||
'pnpm依赖安装',
|
||
'参数验证',
|
||
'错误处理',
|
||
'执行监控',
|
||
'资源清理'
|
||
]
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
module.exports = ToolCommand |