@becrafter/prompt-manager 0.1.22 → 0.2.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 (114) hide show
  1. package/package.json +31 -24
  2. package/packages/resources/tools/agent-browser/README.md +640 -0
  3. package/packages/resources/tools/agent-browser/agent-browser.tool.js +1389 -0
  4. package/packages/resources/tools/thinking/README.md +324 -0
  5. package/packages/resources/tools/thinking/thinking.tool.js +911 -0
  6. package/packages/server/README.md +3 -4
  7. package/packages/server/api/admin.routes.js +668 -664
  8. package/packages/server/api/open.routes.js +68 -67
  9. package/packages/server/api/surge.routes.js +5 -6
  10. package/packages/server/api/tool.routes.js +70 -71
  11. package/packages/server/app.js +70 -73
  12. package/packages/server/configs/authors.json +40 -0
  13. package/packages/server/configs/models/built-in/bigmodel.yaml +6 -6
  14. package/packages/server/configs/models/providers.yaml +4 -4
  15. package/packages/server/configs/templates/built-in/general-iteration.yaml +1 -1
  16. package/packages/server/configs/templates/built-in/general-optimize.yaml +1 -1
  17. package/packages/server/configs/templates/built-in/output-format-optimize.yaml +1 -1
  18. package/packages/server/index.js +3 -9
  19. package/packages/server/mcp/heartbeat-patch.js +1 -3
  20. package/packages/server/mcp/mcp.server.js +64 -134
  21. package/packages/server/mcp/prompt.handler.js +101 -95
  22. package/packages/server/middlewares/auth.middleware.js +31 -31
  23. package/packages/server/server.js +60 -45
  24. package/packages/server/services/TerminalService.js +156 -70
  25. package/packages/server/services/WebSocketService.js +35 -34
  26. package/packages/server/services/author-config.service.js +199 -0
  27. package/packages/server/services/manager.js +66 -60
  28. package/packages/server/services/model.service.js +5 -9
  29. package/packages/server/services/optimization.service.js +25 -22
  30. package/packages/server/services/template.service.js +3 -8
  31. package/packages/server/toolm/author-sync.service.js +97 -0
  32. package/packages/server/toolm/index.js +1 -2
  33. package/packages/server/toolm/package-installer.service.js +47 -50
  34. package/packages/server/toolm/tool-context.service.js +64 -62
  35. package/packages/server/toolm/tool-dependency.service.js +28 -30
  36. package/packages/server/toolm/tool-description-generator-optimized.service.js +55 -55
  37. package/packages/server/toolm/tool-description-generator.service.js +20 -23
  38. package/packages/server/toolm/tool-environment.service.js +45 -44
  39. package/packages/server/toolm/tool-execution.service.js +49 -48
  40. package/packages/server/toolm/tool-loader.service.js +13 -18
  41. package/packages/server/toolm/tool-logger.service.js +33 -39
  42. package/packages/server/toolm/tool-manager.handler.js +17 -15
  43. package/packages/server/toolm/tool-manual-generator.service.js +107 -87
  44. package/packages/server/toolm/tool-mode-handlers.service.js +52 -59
  45. package/packages/server/toolm/tool-storage.service.js +11 -12
  46. package/packages/server/toolm/tool-sync.service.js +36 -39
  47. package/packages/server/toolm/tool-utils.js +0 -1
  48. package/packages/server/toolm/tool-yaml-parser.service.js +12 -11
  49. package/packages/server/toolm/validate-system.js +56 -84
  50. package/packages/server/utils/config.js +97 -12
  51. package/packages/server/utils/logger.js +1 -1
  52. package/packages/server/utils/port-checker.js +8 -8
  53. package/packages/server/utils/util.js +470 -467
  54. package/packages/resources/tools/cognitive-thinking/README.md +0 -284
  55. package/packages/resources/tools/cognitive-thinking/cognitive-thinking.tool.js +0 -837
  56. package/packages/server/mcp/sequential-thinking.handler.js +0 -318
  57. package/packages/server/mcp/think-plan.handler.js +0 -274
  58. package/packages/server/mcp/thinking-toolkit.handler.js +0 -380
  59. package/packages/web/0.d1c5a72339dfc32ad86a.js +0 -1
  60. package/packages/web/112.8807b976372b2b0541a8.js +0 -1
  61. package/packages/web/130.584c7e365da413f5d9be.js +0 -1
  62. package/packages/web/142.72c985bc29720f975cca.js +0 -1
  63. package/packages/web/165.a05fc53bf84d18db36b8.js +0 -2
  64. package/packages/web/165.a05fc53bf84d18db36b8.js.LICENSE.txt +0 -9
  65. package/packages/web/203.724ab9f717b80554c397.js +0 -1
  66. package/packages/web/241.bf941d4f02866795f64a.js +0 -1
  67. package/packages/web/249.54cfb224af63f5f5ec55.js +0 -1
  68. package/packages/web/291.6df35042f8f296fca7cd.js +0 -1
  69. package/packages/web/319.2fab900a31b29873f666.js +0 -1
  70. package/packages/web/32.c78d866281995ec33a7b.js +0 -1
  71. package/packages/web/325.9ca297d0f73f38468ce9.js +0 -1
  72. package/packages/web/366.2f9b48fdbf8eee039e57.js +0 -1
  73. package/packages/web/378.6be08c612cd5a3ef97dc.js +0 -1
  74. package/packages/web/393.7a2f817515c5e90623d7.js +0 -1
  75. package/packages/web/412.062df5f732d5ba203415.js +0 -1
  76. package/packages/web/426.08656fef4918b3fb19ad.js +0 -1
  77. package/packages/web/465.2be8018327130a3bd798.js +0 -1
  78. package/packages/web/48.8ca96fc93667a715e67a.js +0 -1
  79. package/packages/web/480.44c1f1a2927486ac3d4f.js +0 -1
  80. package/packages/web/489.e041a8d0db15dc96d607.js +0 -1
  81. package/packages/web/490.9ffb26c907de020d671b.js +0 -1
  82. package/packages/web/492.58781369e348d91fc06a.js +0 -1
  83. package/packages/web/495.ed63e99791a87167c6b3.js +0 -1
  84. package/packages/web/510.4cc07ab7d30d5c1cd17f.js +0 -1
  85. package/packages/web/543.3af155ed4fa237664308.js +0 -1
  86. package/packages/web/567.f04ab60f8e2c2fb0745a.js +0 -1
  87. package/packages/web/592.f3ad085fa9c1849daa06.js +0 -1
  88. package/packages/web/616.b03fb801b3433b17750f.js +0 -1
  89. package/packages/web/617.d88def54921d2c4dc44c.js +0 -1
  90. package/packages/web/641.d30787d674f548928261.js +0 -1
  91. package/packages/web/672.5269c8399fa42a5af95d.js +0 -1
  92. package/packages/web/731.97cab92b71811c502bda.js +0 -1
  93. package/packages/web/746.3947c6f0235407e420fb.js +0 -1
  94. package/packages/web/756.a53233b3f3913900d5ac.js +0 -1
  95. package/packages/web/77.68801af593a28a631fbf.js +0 -1
  96. package/packages/web/802.53b2bff3cf2a69f7b80c.js +0 -1
  97. package/packages/web/815.b6dfab82265f56c7e046.js +0 -1
  98. package/packages/web/821.f5a13e5c735aac244eb9.js +0 -1
  99. package/packages/web/846.b9bf97d5f559270675ce.js +0 -1
  100. package/packages/web/869.7c10403f500e6201407f.js +0 -1
  101. package/packages/web/885.135050364f99e6924fb5.js +0 -1
  102. package/packages/web/901.fd5aeb9df630609a2b43.js +0 -1
  103. package/packages/web/928.f67e590de3caa4daa3ae.js +0 -1
  104. package/packages/web/955.d833403521ba4dd567ee.js +0 -1
  105. package/packages/web/981.a45cb745cf424044c8c8.js +0 -1
  106. package/packages/web/992.645320b60c74c8787482.js +0 -1
  107. package/packages/web/996.ed9a963dc9e7439eca9a.js +0 -1
  108. package/packages/web/css/codemirror-theme_xq-light.css +0 -43
  109. package/packages/web/css/codemirror.css +0 -344
  110. package/packages/web/css/main.196f434e6a88cd448158.css +0 -7278
  111. package/packages/web/css/terminal-fix.css +0 -571
  112. package/packages/web/index.html +0 -3
  113. package/packages/web/main.dceff50c7307dda04873.js +0 -2
  114. package/packages/web/main.dceff50c7307dda04873.js.LICENSE.txt +0 -3
@@ -27,7 +27,7 @@ class OptimizationService {
27
27
  // 尝试获取第一个系统优化模板
28
28
  template = templateManager.getTemplates().find(t => (t.type || 'optimize') === 'optimize');
29
29
  }
30
-
30
+
31
31
  if (!template) {
32
32
  throw new Error(`未找到可用模板: ${templateId}`);
33
33
  }
@@ -132,15 +132,17 @@ class OptimizationService {
132
132
 
133
133
  // 3. 构建迭代优化消息列表
134
134
  const messages = this.buildIterationMessages(
135
- currentResult,
135
+ currentResult,
136
136
  session.lastResult,
137
- template,
138
- session.count,
137
+ template,
138
+ session.count,
139
139
  guideText,
140
140
  session.originalPrompt // 传入原始提示词
141
141
  );
142
142
 
143
- logger.info(`开始迭代优化(第 ${session.count + 1} 次),使用模板: ${template.name},模型: ${model.name}${guideText ? ',有优化指导' : ''}`);
143
+ logger.info(
144
+ `开始迭代优化(第 ${session.count + 1} 次),使用模板: ${template.name},模型: ${model.name}${guideText ? ',有优化指导' : ''}`
145
+ );
144
146
 
145
147
  // 4. 调用 AI API(流式)
146
148
  const result = await this.callAIModel(messages, model, onChunk);
@@ -186,10 +188,10 @@ class OptimizationService {
186
188
  */
187
189
  buildMessages(prompt, template) {
188
190
  if (template.format === 'advanced' && Array.isArray(template.content)) {
189
- const variables = {
191
+ const variables = {
190
192
  originalPrompt: prompt,
191
- prompt: prompt, // 兼容旧模板
192
- input: prompt // 兼容旧模板
193
+ prompt, // 兼容旧模板
194
+ input: prompt // 兼容旧模板
193
195
  };
194
196
  return template.content.map(msg => ({
195
197
  role: msg.role,
@@ -239,7 +241,7 @@ class OptimizationService {
239
241
  iterationCount: iterationCount + 1,
240
242
  guideText: guideText || ''
241
243
  };
242
-
244
+
243
245
  return template.content.map(msg => ({
244
246
  role: msg.role,
245
247
  content: this.replaceVariables(msg.content, variables)
@@ -261,21 +263,20 @@ class OptimizationService {
261
263
 
262
264
  // 构建默认迭代上下文(如果模板是简单的且没有包含占位符)
263
265
  let finalContent = '';
264
-
266
+
265
267
  // 如果没有使用任何变量占位符,执行默认拼接逻辑
266
- const hasVariables = ['{{prompt}}', '{{previousResult}}', '{{guideText}}']
267
- .some(v => content.includes(v));
268
+ const hasVariables = ['{{prompt}}', '{{previousResult}}', '{{guideText}}'].some(v => content.includes(v));
268
269
 
269
270
  if (!hasVariables) {
270
271
  if (!content.includes('{{previousResult}}')) {
271
272
  finalContent += `这是第 ${variables.iterationCount} 次优化。上一次的优化结果如下:\n\n${variables.previousResult}\n\n`;
272
273
  }
273
-
274
+
274
275
  if (guideText && !content.includes('{{guideText}}')) {
275
276
  finalContent += `本次优化的具体要求:\n${guideText}\n\n`;
276
277
  }
277
278
 
278
- finalContent += `请基于以上上下文,继续优化以下内容:\n\n{{prompt}}`;
279
+ finalContent += '请基于以上上下文,继续优化以下内容:\n\n{{prompt}}';
279
280
  } else {
280
281
  finalContent = content;
281
282
  }
@@ -300,9 +301,11 @@ class OptimizationService {
300
301
  // 记录请求日志
301
302
  logger.info(`[AI Request] 模型: ${model.name}, 消息数: ${messages.length}`);
302
303
  messages.forEach((msg, i) => {
303
- logger.debug(`[Message ${i}] Role: ${msg.role}, Content: ${msg.content.substring(0, 1000)}${msg.content.length > 1000 ? '...' : ''}`);
304
+ logger.debug(
305
+ `[Message ${i}] Role: ${msg.role}, Content: ${msg.content.substring(0, 1000)}${msg.content.length > 1000 ? '...' : ''}`
306
+ );
304
307
  });
305
-
308
+
306
309
  // 构建 API 请求
307
310
  const requestBody = {
308
311
  model: model.model,
@@ -311,8 +314,8 @@ class OptimizationService {
311
314
  };
312
315
 
313
316
  const headers = {
314
- 'Content-Type': 'application/json',
315
- }
317
+ 'Content-Type': 'application/json'
318
+ };
316
319
  if (model.apiKey) {
317
320
  headers['Authorization'] = `Bearer ${model.apiKey}`;
318
321
  }
@@ -320,7 +323,7 @@ class OptimizationService {
320
323
  // 发起请求
321
324
  const response = await fetch(model.apiEndpoint, {
322
325
  method: 'POST',
323
- headers: headers,
326
+ headers,
324
327
  body: JSON.stringify(requestBody)
325
328
  });
326
329
 
@@ -337,7 +340,7 @@ class OptimizationService {
337
340
  } catch (e) {
338
341
  // 如果不是 JSON,限制长度
339
342
  if (errorDetail.length > 200) {
340
- errorDetail = errorDetail.substring(0, 200) + '...';
343
+ errorDetail = `${errorDetail.substring(0, 200)}...`;
341
344
  }
342
345
  }
343
346
  throw new Error(`API 请求失败 (${response.status} ${response.statusText}): ${errorDetail}`);
@@ -348,7 +351,7 @@ class OptimizationService {
348
351
  const decoder = new TextDecoder();
349
352
  let fullContent = '';
350
353
 
351
- while (true) {
354
+ for (;;) {
352
355
  const { done, value } = await reader.read();
353
356
 
354
357
  if (done) {
@@ -454,4 +457,4 @@ class OptimizationService {
454
457
  export const optimizationService = new OptimizationService();
455
458
 
456
459
  // 导出OptimizationService类供测试使用
457
- export { OptimizationService };
460
+ export { OptimizationService };
@@ -1,16 +1,11 @@
1
1
  import fs from 'fs-extra';
2
2
  import path from 'path';
3
- import { fileURLToPath } from 'url';
4
3
  import YAML from 'yaml';
5
4
  import { z } from 'zod';
6
5
  import crypto from 'crypto';
7
- import os from 'os';
8
6
  import { logger } from '../utils/logger.js';
9
- import { config } from '../utils/config.js';
10
7
  import { util } from '../utils/util.js';
11
-
12
- const __filename = fileURLToPath(import.meta.url);
13
- const __dirname = path.dirname(__filename);
8
+ import { config } from '../utils/config.js';
14
9
 
15
10
  /**
16
11
  * 模板数据结构验证schema
@@ -32,7 +27,7 @@ const TemplateSchema = z.object({
32
27
  class TemplateManager {
33
28
  constructor() {
34
29
  this.builtInDir = path.join(util.getBuiltInConfigsDir(), 'templates/built-in');
35
- this.customDir = path.join(os.homedir(), '.prompt-manager/configs/templates');
30
+ this.customDir = config.getTemplatesDir();
36
31
  this.loadedTemplates = new Map();
37
32
  this.idToPathMap = new Map();
38
33
  }
@@ -330,4 +325,4 @@ class TemplateManager {
330
325
  export const templateManager = new TemplateManager();
331
326
 
332
327
  // 导出TemplateManager类供测试使用
333
- export { TemplateManager };
328
+ export { TemplateManager };
@@ -0,0 +1,97 @@
1
+ /**
2
+ * 作者配置同步服务
3
+ *
4
+ * 职责:
5
+ * 1. 将作者配置从 packages/server/configs/ 同步到 ~/.prompt-manager/configs/
6
+ * 2. 确保配置文件在工作目录可用
7
+ */
8
+
9
+ import fs from 'fs-extra';
10
+ import path from 'path';
11
+ import { fileURLToPath } from 'url';
12
+ import { logger } from '../utils/logger.js';
13
+ import { pathExists } from './tool-utils.js';
14
+ import { config } from '../utils/config.js';
15
+
16
+ const __filename = fileURLToPath(import.meta.url);
17
+ const __dirname = path.dirname(__filename);
18
+
19
+ /**
20
+ * 同步作者配置到沙箱环境
21
+ */
22
+ export async function syncAuthorConfig() {
23
+ logger.info('开始同步作者配置到沙箱环境...');
24
+
25
+ let sourceDir;
26
+
27
+ const isElectronPackaged = process.resourcesPath != null;
28
+
29
+ const isNpmPackage = __dirname.includes('node_modules');
30
+
31
+ if (isElectronPackaged) {
32
+ sourceDir = path.join(process.resourcesPath, 'runtime', 'configs');
33
+ logger.debug('Electron 打包环境,使用配置目录:', { sourceDir });
34
+ } else if (isNpmPackage) {
35
+ sourceDir = path.join(__dirname, '..', 'configs');
36
+ logger.debug('NPM 包环境,使用配置目录:', { sourceDir });
37
+ } else {
38
+ sourceDir = path.join(__dirname, '..', '..', 'server', 'configs');
39
+ logger.debug('开发环境,使用配置目录:', { sourceDir });
40
+ }
41
+
42
+ const sourceConfigPath = path.join(sourceDir, 'authors.json');
43
+ const targetConfigPath = config.getConfigsDir();
44
+ const targetConfigFile = path.join(targetConfigPath, 'authors.json');
45
+
46
+ try {
47
+ if (!(await pathExists(sourceConfigPath))) {
48
+ logger.warn(`作者配置源文件不存在,跳过同步: ${sourceConfigPath}`);
49
+ return;
50
+ }
51
+
52
+ await fs.ensureDir(targetConfigPath);
53
+
54
+ const configExists = await pathExists(targetConfigFile);
55
+
56
+ if (!configExists) {
57
+ logger.info('首次同步,复制作者配置文件');
58
+ await fs.copyFile(sourceConfigPath, targetConfigFile);
59
+ logger.info('作者配置同步成功', { source: sourceConfigPath, target: targetConfigFile });
60
+ } else {
61
+ const sourceMtime = (await fs.stat(sourceConfigPath)).mtimeMs;
62
+ const targetMtime = (await fs.stat(targetConfigFile)).mtimeMs;
63
+
64
+ if (sourceMtime > targetMtime) {
65
+ logger.info('源配置更新,同步作者配置文件');
66
+ await fs.copyFile(sourceConfigPath, targetConfigFile);
67
+ logger.info('作者配置同步成功', { source: sourceConfigPath, target: targetConfigFile });
68
+ } else {
69
+ logger.debug('作者配置已是最新,无需同步');
70
+ }
71
+ }
72
+
73
+ const configData = await fs.readJson(targetConfigFile);
74
+ const authorCount = Object.keys(configData.authors || {}).length;
75
+ logger.info('作者配置同步完成', {
76
+ author_count: authorCount,
77
+ default_author: configData.settings?.default_author
78
+ });
79
+ } catch (error) {
80
+ logger.error('同步作者配置失败:', error);
81
+
82
+ if (error.code === 'ENOENT' && !(await pathExists(sourceConfigPath))) {
83
+ logger.warn('源配置目录不存在,使用默认配置');
84
+ const defaultConfig = {
85
+ version: '1.0.0',
86
+ last_updated: new Date().toISOString(),
87
+ authors: {},
88
+ settings: {
89
+ default_author: 'default'
90
+ }
91
+ };
92
+ await fs.ensureDir(targetConfigPath);
93
+ await fs.writeJson(targetConfigFile, defaultConfig);
94
+ logger.info('创建默认作者配置文件');
95
+ }
96
+ }
97
+ }
@@ -1,9 +1,8 @@
1
1
  /**
2
2
  * 工具系统入口
3
- *
3
+ *
4
4
  * 提供工具系统的统一入口,导出所有工具系统组件
5
5
  */
6
6
 
7
7
  export { toolLoaderService } from './tool-loader.service.js';
8
8
  export { handleToolM } from './tool-manager.handler.js';
9
-
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * PackageInstaller - 基于 Arborist 的完整依赖管理器
3
- *
3
+ *
4
4
  * 使用 npm 官方的 @npmcli/arborist,提供与 npm install 完全一致的行为
5
5
  * 自动处理所有传递依赖、版本冲突、循环依赖等复杂场景
6
6
  * 不依赖系统 npm,可在 Electron 环境中直接使用
@@ -8,7 +8,6 @@
8
8
 
9
9
  import path from 'path';
10
10
  import fs from 'fs-extra';
11
- import os from 'os';
12
11
  import { logger } from '../utils/logger.js';
13
12
 
14
13
  class PackageInstaller {
@@ -27,11 +26,12 @@ class PackageInstaller {
27
26
 
28
27
  // 2. 检测是否在中国地区(基于时区)
29
28
  const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
30
- const isChina = timezone?.includes('Shanghai') ||
31
- timezone?.includes('Hong_Kong') ||
32
- timezone?.includes('Beijing') ||
33
- timezone?.includes('Chongqing');
34
-
29
+ const isChina =
30
+ timezone?.includes('Shanghai') ||
31
+ timezone?.includes('Hong_Kong') ||
32
+ timezone?.includes('Beijing') ||
33
+ timezone?.includes('Chongqing');
34
+
35
35
  if (isChina) {
36
36
  // 中国地区使用淘宝镜像
37
37
  const chinaRegistry = 'https://registry.npmmirror.com';
@@ -43,7 +43,6 @@ class PackageInstaller {
43
43
  const defaultRegistry = 'https://registry.npmjs.org/';
44
44
  logger.debug(`[PackageInstaller] Using default registry: ${defaultRegistry}`);
45
45
  return defaultRegistry;
46
-
47
46
  } catch (error) {
48
47
  logger.warn(`[PackageInstaller] Failed to detect optimal registry: ${error.message}`);
49
48
  return 'https://registry.npmjs.org/';
@@ -58,27 +57,27 @@ class PackageInstaller {
58
57
  * @param {number} options.timeout - 超时时间(毫秒)
59
58
  * @returns {Promise<Object>} 安装结果
60
59
  */
61
- static async install({ workingDir, dependencies, timeout = 300000 }) {
60
+ static async install({ workingDir, dependencies }) {
62
61
  const startTime = Date.now();
63
-
62
+
64
63
  // 构建依赖列表字符串用于日志
65
64
  const depsList = this.buildDependenciesList(dependencies);
66
-
65
+
67
66
  logger.info(`[PackageInstaller] Starting installation via Arborist: [${depsList}]`);
68
67
  logger.debug(`[PackageInstaller] Working directory: ${workingDir}`);
69
-
68
+
70
69
  try {
71
70
  // 确保工作目录存在
72
71
  await fs.ensureDir(workingDir);
73
-
72
+
74
73
  // 读取或创建package.json
75
74
  const packageJsonPath = path.join(workingDir, 'package.json');
76
75
  let manifest;
77
-
76
+
78
77
  try {
79
78
  const content = await fs.readFile(packageJsonPath, 'utf8');
80
79
  manifest = JSON.parse(content);
81
- logger.debug(`[PackageInstaller] Found existing package.json`);
80
+ logger.debug('[PackageInstaller] Found existing package.json');
82
81
  } catch (error) {
83
82
  // package.json不存在,创建默认的
84
83
  manifest = {
@@ -88,49 +87,49 @@ class PackageInstaller {
88
87
  private: true,
89
88
  dependencies: {}
90
89
  };
91
- logger.debug(`[PackageInstaller] Creating new package.json`);
90
+ logger.debug('[PackageInstaller] Creating new package.json');
92
91
  }
93
-
92
+
94
93
  // 规范化依赖格式
95
94
  const normalizedDeps = this.normalizeDependencies(dependencies);
96
-
95
+
97
96
  // 更新 manifest 的 dependencies
98
97
  manifest.dependencies = { ...manifest.dependencies, ...normalizedDeps };
99
98
  await fs.writeFile(packageJsonPath, JSON.stringify(manifest, null, 2));
100
-
99
+
101
100
  logger.debug(`[PackageInstaller] Installing ${Object.keys(normalizedDeps).length} dependencies using Arborist`);
102
-
101
+
103
102
  // 使用 Arborist 安装所有依赖(包括传递依赖)
104
103
  // @npmcli/arborist 是 CommonJS 模块,使用 createRequire 导入
105
104
  const { createRequire } = await import('module');
106
105
  const require = createRequire(import.meta.url);
107
106
  const { Arborist } = require('@npmcli/arborist');
108
-
107
+
109
108
  // 获取最优的registry
110
109
  const registry = await this.getOptimalRegistry();
111
-
110
+
112
111
  const arb = new Arborist({
113
112
  path: workingDir,
114
- registry: registry,
115
- cache: path.join(os.homedir(), '.npm', '_cacache'),
116
- save: false, // 不需要再次更新 package.json
117
- omit: [], // 安装所有依赖
113
+ registry,
114
+ cache: path.join(process.env.HOME, '.npm', '_cacache'),
115
+ save: false, // 不需要再次更新 package.json
116
+ omit: [], // 安装所有依赖
118
117
  force: false,
119
118
  fund: false,
120
119
  audit: false,
121
- legacyPeerDeps: true // 兼容旧包
120
+ legacyPeerDeps: true // 兼容旧包
122
121
  });
123
-
122
+
124
123
  // 执行安装 - Arborist 会自动处理所有传递依赖
125
124
  await arb.reify({
126
125
  add: Object.entries(normalizedDeps).map(([name, version]) => `${name}@${version}`)
127
126
  });
128
-
127
+
129
128
  // 加载实际安装的包信息
130
129
  const tree = await arb.loadActual();
131
130
  const installedPackages = [];
132
131
  const installResults = {};
133
-
132
+
134
133
  // 收集安装的包信息
135
134
  for (const [name, node] of tree.children) {
136
135
  if (node && node.package) {
@@ -143,28 +142,27 @@ class PackageInstaller {
143
142
  logger.debug(`[PackageInstaller] ✓ ${name}@${node.package.version} installed`);
144
143
  }
145
144
  }
146
-
145
+
147
146
  const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
148
147
  logger.info(`[PackageInstaller] Installation completed successfully in ${elapsed}s`);
149
148
  logger.info(`[PackageInstaller] Installed ${installedPackages.length} packages with all transitive dependencies`);
150
-
149
+
151
150
  return {
152
151
  success: true,
153
- elapsed: elapsed,
154
- manifest: manifest,
152
+ elapsed,
153
+ manifest,
155
154
  environment: 'arborist',
156
- installedPackages: installedPackages,
155
+ installedPackages,
157
156
  results: installResults
158
157
  };
159
-
160
158
  } catch (error) {
161
159
  const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
162
160
  logger.error(`[PackageInstaller] Installation failed after ${elapsed}s: ${error.message}`);
163
-
161
+
164
162
  throw new Error(`Arborist installation failed: ${error.message}`);
165
163
  }
166
164
  }
167
-
165
+
168
166
  /**
169
167
  * 构建依赖列表字符串用于日志
170
168
  * @param {Object|Array} dependencies - 依赖
@@ -172,7 +170,7 @@ class PackageInstaller {
172
170
  */
173
171
  static buildDependenciesList(dependencies) {
174
172
  if (!dependencies) return '';
175
-
173
+
176
174
  if (typeof dependencies === 'object' && !Array.isArray(dependencies)) {
177
175
  // 对象格式:{"package": "version"}
178
176
  return Object.keys(dependencies)
@@ -182,10 +180,10 @@ class PackageInstaller {
182
180
  // 数组格式:["package@version"]
183
181
  return dependencies.join(', ');
184
182
  }
185
-
183
+
186
184
  return String(dependencies);
187
185
  }
188
-
186
+
189
187
  /**
190
188
  * 规范化依赖格式为对象
191
189
  * @param {Object|Array} dependencies - 原始依赖
@@ -193,12 +191,12 @@ class PackageInstaller {
193
191
  */
194
192
  static normalizeDependencies(dependencies) {
195
193
  if (!dependencies) return {};
196
-
194
+
197
195
  if (typeof dependencies === 'object' && !Array.isArray(dependencies)) {
198
196
  // 已经是对象格式
199
197
  return dependencies;
200
198
  }
201
-
199
+
202
200
  if (Array.isArray(dependencies)) {
203
201
  // 数组格式转对象
204
202
  const normalized = {};
@@ -218,10 +216,10 @@ class PackageInstaller {
218
216
  }
219
217
  return normalized;
220
218
  }
221
-
219
+
222
220
  return {};
223
221
  }
224
-
222
+
225
223
  /**
226
224
  * 检查包是否已安装
227
225
  * @param {string} workingDir - 工作目录
@@ -230,10 +228,10 @@ class PackageInstaller {
230
228
  */
231
229
  static async isPackageInstalled(workingDir, packageName) {
232
230
  try {
233
- const packagePath = packageName.startsWith('@')
231
+ const packagePath = packageName.startsWith('@')
234
232
  ? path.join(workingDir, 'node_modules', ...packageName.split('/'))
235
233
  : path.join(workingDir, 'node_modules', packageName);
236
-
234
+
237
235
  const packageJsonPath = path.join(packagePath, 'package.json');
238
236
  await fs.access(packageJsonPath);
239
237
  return true;
@@ -241,7 +239,7 @@ class PackageInstaller {
241
239
  return false;
242
240
  }
243
241
  }
244
-
242
+
245
243
  /**
246
244
  * 获取已安装包的信息
247
245
  * @param {string} workingDir - 工作目录
@@ -253,7 +251,7 @@ class PackageInstaller {
253
251
  const packagePath = packageName.startsWith('@')
254
252
  ? path.join(workingDir, 'node_modules', ...packageName.split('/'))
255
253
  : path.join(workingDir, 'node_modules', packageName);
256
-
254
+
257
255
  const packageJsonPath = path.join(packagePath, 'package.json');
258
256
  const content = await fs.readFile(packageJsonPath, 'utf8');
259
257
  return JSON.parse(content);
@@ -264,4 +262,3 @@ class PackageInstaller {
264
262
  }
265
263
 
266
264
  export default PackageInstaller;
267
-