@becrafter/prompt-manager 0.1.21 → 0.1.31

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 +23 -23
  2. package/packages/resources/tools/agent-browser/README.md +640 -0
  3. package/packages/resources/tools/agent-browser/agent-browser.tool.js +1388 -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 +98 -13
  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
@@ -1,537 +1,540 @@
1
1
  import crypto from 'crypto';
2
- import fs from 'fs';
3
- import os from 'os';
4
- import path from 'path';
2
+ import { logger } from './logger.js';
5
3
  import fse from 'fs-extra';
6
- import yaml from 'js-yaml';
7
4
  import { fileURLToPath } from 'url';
8
- import { logger } from './logger.js';
5
+ import path from 'path';
6
+ import fs from 'fs';
7
+ import yaml from 'js-yaml';
8
+ import { config } from './config.js';
9
9
 
10
- export const GROUP_META_FILENAME = '.groupmeta.json';
11
10
  // 允许中文、空格、emoji 等字符,禁止路径分隔符及常见非法文件名字符
12
11
  export const GROUP_NAME_REGEX = /^(?![.]{1,2}$)[^\\/:*?"<>|\r\n]{1,64}$/;
12
+ export const GROUP_META_FILENAME = 'group.meta.json';
13
13
 
14
14
  // 获取当前文件的目录路径
15
15
  const __filename = fileURLToPath(import.meta.url);
16
16
  const __dirname = path.dirname(__filename);
17
17
  const projectRoot = path.resolve(__dirname, '../../..');
18
18
  const examplesPromptsRoot = path.join(projectRoot, 'examples', 'prompts');
19
- const promptsDir = path.join(os.homedir(), '.prompt-manager', 'prompts');
19
+ const promptsDir = config.getPromptsDir();
20
20
 
21
21
  let _promptManager;
22
22
 
23
23
  export class Util {
24
- /**
25
- * 检查并初始化prompts目录,如果目录为空则从示例目录复制内容
26
- * @returns
27
- */
28
- async seedPromptsIfEmpty() {
29
- try {
30
- const entries = await fse.readdir(promptsDir);
31
- if (entries.length > 0) {
32
- return;
33
- }
34
- } catch (error) {
35
- logger.warn('读取Prompts目录失败,尝试同步示例数据:', error.message);
36
- }
37
-
38
- try {
39
- const exists = await fse.pathExists(examplesPromptsRoot);
40
- if (!exists) {
41
- return;
42
- }
43
- await fse.copy(examplesPromptsRoot, promptsDir, {
44
- overwrite: false,
45
- errorOnExist: false,
46
- recursive: true
47
- });
48
- logger.info(`已将示例Prompts同步到 ${promptsDir}`);
49
- } catch (error) {
50
- logger.warn('同步示例Prompts失败:', error.message);
51
- }
24
+ /**
25
+ * 检查并初始化prompts目录,如果目录为空则从示例目录复制内容
26
+ * @returns
27
+ */
28
+ async seedPromptsIfEmpty() {
29
+ try {
30
+ const entries = await fse.readdir(promptsDir);
31
+ if (entries.length > 0) {
32
+ return;
33
+ }
34
+ } catch (error) {
35
+ logger.warn('读取Prompts目录失败,尝试同步示例数据:', error.message);
52
36
  }
53
37
 
54
- /**
55
- * 初始化内置配置(如果不存在)
56
- * 同步内置的模型配置和模板到用户配置目录
57
- */
58
- async seedBuiltInConfigsIfEmpty() {
59
- try {
60
- const userConfigDir = path.join(os.homedir(), '.prompt-manager', 'configs');
61
- const builtInConfigsDir = this.getBuiltInConfigsDir();
62
-
63
- // 检查内置配置目录是否存在
64
- const builtInExists = await fse.pathExists(builtInConfigsDir);
65
- if (!builtInExists) {
66
- logger.debug('内置配置目录不存在,跳过同步');
67
- return;
68
- }
69
-
70
- // 确保用户配置目录存在
71
- await fse.ensureDir(userConfigDir);
72
-
73
- // 同步模型配置
74
- const modelsSrc = path.join(builtInConfigsDir, 'models');
75
- const modelsDst = path.join(userConfigDir, 'models');
76
- if (await fse.pathExists(modelsSrc)) {
77
- await fse.copy(modelsSrc, modelsDst, {
78
- overwrite: false,
79
- errorOnExist: false,
80
- recursive: true
81
- });
82
- logger.info(`已同步内置模型配置到 ${modelsDst}`);
83
- }
84
-
85
- // 同步模板配置
86
- const templatesSrc = path.join(builtInConfigsDir, 'templates');
87
- const templatesDst = path.join(userConfigDir, 'templates');
88
- if (await fse.pathExists(templatesSrc)) {
89
- await fse.copy(templatesSrc, templatesDst, {
90
- overwrite: false,
91
- errorOnExist: false,
92
- recursive: true
93
- });
94
- logger.info(`已同步内置模板配置到 ${templatesDst}`);
95
- }
96
-
97
- } catch (error) {
98
- logger.warn('同步内置配置失败:', error.message);
99
- }
38
+ try {
39
+ const exists = await fse.pathExists(examplesPromptsRoot);
40
+ if (!exists) {
41
+ return;
42
+ }
43
+ await fse.copy(examplesPromptsRoot, promptsDir, {
44
+ overwrite: false,
45
+ errorOnExist: false,
46
+ recursive: true
47
+ });
48
+ logger.info(`已将示例Prompts同步到 ${promptsDir}`);
49
+ } catch (error) {
50
+ logger.warn('同步示例Prompts失败:', error.message);
100
51
  }
52
+ }
53
+
54
+ /**
55
+ * 初始化内置配置(如果不存在)
56
+ * 同步内置的模型配置和模板到用户配置目录
57
+ */
58
+ async seedBuiltInConfigsIfEmpty() {
59
+ try {
60
+ const userConfigDir = config.getConfigsDir();
61
+ const builtInConfigsDir = this.getBuiltInConfigsDir();
62
+
63
+ // 检查内置配置目录是否存在
64
+ const builtInExists = await fse.pathExists(builtInConfigsDir);
65
+ if (!builtInExists) {
66
+ logger.debug('内置配置目录不存在,跳过同步');
67
+ return;
68
+ }
101
69
 
102
- /**
103
- * 生成文件唯一标识码
104
- * @param {*} relativePath
105
- * @returns
106
- */
107
- generateUniqueId(relativePath) {
108
- const hash = crypto.createHash('sha256');
109
- hash.update(relativePath);
110
- const hashHex = hash.digest('hex');
111
- return hashHex.substring(0, 8);
112
- }
70
+ // 确保用户配置目录存在
71
+ await fse.ensureDir(userConfigDir);
72
+
73
+ // 同步模型配置
74
+ const modelsSrc = path.join(builtInConfigsDir, 'models');
75
+ const modelsDst = path.join(userConfigDir, 'models');
76
+ if (await fse.pathExists(modelsSrc)) {
77
+ await fse.copy(modelsSrc, modelsDst, {
78
+ overwrite: false,
79
+ errorOnExist: false,
80
+ recursive: true
81
+ });
82
+ logger.info(`已同步内置模型配置到 ${modelsDst}`);
83
+ }
113
84
 
114
- /**
115
- * 从文件中读取提示词
116
- * @returns
117
- */
118
- getPromptsFromFiles() {
119
- const prompts = [];
120
-
121
- const traverseDir = (currentPath, relativeDir = '', inheritedEnabled = true) => {
122
- let currentEnabled = inheritedEnabled;
123
- if (relativeDir) {
124
- const meta = this.readGroupMeta(currentPath);
125
- currentEnabled = currentEnabled && (meta.enabled !== false);
126
- }
85
+ // 同步模板配置
86
+ const templatesSrc = path.join(builtInConfigsDir, 'templates');
87
+ const templatesDst = path.join(userConfigDir, 'templates');
88
+ if (await fse.pathExists(templatesSrc)) {
89
+ await fse.copy(templatesSrc, templatesDst, {
90
+ overwrite: false,
91
+ errorOnExist: false,
92
+ recursive: true
93
+ });
94
+ logger.info(`已同步内置模板配置到 ${templatesDst}`);
95
+ }
96
+ } catch (error) {
97
+ logger.warn('同步内置配置失败:', error.message);
98
+ }
99
+ }
100
+
101
+ /**
102
+ * 生成文件唯一标识码
103
+ * @param {*} relativePath
104
+ * @returns
105
+ */
106
+ generateUniqueId(relativePath) {
107
+ const hash = crypto.createHash('sha256');
108
+ hash.update(relativePath);
109
+ const hashHex = hash.digest('hex');
110
+ return hashHex.substring(0, 8);
111
+ }
112
+
113
+ /**
114
+ * 从文件中读取提示词
115
+ * @returns
116
+ */
117
+ getPromptsFromFiles() {
118
+ const prompts = [];
119
+
120
+ const traverseDir = (currentPath, relativeDir = '', inheritedEnabled = true) => {
121
+ let currentEnabled = inheritedEnabled;
122
+ if (relativeDir) {
123
+ const meta = this.readGroupMeta(currentPath);
124
+ currentEnabled = currentEnabled && meta.enabled !== false;
125
+ }
127
126
 
127
+ try {
128
+ const entries = fs.readdirSync(currentPath, { withFileTypes: true });
129
+ for (const entry of entries) {
130
+ const fullPath = path.join(currentPath, entry.name);
131
+ if (entry.isDirectory()) {
132
+ const childRelativePath = relativeDir ? `${relativeDir}/${entry.name}` : entry.name;
133
+ traverseDir(fullPath, childRelativePath, currentEnabled);
134
+ } else if (entry.isFile() && entry.name.endsWith('.yaml')) {
128
135
  try {
129
- const entries = fs.readdirSync(currentPath, { withFileTypes: true });
130
- for (const entry of entries) {
131
- const fullPath = path.join(currentPath, entry.name);
132
- if (entry.isDirectory()) {
133
- const childRelativePath = relativeDir ? `${relativeDir}/${entry.name}` : entry.name;
134
- traverseDir(fullPath, childRelativePath, currentEnabled);
135
- } else if (entry.isFile() && entry.name.endsWith('.yaml')) {
136
- try {
137
- const fileContent = fs.readFileSync(fullPath, 'utf8');
138
- const prompt = yaml.load(fileContent);
139
- if (prompt && prompt.name) {
140
- const relativePath = path.relative(promptsDir, fullPath);
141
- const normalizedRelativePath = relativePath.split(path.sep).join('/');
142
- const relativeDirForFile = path.dirname(normalizedRelativePath);
143
- const topLevelGroup = relativeDirForFile && relativeDirForFile !== '.' ? relativeDirForFile.split('/')[0] : (prompt.group || 'default');
144
- const groupPath = relativeDirForFile && relativeDirForFile !== '.' ? relativeDirForFile : topLevelGroup;
145
- prompts.push({
146
- ...prompt,
147
- uniqueId: this.generateUniqueId(prompt.name + '.yaml'),
148
- fileName: entry.name,
149
- relativePath: normalizedRelativePath,
150
- group: topLevelGroup,
151
- groupPath,
152
- groupEnabled: currentEnabled
153
- });
154
- }
155
- } catch (error) {
156
- logger.error(`Error processing file ${fullPath}:`, error);
157
- }
158
- }
159
- }
136
+ const fileContent = fs.readFileSync(fullPath, 'utf8');
137
+ const prompt = yaml.load(fileContent);
138
+ if (prompt && prompt.name) {
139
+ const relativePath = path.relative(promptsDir, fullPath);
140
+ const normalizedRelativePath = relativePath.split(path.sep).join('/');
141
+ const relativeDirForFile = path.dirname(normalizedRelativePath);
142
+ const topLevelGroup =
143
+ relativeDirForFile && relativeDirForFile !== '.'
144
+ ? relativeDirForFile.split('/')[0]
145
+ : prompt.group || 'default';
146
+ const groupPath = relativeDirForFile && relativeDirForFile !== '.' ? relativeDirForFile : topLevelGroup;
147
+ prompts.push({
148
+ ...prompt,
149
+ uniqueId: this.generateUniqueId(`${prompt.name}.yaml`),
150
+ fileName: entry.name,
151
+ relativePath: normalizedRelativePath,
152
+ group: topLevelGroup,
153
+ groupPath,
154
+ groupEnabled: currentEnabled
155
+ });
156
+ }
160
157
  } catch (error) {
161
- logger.error(`Error reading directory ${currentPath}:`, error);
158
+ logger.error(`Error processing file ${fullPath}:`, error);
162
159
  }
160
+ }
163
161
  }
162
+ } catch (error) {
163
+ logger.error(`Error reading directory ${currentPath}:`, error);
164
+ }
165
+ };
164
166
 
165
- traverseDir(promptsDir);
166
- return prompts;
167
- }
168
-
169
- /**
170
- * 计算搜索关键词与prompt的相似度得分
171
- * @param {string} searchTerm - 搜索关键词
172
- * @param {Object} prompt - prompt对象
173
- * @returns {number} 相似度得分 (0-100)
174
- */
175
- calculateSimilarityScore(searchTerm, prompt) {
176
- let totalScore = 0;
177
- const searchLower = searchTerm ? searchTerm.toLowerCase() : '';
178
-
179
- // 搜索字段权重配置(专注于内容搜索,不包含ID检索)
180
- const fieldWeights = {
181
- name: 60, // 名称权重高,是主要匹配字段
182
- description: 40 // 描述权重适中,是辅助匹配字段
183
- };
184
-
185
- // 计算name匹配得分
186
- if (prompt && prompt.name && typeof prompt.name === 'string') {
187
- const nameScore = util.getStringMatchScore(searchLower, prompt.name.toLowerCase());
188
- totalScore += nameScore * fieldWeights.name;
189
- }
190
-
191
- // 计算description匹配得分
192
- if (prompt.description) {
193
- const descScore = util.getStringMatchScore(searchLower, prompt.description.toLowerCase());
194
- totalScore += descScore * fieldWeights.description;
195
- }
167
+ traverseDir(promptsDir);
168
+ return prompts;
169
+ }
170
+
171
+ /**
172
+ * 计算搜索关键词与prompt的相似度得分
173
+ * @param {string} searchTerm - 搜索关键词
174
+ * @param {Object} prompt - prompt对象
175
+ * @returns {number} 相似度得分 (0-100)
176
+ */
177
+ calculateSimilarityScore(searchTerm, prompt) {
178
+ let totalScore = 0;
179
+ const searchLower = searchTerm ? searchTerm.toLowerCase() : '';
180
+
181
+ // 搜索字段权重配置(专注于内容搜索,不包含ID检索)
182
+ const fieldWeights = {
183
+ name: 60, // 名称权重高,是主要匹配字段
184
+ description: 40 // 描述权重适中,是辅助匹配字段
185
+ };
196
186
 
197
- // 标准化得分到0-100范围
198
- const maxPossibleScore = Object.values(fieldWeights).reduce((sum, weight) => sum + weight, 0);
199
- return Math.round((totalScore / maxPossibleScore) * 100);
187
+ // 计算name匹配得分
188
+ if (prompt && prompt.name && typeof prompt.name === 'string') {
189
+ const nameScore = util.getStringMatchScore(searchLower, prompt.name.toLowerCase());
190
+ totalScore += nameScore * fieldWeights.name;
200
191
  }
201
192
 
202
- /**
203
- * 计算两个字符串的匹配得分
204
- * @param {string} search - 搜索词 (已转小写)
205
- * @param {string} target - 目标字符串 (已转小写)
206
- * @returns {number} 匹配得分 (0-1)
207
- */
208
- getStringMatchScore(search, target) {
209
- if (!search || !target) return 0;
210
-
211
- // 完全匹配得分最高
212
- if (target === search) return 1.0;
213
-
214
- // 完全包含得分较高
215
- if (target.includes(search)) return 0.8;
216
-
217
- // 部分词匹配
218
- const searchWords = search.split(/\s+/).filter(word => word.length > 0);
219
- const targetWords = target.split(/\s+/).filter(word => word.length > 0);
220
-
221
- let matchedWords = 0;
222
- for (const searchWord of searchWords) {
223
- for (const targetWord of targetWords) {
224
- if (targetWord.includes(searchWord) || searchWord.includes(targetWord)) {
225
- matchedWords++;
226
- break;
227
- }
228
- }
229
- }
193
+ // 计算description匹配得分
194
+ if (prompt.description) {
195
+ const descScore = util.getStringMatchScore(searchLower, prompt.description.toLowerCase());
196
+ totalScore += descScore * fieldWeights.description;
197
+ }
230
198
 
231
- if (searchWords.length > 0) {
232
- const wordMatchRatio = matchedWords / searchWords.length;
233
- return wordMatchRatio * 0.6; // 部分词匹配得分
199
+ // 标准化得分到0-100范围
200
+ const maxPossibleScore = Object.values(fieldWeights).reduce((sum, weight) => sum + weight, 0);
201
+ return Math.round((totalScore / maxPossibleScore) * 100);
202
+ }
203
+
204
+ /**
205
+ * 计算两个字符串的匹配得分
206
+ * @param {string} search - 搜索词 (已转小写)
207
+ * @param {string} target - 目标字符串 (已转小写)
208
+ * @returns {number} 匹配得分 (0-1)
209
+ */
210
+ getStringMatchScore(search, target) {
211
+ if (!search || !target) return 0;
212
+
213
+ // 完全匹配得分最高
214
+ if (target === search) return 1.0;
215
+
216
+ // 完全包含得分较高
217
+ if (target.includes(search)) return 0.8;
218
+
219
+ // 部分词匹配
220
+ const searchWords = search.split(/\s+/).filter(word => word.length > 0);
221
+ const targetWords = target.split(/\s+/).filter(word => word.length > 0);
222
+
223
+ let matchedWords = 0;
224
+ for (const searchWord of searchWords) {
225
+ for (const targetWord of targetWords) {
226
+ if (targetWord.includes(searchWord) || searchWord.includes(targetWord)) {
227
+ matchedWords++;
228
+ break;
234
229
  }
235
-
236
- return 0;
230
+ }
237
231
  }
238
232
 
239
- /**
240
- * 更新目录元数据
241
- * @param {*} dir
242
- * @param {*} meta
243
- */
244
- writeGroupMeta(dir, meta = {}) {
245
- const metaPath = this.getGroupMetaPath(dir);
246
- const data = {
247
- enabled: meta.enabled !== false
248
- };
249
- fs.writeFileSync(metaPath, JSON.stringify(data, null, 2), 'utf8');
233
+ if (searchWords.length > 0) {
234
+ const wordMatchRatio = matchedWords / searchWords.length;
235
+ return wordMatchRatio * 0.6; // 部分词匹配得分
250
236
  }
251
237
 
252
- /**
253
- * 验证目录名称是否有效
254
- * @param {*} name
255
- * @returns
256
- */
257
- isValidGroupName(name) {
258
- if (typeof name !== 'string') return false;
259
- return GROUP_NAME_REGEX.test(name);
238
+ return 0;
239
+ }
240
+
241
+ /**
242
+ * 更新目录元数据
243
+ * @param {*} dir
244
+ * @param {*} meta
245
+ */
246
+ writeGroupMeta(dir, meta = {}) {
247
+ const metaPath = this.getGroupMetaPath(dir);
248
+ const data = {
249
+ enabled: meta.enabled !== false
250
+ };
251
+ fs.writeFileSync(metaPath, JSON.stringify(data, null, 2), 'utf8');
252
+ }
253
+
254
+ /**
255
+ * 验证目录名称是否有效
256
+ * @param {*} name
257
+ * @returns
258
+ */
259
+ isValidGroupName(name) {
260
+ if (typeof name !== 'string') return false;
261
+ return GROUP_NAME_REGEX.test(name);
262
+ }
263
+
264
+ /**
265
+ * 验证目录名称
266
+ * @param {*} relativePath
267
+ * @returns
268
+ */
269
+ validateGroupPath(relativePath) {
270
+ if (!relativePath || typeof relativePath !== 'string') {
271
+ return null;
260
272
  }
261
-
262
- /**
263
- * 验证目录名称
264
- * @param {*} relativePath
265
- * @returns
266
- */
267
- validateGroupPath(relativePath) {
268
- if (!relativePath || typeof relativePath !== 'string') {
269
- return null;
270
- }
271
- const segments = relativePath.split('/').filter(Boolean);
272
- if (!segments.length) {
273
- return null;
274
- }
275
- for (const segment of segments) {
276
- if (!this.isValidGroupName(segment)) {
277
- return null;
278
- }
279
- }
280
- return segments;
273
+ const segments = relativePath.split('/').filter(Boolean);
274
+ if (!segments.length) {
275
+ return null;
281
276
  }
282
-
283
- /**
284
- *
285
- * @param {*} relativePath
286
- * @returns
287
- */
288
- resolveGroupDir(relativePath) {
289
- const segments = this.validateGroupPath(relativePath);
290
- if (!segments) return null;
291
- const targetPath = path.resolve(promptsDir, ...segments);
292
- const normalizedPromptsDir = path.resolve(promptsDir);
293
- if (!targetPath.startsWith(normalizedPromptsDir)) {
294
- return null;
295
- }
296
- return { dir: targetPath, segments };
277
+ for (const segment of segments) {
278
+ if (!this.isValidGroupName(segment)) {
279
+ return null;
280
+ }
297
281
  }
298
-
299
- /**
300
- * 获取目录元数据文件路径
301
- * @param {*} dir
302
- * @returns
303
- */
304
- getGroupMetaPath(dir) {
305
- return path.join(dir, GROUP_META_FILENAME);
282
+ return segments;
283
+ }
284
+
285
+ /**
286
+ *
287
+ * @param {*} relativePath
288
+ * @returns
289
+ */
290
+ resolveGroupDir(relativePath) {
291
+ const segments = this.validateGroupPath(relativePath);
292
+ if (!segments) return null;
293
+ const targetPath = path.resolve(promptsDir, ...segments);
294
+ const normalizedPromptsDir = path.resolve(promptsDir);
295
+ if (!targetPath.startsWith(normalizedPromptsDir)) {
296
+ return null;
306
297
  }
307
-
308
- /**
309
- * 读取目录元数据
310
- * @param {*} dir
311
- * @returns
312
- */
313
- readGroupMeta(dir) {
314
- try {
315
- const metaPath = this.getGroupMetaPath(dir);
316
- if (!fs.existsSync(metaPath)) {
317
- return { enabled: true };
318
- }
319
- const raw = fs.readFileSync(metaPath, 'utf8');
320
- const data = JSON.parse(raw);
321
- return {
322
- enabled: data.enabled !== false
323
- };
324
- } catch (error) {
325
- logger.warn('读取类目元数据失败:', error);
326
- return { enabled: true };
327
- }
298
+ return { dir: targetPath, segments };
299
+ }
300
+
301
+ /**
302
+ * 获取目录元数据文件路径
303
+ * @param {*} dir
304
+ * @returns
305
+ */
306
+ getGroupMetaPath(dir) {
307
+ return path.join(dir, GROUP_META_FILENAME);
308
+ }
309
+
310
+ /**
311
+ * 读取目录元数据
312
+ * @param {*} dir
313
+ * @returns
314
+ */
315
+ readGroupMeta(dir) {
316
+ try {
317
+ const metaPath = this.getGroupMetaPath(dir);
318
+ if (!fs.existsSync(metaPath)) {
319
+ return { enabled: true };
320
+ }
321
+ const raw = fs.readFileSync(metaPath, 'utf8');
322
+ const data = JSON.parse(raw);
323
+ return {
324
+ enabled: data.enabled !== false
325
+ };
326
+ } catch (error) {
327
+ logger.warn('读取类目元数据失败:', error);
328
+ return { enabled: true };
328
329
  }
329
-
330
- /**
331
- * 获取所有分组(直接从目录读取)
332
- * @param {*} baseDir
333
- * @param {*} relativePath
334
- * @returns
335
- */
336
- buildGroupTree(baseDir, relativePath = '') {
337
- const nodes = [];
338
- try {
339
- const entries = fs.readdirSync(baseDir, { withFileTypes: true });
340
- for (const entry of entries) {
341
- if (!entry.isDirectory()) continue;
342
- if (entry.name.startsWith('.')) continue;
343
- const childRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
344
- const childDir = path.join(baseDir, entry.name);
345
- const children = this.buildGroupTree(childDir, childRelativePath);
346
- const meta = this.readGroupMeta(childDir);
347
- nodes.push({
348
- name: entry.name,
349
- path: childRelativePath,
350
- children,
351
- enabled: meta.enabled !== false
352
- });
353
- }
354
- } catch (error) {
355
- logger.error('读取分组目录失败:', error);
356
- }
357
- nodes.sort((a, b) => a.name.localeCompare(b.name, 'zh-CN'));
358
- return nodes;
330
+ }
331
+
332
+ /**
333
+ * 获取所有分组(直接从目录读取)
334
+ * @param {*} baseDir
335
+ * @param {*} relativePath
336
+ * @returns
337
+ */
338
+ buildGroupTree(baseDir, relativePath = '') {
339
+ const nodes = [];
340
+ try {
341
+ const entries = fs.readdirSync(baseDir, { withFileTypes: true });
342
+ for (const entry of entries) {
343
+ if (!entry.isDirectory()) continue;
344
+ if (entry.name.startsWith('.')) continue;
345
+ const childRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
346
+ const childDir = path.join(baseDir, entry.name);
347
+ const children = this.buildGroupTree(childDir, childRelativePath);
348
+ const meta = this.readGroupMeta(childDir);
349
+ nodes.push({
350
+ name: entry.name,
351
+ path: childRelativePath,
352
+ children,
353
+ enabled: meta.enabled !== false
354
+ });
355
+ }
356
+ } catch (error) {
357
+ logger.error('读取分组目录失败:', error);
359
358
  }
359
+ nodes.sort((a, b) => a.name.localeCompare(b.name, 'zh-CN'));
360
+ return nodes;
361
+ }
362
+
363
+ async processPromptContent(prompt, args) {
364
+ let content = '';
365
+
366
+ if (prompt.messages && Array.isArray(prompt.messages)) {
367
+ const userMessages = prompt.messages.filter(msg => msg.role === 'user');
368
+
369
+ for (const message of userMessages) {
370
+ if (message.content && typeof message.content.text === 'string') {
371
+ let text = message.content.text;
360
372
 
361
- async processPromptContent(prompt, args) {
362
- let content = '';
363
-
364
- if (prompt.messages && Array.isArray(prompt.messages)) {
365
- const userMessages = prompt.messages.filter(msg => msg.role === 'user');
366
-
367
- for (const message of userMessages) {
368
- if (message.content && typeof message.content.text === 'string') {
369
- let text = message.content.text;
370
-
371
- // Replace arguments
372
- if (args) {
373
- for (const [key, value] of Object.entries(args)) {
374
- const placeholder = new RegExp(`{{${key}}}`, 'g');
375
- text = text.replace(placeholder, String(value));
376
- }
373
+ // Replace arguments
374
+ if (args) {
375
+ for (const [key, value] of Object.entries(args)) {
376
+ const placeholder = new RegExp(`{{${key}}}`, 'g');
377
+ text = text.replace(placeholder, String(value));
377
378
  }
378
-
379
- content += text + '\n\n';
380
379
  }
380
+
381
+ content += `${text}\n\n`;
381
382
  }
382
383
  }
383
-
384
- return content.trim();
385
384
  }
386
385
 
387
- getWebUiRoot() {
388
- const __filename = fileURLToPath(import.meta.url);
389
- const __dirname = path.dirname(__filename);
390
-
391
- console.log('🔍 getWebUiRoot() called from:', __dirname);
386
+ return content.trim();
387
+ }
392
388
 
393
- // 检查是否在 Electron 环境中运行
394
- const isElectron = typeof process !== 'undefined' &&
395
- process.versions &&
396
- process.versions.electron;
389
+ getWebUiRoot() {
390
+ const __filename = fileURLToPath(import.meta.url);
391
+ const __dirname = path.dirname(__filename);
397
392
 
398
- // 检查是否是打包应用
399
- if (isElectron && process.resourcesPath) {
400
- const ourAppAsar = path.join(process.resourcesPath, 'app.asar');
401
- if (fs.existsSync(ourAppAsar)) {
402
- const asarPath = path.join(process.resourcesPath, 'app.asar', 'web');
403
- console.log('📦 Using Electron ASAR path:', asarPath);
404
- return asarPath;
405
- }
406
- }
393
+ console.log('🔍 getWebUiRoot() called from:', __dirname);
407
394
 
408
- // 在开发环境中,web UI 位于项目目录中的 packages/web
409
- const devWebPath = path.join(__dirname, '..', '..', 'web');
410
- console.log('🔍 Checking dev path:', devWebPath);
411
- if (this._pathExistsSync(devWebPath)) {
412
- console.log('✅ Using dev environment path:', devWebPath);
413
- return devWebPath;
414
- }
395
+ // 检查是否在 Electron 环境中运行
396
+ const isElectron = typeof process !== 'undefined' && process.versions && process.versions.electron;
415
397
 
416
- // 在 npm 包环境中,查找 packages/web
417
- // 从当前文件位置向上查找可能的包根目录
418
- let currentDir = __dirname;
419
- let searchDepth = 0;
420
- const maxDepth = 5;
421
-
422
- while (searchDepth < maxDepth) {
423
- // 检查 packages/web
424
- const webPath = path.join(currentDir, 'packages', 'web');
425
- console.log('🔍 Checking npm package path:', webPath);
426
- if (this._pathExistsSync(webPath)) {
427
- console.log('✅ Found web UI in npm package:', webPath);
428
- return webPath;
429
- }
398
+ // 检查是否是打包应用
399
+ if (isElectron && process.resourcesPath) {
400
+ const ourAppAsar = path.join(process.resourcesPath, 'app.asar');
401
+ if (fs.existsSync(ourAppAsar)) {
402
+ const asarPath = path.join(process.resourcesPath, 'app.asar', 'web');
403
+ console.log('📦 Using Electron ASAR path:', asarPath);
404
+ return asarPath;
405
+ }
406
+ }
430
407
 
431
- // 检查直接的 web 目录(某些打包场景)
432
- const altWebPath = path.join(currentDir, 'web');
433
- console.log('🔍 Checking alternative path:', altWebPath);
434
- if (this._pathExistsSync(altWebPath)) {
435
- console.log('✅ Found web UI in alternative path:', altWebPath);
436
- return altWebPath;
437
- }
408
+ // 在开发环境中,web UI 位于项目目录中的 packages/web
409
+ const devWebPath = path.join(__dirname, '..', '..', 'web');
410
+ console.log('🔍 Checking dev path:', devWebPath);
411
+ if (this._pathExistsSync(devWebPath)) {
412
+ console.log('✅ Using dev environment path:', devWebPath);
413
+ return devWebPath;
414
+ }
438
415
 
439
- // 向上查找
440
- const parentDir = path.dirname(currentDir);
441
- if (parentDir === currentDir) {
442
- // 已经到达根目录
443
- break;
444
- }
445
- currentDir = parentDir;
446
- searchDepth++;
447
- }
416
+ // 在 npm 包环境中,查找 packages/web
417
+ // 从当前文件位置向上查找可能的包根目录
418
+ let currentDir = __dirname;
419
+ let searchDepth = 0;
420
+ const maxDepth = 5;
421
+
422
+ while (searchDepth < maxDepth) {
423
+ // 检查 packages/web
424
+ const webPath = path.join(currentDir, 'packages', 'web');
425
+ console.log('🔍 Checking npm package path:', webPath);
426
+ if (this._pathExistsSync(webPath)) {
427
+ console.log('✅ Found web UI in npm package:', webPath);
428
+ return webPath;
429
+ }
448
430
 
449
- // 如果都找不到,使用当前工作目录作为最后尝试
450
- const cwdWebPath = path.join(process.cwd(), 'packages', 'web');
451
- console.log('🔍 Checking CWD path:', cwdWebPath);
452
- if (this._pathExistsSync(cwdWebPath)) {
453
- console.log('✅ Found web UI in CWD:', cwdWebPath);
454
- return cwdWebPath;
455
- }
431
+ // 检查直接的 web 目录(某些打包场景)
432
+ const altWebPath = path.join(currentDir, 'web');
433
+ console.log('🔍 Checking alternative path:', altWebPath);
434
+ if (this._pathExistsSync(altWebPath)) {
435
+ console.log('✅ Found web UI in alternative path:', altWebPath);
436
+ return altWebPath;
437
+ }
456
438
 
457
- // 最后的fallback
458
- console.error('❌ Web UI not found in any location. This will cause blank pages.');
459
- console.error('Current directory:', __dirname);
460
- console.error('Working directory:', process.cwd());
461
- console.error('Checked paths:');
462
- console.error(' - Dev path:', devWebPath);
463
- console.error(' - CWD path:', cwdWebPath);
439
+ // 向上查找
440
+ const parentDir = path.dirname(currentDir);
441
+ if (parentDir === currentDir) {
442
+ // 已经到达根目录
443
+ break;
444
+ }
445
+ currentDir = parentDir;
446
+ searchDepth++;
447
+ }
464
448
 
465
- return devWebPath; // 返回开发路径作为fallback,虽然它不存在
466
- };
449
+ // 如果都找不到,使用当前工作目录作为最后尝试
450
+ const cwdWebPath = path.join(process.cwd(), 'packages', 'web');
451
+ console.log('🔍 Checking CWD path:', cwdWebPath);
452
+ if (this._pathExistsSync(cwdWebPath)) {
453
+ console.log('✅ Found web UI in CWD:', cwdWebPath);
454
+ return cwdWebPath;
455
+ }
467
456
 
468
- /**
469
- * 获取内置配置文件目录
470
- * 兼容开发环境和打包后的环境
471
- * @returns {string} 配置目录路径
472
- */
473
- getBuiltInConfigsDir() {
474
- // 检查是否在 Electron 环境中运行
475
- const isElectron = typeof process !== 'undefined' &&
476
- process.versions &&
477
- process.versions.electron;
478
-
479
- // 检查是否是打包应用
480
- // 在 macOS 打包应用中,process.resourcesPath 存在
481
- if (process.resourcesPath) {
482
- // 在打包应用中,内置配置位于 Resources/runtime/configs/
483
- const packagedConfigPath = path.join(process.resourcesPath, 'runtime', 'configs');
484
- if (this._pathExistsSync(packagedConfigPath)) {
485
- return packagedConfigPath;
486
- }
487
- }
457
+ // 最后的fallback
458
+ console.error('❌ Web UI not found in any location. This will cause blank pages.');
459
+ console.error('Current directory:', __dirname);
460
+ console.error('Working directory:', process.cwd());
461
+ console.error('Checked paths:');
462
+ console.error(' - Dev path:', devWebPath);
463
+ console.error(' - CWD path:', cwdWebPath);
464
+
465
+ return devWebPath; // 返回开发路径作为fallback,虽然它不存在
466
+ }
467
+
468
+ /**
469
+ * 获取内置配置文件目录
470
+ * 兼容开发环境和打包后的环境
471
+ * @returns {string} 配置目录路径
472
+ */
473
+ getBuiltInConfigsDir() {
474
+ // 检查是否是打包应用
475
+ // 在 macOS 打包应用中,process.resourcesPath 存在
476
+ if (process.resourcesPath) {
477
+ // 在打包应用中,内置配置位于 Resources/runtime/configs/
478
+ const packagedConfigPath = path.join(process.resourcesPath, 'runtime', 'configs');
479
+ if (this._pathExistsSync(packagedConfigPath)) {
480
+ return packagedConfigPath;
481
+ }
482
+ }
488
483
 
489
- // 在 npm 包环境中,配置位于 node_modules 中的包目录
490
- const __filename = fileURLToPath(import.meta.url);
491
- const __dirname = path.dirname(__filename);
492
- const npmConfigPath = path.resolve(__dirname, '../configs');
493
- if (this._pathExistsSync(npmConfigPath)) {
494
- return npmConfigPath;
495
- }
484
+ // 在 npm 包环境中,配置位于 node_modules 中的包目录
485
+ const __filename = fileURLToPath(import.meta.url);
486
+ const __dirname = path.dirname(__filename);
487
+ const npmConfigPath = path.resolve(__dirname, '../configs');
488
+ if (this._pathExistsSync(npmConfigPath)) {
489
+ return npmConfigPath;
490
+ }
496
491
 
497
- // 在开发环境中,配置位于 packages/server/configs/
498
- return path.resolve(__dirname, '../configs');
492
+ // 在开发环境中,配置位于 packages/server/configs/
493
+ return path.resolve(__dirname, '../configs');
494
+ }
495
+
496
+ /**
497
+ * 获取默认用户配置文件路径
498
+ * @returns {string} 用户配置文件路径
499
+ */
500
+ getDefaultUserConfigPath() {
501
+ return path.join(this.getBuiltInConfigsDir(), 'authors.json');
502
+ }
503
+
504
+ _pathExistsSync(filePath) {
505
+ try {
506
+ fs.accessSync(filePath, fs.constants.F_OK);
507
+ return true;
508
+ } catch (error) {
509
+ return false;
499
510
  }
500
-
501
- _pathExistsSync(filePath) {
502
- try {
503
- fs.accessSync(filePath, fs.constants.F_OK);
504
- return true;
505
- } catch (error) {
506
- return false;
507
- }
508
- };
511
+ }
509
512
 
510
- async getPromptManager() {
513
+ async getPromptManager() {
511
514
  if (!_promptManager) {
515
+ try {
516
+ // 首先尝试相对路径导入
517
+ const serviceModule = await import('../services/manager.js');
518
+ _promptManager = serviceModule.promptManager;
519
+ } catch (error) {
520
+ // 如果相对路径导入失败,尝试使用绝对路径
512
521
  try {
513
- // 首先尝试相对路径导入
514
- const serviceModule = await import('../services/manager.js');
515
- _promptManager = serviceModule.promptManager;
516
- } catch (error) {
517
- // 如果相对路径导入失败,尝试使用绝对路径
518
- try {
519
- const path = await import('path');
520
- const { fileURLToPath } = await import('url');
521
- const __filename = fileURLToPath(import.meta.url);
522
- const __dirname = path.dirname(__filename);
523
- const managerPath = path.join(__dirname, '..', 'services', 'manager.js');
524
- const serviceModule = await import(managerPath);
525
- _promptManager = serviceModule.promptManager;
526
- } catch (absolutePathError) {
527
- // 如果绝对路径也失败,记录错误并重新抛出
528
- console.error('Failed to import promptManager with both relative and absolute paths:', absolutePathError);
529
- throw absolutePathError;
530
- }
522
+ const path = await import('path');
523
+ const { fileURLToPath } = await import('url');
524
+ const __filename = fileURLToPath(import.meta.url);
525
+ const __dirname = path.dirname(__filename);
526
+ const managerPath = path.join(__dirname, '..', 'services', 'manager.js');
527
+ const serviceModule = await import(managerPath);
528
+ _promptManager = serviceModule.promptManager;
529
+ } catch (absolutePathError) {
530
+ // 如果绝对路径也失败,记录错误并重新抛出
531
+ console.error('Failed to import promptManager with both relative and absolute paths:', absolutePathError);
532
+ throw absolutePathError;
531
533
  }
534
+ }
532
535
  }
533
536
  return _promptManager;
534
- }
537
+ }
535
538
  }
536
539
 
537
- export const util = new Util()
540
+ export const util = new Util();