@becrafter/prompt-manager 0.0.19 → 0.1.1
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 +216 -48
- 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/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 +6981 -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,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 工具日志记录服务
|
|
3
|
+
*
|
|
4
|
+
* 职责:
|
|
5
|
+
* 1. 为每个工具提供独立的日志记录器
|
|
6
|
+
* 2. 日志自动写入到工具的 run.log 文件
|
|
7
|
+
* 3. 自动清理过期日志
|
|
8
|
+
* 4. 限制日志文件大小
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import fs from 'fs-extra';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import os from 'os';
|
|
14
|
+
import { logger } from '../utils/logger.js';
|
|
15
|
+
import { pathExists } from './tool-utils.js';
|
|
16
|
+
|
|
17
|
+
// 日志队列,用于批量写入
|
|
18
|
+
const logQueues = new Map();
|
|
19
|
+
|
|
20
|
+
// 日志清理间隔(1小时)
|
|
21
|
+
const CLEANUP_INTERVAL = 60 * 60 * 1000;
|
|
22
|
+
|
|
23
|
+
// 日志保留时间(3小时)
|
|
24
|
+
const LOG_RETENTION_HOURS = 3;
|
|
25
|
+
|
|
26
|
+
// 最大日志文件大小(10MB)
|
|
27
|
+
const MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
28
|
+
|
|
29
|
+
// 当日志文件过大时保留的行数
|
|
30
|
+
const MAX_LOG_LINES = 1000;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 获取工具日志记录器
|
|
34
|
+
* @param {string} toolName - 工具名称
|
|
35
|
+
* @returns {object} 日志记录器对象
|
|
36
|
+
*/
|
|
37
|
+
export function getLogger(toolName) {
|
|
38
|
+
if (!logQueues.has(toolName)) {
|
|
39
|
+
logQueues.set(toolName, []);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const logQueue = logQueues.get(toolName);
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
info: (message, meta) => log(toolName, 'INFO', message, meta),
|
|
46
|
+
warn: (message, meta) => log(toolName, 'WARN', message, meta),
|
|
47
|
+
error: (message, meta) => log(toolName, 'ERROR', message, meta),
|
|
48
|
+
debug: (message, meta) => log(toolName, 'DEBUG', message, meta)
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
function log(toolName, level, message, meta = {}) {
|
|
52
|
+
const timestamp = new Date().toISOString();
|
|
53
|
+
const metaStr = meta && Object.keys(meta).length > 0
|
|
54
|
+
? ' ' + JSON.stringify(meta)
|
|
55
|
+
: '';
|
|
56
|
+
|
|
57
|
+
const logEntry = `[${timestamp}] [${level}] ${message}${metaStr}\n`;
|
|
58
|
+
|
|
59
|
+
// 添加到队列
|
|
60
|
+
logQueue.push(logEntry);
|
|
61
|
+
|
|
62
|
+
// 如果队列达到一定大小,立即刷新
|
|
63
|
+
if (logQueue.length >= 10) {
|
|
64
|
+
flushLogQueue(toolName).catch(error => {
|
|
65
|
+
logger.error(`刷新工具日志队列失败: ${toolName}`, { error: error.message });
|
|
66
|
+
});
|
|
67
|
+
} else {
|
|
68
|
+
// 延迟刷新,批量写入
|
|
69
|
+
setTimeout(() => {
|
|
70
|
+
flushLogQueue(toolName).catch(error => {
|
|
71
|
+
logger.error(`刷新工具日志队列失败: ${toolName}`, { error: error.message });
|
|
72
|
+
});
|
|
73
|
+
}, 100);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 刷新日志队列
|
|
80
|
+
* @param {string} toolName - 工具名称
|
|
81
|
+
*/
|
|
82
|
+
async function flushLogQueue(toolName) {
|
|
83
|
+
const logQueue = logQueues.get(toolName);
|
|
84
|
+
if (!logQueue || logQueue.length === 0) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const toolDir = path.join(os.homedir(), '.prompt-manager', 'toolbox', toolName);
|
|
89
|
+
const logFilePath = path.join(toolDir, 'run.log');
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
// 确保目录存在
|
|
93
|
+
await fs.ensureDir(toolDir);
|
|
94
|
+
|
|
95
|
+
// 读取现有日志(如果存在)
|
|
96
|
+
let existingLogs = '';
|
|
97
|
+
if (await pathExists(logFilePath)) {
|
|
98
|
+
existingLogs = await fs.readFile(logFilePath, 'utf-8');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 清理过期日志
|
|
102
|
+
const cleanedLogs = cleanOldLogs(existingLogs);
|
|
103
|
+
|
|
104
|
+
// 合并新日志
|
|
105
|
+
const newLogs = logQueue.join('');
|
|
106
|
+
const allLogs = cleanedLogs + newLogs;
|
|
107
|
+
|
|
108
|
+
// 检查日志文件大小
|
|
109
|
+
const logsToWrite = allLogs.length > MAX_LOG_SIZE
|
|
110
|
+
? keepRecentLogs(allLogs)
|
|
111
|
+
: allLogs;
|
|
112
|
+
|
|
113
|
+
// 写入日志文件
|
|
114
|
+
await fs.writeFile(logFilePath, logsToWrite, 'utf-8');
|
|
115
|
+
|
|
116
|
+
// 清空队列
|
|
117
|
+
logQueue.length = 0;
|
|
118
|
+
|
|
119
|
+
} catch (error) {
|
|
120
|
+
logger.error(`写入工具日志失败: ${toolName}`, { error: error.message });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* 清理过期日志
|
|
126
|
+
* @param {string} logs - 日志内容
|
|
127
|
+
* @returns {string} 清理后的日志
|
|
128
|
+
*/
|
|
129
|
+
function cleanOldLogs(logs) {
|
|
130
|
+
const lines = logs.split('\n');
|
|
131
|
+
const now = Date.now();
|
|
132
|
+
const retentionMs = LOG_RETENTION_HOURS * 60 * 60 * 1000;
|
|
133
|
+
|
|
134
|
+
const validLines = [];
|
|
135
|
+
|
|
136
|
+
for (const line of lines) {
|
|
137
|
+
if (!line.trim()) {
|
|
138
|
+
validLines.push(line);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// 提取时间戳
|
|
143
|
+
const timestampMatch = line.match(/^\[([^\]]+)\]/);
|
|
144
|
+
if (timestampMatch) {
|
|
145
|
+
try {
|
|
146
|
+
const timestamp = new Date(timestampMatch[1]).getTime();
|
|
147
|
+
if (now - timestamp < retentionMs) {
|
|
148
|
+
validLines.push(line);
|
|
149
|
+
}
|
|
150
|
+
} catch (error) {
|
|
151
|
+
// 无法解析时间戳,保留该行
|
|
152
|
+
validLines.push(line);
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
// 没有时间戳,保留该行
|
|
156
|
+
validLines.push(line);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return validLines.join('\n');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* 保留最近的日志行
|
|
165
|
+
* @param {string} logs - 日志内容
|
|
166
|
+
* @returns {string} 保留的日志
|
|
167
|
+
*/
|
|
168
|
+
function keepRecentLogs(logs) {
|
|
169
|
+
const lines = logs.split('\n');
|
|
170
|
+
const recentLines = lines.slice(-MAX_LOG_LINES);
|
|
171
|
+
return recentLines.join('\n') + '\n';
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* 启动定期清理任务
|
|
176
|
+
*/
|
|
177
|
+
export function startLogCleanupTask() {
|
|
178
|
+
setInterval(async () => {
|
|
179
|
+
const toolboxDir = path.join(os.homedir(), '.prompt-manager', 'toolbox');
|
|
180
|
+
|
|
181
|
+
if (!await pathExists(toolboxDir)) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
const entries = await fs.readdir(toolboxDir, { withFileTypes: true });
|
|
187
|
+
|
|
188
|
+
for (const entry of entries) {
|
|
189
|
+
if (!entry.isDirectory()) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const toolName = entry.name;
|
|
194
|
+
const logFilePath = path.join(toolboxDir, toolName, 'run.log');
|
|
195
|
+
|
|
196
|
+
if (await pathExists(logFilePath)) {
|
|
197
|
+
const logs = await fs.readFile(logFilePath, 'utf-8');
|
|
198
|
+
const cleanedLogs = cleanOldLogs(logs);
|
|
199
|
+
|
|
200
|
+
if (cleanedLogs.length < logs.length) {
|
|
201
|
+
await fs.writeFile(logFilePath, cleanedLogs, 'utf-8');
|
|
202
|
+
logger.debug(`清理工具日志: ${toolName}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
} catch (error) {
|
|
207
|
+
logger.error('定期清理日志失败', { error: error.message });
|
|
208
|
+
}
|
|
209
|
+
}, CLEANUP_INTERVAL);
|
|
210
|
+
|
|
211
|
+
logger.info('工具日志清理任务已启动');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* 强制刷新所有日志队列
|
|
216
|
+
*/
|
|
217
|
+
export async function flushAllLogQueues() {
|
|
218
|
+
const toolNames = Array.from(logQueues.keys());
|
|
219
|
+
for (const toolName of toolNames) {
|
|
220
|
+
await flushLogQueue(toolName);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 统一工具管理处理器
|
|
3
|
+
*
|
|
4
|
+
* 职责:
|
|
5
|
+
* 1. 实现 mcp__promptmanager__toolm 工具的处理逻辑
|
|
6
|
+
* 2. 路由到不同的模式处理器
|
|
7
|
+
* 3. 统一错误处理
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { logger } from '../utils/logger.js';
|
|
11
|
+
import { toolLoaderService } from './tool-loader.service.js';
|
|
12
|
+
import { parseToolYaml } from './tool-yaml-parser.service.js';
|
|
13
|
+
import {
|
|
14
|
+
handleManualMode,
|
|
15
|
+
handleExecuteMode,
|
|
16
|
+
handleConfigureMode,
|
|
17
|
+
handleLogMode
|
|
18
|
+
} from './tool-mode-handlers.service.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 处理 toolm 工具调用
|
|
22
|
+
* @param {object} args - 参数对象,包含 yaml 字段
|
|
23
|
+
* @returns {object} MCP 格式的返回结果
|
|
24
|
+
*/
|
|
25
|
+
export async function handleToolM(args) {
|
|
26
|
+
const { yaml: yamlInput } = args;
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
// 初始化工具加载器(如果尚未初始化)
|
|
30
|
+
if (!toolLoaderService.initialized) {
|
|
31
|
+
await toolLoaderService.initialize();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 解析 YAML 配置
|
|
35
|
+
const { toolName, mode, parameters } = parseToolYaml(yamlInput);
|
|
36
|
+
|
|
37
|
+
// 检查工具是否存在
|
|
38
|
+
if (!toolLoaderService.hasTool(toolName)) {
|
|
39
|
+
const availableTools = toolLoaderService.getAllTools().map(t => t.name).join(', ');
|
|
40
|
+
throw new Error(`工具 '${toolName}' 不存在\n可用工具: ${availableTools}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 根据模式路由到对应的处理器
|
|
44
|
+
switch (mode) {
|
|
45
|
+
case 'manual':
|
|
46
|
+
return handleManualMode(toolName);
|
|
47
|
+
|
|
48
|
+
case 'execute':
|
|
49
|
+
return await handleExecuteMode(toolName, parameters);
|
|
50
|
+
|
|
51
|
+
case 'configure':
|
|
52
|
+
return await handleConfigureMode(toolName, parameters);
|
|
53
|
+
|
|
54
|
+
case 'log':
|
|
55
|
+
return await handleLogMode(toolName, parameters);
|
|
56
|
+
|
|
57
|
+
default:
|
|
58
|
+
throw new Error(`不支持的模式: ${mode}\n支持的模式: manual, execute, configure, log`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
} catch (error) {
|
|
62
|
+
// 错误已经在 parseToolYaml 中处理,直接抛出
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 工具手册生成服务
|
|
3
|
+
*
|
|
4
|
+
* 职责:
|
|
5
|
+
* 1. 生成格式化的工具手册(manual模式)
|
|
6
|
+
* 2. 生成错误帮助信息(execute模式出错时)
|
|
7
|
+
* 3. 统一工具文档的格式和样式
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 生成工具手册(完整版)
|
|
12
|
+
* @param {string} toolName - 工具名称
|
|
13
|
+
* @param {object} tool - 工具对象(包含 metadata, schema, businessErrors)
|
|
14
|
+
* @returns {string} Markdown格式的手册
|
|
15
|
+
*/
|
|
16
|
+
export function generateManual(toolName, tool) {
|
|
17
|
+
const { metadata, schema, businessErrors } = tool;
|
|
18
|
+
|
|
19
|
+
let manual = '';
|
|
20
|
+
|
|
21
|
+
// 标题和基本信息
|
|
22
|
+
manual += `# 📚 ${metadata.name || toolName}\n\n`;
|
|
23
|
+
|
|
24
|
+
if (metadata.description) {
|
|
25
|
+
manual += `## 📋 工具描述\n\n${metadata.description}\n\n`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 基本信息卡片
|
|
29
|
+
manual += `## ℹ️ 基本信息\n\n`;
|
|
30
|
+
if (metadata.version) {
|
|
31
|
+
manual += `- **版本**: ${metadata.version}\n`;
|
|
32
|
+
}
|
|
33
|
+
if (metadata.author) {
|
|
34
|
+
manual += `- **作者**: ${metadata.author}\n`;
|
|
35
|
+
}
|
|
36
|
+
if (metadata.tags && metadata.tags.length > 0) {
|
|
37
|
+
manual += `- **标签**: ${metadata.tags.map(t => `\`${t}\``).join(', ')}\n`;
|
|
38
|
+
}
|
|
39
|
+
manual += `\n`;
|
|
40
|
+
|
|
41
|
+
// 使用场景
|
|
42
|
+
if (metadata.scenarios && metadata.scenarios.length > 0) {
|
|
43
|
+
manual += `## 🎯 使用场景\n\n`;
|
|
44
|
+
metadata.scenarios.forEach(scenario => {
|
|
45
|
+
manual += `- ✅ ${scenario}\n`;
|
|
46
|
+
});
|
|
47
|
+
manual += '\n';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 参数说明
|
|
51
|
+
if (schema.parameters) {
|
|
52
|
+
manual += `## 📝 参数说明\n\n`;
|
|
53
|
+
|
|
54
|
+
const props = schema.parameters.properties || {};
|
|
55
|
+
const required = schema.parameters.required || [];
|
|
56
|
+
|
|
57
|
+
// 必需参数
|
|
58
|
+
if (required.length > 0) {
|
|
59
|
+
manual += `### ✅ 必需参数\n\n`;
|
|
60
|
+
for (const key of required) {
|
|
61
|
+
const prop = props[key];
|
|
62
|
+
if (prop) {
|
|
63
|
+
manual += `- **\`${key}\`** (${prop.type || '未指定类型'})`;
|
|
64
|
+
if (prop.enum) {
|
|
65
|
+
manual += ` - 可选值: ${prop.enum.map(v => `\`${v}\``).join(', ')}`;
|
|
66
|
+
}
|
|
67
|
+
manual += `\n`;
|
|
68
|
+
if (prop.description) {
|
|
69
|
+
manual += ` > ${prop.description}\n`;
|
|
70
|
+
}
|
|
71
|
+
if (prop.default !== undefined) {
|
|
72
|
+
manual += ` > 💡 默认值: \`${prop.default}\`\n`;
|
|
73
|
+
}
|
|
74
|
+
manual += `\n`;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 可选参数
|
|
80
|
+
const optional = Object.keys(props).filter(k => !required.includes(k));
|
|
81
|
+
if (optional.length > 0) {
|
|
82
|
+
manual += `### 📌 可选参数\n\n`;
|
|
83
|
+
for (const key of optional) {
|
|
84
|
+
const prop = props[key];
|
|
85
|
+
if (prop) {
|
|
86
|
+
manual += `- **\`${key}\`** (${prop.type || '未指定类型'})`;
|
|
87
|
+
if (prop.default !== undefined) {
|
|
88
|
+
manual += ` - 默认值: \`${prop.default}\``;
|
|
89
|
+
}
|
|
90
|
+
if (prop.enum) {
|
|
91
|
+
manual += ` - 可选值: ${prop.enum.map(v => `\`${v}\``).join(', ')}`;
|
|
92
|
+
}
|
|
93
|
+
manual += `\n`;
|
|
94
|
+
if (prop.description) {
|
|
95
|
+
manual += ` > ${prop.description}\n`;
|
|
96
|
+
}
|
|
97
|
+
manual += `\n`;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 环境变量
|
|
104
|
+
if (schema.environment && schema.environment.properties) {
|
|
105
|
+
manual += `## ⚙️ 环境变量配置\n\n`;
|
|
106
|
+
const envProps = schema.environment.properties;
|
|
107
|
+
for (const [key, value] of Object.entries(envProps)) {
|
|
108
|
+
manual += `### \`${key}\`\n\n`;
|
|
109
|
+
if (value.description) {
|
|
110
|
+
manual += `**说明**: ${value.description}\n\n`;
|
|
111
|
+
}
|
|
112
|
+
if (value.default) {
|
|
113
|
+
manual += `**默认值**: \`${value.default}\`\n\n`;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
manual += `> 💡 使用 \`mode: configure\` 可以配置这些环境变量\n\n`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 错误处理
|
|
121
|
+
if (businessErrors && businessErrors.length > 0) {
|
|
122
|
+
manual += `## ⚠️ 常见错误处理\n\n`;
|
|
123
|
+
businessErrors.forEach(error => {
|
|
124
|
+
manual += `### ${error.code}\n\n`;
|
|
125
|
+
manual += `- **描述**: ${error.description}\n`;
|
|
126
|
+
manual += `- **解决方案**: ${error.solution}\n`;
|
|
127
|
+
manual += `- **可重试**: ${error.retryable ? '✅ 是' : '❌ 否'}\n\n`;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 限制说明
|
|
132
|
+
if (metadata.limitations && metadata.limitations.length > 0) {
|
|
133
|
+
manual += `## ⚠️ 限制说明\n\n`;
|
|
134
|
+
metadata.limitations.forEach(limitation => {
|
|
135
|
+
manual += `- ⚠️ ${limitation}\n`;
|
|
136
|
+
});
|
|
137
|
+
manual += '\n';
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 使用示例
|
|
141
|
+
manual += `## 💡 使用示例\n\n`;
|
|
142
|
+
manual += `### 基础使用\n\n`;
|
|
143
|
+
manual += `\`\`\`yaml\n`;
|
|
144
|
+
manual += `tool: tool://${toolName}\n`;
|
|
145
|
+
manual += `mode: execute\n`;
|
|
146
|
+
manual += `parameters:\n`;
|
|
147
|
+
|
|
148
|
+
// 生成示例参数
|
|
149
|
+
if (schema.parameters && schema.parameters.properties) {
|
|
150
|
+
const props = schema.parameters.properties;
|
|
151
|
+
const required = schema.parameters.required || [];
|
|
152
|
+
|
|
153
|
+
// 添加必需参数示例
|
|
154
|
+
for (const key of required.slice(0, 3)) { // 最多显示3个必需参数
|
|
155
|
+
const prop = props[key];
|
|
156
|
+
if (prop) {
|
|
157
|
+
const exampleValue = generateExampleValue(key, prop);
|
|
158
|
+
manual += ` ${key}: ${exampleValue} # ${prop.description || ''}\n`;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// 添加可选参数示例(最多2个)
|
|
163
|
+
const optional = Object.keys(props).filter(k => !required.includes(k));
|
|
164
|
+
for (const key of optional.slice(0, 2)) {
|
|
165
|
+
const prop = props[key];
|
|
166
|
+
if (prop && prop.default !== undefined) {
|
|
167
|
+
const defaultValue = typeof prop.default === 'string' ? `"${prop.default}"` : prop.default;
|
|
168
|
+
manual += ` # ${key}: ${defaultValue} # ${prop.description || ''} (可选)\n`;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
manual += `\`\`\`\n\n`;
|
|
174
|
+
|
|
175
|
+
return manual;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* 生成错误帮助信息
|
|
180
|
+
* @param {string} toolName - 工具名称
|
|
181
|
+
* @param {Error} error - 错误对象
|
|
182
|
+
* @param {object} tool - 工具对象(包含 metadata, schema)
|
|
183
|
+
* @param {object} parameters - 当前参数
|
|
184
|
+
* @param {object} businessError - 业务错误信息(可选)
|
|
185
|
+
* @returns {string} Markdown格式的帮助信息
|
|
186
|
+
*/
|
|
187
|
+
export function generateHelpInfo(toolName, error, tool, parameters = {}, businessError = null) {
|
|
188
|
+
const { metadata, schema } = tool;
|
|
189
|
+
|
|
190
|
+
let helpText = '';
|
|
191
|
+
|
|
192
|
+
// 错误提示
|
|
193
|
+
helpText += `# ⚠️ 工具执行错误\n\n`;
|
|
194
|
+
helpText += `**工具**: ${metadata.name || toolName}\n\n`;
|
|
195
|
+
helpText += `**错误信息**: \`${error.message}\`\n\n`;
|
|
196
|
+
|
|
197
|
+
if (businessError) {
|
|
198
|
+
helpText += `**错误类型**: ${businessError.description}\n\n`;
|
|
199
|
+
helpText += `**解决方案**: ${businessError.solution}\n\n`;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
helpText += `---\n\n`;
|
|
203
|
+
|
|
204
|
+
// 工具基本信息
|
|
205
|
+
helpText += `## 📋 工具信息\n\n`;
|
|
206
|
+
if (metadata.description) {
|
|
207
|
+
helpText += `**描述**: ${metadata.description}\n\n`;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// 当前参数
|
|
211
|
+
if (parameters && Object.keys(parameters).length > 0) {
|
|
212
|
+
helpText += `## 📥 当前参数\n\n`;
|
|
213
|
+
helpText += `\`\`\`json\n${JSON.stringify(parameters, null, 2)}\n\`\`\`\n\n`;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// 参数说明
|
|
217
|
+
if (schema.parameters) {
|
|
218
|
+
helpText += `## 📝 参数说明\n\n`;
|
|
219
|
+
|
|
220
|
+
const props = schema.parameters.properties || {};
|
|
221
|
+
const required = schema.parameters.required || [];
|
|
222
|
+
|
|
223
|
+
// 必需参数
|
|
224
|
+
if (required.length > 0) {
|
|
225
|
+
helpText += `### ✅ 必需参数\n\n`;
|
|
226
|
+
for (const key of required) {
|
|
227
|
+
const prop = props[key];
|
|
228
|
+
if (prop) {
|
|
229
|
+
helpText += `- **\`${key}\`** (${prop.type || '未指定类型'})`;
|
|
230
|
+
if (prop.enum) {
|
|
231
|
+
helpText += ` - 可选值: ${prop.enum.map(v => `\`${v}\``).join(', ')}`;
|
|
232
|
+
}
|
|
233
|
+
helpText += `\n`;
|
|
234
|
+
if (prop.description) {
|
|
235
|
+
helpText += ` > ${prop.description}\n`;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
helpText += `\n`;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// 可选参数
|
|
243
|
+
const optional = Object.keys(props).filter(k => !required.includes(k));
|
|
244
|
+
if (optional.length > 0) {
|
|
245
|
+
helpText += `### 📌 可选参数\n\n`;
|
|
246
|
+
for (const key of optional) {
|
|
247
|
+
const prop = props[key];
|
|
248
|
+
if (prop) {
|
|
249
|
+
helpText += `- **\`${key}\`** (${prop.type || '未指定类型'})`;
|
|
250
|
+
if (prop.default !== undefined) {
|
|
251
|
+
helpText += ` - 默认值: \`${prop.default}\``;
|
|
252
|
+
}
|
|
253
|
+
if (prop.enum) {
|
|
254
|
+
helpText += ` - 可选值: ${prop.enum.map(v => `\`${v}\``).join(', ')}`;
|
|
255
|
+
}
|
|
256
|
+
helpText += `\n`;
|
|
257
|
+
if (prop.description) {
|
|
258
|
+
helpText += ` > ${prop.description}\n`;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
helpText += `\n`;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// 使用示例
|
|
267
|
+
helpText += `## 💡 使用示例\n\n`;
|
|
268
|
+
|
|
269
|
+
// 根据错误类型生成不同的示例
|
|
270
|
+
if (error.message.includes('不支持的方法')) {
|
|
271
|
+
// 方法错误,显示所有支持的方法
|
|
272
|
+
if (schema.parameters && schema.parameters.properties && schema.parameters.properties.method) {
|
|
273
|
+
const methodEnum = schema.parameters.properties.method.enum || [];
|
|
274
|
+
if (methodEnum.length > 0) {
|
|
275
|
+
helpText += `### ❌ 错误:不支持的方法\n\n`;
|
|
276
|
+
helpText += `**支持的方法列表**:\n\n`;
|
|
277
|
+
methodEnum.forEach(method => {
|
|
278
|
+
helpText += `- \`${method}\`\n`;
|
|
279
|
+
});
|
|
280
|
+
helpText += `\n`;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (error.message.includes('缺少必需参数') || error.message.includes('缺少参数')) {
|
|
286
|
+
// 提取缺失的参数名
|
|
287
|
+
const missingMatch = error.message.match(/缺少.*参数[::]\s*([^\n]+)/i);
|
|
288
|
+
if (missingMatch) {
|
|
289
|
+
const missingParams = missingMatch[1].split(',').map(p => p.trim());
|
|
290
|
+
helpText += `### ❌ 错误:缺少必需参数\n\n`;
|
|
291
|
+
helpText += `**缺失的参数**:${missingParams.map(p => `\`${p}\``).join(', ')}\n\n`;
|
|
292
|
+
helpText += `**这些参数是必需的,必须提供**\n\n`;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// 生成正确的使用示例
|
|
297
|
+
helpText += `### ✅ 正确使用方式\n\n`;
|
|
298
|
+
helpText += `\`\`\`yaml\n`;
|
|
299
|
+
helpText += `tool: tool://${toolName}\n`;
|
|
300
|
+
helpText += `mode: execute\n`;
|
|
301
|
+
helpText += `parameters:\n`;
|
|
302
|
+
|
|
303
|
+
// 根据schema生成示例参数
|
|
304
|
+
if (schema.parameters && schema.parameters.properties) {
|
|
305
|
+
const props = schema.parameters.properties;
|
|
306
|
+
const required = schema.parameters.required || [];
|
|
307
|
+
|
|
308
|
+
// 先添加必需参数
|
|
309
|
+
for (const key of required) {
|
|
310
|
+
const prop = props[key];
|
|
311
|
+
if (prop) {
|
|
312
|
+
const exampleValue = generateExampleValue(key, prop);
|
|
313
|
+
helpText += ` ${key}: ${exampleValue} # ${prop.description || ''}\n`;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// 添加一些常用的可选参数(最多3个)
|
|
318
|
+
const optional = Object.keys(props).filter(k => !required.includes(k));
|
|
319
|
+
let shownOptional = 0;
|
|
320
|
+
for (const key of optional) {
|
|
321
|
+
if (shownOptional >= 3) break;
|
|
322
|
+
const prop = props[key];
|
|
323
|
+
if (prop) {
|
|
324
|
+
if (prop.default !== undefined) {
|
|
325
|
+
const defaultValue = typeof prop.default === 'string' ? `"${prop.default}"` : prop.default;
|
|
326
|
+
helpText += ` # ${key}: ${defaultValue} # ${prop.description || ''} (可选,默认值: ${prop.default})\n`;
|
|
327
|
+
shownOptional++;
|
|
328
|
+
} else if (prop.enum && prop.enum.length > 0) {
|
|
329
|
+
helpText += ` # ${key}: ${prop.enum[0]} # ${prop.description || ''} (可选)\n`;
|
|
330
|
+
shownOptional++;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
helpText += `\`\`\`\n\n`;
|
|
337
|
+
|
|
338
|
+
// 查看完整手册的提示
|
|
339
|
+
helpText += `---\n\n`;
|
|
340
|
+
helpText += `## 🔍 需要更多帮助?\n\n`;
|
|
341
|
+
helpText += `使用以下命令查看完整的工具手册:\n\n`;
|
|
342
|
+
helpText += `\`\`\`yaml\n`;
|
|
343
|
+
helpText += `tool: tool://${toolName}\n`;
|
|
344
|
+
helpText += `mode: manual\n`;
|
|
345
|
+
helpText += `\`\`\`\n\n`;
|
|
346
|
+
|
|
347
|
+
return helpText;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* 生成参数示例值
|
|
352
|
+
* @param {string} key - 参数名
|
|
353
|
+
* @param {object} prop - 参数属性
|
|
354
|
+
* @returns {string} 示例值字符串
|
|
355
|
+
*/
|
|
356
|
+
function generateExampleValue(key, prop) {
|
|
357
|
+
if (prop.enum && prop.enum.length > 0) {
|
|
358
|
+
return prop.enum[0];
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (prop.type === 'string') {
|
|
362
|
+
// 根据参数名提供更合适的示例值
|
|
363
|
+
if (key.includes('path') || key.includes('url') || key.includes('file')) {
|
|
364
|
+
return key.includes('url') ? '"https://example.com/file.txt"' : '"~/.prompt-manager/file.txt"';
|
|
365
|
+
} else if (key.includes('method')) {
|
|
366
|
+
return prop.enum ? prop.enum[0] : '"method_name"';
|
|
367
|
+
}
|
|
368
|
+
return '"示例值"';
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if (prop.type === 'number') {
|
|
372
|
+
return '0';
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (prop.type === 'boolean') {
|
|
376
|
+
return 'true';
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (prop.type === 'array') {
|
|
380
|
+
return '[]';
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (prop.type === 'object') {
|
|
384
|
+
return '{}';
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return '# 请填写';
|
|
388
|
+
}
|
|
389
|
+
|