@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.
Files changed (103) 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 +216 -48
  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/ui/admin-window-manager.js +757 -0
  16. package/app/desktop/src/ui/splash-manager.js +253 -0
  17. package/app/desktop/src/ui/tray-manager.js +8 -24
  18. package/app/desktop/src/utils/icon-manager.js +39 -47
  19. package/app/desktop/src/utils/resource-paths.js +0 -23
  20. package/app/desktop/src/utils/resource-sync.js +260 -0
  21. package/app/desktop/src/utils/runtime-sync.js +241 -0
  22. package/examples/prompts/recommend/human_3-0_growth_diagnostic_coach_prompt.yaml +105 -0
  23. package/package.json +16 -13
  24. package/packages/admin-ui/.babelrc +3 -0
  25. package/packages/admin-ui/admin.html +237 -4784
  26. package/packages/admin-ui/css/main.css +2592 -0
  27. package/packages/admin-ui/css/recommended-prompts.css +610 -0
  28. package/packages/admin-ui/package-lock.json +6981 -0
  29. package/packages/admin-ui/package.json +36 -0
  30. package/packages/admin-ui/src/codemirror.js +53 -0
  31. package/packages/admin-ui/src/index.js +3188 -0
  32. package/packages/admin-ui/webpack.config.js +76 -0
  33. package/packages/resources/tools/chrome-devtools/README.md +310 -0
  34. package/packages/resources/tools/chrome-devtools/chrome-devtools.tool.js +1703 -0
  35. package/packages/resources/tools/file-reader/README.md +289 -0
  36. package/packages/resources/tools/file-reader/file-reader.tool.js +1545 -0
  37. package/packages/resources/tools/filesystem/README.md +359 -0
  38. package/packages/resources/tools/filesystem/filesystem.tool.js +514 -160
  39. package/packages/resources/tools/ollama-remote/README.md +192 -0
  40. package/packages/resources/tools/ollama-remote/ollama-remote.tool.js +421 -0
  41. package/packages/resources/tools/pdf-reader/README.md +236 -0
  42. package/packages/resources/tools/pdf-reader/pdf-reader.tool.js +565 -0
  43. package/packages/resources/tools/playwright/README.md +306 -0
  44. package/packages/resources/tools/playwright/playwright.tool.js +1186 -0
  45. package/packages/resources/tools/todolist/README.md +394 -0
  46. package/packages/resources/tools/todolist/todolist.tool.js +1312 -0
  47. package/packages/server/README.md +142 -0
  48. package/packages/server/api/admin.routes.js +42 -11
  49. package/packages/server/api/surge.routes.js +43 -0
  50. package/packages/server/app.js +119 -14
  51. package/packages/server/index.js +39 -0
  52. package/packages/server/mcp/mcp.server.js +324 -105
  53. package/packages/server/mcp/sequential-thinking.handler.js +318 -0
  54. package/packages/server/mcp/think-plan.handler.js +274 -0
  55. package/packages/server/middlewares/auth.middleware.js +6 -0
  56. package/packages/server/package.json +51 -0
  57. package/packages/server/server.js +37 -1
  58. package/packages/server/toolm/index.js +9 -0
  59. package/packages/server/toolm/package-installer.service.js +267 -0
  60. package/packages/server/toolm/test-tools.js +264 -0
  61. package/packages/server/toolm/tool-context.service.js +334 -0
  62. package/packages/server/toolm/tool-dependency.service.js +168 -0
  63. package/packages/server/toolm/tool-description-generator-optimized.service.js +375 -0
  64. package/packages/server/toolm/tool-description-generator.service.js +312 -0
  65. package/packages/server/toolm/tool-environment.service.js +200 -0
  66. package/packages/server/toolm/tool-execution.service.js +277 -0
  67. package/packages/server/toolm/tool-loader.service.js +219 -0
  68. package/packages/server/toolm/tool-logger.service.js +223 -0
  69. package/packages/server/toolm/tool-manager.handler.js +65 -0
  70. package/packages/server/toolm/tool-manual-generator.service.js +389 -0
  71. package/packages/server/toolm/tool-mode-handlers.service.js +224 -0
  72. package/packages/server/toolm/tool-storage.service.js +111 -0
  73. package/packages/server/toolm/tool-sync.service.js +138 -0
  74. package/packages/server/toolm/tool-utils.js +20 -0
  75. package/packages/server/toolm/tool-yaml-parser.service.js +81 -0
  76. package/packages/server/toolm/validate-system.js +421 -0
  77. package/packages/server/utils/config.js +49 -5
  78. package/packages/server/utils/util.js +65 -10
  79. package/scripts/build-icons.js +99 -69
  80. package/scripts/build.sh +57 -0
  81. package/scripts/surge/CNAME +1 -0
  82. package/scripts/surge/README.md +47 -0
  83. package/scripts/surge/package-lock.json +34 -0
  84. package/scripts/surge/package.json +20 -0
  85. package/scripts/surge/sync-to-surge.js +151 -0
  86. package/app/desktop/assets/icons/icon_1024x1024.png +0 -0
  87. package/app/desktop/assets/icons/icon_128x128.png +0 -0
  88. package/app/desktop/assets/icons/icon_16x16.png +0 -0
  89. package/app/desktop/assets/icons/icon_24x24.png +0 -0
  90. package/app/desktop/assets/icons/icon_256x256.png +0 -0
  91. package/app/desktop/assets/icons/icon_32x32.png +0 -0
  92. package/app/desktop/assets/icons/icon_48x48.png +0 -0
  93. package/app/desktop/assets/icons/icon_512x512.png +0 -0
  94. package/app/desktop/assets/icons/icon_64x64.png +0 -0
  95. package/app/desktop/assets/icons/icon_96x96.png +0 -0
  96. package/packages/admin-ui/js/closebrackets.min.js +0 -8
  97. package/packages/admin-ui/js/codemirror.min.js +0 -8
  98. package/packages/admin-ui/js/js-yaml.min.js +0 -2
  99. package/packages/admin-ui/js/markdown.min.js +0 -8
  100. package/packages/resources/tools/index.js +0 -16
  101. package/packages/server/mcp/toolx.handler.js +0 -131
  102. package/scripts/icns-builder/package.json +0 -12
  103. /package/packages/server/mcp/{mcp.handler.js → prompt.handler.js} +0 -0
@@ -0,0 +1,334 @@
1
+ /**
2
+ * 工具执行上下文服务
3
+ *
4
+ * 职责:
5
+ * 1. 创建工具执行上下文(包含 API、路径解析、模块导入等)
6
+ * 2. 提供统一的工具执行环境
7
+ */
8
+
9
+ import path from 'path';
10
+ import os from 'os';
11
+ import { createRequire } from 'module';
12
+ import { logger } from '../utils/logger.js';
13
+ import { getLogger } from './tool-logger.service.js';
14
+ import { getStorage } from './tool-storage.service.js';
15
+ import { loadToolEnvironment, saveToolEnvironment } from './tool-environment.service.js';
16
+
17
+ /**
18
+ * 创建工具执行上下文
19
+ * @param {string} toolName - 工具名称
20
+ * @param {object} toolModule - 工具模块
21
+ * @returns {object} 工具执行上下文
22
+ */
23
+ export async function createToolContext(toolName, toolModule) {
24
+ const toolDir = path.join(os.homedir(), '.prompt-manager', 'toolbox', toolName);
25
+
26
+ // 1. 加载工具环境变量
27
+ const toolEnvVars = await loadToolEnvironment(toolName);
28
+
29
+ // 2. 创建工具日志记录器
30
+ const toolLogger = getLogger(toolName);
31
+
32
+ // 3. 创建工具存储服务
33
+ const toolStorage = getStorage(toolName);
34
+
35
+ // 4. 创建 API 上下文
36
+ const apiContext = {
37
+ logger: toolLogger,
38
+ storage: toolStorage,
39
+ environment: {
40
+ get: (key) => {
41
+ // 优先从工具环境变量获取
42
+ if (toolEnvVars[key] !== undefined) {
43
+ return toolEnvVars[key];
44
+ }
45
+ // 然后从系统环境变量获取
46
+ return process.env[key];
47
+ },
48
+ set: async (key, value) => {
49
+ // 更新工具环境变量
50
+ toolEnvVars[key] = value;
51
+ await saveToolEnvironment(toolName, { [key]: value });
52
+ }
53
+ }
54
+ };
55
+
56
+ // 5. 创建工具专用的 require 函数
57
+ const packageJsonPath = path.resolve(path.join(toolDir, 'package.json'));
58
+ const toolRequire = createRequire(packageJsonPath);
59
+
60
+ // 6. 创建工具执行上下文
61
+ const toolContext = {
62
+ api: apiContext,
63
+ __toolDir: toolDir,
64
+ __toolName: toolName,
65
+ // 文件系统初始化状态(框架管理)
66
+ _filesystemInitialized: false,
67
+ _allowedDirectories: null,
68
+
69
+ // 框架提供的文件系统基础能力
70
+ getAllowedDirectories() {
71
+ const { api } = this;
72
+
73
+ // 默认值
74
+ let allowedDirs = ['~/.prompt-manager'];
75
+
76
+ if (api && api.environment) {
77
+ try {
78
+ let configStr = api.environment.get('ALLOWED_DIRECTORIES');
79
+
80
+ // 如果直接获取失败,尝试使用工具特定的环境变量名
81
+ if (!configStr) {
82
+ const toolSpecificKey = `${toolName.toUpperCase().replace(/-/g, '_')}_ALLOWED_DIRECTORIES`;
83
+ configStr = process.env[toolSpecificKey];
84
+ }
85
+
86
+ if (configStr) {
87
+ // 处理转义字符(从 .env 文件解析时可能需要)
88
+ configStr = configStr.replace(/\\"/g, '"').replace(/\\\\/g, '\\');
89
+
90
+ // 首先尝试作为 JSON 字符串解析
91
+ try {
92
+ const parsed = JSON.parse(configStr);
93
+ if (Array.isArray(parsed) && parsed.length > 0) {
94
+ allowedDirs = parsed;
95
+ }
96
+ } catch (parseError) {
97
+ // 如果不是 JSON,尝试作为逗号分隔的字符串
98
+ if (typeof configStr === 'string' && configStr.includes(',')) {
99
+ allowedDirs = configStr.split(',').map(s => s.trim()).filter(s => s);
100
+ } else if (configStr) {
101
+ allowedDirs = [configStr];
102
+ }
103
+ }
104
+ }
105
+ } catch (error) {
106
+ // 回退到默认值
107
+ api?.logger?.warn('Failed to parse ALLOWED_DIRECTORIES', { error: error.message });
108
+ }
109
+ }
110
+
111
+ // 展开 ~ 到主目录并规范化路径
112
+ return allowedDirs.map(dir => {
113
+ const expanded = dir.replace(/^~/, os.homedir());
114
+ return path.resolve(expanded);
115
+ });
116
+ },
117
+
118
+ async initializeFilesystem() {
119
+ if (!this._filesystemInitialized) {
120
+ // 获取允许的目录列表
121
+ const allowedDirectories = this.getAllowedDirectories();
122
+ this._allowedDirectories = allowedDirectories;
123
+ this._filesystemInitialized = true;
124
+
125
+ // 记录日志
126
+ const { api } = this;
127
+ api?.logger?.info('Filesystem initialized', {
128
+ allowedDirectories: this._allowedDirectories
129
+ });
130
+ }
131
+ },
132
+
133
+ resolvePromptManagerPath(inputPath) {
134
+ const { api } = this;
135
+
136
+ // 获取允许的目录列表
137
+ const allowedDirs = this._allowedDirectories || this.getAllowedDirectories();
138
+
139
+ if (!inputPath) {
140
+ // 没有路径时返回第一个允许的目录
141
+ return allowedDirs[0];
142
+ }
143
+
144
+ // 处理 ~ 开头的路径
145
+ const expandedPath = inputPath.replace(/^~/, os.homedir());
146
+
147
+ // 如果是绝对路径
148
+ if (path.isAbsolute(expandedPath)) {
149
+ const resolved = path.resolve(expandedPath);
150
+ const normalizedResolved = path.normalize(resolved);
151
+
152
+ // 检查路径是否在允许的目录范围内
153
+ const isAllowed = allowedDirs.some(dir => {
154
+ const normalizedDir = path.normalize(dir);
155
+
156
+ // 完全匹配允许的目录
157
+ if (normalizedResolved === normalizedDir) {
158
+ return true;
159
+ }
160
+
161
+ // 检查是否是允许目录的子路径
162
+ const relativePath = path.relative(normalizedDir, normalizedResolved);
163
+
164
+ // 如果 relativePath 以 .. 开头,说明路径不在允许目录内
165
+ if (relativePath.startsWith('..')) {
166
+ return false;
167
+ }
168
+
169
+ // 如果 relativePath 为空,说明是完全匹配(已在上面检查)
170
+ if (relativePath === '') {
171
+ return false;
172
+ }
173
+
174
+ // 相对路径存在且不以 .. 开头,说明是允许目录的子路径
175
+ return true;
176
+ });
177
+
178
+ if (!isAllowed) {
179
+ const dirsStr = allowedDirs.join(', ');
180
+ api?.logger?.warn('Path access denied', { path: resolved, allowedDirs });
181
+ throw new Error(`路径越权: ${inputPath} 不在允许的目录范围内 [${dirsStr}]`);
182
+ }
183
+
184
+ return resolved;
185
+ }
186
+
187
+ // 相对路径,尝试在每个允许的目录中解析
188
+ const baseDir = allowedDirs[0];
189
+ const fullPath = path.join(baseDir, expandedPath);
190
+ const resolved = path.resolve(fullPath);
191
+ const normalizedResolved = path.normalize(resolved);
192
+
193
+ // 安全检查:确保解析后的路径在允许的目录内或其子目录中
194
+ const isAllowed = allowedDirs.some(dir => {
195
+ const normalizedDir = path.normalize(dir);
196
+
197
+ // 完全匹配允许的目录
198
+ if (normalizedResolved === normalizedDir) {
199
+ return true;
200
+ }
201
+
202
+ // 检查是否是允许目录的子路径
203
+ const relativePath = path.relative(normalizedDir, normalizedResolved);
204
+
205
+ // 如果 relativePath 以 .. 开头,说明路径不在允许目录内
206
+ if (relativePath.startsWith('..')) {
207
+ return false;
208
+ }
209
+
210
+ // 如果 relativePath 为空,说明是完全匹配(已在上面检查)
211
+ if (relativePath === '') {
212
+ return false;
213
+ }
214
+
215
+ // 相对路径存在且不以 .. 开头,说明是允许目录的子路径
216
+ return true;
217
+ });
218
+
219
+ if (!isAllowed) {
220
+ const dirsStr = allowedDirs.join(', ');
221
+ api?.logger?.warn('Path resolution failed', { path: inputPath, resolved, allowedDirs });
222
+ throw new Error(`路径越权: ${inputPath} 解析后超出允许的目录范围 [${dirsStr}]`);
223
+ }
224
+
225
+ return resolved;
226
+ },
227
+
228
+ // 提供工具专用的模块导入函数
229
+ requireToolModule: (moduleName) => {
230
+ try {
231
+ // 首先尝试从工具的 node_modules 导入
232
+ return toolRequire(moduleName);
233
+ } catch (error) {
234
+ // 如果失败,尝试从系统 node_modules 导入
235
+ try {
236
+ return require(moduleName);
237
+ } catch (e) {
238
+ throw new Error(`无法导入模块 ${moduleName}: ${error.message}`);
239
+ }
240
+ }
241
+ },
242
+
243
+ // 提供工具专用的动态导入函数(支持 ES 模块和 CommonJS)
244
+ importToolModule: async (moduleName) => {
245
+ try {
246
+ // 首先尝试使用 require 导入(适用于 CommonJS 模块,如 pdf-parse)
247
+ const module = toolRequire(moduleName);
248
+
249
+ logger.debug(`模块 ${moduleName} require 成功`, {
250
+ type: typeof module,
251
+ isFunction: typeof module === 'function',
252
+ isObject: typeof module === 'object',
253
+ keys: typeof module === 'object' ? Object.keys(module).slice(0, 10) : []
254
+ });
255
+
256
+ // pdf-parse 是 CommonJS 模块,直接导出函数
257
+ // 如果 module 是函数,直接返回
258
+ if (typeof module === 'function') {
259
+ return { default: module };
260
+ }
261
+
262
+ // 如果是对象,检查是否有 default
263
+ if (module && typeof module === 'object') {
264
+ // 如果已经有 default,直接返回
265
+ if (module.default) {
266
+ return module;
267
+ }
268
+
269
+ // 检查是否有常见的导出名称(如 PDFParse)
270
+ if (module.PDFParse && typeof module.PDFParse === 'function') {
271
+ return { default: module.PDFParse };
272
+ }
273
+
274
+ // 否则包装为 { default: module }
275
+ return { default: module, ...module };
276
+ }
277
+
278
+ return module;
279
+ } catch (requireError) {
280
+ logger.debug(`require 失败,尝试 import: ${moduleName}`, { error: requireError.message });
281
+ // 如果 require 失败,尝试使用 import(适用于 ES 模块)
282
+ try {
283
+ const modulePath = toolRequire.resolve(moduleName);
284
+ logger.debug(`使用 import 导入模块: ${moduleName}`, { path: modulePath });
285
+ return await import(modulePath);
286
+ } catch (resolveError) {
287
+ logger.debug(`resolve 失败,尝试特殊路径: ${moduleName}`, { error: resolveError.message });
288
+
289
+ // 特殊处理:某些包的 main 字段指向错误的路径,需要尝试备用路径
290
+ const fallbackPaths = {
291
+ 'chrome-devtools-mcp': 'chrome-devtools-mcp/build/src/index.js'
292
+ };
293
+
294
+ if (fallbackPaths[moduleName]) {
295
+ try {
296
+ const fallbackPath = toolRequire.resolve(fallbackPaths[moduleName]);
297
+ logger.debug(`使用备用路径导入 ${moduleName}`, { path: fallbackPath });
298
+ return await import(fallbackPath);
299
+ } catch (fallbackError) {
300
+ logger.debug(`备用路径也失败: ${moduleName}`, { error: fallbackError.message });
301
+ }
302
+ }
303
+
304
+ // 如果 resolve 失败,尝试直接导入(可能从系统 node_modules)
305
+ try {
306
+ logger.debug(`尝试直接导入模块: ${moduleName}`);
307
+ return await import(moduleName);
308
+ } catch (importError) {
309
+ logger.error(`导入模块失败: ${moduleName}`, {
310
+ requireError: requireError.message,
311
+ resolveError: resolveError.message,
312
+ importError: importError.message,
313
+ toolDir
314
+ });
315
+ throw new Error(`无法导入模块 ${moduleName}。请确保依赖已安装。错误: ${requireError.message}`);
316
+ }
317
+ }
318
+ }
319
+ }
320
+ };
321
+
322
+ // 7. 将工具模块的所有方法复制到上下文中,并绑定this
323
+ // 这样工具方法之间可以相互调用(如 execute 调用 this.initializeFilesystem)
324
+ for (const [key, value] of Object.entries(toolModule)) {
325
+ if (typeof value === 'function') {
326
+ toolContext[key] = value.bind(toolContext);
327
+ } else {
328
+ toolContext[key] = value;
329
+ }
330
+ }
331
+
332
+ return toolContext;
333
+ }
334
+
@@ -0,0 +1,168 @@
1
+ /**
2
+ * 工具依赖管理服务
3
+ *
4
+ * 职责:
5
+ * 1. 检查工具依赖是否已安装
6
+ * 2. 自动安装工具依赖(使用 @npmcli/arborist,不依赖系统 npm)
7
+ * 3. 验证依赖版本匹配
8
+ *
9
+ * 使用 PackageInstaller 基于 @npmcli/arborist 实现,可在 Electron 环境中直接使用
10
+ */
11
+
12
+ import fs from 'fs-extra';
13
+ import path from 'path';
14
+ import os from 'os';
15
+ import { logger } from '../utils/logger.js';
16
+ import { pathExists } from './tool-utils.js';
17
+ import PackageInstaller from './package-installer.service.js';
18
+
19
+ /**
20
+ * 确保工具依赖已安装
21
+ * @param {string} toolName - 工具名称
22
+ * @param {object} toolModule - 工具模块(可选,用于自动创建 package.json)
23
+ */
24
+ export async function ensureToolDependencies(toolName, toolModule = null) {
25
+ const toolDir = path.join(os.homedir(), '.prompt-manager', 'toolbox', toolName);
26
+ const packageJsonPath = path.join(toolDir, 'package.json');
27
+ const nodeModulesPath = path.join(toolDir, 'node_modules');
28
+
29
+ // 检查 package.json 是否存在,如果不存在则自动创建
30
+ if (!await pathExists(packageJsonPath)) {
31
+ logger.info(`工具 ${toolName} 缺少 package.json,正在自动创建...`);
32
+ await createPackageJson(toolName, toolDir, toolModule);
33
+ }
34
+
35
+ const packageJson = await fs.readJson(packageJsonPath);
36
+ const dependencies = packageJson.dependencies || {};
37
+
38
+ // 如果没有依赖,直接返回
39
+ if (Object.keys(dependencies).length === 0) {
40
+ logger.debug(`工具 ${toolName} 无需依赖`);
41
+ return;
42
+ }
43
+
44
+ // 检查 node_modules 是否存在,或者检查依赖是否需要更新
45
+ const needsInstall = !await pathExists(nodeModulesPath) || await needsDependencyUpdate(toolDir, dependencies);
46
+
47
+ if (needsInstall) {
48
+ logger.info(`工具 ${toolName} 依赖需要安装或更新,开始安装...`);
49
+ await installDependencies(toolDir, dependencies);
50
+ return;
51
+ }
52
+
53
+ logger.debug(`工具 ${toolName} 依赖已存在且为最新`);
54
+ }
55
+
56
+ /**
57
+ * 自动创建 package.json 文件
58
+ * @param {string} toolName - 工具名称
59
+ * @param {string} toolDir - 工具目录
60
+ * @param {object} toolModule - 工具模块(可选)
61
+ */
62
+ async function createPackageJson(toolName, toolDir, toolModule) {
63
+ // 确保工具目录存在
64
+ await fs.ensureDir(toolDir);
65
+
66
+ // 获取工具依赖(如果工具模块提供了 getDependencies 方法)
67
+ let dependencies = {};
68
+ if (toolModule && typeof toolModule.getDependencies === 'function') {
69
+ try {
70
+ dependencies = toolModule.getDependencies() || {};
71
+ } catch (error) {
72
+ logger.warn(`获取工具 ${toolName} 依赖失败:`, error.message);
73
+ }
74
+ }
75
+
76
+ // 获取工具元数据(用于 package.json 的基本信息)
77
+ let metadata = { name: toolName, version: '1.0.0' };
78
+ if (toolModule && typeof toolModule.getMetadata === 'function') {
79
+ try {
80
+ metadata = toolModule.getMetadata() || metadata;
81
+ } catch (error) {
82
+ logger.warn(`获取工具 ${toolName} 元数据失败:`, error.message);
83
+ }
84
+ }
85
+
86
+ // 创建 package.json
87
+ const packageJson = {
88
+ name: `@prompt-manager/tool-${toolName}`,
89
+ version: metadata.version || '1.0.0',
90
+ description: metadata.description || `Prompt Manager tool: ${toolName}`,
91
+ main: `${toolName}.tool.js`,
92
+ type: 'module',
93
+ dependencies: dependencies
94
+ };
95
+
96
+ // 写入 package.json
97
+ const packageJsonPath = path.join(toolDir, 'package.json');
98
+ await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
99
+
100
+ logger.info(`已为工具 ${toolName} 创建 package.json`, {
101
+ dependencies: Object.keys(dependencies).length
102
+ });
103
+ }
104
+
105
+ /**
106
+ * 检查依赖是否需要更新
107
+ * @param {string} toolDir - 工具目录
108
+ * @param {Object} expectedDependencies - 期望的依赖列表
109
+ * @returns {Promise<boolean>} 是否需要更新
110
+ */
111
+ async function needsDependencyUpdate(toolDir, expectedDependencies) {
112
+ try {
113
+ // 检查每个依赖是否已安装且版本匹配
114
+ for (const [packageName, expectedVersion] of Object.entries(expectedDependencies)) {
115
+ const isInstalled = await PackageInstaller.isPackageInstalled(toolDir, packageName);
116
+ if (!isInstalled) {
117
+ logger.debug(`依赖 ${packageName} 未安装,需要更新`);
118
+ return true;
119
+ }
120
+
121
+ // 可以进一步检查版本是否匹配(简化版本,暂时只检查是否存在)
122
+ // 如果需要精确版本匹配,可以读取 package.json 并比较版本
123
+ }
124
+
125
+ return false;
126
+ } catch (error) {
127
+ logger.warn('检查依赖更新状态失败,将重新安装', { error: error.message });
128
+ return true; // 出错时重新安装
129
+ }
130
+ }
131
+
132
+ /**
133
+ * 安装工具依赖(使用 PackageInstaller 基于 @npmcli/arborist)
134
+ * @param {string} toolDir - 工具目录
135
+ * @param {Object} dependencies - 依赖列表
136
+ */
137
+ async function installDependencies(toolDir, dependencies) {
138
+ logger.info(`在目录 ${toolDir} 中安装依赖...`, { dependencies });
139
+
140
+ try {
141
+ // 使用 PackageInstaller 安装依赖
142
+ // PackageInstaller 使用 @npmcli/arborist,不依赖系统 npm
143
+ // 可在 Electron 环境中直接使用
144
+ const result = await PackageInstaller.install({
145
+ workingDir: toolDir,
146
+ dependencies: dependencies,
147
+ timeout: 300000 // 5分钟超时
148
+ });
149
+
150
+ logger.info('依赖安装成功', {
151
+ elapsed: result.elapsed,
152
+ installedCount: result.installedPackages.length,
153
+ packages: result.installedPackages
154
+ });
155
+
156
+ return result;
157
+
158
+ } catch (error) {
159
+ logger.error('依赖安装失败', {
160
+ error: error.message,
161
+ toolDir,
162
+ dependencies,
163
+ stack: error.stack
164
+ });
165
+ throw new Error(`依赖安装失败: ${error.message}`);
166
+ }
167
+ }
168
+