@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.
Files changed (104) hide show
  1. package/README.md +145 -234
  2. package/app/desktop/assets/app.1.png +0 -0
  3. package/app/desktop/assets/app.png +0 -0
  4. package/app/desktop/assets/icons/icon.icns +0 -0
  5. package/app/desktop/assets/icons/icon.ico +0 -0
  6. package/app/desktop/assets/icons/icon.png +0 -0
  7. package/app/desktop/assets/icons/tray.png +0 -0
  8. package/app/desktop/assets/tray.1.png +0 -0
  9. package/app/desktop/assets/tray.png +0 -0
  10. package/app/desktop/main.js +27 -0
  11. package/app/desktop/package-lock.json +201 -4
  12. package/app/desktop/package.json +23 -29
  13. package/app/desktop/src/services/module-loader.js +43 -22
  14. package/app/desktop/src/services/runtime-manager.js +172 -23
  15. package/app/desktop/src/services/update-manager.js +6 -7
  16. package/app/desktop/src/ui/admin-window-manager.js +757 -0
  17. package/app/desktop/src/ui/splash-manager.js +253 -0
  18. package/app/desktop/src/ui/tray-manager.js +8 -24
  19. package/app/desktop/src/utils/icon-manager.js +39 -47
  20. package/app/desktop/src/utils/resource-paths.js +0 -23
  21. package/app/desktop/src/utils/resource-sync.js +260 -0
  22. package/app/desktop/src/utils/runtime-sync.js +241 -0
  23. package/examples/prompts/recommend/human_3-0_growth_diagnostic_coach_prompt.yaml +105 -0
  24. package/package.json +16 -13
  25. package/packages/admin-ui/.babelrc +3 -0
  26. package/packages/admin-ui/admin.html +237 -4784
  27. package/packages/admin-ui/css/main.css +2592 -0
  28. package/packages/admin-ui/css/recommended-prompts.css +610 -0
  29. package/packages/admin-ui/package-lock.json +6973 -0
  30. package/packages/admin-ui/package.json +36 -0
  31. package/packages/admin-ui/src/codemirror.js +53 -0
  32. package/packages/admin-ui/src/index.js +3188 -0
  33. package/packages/admin-ui/webpack.config.js +76 -0
  34. package/packages/resources/tools/chrome-devtools/README.md +310 -0
  35. package/packages/resources/tools/chrome-devtools/chrome-devtools.tool.js +1703 -0
  36. package/packages/resources/tools/file-reader/README.md +289 -0
  37. package/packages/resources/tools/file-reader/file-reader.tool.js +1545 -0
  38. package/packages/resources/tools/filesystem/README.md +359 -0
  39. package/packages/resources/tools/filesystem/filesystem.tool.js +514 -160
  40. package/packages/resources/tools/ollama-remote/README.md +192 -0
  41. package/packages/resources/tools/ollama-remote/ollama-remote.tool.js +421 -0
  42. package/packages/resources/tools/pdf-reader/README.md +236 -0
  43. package/packages/resources/tools/pdf-reader/pdf-reader.tool.js +565 -0
  44. package/packages/resources/tools/playwright/README.md +306 -0
  45. package/packages/resources/tools/playwright/playwright.tool.js +1186 -0
  46. package/packages/resources/tools/todolist/README.md +394 -0
  47. package/packages/resources/tools/todolist/todolist.tool.js +1312 -0
  48. package/packages/server/README.md +142 -0
  49. package/packages/server/api/admin.routes.js +42 -11
  50. package/packages/server/api/surge.routes.js +43 -0
  51. package/packages/server/app.js +119 -14
  52. package/packages/server/index.js +39 -0
  53. package/packages/server/mcp/mcp.server.js +324 -105
  54. package/packages/server/mcp/sequential-thinking.handler.js +318 -0
  55. package/packages/server/mcp/think-plan.handler.js +274 -0
  56. package/packages/server/middlewares/auth.middleware.js +6 -0
  57. package/packages/server/package.json +51 -0
  58. package/packages/server/server.js +37 -1
  59. package/packages/server/toolm/index.js +9 -0
  60. package/packages/server/toolm/package-installer.service.js +267 -0
  61. package/packages/server/toolm/test-tools.js +264 -0
  62. package/packages/server/toolm/tool-context.service.js +334 -0
  63. package/packages/server/toolm/tool-dependency.service.js +168 -0
  64. package/packages/server/toolm/tool-description-generator-optimized.service.js +375 -0
  65. package/packages/server/toolm/tool-description-generator.service.js +312 -0
  66. package/packages/server/toolm/tool-environment.service.js +200 -0
  67. package/packages/server/toolm/tool-execution.service.js +277 -0
  68. package/packages/server/toolm/tool-loader.service.js +219 -0
  69. package/packages/server/toolm/tool-logger.service.js +223 -0
  70. package/packages/server/toolm/tool-manager.handler.js +65 -0
  71. package/packages/server/toolm/tool-manual-generator.service.js +389 -0
  72. package/packages/server/toolm/tool-mode-handlers.service.js +224 -0
  73. package/packages/server/toolm/tool-storage.service.js +111 -0
  74. package/packages/server/toolm/tool-sync.service.js +138 -0
  75. package/packages/server/toolm/tool-utils.js +20 -0
  76. package/packages/server/toolm/tool-yaml-parser.service.js +81 -0
  77. package/packages/server/toolm/validate-system.js +421 -0
  78. package/packages/server/utils/config.js +49 -5
  79. package/packages/server/utils/util.js +65 -10
  80. package/scripts/build-icons.js +99 -69
  81. package/scripts/build.sh +57 -0
  82. package/scripts/surge/CNAME +1 -0
  83. package/scripts/surge/README.md +47 -0
  84. package/scripts/surge/package-lock.json +34 -0
  85. package/scripts/surge/package.json +20 -0
  86. package/scripts/surge/sync-to-surge.js +151 -0
  87. package/app/desktop/assets/icons/icon_1024x1024.png +0 -0
  88. package/app/desktop/assets/icons/icon_128x128.png +0 -0
  89. package/app/desktop/assets/icons/icon_16x16.png +0 -0
  90. package/app/desktop/assets/icons/icon_24x24.png +0 -0
  91. package/app/desktop/assets/icons/icon_256x256.png +0 -0
  92. package/app/desktop/assets/icons/icon_32x32.png +0 -0
  93. package/app/desktop/assets/icons/icon_48x48.png +0 -0
  94. package/app/desktop/assets/icons/icon_512x512.png +0 -0
  95. package/app/desktop/assets/icons/icon_64x64.png +0 -0
  96. package/app/desktop/assets/icons/icon_96x96.png +0 -0
  97. package/packages/admin-ui/js/closebrackets.min.js +0 -8
  98. package/packages/admin-ui/js/codemirror.min.js +0 -8
  99. package/packages/admin-ui/js/js-yaml.min.js +0 -2
  100. package/packages/admin-ui/js/markdown.min.js +0 -8
  101. package/packages/resources/tools/index.js +0 -16
  102. package/packages/server/mcp/toolx.handler.js +0 -131
  103. package/scripts/icns-builder/package.json +0 -12
  104. /package/packages/server/mcp/{mcp.handler.js → prompt.handler.js} +0 -0
@@ -1,184 +1,538 @@
1
- import fs from 'fs-extra';
2
- import path from 'path';
3
-
4
1
  /**
5
- * 文件系统工具
6
- * @param {Object} params - 工具参数
7
- * @param {string} mode - 操作模式
8
- * @returns {Promise<Object>} 执行结果
2
+ * Filesystem Tool - Prompt Manager 体系的文件系统基础设施
3
+ *
4
+ * 战略意义:
5
+ *
6
+ * 1. 架构隔离性
7
+ * 专为 Prompt Manager 体系设计,通过沙箱隔离确保文件操作不会影响
8
+ * Prompt Manager 核心功能。即使 AI Agent 出错,也不会破坏系统稳定性。
9
+ *
10
+ * 2. 平台独立性
11
+ * 虽然很多 AI 平台自带文件工具,但 Prompt Manager 需要自己的实现来保证:
12
+ * - 在无本地工具的 Web Agent 平台上也能工作
13
+ * - 统一的操作语义,不依赖特定 AI 平台
14
+ * - 可移植到任何支持 MCP 协议的环境
15
+ *
16
+ * 3. 生态自主性
17
+ * 作为 Prompt Manager 工具生态的基础组件,filesystem 确保了:
18
+ * - 其他工具可以依赖稳定的文件操作接口
19
+ * - 用户数据始终在 Prompt Manager 控制范围内
20
+ * - 未来可扩展更多存储后端(云存储、分布式等)
21
+ *
22
+ * 这不仅是一个文件操作工具,更是 Prompt Manager 实现平台独立、
23
+ * 生态自主的关键基础设施。
9
24
  */
10
- export default async function filesystem(params, mode = 'execute') {
11
- // 根据模式执行不同的操作
12
- switch (mode) {
13
- case 'manual':
14
- // 生成 Markdown 格式的手册
15
- return generateManual();
16
25
 
17
- case 'execute':
18
- // 执行模式 - 实际执行操作
19
- const { action, path: filePath, content } = params;
26
+ import path from 'path';
27
+ import os from 'os';
28
+
29
+ export default {
30
+ /**
31
+ * 获取工具依赖
32
+ * 使用 Node.js 内置模块,无需额外依赖
33
+ */
34
+ getDependencies() {
35
+ return {
36
+ // 使用 Node.js 内置 fs、path 等模块,无需额外依赖
37
+ };
38
+ },
39
+
40
+ /**
41
+ * 获取工具元信息
42
+ */
43
+ getMetadata() {
44
+ return {
45
+ id: 'filesystem',
46
+ name: 'filesystem',
47
+ description: '基于MCP标准的文件系统操作工具,提供读写、搜索、编辑等功能',
48
+ version: '2.0.0',
49
+ category: 'system',
50
+ author: 'Prompt Manager',
51
+ tags: ['file', 'system', 'io', 'mcp'],
52
+ scenarios: [
53
+ '文件读写操作',
54
+ '目录管理和遍历',
55
+ '文件搜索和批量处理',
56
+ 'Prompt Manager资源文件管理',
57
+ '项目文件结构分析'
58
+ ],
59
+ limitations: [
60
+ '默认只能访问 ~/.prompt-manager 目录',
61
+ '可通过环境变量配置额外允许的目录',
62
+ '不支持符号链接操作',
63
+ '单文件大小建议不超过10MB'
64
+ ]
65
+ };
66
+ },
67
+
68
+ /**
69
+ * 获取参数Schema
70
+ */
71
+ getSchema() {
72
+ return {
73
+ parameters: {
74
+ type: 'object',
75
+ properties: {
76
+ method: {
77
+ type: 'string',
78
+ description: 'MCP方法名',
79
+ enum: [
80
+ 'read_text_file',
81
+ 'read_media_file',
82
+ 'read_multiple_files',
83
+ 'write_file',
84
+ 'edit_file',
85
+ 'create_directory',
86
+ 'list_directory',
87
+ 'list_directory_with_sizes',
88
+ 'directory_tree',
89
+ 'move_file',
90
+ 'search_files',
91
+ 'get_file_info',
92
+ 'list_allowed_directories'
93
+ ]
94
+ },
95
+ // 通用参数,根据method动态使用
96
+ path: { type: 'string', description: '文件或目录路径' },
97
+ paths: { type: 'array', items: { type: 'string' }, description: '多个文件路径' },
98
+ content: { type: 'string', description: '文件内容' },
99
+ head: { type: 'number', description: '读取前N行' },
100
+ tail: { type: 'number', description: '读取后N行' },
101
+ edits: {
102
+ type: 'array',
103
+ description: '编辑操作列表,每个元素为对象: {oldText: "要替换的文本", newText: "新文本"}',
104
+ items: {
105
+ type: 'object',
106
+ properties: {
107
+ oldText: {
108
+ type: 'string',
109
+ description: '要替换的原始文本(必须完全匹配)'
110
+ },
111
+ newText: {
112
+ type: 'string',
113
+ description: '替换后的新文本'
114
+ }
115
+ },
116
+ required: ['oldText', 'newText']
117
+ }
118
+ },
119
+ dryRun: { type: 'boolean', description: '仅预览不执行' },
120
+ source: { type: 'string', description: '源路径' },
121
+ destination: { type: 'string', description: '目标路径' },
122
+ pattern: { type: 'string', description: '搜索模式' },
123
+ excludePatterns: { type: 'array', items: { type: 'string' }, description: '排除模式' },
124
+ sortBy: { type: 'string', enum: ['name', 'size'], description: '排序方式' }
125
+ },
126
+ required: ['method']
127
+ },
128
+ environment: {
129
+ type: 'object',
130
+ properties: {
131
+ ALLOWED_DIRECTORIES: {
132
+ type: 'string',
133
+ description: '允许访问的目录列表(JSON数组格式),默认为 ["~/.prompt-manager"]',
134
+ default: '["~/.prompt-manager"]'
135
+ }
136
+ },
137
+ required: []
138
+ }
139
+ };
140
+ },
141
+
142
+ /**
143
+ * 获取业务错误定义
144
+ */
145
+ getBusinessErrors() {
146
+ return [
147
+ {
148
+ code: 'PATH_OUTSIDE_SCOPE',
149
+ description: '路径越权访问',
150
+ match: /路径越权/,
151
+ solution: '确保路径在允许的目录范围内',
152
+ retryable: false
153
+ },
154
+ {
155
+ code: 'FILE_NOT_FOUND',
156
+ description: '文件或目录不存在',
157
+ match: /ENOENT|no such file|cannot find/i,
158
+ solution: '检查文件路径是否正确',
159
+ retryable: false
160
+ },
161
+ {
162
+ code: 'PERMISSION_DENIED',
163
+ description: '权限不足',
164
+ match: /EACCES|permission denied/i,
165
+ solution: '检查文件或目录的访问权限',
166
+ retryable: false
167
+ },
168
+ {
169
+ code: 'FILE_TOO_LARGE',
170
+ description: '文件过大',
171
+ match: /File too large|ENOBUFS|too big/i,
172
+ solution: '文件大小不应超过10MB',
173
+ retryable: false
174
+ },
175
+ {
176
+ code: 'DIRECTORY_NOT_EMPTY',
177
+ description: '目录非空',
178
+ match: /ENOTEMPTY|directory not empty/i,
179
+ solution: '清空目录后再试',
180
+ retryable: false
181
+ },
182
+ {
183
+ code: 'INVALID_PATH',
184
+ description: '无效路径',
185
+ match: /invalid path|illegal characters/i,
186
+ solution: '检查路径格式是否正确',
187
+ retryable: false
188
+ }
189
+ ];
190
+ },
191
+
192
+ /**
193
+ * 执行工具 - 包装MCP实现
194
+ *
195
+ * 注意:文件系统基础能力(getAllowedDirectories、initializeFilesystem、resolvePromptManagerPath)
196
+ * 已由框架层提供,工具可直接通过 this 调用这些方法
197
+ */
198
+ async execute(params) {
199
+ const { api } = this;
200
+
201
+ // 记录执行开始
202
+ api?.logger?.info('Executing filesystem operation', {
203
+ method: params.method,
204
+ path: params.path || params.paths || params.source
205
+ });
206
+
207
+ // 参数验证由 ToolValidator 根据 getSchema() 自动处理
208
+ // 这里进行 method 相关的业务验证
209
+ const methodRequirements = {
210
+ 'read_text_file': ['path'],
211
+ 'read_media_file': ['path'],
212
+ 'read_multiple_files': ['paths'],
213
+ 'write_file': ['path', 'content'],
214
+ 'edit_file': ['path', 'edits'],
215
+ 'create_directory': ['path'],
216
+ 'list_directory': ['path'],
217
+ 'list_directory_with_sizes': ['path'],
218
+ 'directory_tree': ['path'],
219
+ 'move_file': ['source', 'destination'],
220
+ 'search_files': ['path', 'pattern'],
221
+ 'get_file_info': ['path'],
222
+ 'list_allowed_directories': []
223
+ };
224
+
225
+ const required = methodRequirements[params.method];
226
+ if (!required) {
227
+ throw new Error(`不支持的方法: ${params.method}`);
228
+ }
229
+
230
+ const missing = required.filter(field => !params[field]);
231
+ if (missing.length > 0) {
232
+ throw new Error(`方法 ${params.method} 缺少必需参数: ${missing.join(', ')}`);
233
+ }
234
+
235
+ try {
236
+ // 初始化文件系统
237
+ await this.initializeFilesystem();
238
+
239
+ // 导入 fs 模块
240
+ const fs = await import('fs');
241
+ const fsPromises = fs.promises;
20
242
 
21
- if (!action) {
22
- throw new Error('缺少必需参数: action');
243
+ // 特殊处理list_allowed_directories
244
+ if (params.method === 'list_allowed_directories') {
245
+ // 使用已初始化的目录列表,如果未初始化则获取
246
+ const dirs = this._allowedDirectories ?? this.getAllowedDirectories();
247
+ api?.logger?.info('Returning allowed directories', { directories: dirs });
248
+ return dirs;
23
249
  }
250
+
251
+ // 准备MCP调用参数
252
+ let mcpParams = { ...params };
24
253
 
25
- if (!filePath) {
26
- throw new Error('缺少必需参数: path');
254
+ // 路径参数转换
255
+ if (params.path) {
256
+ mcpParams.path = this.resolvePromptManagerPath(params.path);
27
257
  }
28
258
 
29
- switch (action) {
30
- case 'read':
31
- try {
32
- const fileContent = await fs.readFile(filePath, 'utf8');
33
- return {
34
- success: true,
35
- action: 'read',
36
- path: filePath,
37
- content: fileContent
38
- };
39
- } catch (error) {
40
- throw new Error(`读取文件失败: ${error.message}`);
41
- }
259
+ if (params.paths) {
260
+ mcpParams.paths = params.paths.map(p => this.resolvePromptManagerPath(p));
261
+ }
262
+
263
+ if (params.source) {
264
+ mcpParams.source = this.resolvePromptManagerPath(params.source);
265
+ }
266
+
267
+ if (params.destination) {
268
+ mcpParams.destination = this.resolvePromptManagerPath(params.destination);
269
+ }
270
+
271
+ // 执行对应的文件系统操作
272
+ let result;
273
+ switch (params.method) {
274
+ case 'read_text_file': {
275
+ const content = await fsPromises.readFile(mcpParams.path, 'utf-8');
42
276
 
43
- case 'write':
44
- if (content === undefined) {
45
- throw new Error('写入文件需要提供 content 参数');
277
+ if (params.head) {
278
+ // 返回前 N
279
+ const lines = content.split('\n');
280
+ result = lines.slice(0, params.head).join('\n');
281
+ } else if (params.tail) {
282
+ // 返回后 N 行
283
+ const lines = content.split('\n');
284
+ result = lines.slice(-params.tail).join('\n');
285
+ } else {
286
+ result = content;
46
287
  }
288
+ break;
289
+ }
290
+
291
+ case 'read_media_file': {
292
+ // 读取二进制文件并转base64
293
+ const buffer = await fsPromises.readFile(mcpParams.path);
294
+ const base64 = buffer.toString('base64');
295
+ const ext = path.extname(mcpParams.path).toLowerCase();
296
+ const mimeTypes = {
297
+ '.png': 'image/png',
298
+ '.jpg': 'image/jpeg',
299
+ '.jpeg': 'image/jpeg',
300
+ '.gif': 'image/gif',
301
+ '.webp': 'image/webp',
302
+ '.svg': 'image/svg+xml',
303
+ '.mp3': 'audio/mpeg',
304
+ '.wav': 'audio/wav'
305
+ };
306
+ result = {
307
+ base64: base64,
308
+ mimeType: mimeTypes[ext] || 'application/octet-stream'
309
+ };
310
+ break;
311
+ }
312
+
313
+ case 'read_multiple_files':
314
+ result = await Promise.all(
315
+ mcpParams.paths.map(async (filePath, index) => {
316
+ try {
317
+ const content = await fsPromises.readFile(filePath, 'utf-8');
318
+ return {
319
+ path: params.paths[index], // 返回原始相对路径
320
+ content: content,
321
+ success: true
322
+ };
323
+ } catch (error) {
324
+ return {
325
+ path: params.paths[index],
326
+ error: error.message,
327
+ success: false
328
+ };
329
+ }
330
+ })
331
+ );
332
+ break;
333
+
334
+ case 'write_file': {
335
+ // 自动创建父目录
336
+ const dirPath = path.dirname(mcpParams.path);
337
+
47
338
  try {
48
- await fs.writeFile(filePath, content, 'utf8');
49
- return {
50
- success: true,
51
- action: 'write',
52
- path: filePath,
53
- message: '文件写入成功'
54
- };
55
- } catch (error) {
56
- throw new Error(`写入文件失败: ${error.message}`);
339
+ // 检查目录是否存在,不存在则创建
340
+ await fsPromises.access(dirPath);
341
+ } catch {
342
+ // 目录不存在,创建它
343
+ await fsPromises.mkdir(dirPath, { recursive: true });
344
+ api?.logger?.info('Auto-created directory for write_file', { directory: dirPath });
57
345
  }
58
346
 
59
- case 'list':
60
- try {
61
- const items = await fs.readdir(filePath);
62
- return {
63
- success: true,
64
- action: 'list',
65
- path: filePath,
66
- items: items
347
+ await fsPromises.writeFile(mcpParams.path, params.content, 'utf-8');
348
+ result = {
349
+ bytesWritten: Buffer.byteLength(params.content, 'utf-8'),
350
+ path: params.path
351
+ };
352
+ break;
353
+ }
354
+
355
+ case 'edit_file': {
356
+ // 读取文件内容
357
+ let content = await fsPromises.readFile(mcpParams.path, 'utf-8');
358
+
359
+ // 应用编辑
360
+ for (const edit of params.edits) {
361
+ content = content.replace(edit.oldText, edit.newText);
362
+ }
363
+
364
+ if (params.dryRun) {
365
+ // 预览模式,返回修改后的内容
366
+ result = content;
367
+ } else {
368
+ // 写回文件
369
+ await fsPromises.writeFile(mcpParams.path, content, 'utf-8');
370
+ result = {
371
+ editsApplied: params.edits.length,
372
+ path: params.path
67
373
  };
68
- } catch (error) {
69
- throw new Error(`列出目录内容失败: ${error.message}`);
70
374
  }
375
+ break;
376
+ }
71
377
 
72
- case 'delete':
73
- try {
74
- const stats = await fs.stat(filePath);
75
- if (stats.isDirectory()) {
76
- await fs.rm(filePath, { recursive: true });
77
- return {
78
- success: true,
79
- action: 'delete',
80
- path: filePath,
81
- type: 'directory',
82
- message: '目录删除成功'
83
- };
378
+ case 'create_directory': {
379
+ await fsPromises.mkdir(mcpParams.path, { recursive: true });
380
+ result = { created: mcpParams.path };
381
+ break;
382
+ }
383
+
384
+ case 'list_directory':
385
+ case 'list_directory_with_sizes': {
386
+ const entries = await fsPromises.readdir(mcpParams.path, { withFileTypes: true });
387
+
388
+ if (params.method === 'list_directory') {
389
+ result = entries.map(entry => ({
390
+ name: entry.name,
391
+ type: entry.isDirectory() ? 'directory' : 'file'
392
+ }));
393
+ } else {
394
+ result = await Promise.all(
395
+ entries.map(async (entry) => {
396
+ const entryPath = path.join(mcpParams.path, entry.name);
397
+ const stats = await fsPromises.stat(entryPath);
398
+ return {
399
+ name: entry.name,
400
+ type: entry.isDirectory() ? 'directory' : 'file',
401
+ size: stats.size,
402
+ modified: stats.mtime
403
+ };
404
+ })
405
+ );
406
+
407
+ if (params.sortBy === 'size') {
408
+ result.sort((a, b) => b.size - a.size);
84
409
  } else {
85
- await fs.unlink(filePath);
86
- return {
87
- success: true,
88
- action: 'delete',
89
- path: filePath,
90
- type: 'file',
91
- message: '文件删除成功'
92
- };
410
+ result.sort((a, b) => a.name.localeCompare(b.name));
93
411
  }
94
- } catch (error) {
95
- throw new Error(`删除文件或目录失败: ${error.message}`);
96
412
  }
413
+ break;
414
+ }
415
+
416
+ case 'directory_tree': {
417
+ // 构建目录树
418
+ const buildTree = async (currentPath) => {
419
+ const entries = await fsPromises.readdir(currentPath, { withFileTypes: true });
420
+ const tree = [];
421
+
422
+ for (const entry of entries) {
423
+ const entryPath = path.join(currentPath, entry.name);
424
+ const node = {
425
+ name: entry.name,
426
+ type: entry.isDirectory() ? 'directory' : 'file'
427
+ };
428
+
429
+ if (entry.isDirectory()) {
430
+ try {
431
+ node.children = await buildTree(entryPath);
432
+ } catch (error) {
433
+ node.children = [];
434
+ node.error = error.message;
435
+ }
436
+ }
437
+
438
+ tree.push(node);
439
+ }
440
+
441
+ return tree;
442
+ };
443
+
444
+ result = await buildTree(mcpParams.path);
445
+ break;
446
+ }
447
+
448
+ case 'move_file': {
449
+ await fsPromises.rename(mcpParams.source, mcpParams.destination);
450
+ result = {
451
+ from: params.source,
452
+ to: params.destination
453
+ };
454
+ break;
455
+ }
456
+
457
+ case 'search_files': {
458
+ // 递归搜索文件(使用内置fs,不依赖glob)
459
+ const searchFiles = async (dir, pattern) => {
460
+ const results = [];
461
+ const entries = await fsPromises.readdir(dir, { withFileTypes: true });
462
+
463
+ for (const entry of entries) {
464
+ const fullPath = path.join(dir, entry.name);
465
+
466
+ // 检查排除模式
467
+ if (params.excludePatterns) {
468
+ const shouldExclude = params.excludePatterns.some(excludePattern => {
469
+ return entry.name.match(new RegExp(excludePattern.replace(/\*/g, '.*')));
470
+ });
471
+ if (shouldExclude) continue;
472
+ }
473
+
474
+ if (entry.isDirectory()) {
475
+ // 递归搜索子目录
476
+ try {
477
+ const subResults = await searchFiles(fullPath, pattern);
478
+ results.push(...subResults);
479
+ } catch (error) {
480
+ // 忽略无权访问的目录
481
+ }
482
+ } else {
483
+ // 检查文件名是否匹配模式
484
+ const regex = new RegExp(pattern.replace(/\*/g, '.*').replace(/\?/g, '.'));
485
+ if (regex.test(entry.name)) {
486
+ results.push(fullPath);
487
+ }
488
+ }
489
+ }
490
+
491
+ return results;
492
+ };
493
+
494
+ const foundFiles = await searchFiles(mcpParams.path, params.pattern);
495
+
496
+ // 转换为相对路径(相对于第一个允许的目录)
497
+ const allowedDirs = this._allowedDirectories ?? this.getAllowedDirectories();
498
+ const baseDir = allowedDirs[0];
499
+ result = foundFiles.map(file => path.relative(baseDir, file));
500
+ break;
501
+ }
502
+
503
+ case 'get_file_info': {
504
+ const stats = await fsPromises.stat(mcpParams.path);
505
+ result = {
506
+ size: stats.size,
507
+ created: stats.birthtime,
508
+ modified: stats.mtime,
509
+ accessed: stats.atime,
510
+ isDirectory: stats.isDirectory(),
511
+ isFile: stats.isFile(),
512
+ permissions: stats.mode
513
+ };
514
+ break;
515
+ }
97
516
 
98
517
  default:
99
- throw new Error(`不支持的操作类型: ${action}`);
518
+ throw new Error(`不支持的方法: ${params.method}`);
100
519
  }
101
520
 
102
- default:
103
- throw new Error(`不支持的模式: ${mode}`);
521
+ // 记录执行成功
522
+ api?.logger?.info('Filesystem operation completed', {
523
+ method: params.method,
524
+ success: true
525
+ });
526
+
527
+ return result;
528
+
529
+ } catch (error) {
530
+ // 记录错误
531
+ api?.logger?.error('Filesystem operation failed', {
532
+ method: params.method,
533
+ error: error.message
534
+ });
535
+ throw error;
536
+ }
104
537
  }
105
- }
106
-
107
- /**
108
- * 生成 Markdown 格式的手册
109
- * @returns {string} Markdown 格式的手册
110
- */
111
- function generateManual() {
112
- return `# 🔧 filesystem
113
-
114
- > 文件系统工具 - 用于文件操作
115
-
116
- ## 📋 基础信息
117
-
118
- - **标识**: \`tool://filesystem\`
119
- - **分类**: 系统工具
120
-
121
- ## ✅ 适用场景
122
-
123
- - 读取文件内容进行分析
124
- - 写入文件内容保存数据
125
- - 列出目录内容查看文件结构
126
- - 删除文件或目录进行清理
127
-
128
- ## 📝 参数定义
129
-
130
- ### execute 模式参数
131
-
132
- | 参数 | 类型 | 必需 | 描述 | 默认值 |
133
- |------|------|------|------|--------|
134
- | action | string (read|write|list|delete) | ✅ | 操作类型 | - |
135
- | path | string | ✅ | 文件或目录路径 | - |
136
- | content | string | ❌ | 写入的文件内容 (仅在action为write时需要) | - |
137
-
138
- ## 💻 使用示例
139
-
140
- 通过 toolx 调用,使用 YAML 格式:
141
-
142
- \`\`\`yaml
143
- # 读取文件内容
144
- tool: tool://filesystem
145
- mode: execute
146
- parameters:
147
- action: read
148
- path: /path/to/file.txt
149
-
150
- # 写入文件内容
151
- tool: tool://filesystem
152
- mode: execute
153
- parameters:
154
- action: write
155
- path: /path/to/file.txt
156
- content: "Hello, World!"
157
-
158
- # 列出目录内容
159
- tool: tool://filesystem
160
- mode: execute
161
- parameters:
162
- action: list
163
- path: /path/to/directory
164
-
165
- # 删除文件或目录
166
- tool: tool://filesystem
167
- mode: execute
168
- parameters:
169
- action: delete
170
- path: /path/to/file-or-directory
171
- \`\`\`
172
-
173
- ## 🚨 业务错误
174
-
175
- | 错误码 | 描述 | 解决方案 | 可重试 |
176
- |--------|------|----------|--------|
177
- | MISSING_ACTION | 缺少必需参数: action | 提供 action 参数 | ❌ |
178
- | MISSING_PATH | 缺少必需参数: path | 提供 path 参数 | ❌ |
179
- | READ_FAILED | 读取文件失败 | 检查文件路径是否存在且可读 | ✅ |
180
- | WRITE_FAILED | 写入文件失败 | 检查文件路径是否可写 | ✅ |
181
- | LIST_FAILED | 列出目录内容失败 | 检查目录路径是否存在且可读 | ✅ |
182
- | DELETE_FAILED | 删除文件或目录失败 | 检查文件或目录是否存在且可删除 | ✅ |
183
- `;
184
- }
538
+ };