@becrafter/prompt-manager 0.0.19 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -234
- package/app/desktop/assets/app.1.png +0 -0
- package/app/desktop/assets/app.png +0 -0
- package/app/desktop/assets/icons/icon.icns +0 -0
- package/app/desktop/assets/icons/icon.ico +0 -0
- package/app/desktop/assets/icons/icon.png +0 -0
- package/app/desktop/assets/icons/tray.png +0 -0
- package/app/desktop/assets/tray.1.png +0 -0
- package/app/desktop/assets/tray.png +0 -0
- package/app/desktop/main.js +27 -0
- package/app/desktop/package-lock.json +201 -4
- package/app/desktop/package.json +23 -29
- package/app/desktop/src/services/module-loader.js +43 -22
- package/app/desktop/src/services/runtime-manager.js +172 -23
- package/app/desktop/src/services/update-manager.js +6 -7
- package/app/desktop/src/ui/admin-window-manager.js +757 -0
- package/app/desktop/src/ui/splash-manager.js +253 -0
- package/app/desktop/src/ui/tray-manager.js +8 -24
- package/app/desktop/src/utils/icon-manager.js +39 -47
- package/app/desktop/src/utils/resource-paths.js +0 -23
- package/app/desktop/src/utils/resource-sync.js +260 -0
- package/app/desktop/src/utils/runtime-sync.js +241 -0
- package/examples/prompts/recommend/human_3-0_growth_diagnostic_coach_prompt.yaml +105 -0
- package/package.json +16 -13
- package/packages/admin-ui/.babelrc +3 -0
- package/packages/admin-ui/admin.html +237 -4784
- package/packages/admin-ui/css/main.css +2592 -0
- package/packages/admin-ui/css/recommended-prompts.css +610 -0
- package/packages/admin-ui/package-lock.json +6973 -0
- package/packages/admin-ui/package.json +36 -0
- package/packages/admin-ui/src/codemirror.js +53 -0
- package/packages/admin-ui/src/index.js +3188 -0
- package/packages/admin-ui/webpack.config.js +76 -0
- package/packages/resources/tools/chrome-devtools/README.md +310 -0
- package/packages/resources/tools/chrome-devtools/chrome-devtools.tool.js +1703 -0
- package/packages/resources/tools/file-reader/README.md +289 -0
- package/packages/resources/tools/file-reader/file-reader.tool.js +1545 -0
- package/packages/resources/tools/filesystem/README.md +359 -0
- package/packages/resources/tools/filesystem/filesystem.tool.js +514 -160
- package/packages/resources/tools/ollama-remote/README.md +192 -0
- package/packages/resources/tools/ollama-remote/ollama-remote.tool.js +421 -0
- package/packages/resources/tools/pdf-reader/README.md +236 -0
- package/packages/resources/tools/pdf-reader/pdf-reader.tool.js +565 -0
- package/packages/resources/tools/playwright/README.md +306 -0
- package/packages/resources/tools/playwright/playwright.tool.js +1186 -0
- package/packages/resources/tools/todolist/README.md +394 -0
- package/packages/resources/tools/todolist/todolist.tool.js +1312 -0
- package/packages/server/README.md +142 -0
- package/packages/server/api/admin.routes.js +42 -11
- package/packages/server/api/surge.routes.js +43 -0
- package/packages/server/app.js +119 -14
- package/packages/server/index.js +39 -0
- package/packages/server/mcp/mcp.server.js +324 -105
- package/packages/server/mcp/sequential-thinking.handler.js +318 -0
- package/packages/server/mcp/think-plan.handler.js +274 -0
- package/packages/server/middlewares/auth.middleware.js +6 -0
- package/packages/server/package.json +51 -0
- package/packages/server/server.js +37 -1
- package/packages/server/toolm/index.js +9 -0
- package/packages/server/toolm/package-installer.service.js +267 -0
- package/packages/server/toolm/test-tools.js +264 -0
- package/packages/server/toolm/tool-context.service.js +334 -0
- package/packages/server/toolm/tool-dependency.service.js +168 -0
- package/packages/server/toolm/tool-description-generator-optimized.service.js +375 -0
- package/packages/server/toolm/tool-description-generator.service.js +312 -0
- package/packages/server/toolm/tool-environment.service.js +200 -0
- package/packages/server/toolm/tool-execution.service.js +277 -0
- package/packages/server/toolm/tool-loader.service.js +219 -0
- package/packages/server/toolm/tool-logger.service.js +223 -0
- package/packages/server/toolm/tool-manager.handler.js +65 -0
- package/packages/server/toolm/tool-manual-generator.service.js +389 -0
- package/packages/server/toolm/tool-mode-handlers.service.js +224 -0
- package/packages/server/toolm/tool-storage.service.js +111 -0
- package/packages/server/toolm/tool-sync.service.js +138 -0
- package/packages/server/toolm/tool-utils.js +20 -0
- package/packages/server/toolm/tool-yaml-parser.service.js +81 -0
- package/packages/server/toolm/validate-system.js +421 -0
- package/packages/server/utils/config.js +49 -5
- package/packages/server/utils/util.js +65 -10
- package/scripts/build-icons.js +99 -69
- package/scripts/build.sh +57 -0
- package/scripts/surge/CNAME +1 -0
- package/scripts/surge/README.md +47 -0
- package/scripts/surge/package-lock.json +34 -0
- package/scripts/surge/package.json +20 -0
- package/scripts/surge/sync-to-surge.js +151 -0
- package/app/desktop/assets/icons/icon_1024x1024.png +0 -0
- package/app/desktop/assets/icons/icon_128x128.png +0 -0
- package/app/desktop/assets/icons/icon_16x16.png +0 -0
- package/app/desktop/assets/icons/icon_24x24.png +0 -0
- package/app/desktop/assets/icons/icon_256x256.png +0 -0
- package/app/desktop/assets/icons/icon_32x32.png +0 -0
- package/app/desktop/assets/icons/icon_48x48.png +0 -0
- package/app/desktop/assets/icons/icon_512x512.png +0 -0
- package/app/desktop/assets/icons/icon_64x64.png +0 -0
- package/app/desktop/assets/icons/icon_96x96.png +0 -0
- package/packages/admin-ui/js/closebrackets.min.js +0 -8
- package/packages/admin-ui/js/codemirror.min.js +0 -8
- package/packages/admin-ui/js/js-yaml.min.js +0 -2
- package/packages/admin-ui/js/markdown.min.js +0 -8
- package/packages/resources/tools/index.js +0 -16
- package/packages/server/mcp/toolx.handler.js +0 -131
- package/scripts/icns-builder/package.json +0 -12
- /package/packages/server/mcp/{mcp.handler.js → prompt.handler.js} +0 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 工具环境变量管理服务
|
|
3
|
+
*
|
|
4
|
+
* 职责:
|
|
5
|
+
* 1. 管理工具的环境变量配置(.env 文件)
|
|
6
|
+
* 2. 加载环境变量到工具执行环境
|
|
7
|
+
* 3. 支持 configure 模式更新配置
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import fs from 'fs-extra';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import os from 'os';
|
|
13
|
+
import { logger } from '../utils/logger.js';
|
|
14
|
+
import { pathExists } from './tool-utils.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 加载工具环境变量
|
|
18
|
+
* @param {string} toolName - 工具名称
|
|
19
|
+
* @returns {object} 环境变量对象
|
|
20
|
+
*/
|
|
21
|
+
export async function loadToolEnvironment(toolName) {
|
|
22
|
+
const toolDir = path.join(os.homedir(), '.prompt-manager', 'toolbox', toolName);
|
|
23
|
+
const envFilePath = path.join(toolDir, '.env');
|
|
24
|
+
|
|
25
|
+
// 检查 .env 文件是否存在
|
|
26
|
+
if (!await pathExists(envFilePath)) {
|
|
27
|
+
logger.debug(`工具 ${toolName} 的 .env 文件不存在`);
|
|
28
|
+
return {};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
// 读取 .env 文件内容
|
|
33
|
+
const envContent = await fs.readFile(envFilePath, 'utf-8');
|
|
34
|
+
|
|
35
|
+
// 解析 .env 文件内容
|
|
36
|
+
const envVars = {};
|
|
37
|
+
const lines = envContent.split('\n');
|
|
38
|
+
|
|
39
|
+
for (const line of lines) {
|
|
40
|
+
// 跳过注释和空行
|
|
41
|
+
const trimmedLine = line.trim();
|
|
42
|
+
if (!trimmedLine || trimmedLine.startsWith('#')) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 解析键值对
|
|
47
|
+
const equalIndex = trimmedLine.indexOf('=');
|
|
48
|
+
if (equalIndex === -1) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const key = trimmedLine.substring(0, equalIndex).trim();
|
|
53
|
+
let value = trimmedLine.substring(equalIndex + 1).trim();
|
|
54
|
+
|
|
55
|
+
// 处理带引号的值
|
|
56
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
57
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
58
|
+
value = value.substring(1, value.length - 1);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 处理转义字符
|
|
62
|
+
value = value.replace(/\\n/g, '\n').replace(/\\t/g, '\t');
|
|
63
|
+
|
|
64
|
+
// 尝试解析JSON(如果是JSON数组或对象)
|
|
65
|
+
try {
|
|
66
|
+
if ((value.startsWith('[') && value.endsWith(']')) ||
|
|
67
|
+
(value.startsWith('{') && value.endsWith('}'))) {
|
|
68
|
+
const parsed = JSON.parse(value);
|
|
69
|
+
envVars[key] = parsed;
|
|
70
|
+
} else {
|
|
71
|
+
envVars[key] = value;
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
// 不是有效的JSON,直接使用字符串值
|
|
75
|
+
envVars[key] = value;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
logger.debug(`工具 ${toolName} 环境变量加载成功`, {
|
|
80
|
+
count: Object.keys(envVars).length
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return envVars;
|
|
84
|
+
|
|
85
|
+
} catch (error) {
|
|
86
|
+
logger.error(`加载工具 ${toolName} 的环境变量失败`, { error: error.message });
|
|
87
|
+
return {};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 保存工具环境变量配置
|
|
93
|
+
* @param {string} toolName - 工具名称
|
|
94
|
+
* @param {object} envVars - 环境变量对象
|
|
95
|
+
*/
|
|
96
|
+
export async function saveToolEnvironment(toolName, envVars) {
|
|
97
|
+
const toolDir = path.join(os.homedir(), '.prompt-manager', 'toolbox', toolName);
|
|
98
|
+
await fs.ensureDir(toolDir);
|
|
99
|
+
|
|
100
|
+
const envFilePath = path.join(toolDir, '.env');
|
|
101
|
+
|
|
102
|
+
// 读取现有配置(如果存在)
|
|
103
|
+
const existingVars = await loadToolEnvironment(toolName);
|
|
104
|
+
|
|
105
|
+
// 合并配置
|
|
106
|
+
const mergedVars = {
|
|
107
|
+
...existingVars,
|
|
108
|
+
...envVars
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// 生成 .env 文件内容
|
|
112
|
+
let envContent = `# Tool Environment Variables
|
|
113
|
+
# Tool: ${toolName}
|
|
114
|
+
# Generated by PromptManager ToolEnvironment
|
|
115
|
+
# Last modified: ${new Date().toISOString()}
|
|
116
|
+
|
|
117
|
+
`;
|
|
118
|
+
|
|
119
|
+
// 写入环境变量
|
|
120
|
+
for (const [key, value] of Object.entries(mergedVars)) {
|
|
121
|
+
let escapedValue;
|
|
122
|
+
|
|
123
|
+
// 如果值是对象或数组,转换为JSON字符串
|
|
124
|
+
if (typeof value === 'object' && value !== null) {
|
|
125
|
+
escapedValue = JSON.stringify(value);
|
|
126
|
+
} else {
|
|
127
|
+
// 转义特殊字符
|
|
128
|
+
escapedValue = String(value)
|
|
129
|
+
.replace(/\\/g, '\\\\')
|
|
130
|
+
.replace(/"/g, '\\"')
|
|
131
|
+
.replace(/\n/g, '\\n')
|
|
132
|
+
.replace(/\t/g, '\\t');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 如果值包含空格、等号或特殊字符,用引号包裹
|
|
136
|
+
if (escapedValue.includes(' ') || escapedValue.includes('=') || escapedValue.includes('[') || escapedValue.includes('{')) {
|
|
137
|
+
escapedValue = `"${escapedValue}"`;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
envContent += `${key}=${escapedValue}\n`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 写入文件
|
|
144
|
+
await fs.writeFile(envFilePath, envContent, 'utf-8');
|
|
145
|
+
|
|
146
|
+
logger.info(`工具 ${toolName} 环境变量配置已保存`, {
|
|
147
|
+
count: Object.keys(mergedVars).length
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* 获取工具环境变量配置信息
|
|
153
|
+
* @param {string} toolName - 工具名称
|
|
154
|
+
* @param {object} schema - 工具的环境变量Schema
|
|
155
|
+
* @returns {object} 配置信息对象
|
|
156
|
+
*/
|
|
157
|
+
export async function getToolEnvironmentInfo(toolName, schema) {
|
|
158
|
+
const toolDir = path.join(os.homedir(), '.prompt-manager', 'toolbox', toolName);
|
|
159
|
+
const envFilePath = path.join(toolDir, '.env');
|
|
160
|
+
|
|
161
|
+
// 加载当前配置
|
|
162
|
+
const currentVars = await loadToolEnvironment(toolName);
|
|
163
|
+
|
|
164
|
+
// 获取环境变量定义
|
|
165
|
+
const envProps = schema?.environment?.properties || {};
|
|
166
|
+
|
|
167
|
+
// 构建配置信息
|
|
168
|
+
const configured = [];
|
|
169
|
+
const unconfigured = [];
|
|
170
|
+
|
|
171
|
+
for (const [key, def] of Object.entries(envProps)) {
|
|
172
|
+
const value = currentVars[key];
|
|
173
|
+
const defaultValue = def.default;
|
|
174
|
+
|
|
175
|
+
if (value !== undefined) {
|
|
176
|
+
configured.push({
|
|
177
|
+
key,
|
|
178
|
+
value,
|
|
179
|
+
description: def.description || '',
|
|
180
|
+
status: '✅ 已配置'
|
|
181
|
+
});
|
|
182
|
+
} else {
|
|
183
|
+
unconfigured.push({
|
|
184
|
+
key,
|
|
185
|
+
defaultValue,
|
|
186
|
+
description: def.description || '',
|
|
187
|
+
status: '⚠️ 使用默认值'
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
toolName,
|
|
194
|
+
envFilePath,
|
|
195
|
+
configured,
|
|
196
|
+
unconfigured,
|
|
197
|
+
hasConfig: await pathExists(envFilePath)
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 工具执行服务
|
|
3
|
+
*
|
|
4
|
+
* 职责:
|
|
5
|
+
* 1. 处理工具执行模式
|
|
6
|
+
* 2. 创建执行上下文
|
|
7
|
+
* 3. 执行工具并处理错误
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { logger } from '../utils/logger.js';
|
|
11
|
+
import { toolLoaderService } from './tool-loader.service.js';
|
|
12
|
+
import { ensureToolDependencies } from './tool-dependency.service.js';
|
|
13
|
+
import { createToolContext } from './tool-context.service.js';
|
|
14
|
+
import { generateHelpInfo } from './tool-manual-generator.service.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 执行工具
|
|
18
|
+
* @param {string} toolName - 工具名称
|
|
19
|
+
* @param {object} parameters - 工具参数
|
|
20
|
+
* @returns {object} MCP 格式的返回结果
|
|
21
|
+
*/
|
|
22
|
+
export async function executeTool(toolName, parameters) {
|
|
23
|
+
logger.info(`执行工具: ${toolName}`, { parameters });
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const tool = toolLoaderService.getTool(toolName);
|
|
27
|
+
const toolModule = tool.module;
|
|
28
|
+
|
|
29
|
+
// 1. 确保工具运行环境已初始化(创建 package.json 并安装依赖)
|
|
30
|
+
await ensureToolDependencies(toolName, toolModule);
|
|
31
|
+
|
|
32
|
+
// 2. 创建工具执行上下文
|
|
33
|
+
const toolContext = await createToolContext(toolName, toolModule);
|
|
34
|
+
|
|
35
|
+
// 3. 记录执行开始
|
|
36
|
+
toolContext.api.logger.info('执行开始', {
|
|
37
|
+
tool: toolName,
|
|
38
|
+
parameters: Object.keys(parameters)
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// 4. 执行工具(使用绑定后的execute方法)
|
|
42
|
+
let result;
|
|
43
|
+
try {
|
|
44
|
+
result = await toolContext.execute(parameters);
|
|
45
|
+
|
|
46
|
+
// 5. 记录执行完成
|
|
47
|
+
toolContext.api.logger.info('执行完成', {
|
|
48
|
+
tool: toolName,
|
|
49
|
+
success: true
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
} catch (error) {
|
|
53
|
+
// 记录执行错误
|
|
54
|
+
toolContext.api.logger.error('执行失败', {
|
|
55
|
+
tool: toolName,
|
|
56
|
+
error: error.message,
|
|
57
|
+
stack: error.stack
|
|
58
|
+
});
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
logger.info(`工具执行成功: ${toolName}`);
|
|
63
|
+
|
|
64
|
+
// 6. 格式化并返回结果
|
|
65
|
+
const content = formatToolResult(result, toolName);
|
|
66
|
+
return { content };
|
|
67
|
+
|
|
68
|
+
} catch (error) {
|
|
69
|
+
logger.error(`工具执行失败: ${toolName}`, { error: error.message });
|
|
70
|
+
|
|
71
|
+
const tool = toolLoaderService.getTool(toolName);
|
|
72
|
+
const helpInfo = generateErrorHelpInfo(toolName, error, tool, parameters);
|
|
73
|
+
|
|
74
|
+
return createTextResponse(helpInfo);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ============================================================================
|
|
79
|
+
// 辅助函数
|
|
80
|
+
// ============================================================================
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 格式化工具执行结果
|
|
84
|
+
* 根据结果类型自动识别并转换为 MCP 格式的内容
|
|
85
|
+
*
|
|
86
|
+
* @param {any} result - 工具执行结果
|
|
87
|
+
* @param {string} toolName - 工具名称
|
|
88
|
+
* @returns {Array} MCP 格式的内容数组
|
|
89
|
+
*/
|
|
90
|
+
function formatToolResult(result, toolName) {
|
|
91
|
+
const content = [];
|
|
92
|
+
|
|
93
|
+
// 尝试提取图片数据
|
|
94
|
+
const imageContent = extractImageContent(result);
|
|
95
|
+
if (imageContent.length > 0) {
|
|
96
|
+
content.push(...imageContent);
|
|
97
|
+
|
|
98
|
+
// 对于截图操作,只返回图片
|
|
99
|
+
if (result?.method === 'takeScreenshot') {
|
|
100
|
+
logger.info('截图成功,返回图片数据');
|
|
101
|
+
return content;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 其他操作有图片时,添加文本信息
|
|
105
|
+
if (result?.text) {
|
|
106
|
+
content.push(createTextContent(result.text));
|
|
107
|
+
} else if (result?.method) {
|
|
108
|
+
content.push(createTextContent(`操作成功:${result.method}`));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return content;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 处理 file-reader 工具的其他格式(二进制、媒体元信息等)
|
|
115
|
+
if (result?.format && result?.content) {
|
|
116
|
+
return [createJsonTextContent(result, toolName)];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 默认情况:返回 JSON 格式
|
|
120
|
+
return [createJsonTextContent(result, toolName)];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* 从工具结果中提取图片内容
|
|
125
|
+
* 支持多种工具返回格式:
|
|
126
|
+
* 1. chrome-devtools: result.images 数组
|
|
127
|
+
* 2. filesystem: result.base64 + result.mimeType (图片类型)
|
|
128
|
+
* 3. file-reader: result.format === 'image' + result.content.base64
|
|
129
|
+
*
|
|
130
|
+
* @param {any} result - 工具执行结果
|
|
131
|
+
* @returns {Array} 图片内容数组
|
|
132
|
+
*/
|
|
133
|
+
function extractImageContent(result) {
|
|
134
|
+
const images = [];
|
|
135
|
+
|
|
136
|
+
if (!result) {
|
|
137
|
+
return images;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 情况1: chrome-devtools 工具返回的图片数组
|
|
141
|
+
if (Array.isArray(result.images) && result.images.length > 0) {
|
|
142
|
+
for (const image of result.images) {
|
|
143
|
+
if (image?.data && image?.mimeType) {
|
|
144
|
+
images.push(createImageContent(image.data, image.mimeType));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return images;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// 情况2: filesystem 工具的 read_media_file 返回的 base64 + mimeType
|
|
151
|
+
if (result.base64 && result.mimeType && result.mimeType.startsWith('image/')) {
|
|
152
|
+
images.push(createImageContent(result.base64, result.mimeType));
|
|
153
|
+
logger.info('读取图片文件成功,返回图片数据');
|
|
154
|
+
return images;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// 情况3: file-reader 工具返回的图片格式
|
|
158
|
+
if (result.format === 'image' && result.content?.base64 && result.content?.mimeType) {
|
|
159
|
+
images.push(createImageContent(result.content.base64, result.content.mimeType));
|
|
160
|
+
|
|
161
|
+
// 如果有描述信息,也一并返回
|
|
162
|
+
if (result.content.description) {
|
|
163
|
+
images.push(createTextContent(result.content.description));
|
|
164
|
+
}
|
|
165
|
+
logger.info('读取图片文件成功,返回图片数据');
|
|
166
|
+
return images;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return images;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* 创建图片类型的内容
|
|
174
|
+
*
|
|
175
|
+
* @param {string} data - base64 编码的图片数据
|
|
176
|
+
* @param {string} mimeType - 图片 MIME 类型
|
|
177
|
+
* @returns {object} MCP 图片内容对象
|
|
178
|
+
*/
|
|
179
|
+
function createImageContent(data, mimeType) {
|
|
180
|
+
return {
|
|
181
|
+
type: "image",
|
|
182
|
+
data: data,
|
|
183
|
+
mimeType: mimeType
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* 创建文本类型的内容
|
|
189
|
+
*
|
|
190
|
+
* @param {string} text - 文本内容
|
|
191
|
+
* @returns {object} MCP 文本内容对象
|
|
192
|
+
*/
|
|
193
|
+
function createTextContent(text) {
|
|
194
|
+
return {
|
|
195
|
+
type: "text",
|
|
196
|
+
text: text
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* 创建 JSON 格式的文本内容
|
|
202
|
+
*
|
|
203
|
+
* @param {any} result - 工具执行结果
|
|
204
|
+
* @param {string} toolName - 工具名称
|
|
205
|
+
* @returns {object} MCP 文本内容对象
|
|
206
|
+
*/
|
|
207
|
+
function createJsonTextContent(result, toolName) {
|
|
208
|
+
return createTextContent(
|
|
209
|
+
JSON.stringify({
|
|
210
|
+
success: true,
|
|
211
|
+
tool: toolName,
|
|
212
|
+
mode: 'execute',
|
|
213
|
+
result: result
|
|
214
|
+
}, null, 2)
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* 创建文本类型的响应
|
|
220
|
+
*
|
|
221
|
+
* @param {string} text - 文本内容
|
|
222
|
+
* @returns {object} MCP 格式的响应对象
|
|
223
|
+
*/
|
|
224
|
+
function createTextResponse(text) {
|
|
225
|
+
return {
|
|
226
|
+
content: [createTextContent(text)]
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* 生成错误帮助信息
|
|
232
|
+
*
|
|
233
|
+
* @param {string} toolName - 工具名称
|
|
234
|
+
* @param {Error} error - 错误对象
|
|
235
|
+
* @param {object} tool - 工具对象
|
|
236
|
+
* @param {object} parameters - 工具参数
|
|
237
|
+
* @returns {string} 帮助信息文本
|
|
238
|
+
*/
|
|
239
|
+
function generateErrorHelpInfo(toolName, error, tool, parameters) {
|
|
240
|
+
// 检查是否是参数验证错误
|
|
241
|
+
if (isValidationErrorType(error.message)) {
|
|
242
|
+
return generateHelpInfo(toolName, error, tool, parameters);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// 检查是否是业务错误
|
|
246
|
+
const businessErrors = tool?.businessErrors || [];
|
|
247
|
+
for (const businessError of businessErrors) {
|
|
248
|
+
if (businessError.match?.test(error.message)) {
|
|
249
|
+
return generateHelpInfo(toolName, error, tool, parameters, businessError);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// 其他错误也返回帮助信息
|
|
254
|
+
return generateHelpInfo(toolName, error, tool, parameters);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* 判断是否是参数验证错误
|
|
259
|
+
*
|
|
260
|
+
* @param {string} errorMessage - 错误消息
|
|
261
|
+
* @returns {boolean} 是否是参数验证错误
|
|
262
|
+
*/
|
|
263
|
+
function isValidationErrorType(errorMessage) {
|
|
264
|
+
const validationPatterns = [
|
|
265
|
+
/不支持的方法/i,
|
|
266
|
+
/缺少必需参数/i,
|
|
267
|
+
/缺少参数/i,
|
|
268
|
+
/参数.*必须是/i,
|
|
269
|
+
/参数.*的值必须是/i,
|
|
270
|
+
/无效的参数/i,
|
|
271
|
+
/参数.*类型错误/i,
|
|
272
|
+
/参数.*格式错误/i
|
|
273
|
+
];
|
|
274
|
+
|
|
275
|
+
return validationPatterns.some(pattern => pattern.test(errorMessage));
|
|
276
|
+
}
|
|
277
|
+
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 工具加载服务
|
|
3
|
+
*
|
|
4
|
+
* 职责:
|
|
5
|
+
* 1. 扫描并加载 ~/.prompt-manager/tools 和 packages/resources/tools 目录下的所有工具
|
|
6
|
+
* 2. 验证工具是否符合标准接口规范
|
|
7
|
+
* 3. 提供工具元数据注册表
|
|
8
|
+
* 4. 管理工具体验生命周期
|
|
9
|
+
* 5. 实现工具的四种运行模式 (manual, execute, configure, log)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import fs from 'fs-extra';
|
|
13
|
+
import path from 'path';
|
|
14
|
+
import os from 'os';
|
|
15
|
+
import { fileURLToPath } from 'url';
|
|
16
|
+
import { logger } from '../utils/logger.js';
|
|
17
|
+
import { pathExists } from './tool-utils.js';
|
|
18
|
+
import { generateManual as generateManualFromService } from './tool-manual-generator.service.js';
|
|
19
|
+
|
|
20
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
21
|
+
const __dirname = path.dirname(__filename);
|
|
22
|
+
|
|
23
|
+
class ToolLoaderService {
|
|
24
|
+
constructor() {
|
|
25
|
+
// 工具缓存:toolName -> toolModule
|
|
26
|
+
this.toolCache = new Map();
|
|
27
|
+
|
|
28
|
+
// 工具目录列表(所有工具都在沙箱环境中)
|
|
29
|
+
this.toolDirectories = [
|
|
30
|
+
// 沙箱工具目录(系统工具和用户工具都在这里)
|
|
31
|
+
path.join(os.homedir(), '.prompt-manager', 'toolbox')
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
// 已初始化标志
|
|
35
|
+
this.initialized = false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 初始化工具加载器
|
|
40
|
+
* 扫描所有工具目录并加载工具
|
|
41
|
+
*/
|
|
42
|
+
async initialize() {
|
|
43
|
+
if (this.initialized) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
logger.info('初始化工具加载器...');
|
|
48
|
+
|
|
49
|
+
// 确保工具箱目录存在
|
|
50
|
+
const toolboxDir = path.join(os.homedir(), '.prompt-manager', 'toolbox');
|
|
51
|
+
await fs.ensureDir(toolboxDir);
|
|
52
|
+
|
|
53
|
+
// 扫描并加载所有工具
|
|
54
|
+
await this.scanAndLoadTools();
|
|
55
|
+
|
|
56
|
+
this.initialized = true;
|
|
57
|
+
logger.info(`工具加载器初始化完成,共加载 ${this.toolCache.size} 个工具`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 扫描并加载所有工具目录中的工具
|
|
62
|
+
*/
|
|
63
|
+
async scanAndLoadTools() {
|
|
64
|
+
for (const toolsDir of this.toolDirectories) {
|
|
65
|
+
if (!await pathExists(toolsDir)) {
|
|
66
|
+
logger.debug(`工具目录不存在,跳过: ${toolsDir}`);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
const entries = await fs.readdir(toolsDir, { withFileTypes: true });
|
|
72
|
+
|
|
73
|
+
for (const entry of entries) {
|
|
74
|
+
if (!entry.isDirectory()) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const toolName = entry.name;
|
|
79
|
+
const toolDir = path.join(toolsDir, toolName);
|
|
80
|
+
const toolFile = path.join(toolDir, `${toolName}.tool.js`);
|
|
81
|
+
|
|
82
|
+
if (await pathExists(toolFile)) {
|
|
83
|
+
try {
|
|
84
|
+
await this.loadTool(toolName, toolFile);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
logger.error(`加载工具失败: ${toolName}`, { error: error.message });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} catch (error) {
|
|
91
|
+
logger.error(`扫描工具目录失败: ${toolsDir}`, { error: error.message });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 加载单个工具
|
|
98
|
+
* @param {string} toolName - 工具名称
|
|
99
|
+
* @param {string} toolFile - 工具文件路径
|
|
100
|
+
*/
|
|
101
|
+
async loadTool(toolName, toolFile) {
|
|
102
|
+
logger.debug(`加载工具: ${toolName}`, { file: toolFile });
|
|
103
|
+
|
|
104
|
+
// 动态导入工具模块
|
|
105
|
+
const toolModule = await import(toolFile);
|
|
106
|
+
const tool = toolModule.default || toolModule;
|
|
107
|
+
|
|
108
|
+
// 验证工具接口
|
|
109
|
+
this.validateToolInterface(toolName, tool);
|
|
110
|
+
|
|
111
|
+
// 缓存工具实例
|
|
112
|
+
this.toolCache.set(toolName, {
|
|
113
|
+
name: toolName,
|
|
114
|
+
file: toolFile,
|
|
115
|
+
module: tool,
|
|
116
|
+
metadata: tool.getMetadata ? tool.getMetadata() : { name: toolName },
|
|
117
|
+
schema: tool.getSchema ? tool.getSchema() : {},
|
|
118
|
+
businessErrors: tool.getBusinessErrors ? tool.getBusinessErrors() : []
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
logger.info(`工具加载成功: ${toolName}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* 验证工具接口是否符合规范
|
|
126
|
+
* @param {string} toolName - 工具名称
|
|
127
|
+
* @param {object} tool - 工具模块
|
|
128
|
+
*/
|
|
129
|
+
validateToolInterface(toolName, tool) {
|
|
130
|
+
// 必需的方法
|
|
131
|
+
const requiredMethods = ['execute'];
|
|
132
|
+
|
|
133
|
+
// 推荐的方法
|
|
134
|
+
const recommendedMethods = ['getMetadata', 'getSchema', 'getDependencies', 'getBusinessErrors'];
|
|
135
|
+
|
|
136
|
+
// 检查必需方法
|
|
137
|
+
for (const method of requiredMethods) {
|
|
138
|
+
if (typeof tool[method] !== 'function') {
|
|
139
|
+
throw new Error(`工具 ${toolName} 缺少必需方法: ${method}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 检查推荐方法
|
|
144
|
+
for (const method of recommendedMethods) {
|
|
145
|
+
if (typeof tool[method] !== 'function') {
|
|
146
|
+
logger.warn(`工具 ${toolName} 缺少推荐方法: ${method}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* 获取工具实例
|
|
153
|
+
* @param {string} toolName - 工具名称
|
|
154
|
+
* @returns {object} 工具实例
|
|
155
|
+
*/
|
|
156
|
+
getTool(toolName) {
|
|
157
|
+
if (!this.initialized) {
|
|
158
|
+
throw new Error('工具加载器尚未初始化,请先调用 initialize()');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const tool = this.toolCache.get(toolName);
|
|
162
|
+
if (!tool) {
|
|
163
|
+
const availableTools = Array.from(this.toolCache.keys()).join(', ');
|
|
164
|
+
throw new Error(`工具 '${toolName}' 不存在。可用工具: ${availableTools}`);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return tool;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* 获取所有已加载的工具列表
|
|
172
|
+
* @returns {Array} 工具列表
|
|
173
|
+
*/
|
|
174
|
+
getAllTools() {
|
|
175
|
+
if (!this.initialized) {
|
|
176
|
+
throw new Error('工具加载器尚未初始化,请先调用 initialize()');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return Array.from(this.toolCache.values()).map(tool => ({
|
|
180
|
+
name: tool.name,
|
|
181
|
+
metadata: tool.metadata,
|
|
182
|
+
schema: tool.schema
|
|
183
|
+
}));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* 检查工具是否存在
|
|
188
|
+
* @param {string} toolName - 工具名称
|
|
189
|
+
* @returns {boolean}
|
|
190
|
+
*/
|
|
191
|
+
hasTool(toolName) {
|
|
192
|
+
return this.toolCache.has(toolName);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* 重新加载所有工具
|
|
197
|
+
*/
|
|
198
|
+
async reload() {
|
|
199
|
+
logger.info('重新加载所有工具...');
|
|
200
|
+
this.toolCache.clear();
|
|
201
|
+
this.initialized = false;
|
|
202
|
+
await this.initialize();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* 生成工具手册(manual模式)
|
|
207
|
+
* @param {string} toolName - 工具名称
|
|
208
|
+
* @returns {string} Markdown格式的手册
|
|
209
|
+
*/
|
|
210
|
+
generateManual(toolName) {
|
|
211
|
+
// 使用统一的工具手册生成服务
|
|
212
|
+
const tool = this.getTool(toolName);
|
|
213
|
+
return generateManualFromService(toolName, tool);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// 导出单例实例
|
|
218
|
+
export const toolLoaderService = new ToolLoaderService();
|
|
219
|
+
|