@becrafter/prompt-manager 0.0.18 → 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.
Files changed (108) hide show
  1. package/IFLOW.md +175 -0
  2. package/README.md +145 -234
  3. package/app/desktop/assets/app.1.png +0 -0
  4. package/app/desktop/assets/app.png +0 -0
  5. package/app/desktop/assets/icons/icon.icns +0 -0
  6. package/app/desktop/assets/icons/icon.ico +0 -0
  7. package/app/desktop/assets/icons/icon.png +0 -0
  8. package/app/desktop/assets/icons/tray.png +0 -0
  9. package/app/desktop/assets/templates/about.html +147 -0
  10. package/app/desktop/assets/tray.png +0 -0
  11. package/app/desktop/main.js +187 -732
  12. package/app/desktop/package-lock.json +723 -522
  13. package/app/desktop/package.json +54 -25
  14. package/app/desktop/preload.js +7 -0
  15. package/app/desktop/src/core/error-handler.js +108 -0
  16. package/app/desktop/src/core/event-emitter.js +84 -0
  17. package/app/desktop/src/core/logger.js +108 -0
  18. package/app/desktop/src/core/state-manager.js +125 -0
  19. package/app/desktop/src/services/module-loader.js +214 -0
  20. package/app/desktop/src/services/runtime-manager.js +301 -0
  21. package/app/desktop/src/services/service-manager.js +169 -0
  22. package/app/desktop/src/services/update-manager.js +268 -0
  23. package/app/desktop/src/ui/about-dialog-manager.js +208 -0
  24. package/app/desktop/src/ui/admin-window-manager.js +757 -0
  25. package/app/desktop/src/ui/splash-manager.js +253 -0
  26. package/app/desktop/src/ui/tray-manager.js +186 -0
  27. package/app/desktop/src/utils/icon-manager.js +133 -0
  28. package/app/desktop/src/utils/path-utils.js +58 -0
  29. package/app/desktop/src/utils/resource-paths.js +49 -0
  30. package/app/desktop/src/utils/resource-sync.js +260 -0
  31. package/app/desktop/src/utils/runtime-sync.js +241 -0
  32. package/app/desktop/src/utils/template-renderer.js +284 -0
  33. package/app/desktop/src/utils/version-utils.js +59 -0
  34. package/examples/prompts/engineer/engineer-professional.yaml +92 -0
  35. package/examples/prompts/engineer/laowang-engineer.yaml +132 -0
  36. package/examples/prompts/engineer/nekomata-engineer.yaml +123 -0
  37. package/examples/prompts/engineer/ojousama-engineer.yaml +124 -0
  38. package/examples/prompts/recommend/human_3-0_growth_diagnostic_coach_prompt.yaml +105 -0
  39. package/examples/prompts/workflow/sixstep-workflow.yaml +192 -0
  40. package/package.json +18 -9
  41. package/packages/admin-ui/.babelrc +3 -0
  42. package/packages/admin-ui/admin.html +237 -4784
  43. package/packages/admin-ui/css/main.css +2592 -0
  44. package/packages/admin-ui/css/recommended-prompts.css +610 -0
  45. package/packages/admin-ui/package-lock.json +6981 -0
  46. package/packages/admin-ui/package.json +36 -0
  47. package/packages/admin-ui/src/codemirror.js +53 -0
  48. package/packages/admin-ui/src/index.js +3188 -0
  49. package/packages/admin-ui/webpack.config.js +76 -0
  50. package/packages/resources/tools/chrome-devtools/README.md +310 -0
  51. package/packages/resources/tools/chrome-devtools/chrome-devtools.tool.js +1703 -0
  52. package/packages/resources/tools/file-reader/README.md +289 -0
  53. package/packages/resources/tools/file-reader/file-reader.tool.js +1545 -0
  54. package/packages/resources/tools/filesystem/README.md +359 -0
  55. package/packages/resources/tools/filesystem/filesystem.tool.js +538 -0
  56. package/packages/resources/tools/ollama-remote/README.md +192 -0
  57. package/packages/resources/tools/ollama-remote/ollama-remote.tool.js +421 -0
  58. package/packages/resources/tools/pdf-reader/README.md +236 -0
  59. package/packages/resources/tools/pdf-reader/pdf-reader.tool.js +565 -0
  60. package/packages/resources/tools/playwright/README.md +306 -0
  61. package/packages/resources/tools/playwright/playwright.tool.js +1186 -0
  62. package/packages/resources/tools/todolist/README.md +394 -0
  63. package/packages/resources/tools/todolist/todolist.tool.js +1312 -0
  64. package/packages/server/README.md +142 -0
  65. package/packages/server/api/admin.routes.js +42 -11
  66. package/packages/server/api/surge.routes.js +43 -0
  67. package/packages/server/app.js +119 -14
  68. package/packages/server/index.js +39 -0
  69. package/packages/server/mcp/mcp.server.js +346 -28
  70. package/packages/server/mcp/{mcp.handler.js → prompt.handler.js} +108 -9
  71. package/packages/server/mcp/sequential-thinking.handler.js +318 -0
  72. package/packages/server/mcp/think-plan.handler.js +274 -0
  73. package/packages/server/middlewares/auth.middleware.js +6 -0
  74. package/packages/server/package.json +51 -0
  75. package/packages/server/server.js +37 -1
  76. package/packages/server/toolm/index.js +9 -0
  77. package/packages/server/toolm/package-installer.service.js +267 -0
  78. package/packages/server/toolm/test-tools.js +264 -0
  79. package/packages/server/toolm/tool-context.service.js +334 -0
  80. package/packages/server/toolm/tool-dependency.service.js +168 -0
  81. package/packages/server/toolm/tool-description-generator-optimized.service.js +375 -0
  82. package/packages/server/toolm/tool-description-generator.service.js +312 -0
  83. package/packages/server/toolm/tool-environment.service.js +200 -0
  84. package/packages/server/toolm/tool-execution.service.js +277 -0
  85. package/packages/server/toolm/tool-loader.service.js +219 -0
  86. package/packages/server/toolm/tool-logger.service.js +223 -0
  87. package/packages/server/toolm/tool-manager.handler.js +65 -0
  88. package/packages/server/toolm/tool-manual-generator.service.js +389 -0
  89. package/packages/server/toolm/tool-mode-handlers.service.js +224 -0
  90. package/packages/server/toolm/tool-storage.service.js +111 -0
  91. package/packages/server/toolm/tool-sync.service.js +138 -0
  92. package/packages/server/toolm/tool-utils.js +20 -0
  93. package/packages/server/toolm/tool-yaml-parser.service.js +81 -0
  94. package/packages/server/toolm/validate-system.js +421 -0
  95. package/packages/server/utils/config.js +49 -5
  96. package/packages/server/utils/util.js +65 -10
  97. package/scripts/build-icons.js +135 -0
  98. package/scripts/build.sh +57 -0
  99. package/scripts/surge/CNAME +1 -0
  100. package/scripts/surge/README.md +47 -0
  101. package/scripts/surge/package-lock.json +34 -0
  102. package/scripts/surge/package.json +20 -0
  103. package/scripts/surge/sync-to-surge.js +151 -0
  104. package/packages/admin-ui/js/closebrackets.min.js +0 -8
  105. package/packages/admin-ui/js/codemirror.min.js +0 -8
  106. package/packages/admin-ui/js/js-yaml.min.js +0 -2
  107. package/packages/admin-ui/js/markdown.min.js +0 -8
  108. /package/app/desktop/assets/{icon.png → tray.1.png} +0 -0
@@ -0,0 +1,224 @@
1
+ /**
2
+ * 工具模式处理器服务
3
+ *
4
+ * 职责:
5
+ * 1. 处理 manual 模式 - 显示工具手册
6
+ * 2. 处理 configure 模式 - 配置工具环境变量
7
+ * 3. 处理 log 模式 - 查看工具日志
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 { toolLoaderService } from './tool-loader.service.js';
15
+ import { saveToolEnvironment, getToolEnvironmentInfo } from './tool-environment.service.js';
16
+ import { flushAllLogQueues } from './tool-logger.service.js';
17
+ import { pathExists } from './tool-utils.js';
18
+ import { executeTool } from './tool-execution.service.js';
19
+
20
+ /**
21
+ * 处理 manual 模式 - 显示工具手册
22
+ * @param {string} toolName - 工具名称
23
+ * @returns {object} MCP 格式的返回结果
24
+ */
25
+ export function handleManualMode(toolName) {
26
+ logger.info(`显示工具手册: ${toolName}`);
27
+
28
+ try {
29
+ const manual = toolLoaderService.generateManual(toolName);
30
+
31
+ return {
32
+ content: [
33
+ {
34
+ type: "text",
35
+ text: manual
36
+ }
37
+ ]
38
+ };
39
+ } catch (error) {
40
+ logger.error(`生成工具手册失败: ${toolName}`, { error: error.message });
41
+ throw error;
42
+ }
43
+ }
44
+
45
+ /**
46
+ * 处理 execute 模式 - 执行工具
47
+ * @param {string} toolName - 工具名称
48
+ * @param {object} parameters - 工具参数
49
+ * @returns {object} MCP 格式的返回结果
50
+ */
51
+ export async function handleExecuteMode(toolName, parameters) {
52
+ return await executeTool(toolName, parameters);
53
+ }
54
+
55
+ /**
56
+ * 处理 configure 模式 - 配置工具环境变量
57
+ * @param {string} toolName - 工具名称
58
+ * @param {object} parameters - 配置参数
59
+ * @returns {object} MCP 格式的返回结果
60
+ */
61
+ export async function handleConfigureMode(toolName, parameters) {
62
+ logger.info(`配置工具: ${toolName}`, { parameters });
63
+
64
+ try {
65
+ const tool = toolLoaderService.getTool(toolName);
66
+
67
+ // 获取工具的环境变量定义
68
+ const schema = tool.schema || {};
69
+ const envProps = schema.environment?.properties || {};
70
+
71
+ // 如果有配置参数,保存配置
72
+ if (parameters && Object.keys(parameters).length > 0) {
73
+ // 验证配置参数
74
+ const configuredVars = {};
75
+ for (const [key, value] of Object.entries(parameters)) {
76
+ if (!envProps[key]) {
77
+ logger.warn(`未知的环境变量: ${key}`, { tool: toolName });
78
+ }
79
+ configuredVars[key] = value;
80
+ }
81
+
82
+ // 保存到 .env 文件
83
+ await saveToolEnvironment(toolName, configuredVars);
84
+
85
+ logger.info(`工具配置成功: ${toolName}`, { configured: Object.keys(configuredVars) });
86
+ }
87
+
88
+ // 获取当前配置信息
89
+ const envInfo = await getToolEnvironmentInfo(toolName, schema);
90
+ const metadata = tool.metadata || {};
91
+
92
+ // 生成格式化的配置信息
93
+ let configText = `# 工具配置信息\n\n`;
94
+ configText += `## 工具信息\n\n`;
95
+ configText += `**工具名称**: ${toolName}\n`;
96
+ configText += `**工具版本**: ${metadata.version || '1.0.0'}\n`;
97
+ configText += `**配置状态**: ${envInfo.configured.length > 0 ? '✅ 已配置' : '⚠️ 未配置'}\n\n`;
98
+
99
+ configText += `## 环境配置路径\n\n`;
100
+ configText += `**配置文件路径**: \`${envInfo.envFilePath}\`\n\n`;
101
+
102
+ if (envInfo.configured.length > 0) {
103
+ configText += `## 当前配置\n\n`;
104
+ configText += `### 已配置项\n\n`;
105
+ configText += `| 配置项 | 当前值 | 说明 | 状态 |\n`;
106
+ configText += `|--------|--------|------|------|\n`;
107
+ for (const item of envInfo.configured) {
108
+ const value = String(item.value).length > 50
109
+ ? String(item.value).substring(0, 50) + '...'
110
+ : String(item.value);
111
+ configText += `| ${item.key} | ${value} | ${item.description} | ${item.status} |\n`;
112
+ }
113
+ configText += `\n`;
114
+ }
115
+
116
+ if (envInfo.unconfigured.length > 0) {
117
+ configText += `### 未配置项(使用默认值)\n\n`;
118
+ configText += `| 配置项 | 默认值 | 说明 | 状态 |\n`;
119
+ configText += `|--------|--------|------|------|\n`;
120
+ for (const item of envInfo.unconfigured) {
121
+ const defaultValue = item.defaultValue !== undefined
122
+ ? String(item.defaultValue)
123
+ : '(无)';
124
+ configText += `| ${item.key} | ${defaultValue} | ${item.description} | ${item.status} |\n`;
125
+ }
126
+ configText += `\n`;
127
+ }
128
+
129
+ configText += `## 配置操作\n\n`;
130
+ configText += `### 如何更新配置\n\n`;
131
+ configText += `使用以下命令更新配置:\n\n`;
132
+ configText += `\`\`\`yaml\n`;
133
+ configText += `tool: tool://${toolName}\n`;
134
+ configText += `mode: configure\n`;
135
+ configText += `parameters:\n`;
136
+ if (envProps && Object.keys(envProps).length > 0) {
137
+ const firstKey = Object.keys(envProps)[0];
138
+ const firstDef = envProps[firstKey];
139
+ configText += ` ${firstKey}: ${firstDef.default || 'value'}\n`;
140
+ }
141
+ configText += `\`\`\`\n\n`;
142
+
143
+ return {
144
+ content: [
145
+ {
146
+ type: "text",
147
+ text: configText
148
+ }
149
+ ]
150
+ };
151
+
152
+ } catch (error) {
153
+ logger.error(`工具配置失败: ${toolName}`, { error: error.message });
154
+ throw error;
155
+ }
156
+ }
157
+
158
+ /**
159
+ * 处理 log 模式 - 查看工具日志
160
+ * @param {string} toolName - 工具名称
161
+ * @param {object} parameters - 日志参数
162
+ * @returns {object} MCP 格式的返回结果
163
+ */
164
+ export async function handleLogMode(toolName, parameters) {
165
+ logger.info(`查看工具日志: ${toolName}`, { parameters });
166
+
167
+ try {
168
+ // 先刷新日志队列,确保最新日志已写入
169
+ await flushAllLogQueues();
170
+
171
+ const toolDir = path.join(os.homedir(), '.prompt-manager', 'toolbox', toolName);
172
+ const logFilePath = path.join(toolDir, 'run.log');
173
+
174
+ // 检查日志文件是否存在
175
+ if (!await pathExists(logFilePath)) {
176
+ return {
177
+ content: [
178
+ {
179
+ type: "text",
180
+ text: `工具 ${toolName} 暂无日志文件\n\n日志文件路径: \`${logFilePath}\``
181
+ }
182
+ ]
183
+ };
184
+ }
185
+
186
+ // 读取日志文件
187
+ const logContent = await fs.readFile(logFilePath, 'utf-8');
188
+ const logLines = logContent.split('\n').filter(line => line.trim());
189
+
190
+ const action = parameters.action || 'tail';
191
+ const lines = parameters.lines || 50;
192
+
193
+ let selectedLines = [];
194
+
195
+ if (action === 'tail') {
196
+ // 取最后N行
197
+ selectedLines = logLines.slice(-lines);
198
+ } else if (action === 'head') {
199
+ // 取前N行
200
+ selectedLines = logLines.slice(0, lines);
201
+ } else if (action === 'all') {
202
+ // 取所有行
203
+ selectedLines = logLines;
204
+ } else {
205
+ throw new Error(`不支持的操作: ${action},支持的操作: tail, head, all`);
206
+ }
207
+
208
+ const logText = selectedLines.join('\n');
209
+
210
+ return {
211
+ content: [
212
+ {
213
+ type: "text",
214
+ text: `# 工具日志\n\n**工具**: ${toolName}\n**日志文件**: \`${logFilePath}\`\n**操作**: ${action}\n**行数**: ${selectedLines.length}\n\n\`\`\`\n${logText}\n\`\`\``
215
+ }
216
+ ]
217
+ };
218
+
219
+ } catch (error) {
220
+ logger.error(`查看工具日志失败: ${toolName}`, { error: error.message });
221
+ throw error;
222
+ }
223
+ }
224
+
@@ -0,0 +1,111 @@
1
+ /**
2
+ * 工具存储服务
3
+ *
4
+ * 职责:
5
+ * 1. 为每个工具提供独立的存储空间
6
+ * 2. 数据持久化到工具目录的 data 目录
7
+ * 3. 提供简单的 key-value 存储接口
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
+ // 存储缓存:toolName -> storage data
17
+ const storageCache = new Map();
18
+
19
+ /**
20
+ * 获取工具存储服务
21
+ * @param {string} toolName - 工具名称
22
+ * @returns {object} 存储服务对象
23
+ */
24
+ export function getStorage(toolName) {
25
+ if (!storageCache.has(toolName)) {
26
+ storageCache.set(toolName, {});
27
+ }
28
+
29
+ const storage = storageCache.get(toolName);
30
+ const storageFile = getStorageFilePath(toolName);
31
+
32
+ // 加载存储数据(如果存在)
33
+ loadStorageData(toolName, storageFile, storage);
34
+
35
+ return {
36
+ getItem: (key) => {
37
+ return storage[key];
38
+ },
39
+
40
+ setItem: (key, value) => {
41
+ storage[key] = value;
42
+ // 异步保存
43
+ saveStorageData(toolName, storageFile, storage).catch(error => {
44
+ logger.error(`保存工具存储失败: ${toolName}`, { error: error.message });
45
+ });
46
+ },
47
+
48
+ removeItem: (key) => {
49
+ delete storage[key];
50
+ // 异步保存
51
+ saveStorageData(toolName, storageFile, storage).catch(error => {
52
+ logger.error(`保存工具存储失败: ${toolName}`, { error: error.message });
53
+ });
54
+ },
55
+
56
+ clear: () => {
57
+ storageCache.set(toolName, {});
58
+ // 删除存储文件
59
+ fs.remove(storageFile).catch(error => {
60
+ logger.error(`删除工具存储文件失败: ${toolName}`, { error: error.message });
61
+ });
62
+ }
63
+ };
64
+ }
65
+
66
+ /**
67
+ * 获取存储文件路径
68
+ * @param {string} toolName - 工具名称
69
+ * @returns {string} 存储文件路径
70
+ */
71
+ function getStorageFilePath(toolName) {
72
+ const toolDir = path.join(os.homedir(), '.prompt-manager', 'toolbox', toolName);
73
+ return path.join(toolDir, 'data', 'storage.json');
74
+ }
75
+
76
+ /**
77
+ * 加载存储数据
78
+ * @param {string} toolName - 工具名称
79
+ * @param {string} storageFile - 存储文件路径
80
+ * @param {object} storage - 存储对象
81
+ */
82
+ async function loadStorageData(toolName, storageFile, storage) {
83
+ try {
84
+ if (await pathExists(storageFile)) {
85
+ const data = await fs.readJson(storageFile);
86
+ Object.assign(storage, data);
87
+ logger.debug(`工具 ${toolName} 存储数据已加载`);
88
+ }
89
+ } catch (error) {
90
+ logger.warn(`加载工具存储数据失败: ${toolName}`, { error: error.message });
91
+ }
92
+ }
93
+
94
+ /**
95
+ * 保存存储数据
96
+ * @param {string} toolName - 工具名称
97
+ * @param {string} storageFile - 存储文件路径
98
+ * @param {object} storage - 存储对象
99
+ */
100
+ async function saveStorageData(toolName, storageFile, storage) {
101
+ try {
102
+ const toolDir = path.dirname(storageFile);
103
+ await fs.ensureDir(toolDir);
104
+ await fs.writeJson(storageFile, storage, { spaces: 2 });
105
+ logger.debug(`工具 ${toolName} 存储数据已保存`);
106
+ } catch (error) {
107
+ logger.error(`保存工具存储数据失败: ${toolName}`, { error: error.message });
108
+ throw error;
109
+ }
110
+ }
111
+
@@ -0,0 +1,138 @@
1
+ /**
2
+ * 工具同步服务
3
+ *
4
+ * 职责:
5
+ * 1. 将系统内置工具从 packages/resources/tools/ 同步到 ~/.prompt-manager/toolbox/
6
+ * 2. 为每个工具创建独立的沙箱目录结构
7
+ * 3. 生成 package.json 文件(如果不存在)
8
+ *
9
+ * 执行时机:
10
+ * - HTTP 服务启动时(server.js 的 startServer() 函数中)
11
+ * - 在 MCP 服务器初始化之前执行
12
+ */
13
+
14
+ import fs from 'fs-extra';
15
+ import path from 'path';
16
+ import os from 'os';
17
+ import { fileURLToPath } from 'url';
18
+ import { logger } from '../utils/logger.js';
19
+ import { pathExists } from './tool-utils.js';
20
+
21
+ const __filename = fileURLToPath(import.meta.url);
22
+ const __dirname = path.dirname(__filename);
23
+
24
+ /**
25
+ * 同步系统工具到沙箱环境
26
+ */
27
+ export async function syncSystemTools() {
28
+ logger.info('开始同步系统工具到沙箱环境...');
29
+
30
+ // 系统工具源目录
31
+ const toolsDir = path.join(__dirname, '..', '..', 'resources', 'tools');
32
+
33
+ // 目标沙箱目录
34
+ const toolboxDir = path.join(os.homedir(), '.prompt-manager', 'toolbox');
35
+
36
+ // 确保工具箱目录存在
37
+ await fs.ensureDir(toolboxDir);
38
+
39
+ // 检查源目录是否存在
40
+ if (!await pathExists(toolsDir)) {
41
+ logger.warn(`系统工具目录不存在,跳过同步: ${toolsDir}`);
42
+ return;
43
+ }
44
+
45
+ try {
46
+ // 读取系统工具目录
47
+ const entries = await fs.readdir(toolsDir, { withFileTypes: true });
48
+
49
+ let syncedCount = 0;
50
+
51
+ for (const entry of entries) {
52
+ if (!entry.isDirectory()) {
53
+ continue;
54
+ }
55
+
56
+ const toolName = entry.name;
57
+ const toolDir = path.join(toolsDir, toolName);
58
+ const toolFile = path.join(toolDir, `${toolName}.tool.js`);
59
+
60
+ // 检查工具文件是否存在
61
+ if (!await pathExists(toolFile)) {
62
+ logger.debug(`工具文件不存在,跳过: ${toolFile}`);
63
+ continue;
64
+ }
65
+
66
+ try {
67
+ // 创建目标沙箱目录
68
+ const sandboxDir = path.join(toolboxDir, toolName);
69
+ await fs.ensureDir(sandboxDir);
70
+
71
+ // 复制工具文件
72
+ const sandboxToolFile = path.join(sandboxDir, `${toolName}.tool.js`);
73
+ await fs.copyFile(toolFile, sandboxToolFile);
74
+
75
+ // 创建或更新 package.json
76
+ const packageJsonPath = path.join(sandboxDir, 'package.json');
77
+ let packageJson = {};
78
+
79
+ if (await pathExists(packageJsonPath)) {
80
+ // 读取现有的 package.json
81
+ try {
82
+ packageJson = await fs.readJson(packageJsonPath);
83
+ } catch (error) {
84
+ logger.warn(`读取 package.json 失败,将重新创建: ${packageJsonPath}`, { error: error.message });
85
+ }
86
+ }
87
+
88
+ // 读取工具模块获取依赖信息
89
+ let dependencies = {};
90
+ try {
91
+ const toolModule = await import(toolFile);
92
+ if (toolModule.default && typeof toolModule.default.getDependencies === 'function') {
93
+ dependencies = toolModule.default.getDependencies() || {};
94
+ }
95
+ } catch (error) {
96
+ logger.warn(`读取工具依赖失败: ${toolName}`, { error: error.message });
97
+ }
98
+
99
+ // 更新 package.json
100
+ packageJson = {
101
+ name: `@prompt-manager/${toolName}`,
102
+ version: packageJson.version || '1.0.0',
103
+ description: packageJson.description || `Prompt Manager System Tool: ${toolName}`,
104
+ main: `${toolName}.tool.js`,
105
+ type: 'module',
106
+ dependencies: {
107
+ ...(packageJson.dependencies || {}),
108
+ ...dependencies
109
+ },
110
+ private: true
111
+ };
112
+
113
+ // 写入 package.json
114
+ await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
115
+
116
+ // 创建必要的子目录
117
+ await fs.ensureDir(path.join(sandboxDir, 'data'));
118
+ await fs.ensureDir(path.join(sandboxDir, 'logs'));
119
+
120
+ syncedCount++;
121
+ logger.info(`系统工具 ${toolName} 已同步到沙箱环境`, {
122
+ source: toolFile,
123
+ target: sandboxDir
124
+ });
125
+
126
+ } catch (error) {
127
+ logger.error(`同步工具失败: ${toolName}`, { error: error.message });
128
+ }
129
+ }
130
+
131
+ logger.info(`系统工具同步完成,共同步 ${syncedCount} 个工具`);
132
+
133
+ } catch (error) {
134
+ logger.error('同步系统工具失败', { error: error.message });
135
+ throw error;
136
+ }
137
+ }
138
+
@@ -0,0 +1,20 @@
1
+ /**
2
+ * 工具模块共享工具函数
3
+ */
4
+
5
+ import { access } from 'fs/promises';
6
+
7
+ /**
8
+ * 检查路径是否存在(替代 fs.pathExists,因为 fs-extra v11 中已移除)
9
+ * @param {string} filePath - 文件或目录路径
10
+ * @returns {Promise<boolean>}
11
+ */
12
+ export async function pathExists(filePath) {
13
+ try {
14
+ await access(filePath);
15
+ return true;
16
+ } catch {
17
+ return false;
18
+ }
19
+ }
20
+
@@ -0,0 +1,81 @@
1
+ /**
2
+ * 工具 YAML 解析服务
3
+ *
4
+ * 职责:
5
+ * 1. 解析和规范化 YAML 配置
6
+ * 2. 自动修正常见的 AI 错误
7
+ * 3. 验证必需字段
8
+ */
9
+
10
+ import YAML from 'yaml';
11
+ import { logger } from '../utils/logger.js';
12
+
13
+ /**
14
+ * 解析和规范化 YAML 配置
15
+ * @param {string} yamlInput - 原始 YAML 输入
16
+ * @returns {object} 解析后的配置对象 { toolName, mode, parameters }
17
+ */
18
+ export function parseToolYaml(yamlInput) {
19
+ if (!yamlInput) {
20
+ throw new Error("缺少必需参数: yaml");
21
+ }
22
+
23
+ try {
24
+ // Auto-correct common AI mistakes
25
+ let yamlContent = yamlInput.trim();
26
+
27
+ // Case 1: Just a plain URL string like "tool://filesystem" or "@tool://filesystem"
28
+ if (yamlContent.match(/^@?tool:\/\/[\w-]+$/)) {
29
+ const toolName = yamlContent.replace(/^@?tool:\/\//, '');
30
+ yamlContent = `tool: tool://${toolName}\nmode: execute`;
31
+ }
32
+
33
+ // Case 2: Handle escaped backslashes and quotes: tool: \"@tool://xxx\"
34
+ yamlContent = yamlContent.replace(/\\\\/g, '\\').replace(/\\"/g, '"');
35
+
36
+ // Case 3: Remove @ prefix from tool URLs: @tool://xxx -> tool://xxx
37
+ yamlContent = yamlContent.replace(/@tool:\/\//g, 'tool://');
38
+
39
+ // Case 4: Remove quotes around tool URLs: tool: "tool://xxx" -> tool: tool://xxx
40
+ yamlContent = yamlContent.replace(/(tool|toolUrl|url):\s*"(tool:\/\/[^\"]+)"/g, '$1: $2');
41
+
42
+ // YAML → JSON conversion
43
+ const configObj = YAML.parse(yamlContent);
44
+
45
+ // Normalize field names
46
+ const toolIdentifier = configObj.tool || configObj.toolUrl || configObj.url;
47
+ const operationMode = configObj.mode || configObj.operation || 'execute';
48
+ const parameters = configObj.parameters || {};
49
+
50
+ // Validate required fields
51
+ if (!toolIdentifier) {
52
+ throw new Error('Missing required field: tool\nExample: tool: tool://filesystem\nAliases supported: tool / toolUrl / url');
53
+ }
54
+
55
+ // Validate URL format
56
+ if (!toolIdentifier.startsWith('tool://')) {
57
+ throw new Error(`Invalid tool format: ${toolIdentifier}\nMust start with tool://`);
58
+ }
59
+
60
+ // Get tool name
61
+ const toolName = toolIdentifier.replace('tool://', '');
62
+
63
+ return {
64
+ toolName,
65
+ mode: operationMode,
66
+ parameters
67
+ };
68
+
69
+ } catch (error) {
70
+ // YAML parsing errors
71
+ if (error.name === 'YAMLException') {
72
+ if (error.message.includes('bad indentation') || error.message.includes('mapping entry')) {
73
+ throw new Error(`YAML format error: ${error.message}\n\nMultiline content requires | symbol, example:\ncontent: |\n First line\n Second line\n\nNote: Newline after |, indent content with 2 spaces`);
74
+ }
75
+ throw new Error(`YAML format error: ${error.message}\nCheck indentation (use spaces) and syntax`);
76
+ }
77
+
78
+ throw error;
79
+ }
80
+ }
81
+