@becrafter/prompt-manager 0.1.2 → 0.1.9
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.
- package/README.md +304 -121
- package/app/cli/commands/start.js +65 -4
- package/app/cli/support/argv.js +6 -0
- package/env.example +32 -0
- package/package.json +36 -7
- package/packages/server/api/admin.routes.js +409 -1
- package/packages/server/api/open.routes.js +7 -2
- package/packages/server/api/tool.routes.js +479 -0
- package/packages/server/app.js +97 -25
- package/packages/server/configs/models/built-in/bigmodel.yaml +6 -0
- package/packages/server/configs/models/providers.yaml +50 -0
- package/packages/server/configs/templates/built-in/general-iteration.yaml +60 -0
- package/packages/server/configs/templates/built-in/general-optimize.yaml +63 -0
- package/packages/server/configs/templates/built-in/output-format-optimize.yaml +95 -0
- package/packages/server/mcp/heartbeat-patch.js +73 -0
- package/packages/server/mcp/mcp.server.js +63 -314
- package/packages/server/mcp/prompt.handler.js +26 -0
- package/packages/server/mcp/thinking-toolkit.handler.js +380 -0
- package/packages/server/package.json +35 -3
- package/packages/server/server.js +114 -12
- package/packages/server/services/TerminalService.js +498 -0
- package/packages/server/services/WebSocketService.js +484 -0
- package/packages/server/services/manager.js +38 -7
- package/packages/server/services/model.service.js +473 -0
- package/packages/server/services/optimization.service.js +457 -0
- package/packages/server/services/template.service.js +333 -0
- package/packages/server/toolm/tool-description-generator-optimized.service.js +5 -2
- package/packages/server/toolm/tool-sync.service.js +47 -3
- package/packages/server/utils/config.js +8 -1
- package/packages/server/utils/port-checker.js +63 -0
- package/packages/server/utils/util.js +27 -0
- package/IFLOW.md +0 -175
- package/app/desktop/assets/app.1.png +0 -0
- package/app/desktop/assets/app.png +0 -0
- package/app/desktop/assets/icons/icon.icns +0 -0
- package/app/desktop/assets/icons/icon.ico +0 -0
- package/app/desktop/assets/icons/icon.png +0 -0
- package/app/desktop/assets/icons/tray.png +0 -0
- package/app/desktop/assets/templates/about.html +0 -147
- package/app/desktop/assets/tray.1.png +0 -0
- package/app/desktop/assets/tray.png +0 -0
- package/app/desktop/main.js +0 -241
- package/app/desktop/package-lock.json +0 -5026
- package/app/desktop/package.json +0 -100
- package/app/desktop/preload.js +0 -7
- package/app/desktop/src/core/error-handler.js +0 -108
- package/app/desktop/src/core/event-emitter.js +0 -84
- package/app/desktop/src/core/logger.js +0 -108
- package/app/desktop/src/core/state-manager.js +0 -125
- package/app/desktop/src/services/module-loader.js +0 -214
- package/app/desktop/src/services/runtime-manager.js +0 -301
- package/app/desktop/src/services/service-manager.js +0 -169
- package/app/desktop/src/services/update-manager.js +0 -267
- package/app/desktop/src/ui/about-dialog-manager.js +0 -208
- package/app/desktop/src/ui/admin-window-manager.js +0 -757
- package/app/desktop/src/ui/splash-manager.js +0 -253
- package/app/desktop/src/ui/tray-manager.js +0 -186
- package/app/desktop/src/utils/icon-manager.js +0 -133
- package/app/desktop/src/utils/path-utils.js +0 -58
- package/app/desktop/src/utils/resource-paths.js +0 -49
- package/app/desktop/src/utils/resource-sync.js +0 -260
- package/app/desktop/src/utils/runtime-sync.js +0 -241
- package/app/desktop/src/utils/template-renderer.js +0 -284
- package/app/desktop/src/utils/version-utils.js +0 -59
- package/examples/prompts/developer/code-review.yaml +0 -32
- package/examples/prompts/developer/code_refactoring.yaml +0 -31
- package/examples/prompts/developer/doc-generator.yaml +0 -36
- package/examples/prompts/developer/error-code-fixer.yaml +0 -35
- package/examples/prompts/engineer/engineer-professional.yaml +0 -92
- package/examples/prompts/engineer/laowang-engineer.yaml +0 -132
- package/examples/prompts/engineer/nekomata-engineer.yaml +0 -123
- package/examples/prompts/engineer/ojousama-engineer.yaml +0 -124
- package/examples/prompts/generator/gen_3d_edu_webpage_html.yaml +0 -117
- package/examples/prompts/generator/gen_3d_webpage_html.yaml +0 -75
- package/examples/prompts/generator/gen_bento_grid_html.yaml +0 -112
- package/examples/prompts/generator/gen_html_web_page.yaml +0 -88
- package/examples/prompts/generator/gen_knowledge_card_html.yaml +0 -83
- package/examples/prompts/generator/gen_magazine_card_html.yaml +0 -82
- package/examples/prompts/generator/gen_mimeng_headline_title.yaml +0 -71
- package/examples/prompts/generator/gen_podcast_script.yaml +0 -69
- package/examples/prompts/generator/gen_prd_prototype_html.yaml +0 -175
- package/examples/prompts/generator/gen_summarize.yaml +0 -157
- package/examples/prompts/generator/gen_title.yaml +0 -119
- package/examples/prompts/generator/others/api_documentation.yaml +0 -32
- package/examples/prompts/generator/others/build_mcp_server.yaml +0 -26
- package/examples/prompts/generator/others/project_architecture.yaml +0 -31
- package/examples/prompts/generator/others/test_case_generator.yaml +0 -30
- package/examples/prompts/generator/others/writing_assistant.yaml +0 -72
- package/examples/prompts/recommend/human_3-0_growth_diagnostic_coach_prompt.yaml +0 -105
- package/examples/prompts/workflow/sixstep-workflow.yaml +0 -192
- package/packages/admin-ui/.babelrc +0 -3
- package/packages/admin-ui/admin.html +0 -412
- package/packages/admin-ui/css/codemirror-theme_xq-light.css +0 -43
- package/packages/admin-ui/css/codemirror.css +0 -344
- package/packages/admin-ui/css/main.css +0 -2592
- package/packages/admin-ui/css/recommended-prompts.css +0 -610
- package/packages/admin-ui/package-lock.json +0 -6973
- package/packages/admin-ui/package.json +0 -36
- package/packages/admin-ui/src/codemirror.js +0 -53
- package/packages/admin-ui/src/index.js +0 -3188
- package/packages/admin-ui/webpack.config.js +0 -76
- package/packages/server/toolm/test-tools.js +0 -264
- package/scripts/build-icons.js +0 -135
- package/scripts/build.sh +0 -57
- package/scripts/postinstall.js +0 -34
- package/scripts/surge/CNAME +0 -1
- package/scripts/surge/README.md +0 -47
- package/scripts/surge/package-lock.json +0 -34
- package/scripts/surge/package.json +0 -20
- package/scripts/surge/sync-to-surge.js +0 -151
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import YAML from 'yaml';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import crypto from 'crypto';
|
|
7
|
+
import os from 'os';
|
|
8
|
+
import { logger } from '../utils/logger.js';
|
|
9
|
+
import { config } from '../utils/config.js';
|
|
10
|
+
import { util } from '../utils/util.js';
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = path.dirname(__filename);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 模板数据结构验证schema
|
|
17
|
+
*/
|
|
18
|
+
const TemplateSchema = z.object({
|
|
19
|
+
id: z.string().optional(),
|
|
20
|
+
name: z.string().min(1, '模板名称不能为空'),
|
|
21
|
+
description: z.string().optional(),
|
|
22
|
+
content: z.union([z.string(), z.array(z.any())]).default(''),
|
|
23
|
+
type: z.enum(['optimize', 'iterate']).default('optimize'),
|
|
24
|
+
format: z.enum(['simple', 'advanced']).default('simple'),
|
|
25
|
+
isBuiltIn: z.boolean().optional().default(false),
|
|
26
|
+
filePath: z.string().optional()
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 模板管理器类
|
|
31
|
+
*/
|
|
32
|
+
class TemplateManager {
|
|
33
|
+
constructor() {
|
|
34
|
+
this.builtInDir = path.join(util.getBuiltInConfigsDir(), 'templates/built-in');
|
|
35
|
+
this.customDir = path.join(os.homedir(), '.prompt-manager/configs/templates');
|
|
36
|
+
this.loadedTemplates = new Map();
|
|
37
|
+
this.idToPathMap = new Map();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 基于文件路径生成固定长度的唯一ID
|
|
42
|
+
* @param {string} relativePath - 相对于模板目录的路径
|
|
43
|
+
* @returns {string} 固定长度的唯一ID字符串(8位)
|
|
44
|
+
*/
|
|
45
|
+
generateUniqueId(relativePath) {
|
|
46
|
+
const hash = crypto.createHash('sha256');
|
|
47
|
+
hash.update(relativePath);
|
|
48
|
+
const hashHex = hash.digest('hex');
|
|
49
|
+
return hashHex.substring(0, 8);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 注册ID到路径的映射
|
|
54
|
+
* @param {string} id - 唯一ID
|
|
55
|
+
* @param {string} relativePath - 相对路径
|
|
56
|
+
*/
|
|
57
|
+
registerIdPathMapping(id, relativePath) {
|
|
58
|
+
this.idToPathMap.set(id, relativePath);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 确保目录存在
|
|
63
|
+
*/
|
|
64
|
+
async ensureDirectories() {
|
|
65
|
+
await fs.ensureDir(this.builtInDir);
|
|
66
|
+
await fs.ensureDir(this.customDir);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 加载所有模板
|
|
71
|
+
*/
|
|
72
|
+
async loadTemplates() {
|
|
73
|
+
try {
|
|
74
|
+
logger.info('开始加载优化模板');
|
|
75
|
+
|
|
76
|
+
// 确保目录存在
|
|
77
|
+
await this.ensureDirectories();
|
|
78
|
+
|
|
79
|
+
// 清空之前的加载结果
|
|
80
|
+
this.loadedTemplates.clear();
|
|
81
|
+
this.idToPathMap.clear();
|
|
82
|
+
|
|
83
|
+
// 加载内置模板
|
|
84
|
+
await this.loadTemplatesFromDir(this.builtInDir, true);
|
|
85
|
+
|
|
86
|
+
// 加载用户自定义模板
|
|
87
|
+
await this.loadTemplatesFromDir(this.customDir, false);
|
|
88
|
+
|
|
89
|
+
logger.info(`模板加载完成: 共 ${this.loadedTemplates.size} 个模板`);
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
success: this.loadedTemplates.size,
|
|
93
|
+
templates: Array.from(this.loadedTemplates.values())
|
|
94
|
+
};
|
|
95
|
+
} catch (error) {
|
|
96
|
+
logger.error('加载模板时发生错误:', error);
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 从指定目录加载模板
|
|
103
|
+
* @param {string} dir - 目录路径
|
|
104
|
+
* @param {boolean} isBuiltIn - 是否为内置模板
|
|
105
|
+
*/
|
|
106
|
+
async loadTemplatesFromDir(dir, isBuiltIn) {
|
|
107
|
+
try {
|
|
108
|
+
if (!fs.existsSync(dir)) {
|
|
109
|
+
logger.warn(`模板目录不存在: ${dir}`);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const files = await fs.readdir(dir);
|
|
114
|
+
const templateFiles = files.filter(file => {
|
|
115
|
+
const ext = path.extname(file).toLowerCase();
|
|
116
|
+
return ['.yaml', '.yml', '.json'].includes(ext);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
for (const fileName of templateFiles) {
|
|
120
|
+
const filePath = path.join(dir, fileName);
|
|
121
|
+
const relativePath = fileName;
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
125
|
+
const ext = path.extname(fileName).toLowerCase();
|
|
126
|
+
|
|
127
|
+
let templateData;
|
|
128
|
+
if (ext === '.json') {
|
|
129
|
+
templateData = JSON.parse(content);
|
|
130
|
+
} else {
|
|
131
|
+
templateData = YAML.parse(content);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// 验证模板数据结构
|
|
135
|
+
const validatedTemplate = TemplateSchema.parse(templateData);
|
|
136
|
+
|
|
137
|
+
// 添加元数据
|
|
138
|
+
validatedTemplate.isBuiltIn = isBuiltIn;
|
|
139
|
+
validatedTemplate.filePath = filePath;
|
|
140
|
+
|
|
141
|
+
// 生成唯一ID
|
|
142
|
+
const uniqueId = this.generateUniqueId(relativePath);
|
|
143
|
+
validatedTemplate.id = uniqueId;
|
|
144
|
+
|
|
145
|
+
// 注册ID到路径的映射
|
|
146
|
+
this.registerIdPathMapping(uniqueId, relativePath);
|
|
147
|
+
|
|
148
|
+
// 存储模板
|
|
149
|
+
this.loadedTemplates.set(uniqueId, validatedTemplate);
|
|
150
|
+
|
|
151
|
+
logger.debug(`加载模板: ${validatedTemplate.name} -> ID: ${uniqueId} (${isBuiltIn ? '内置' : '自定义'})`);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
logger.error(`加载模板文件 ${fileName} 失败:`, error.message);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
} catch (error) {
|
|
157
|
+
logger.error(`扫描模板目录 ${dir} 时发生错误:`, error.message);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* 获取所有已加载的模板
|
|
163
|
+
*/
|
|
164
|
+
getTemplates() {
|
|
165
|
+
return Array.from(this.loadedTemplates.values());
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* 根据ID获取模板
|
|
170
|
+
* @param {string} id - 模板ID
|
|
171
|
+
*/
|
|
172
|
+
getTemplate(id) {
|
|
173
|
+
return this.loadedTemplates.get(id) || null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* 创建新模板
|
|
178
|
+
* @param {Object} templateData - 模板数据
|
|
179
|
+
* @returns {Object} 创建的模板
|
|
180
|
+
*/
|
|
181
|
+
async createTemplate(templateData) {
|
|
182
|
+
try {
|
|
183
|
+
// 验证模板数据
|
|
184
|
+
const validatedTemplate = TemplateSchema.parse(templateData);
|
|
185
|
+
|
|
186
|
+
// 生成文件名(使用名称的拼音或英文,这里简化为使用名称)
|
|
187
|
+
const fileName = `${validatedTemplate.name.replace(/\s+/g, '_')}.yaml`;
|
|
188
|
+
const filePath = path.join(this.customDir, fileName);
|
|
189
|
+
|
|
190
|
+
// 检查文件是否已存在
|
|
191
|
+
if (fs.existsSync(filePath)) {
|
|
192
|
+
throw new Error(`模板 ${validatedTemplate.name} 已存在`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// 准备保存的数据
|
|
196
|
+
const dataToSave = {
|
|
197
|
+
name: validatedTemplate.name,
|
|
198
|
+
description: validatedTemplate.description || '',
|
|
199
|
+
content: validatedTemplate.content,
|
|
200
|
+
type: validatedTemplate.type,
|
|
201
|
+
format: validatedTemplate.format
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// 保存为 YAML 文件
|
|
205
|
+
await fs.writeFile(filePath, YAML.stringify(dataToSave), 'utf8');
|
|
206
|
+
|
|
207
|
+
// 生成唯一ID
|
|
208
|
+
const uniqueId = this.generateUniqueId(fileName);
|
|
209
|
+
|
|
210
|
+
// 更新模板数据
|
|
211
|
+
validatedTemplate.id = uniqueId;
|
|
212
|
+
validatedTemplate.isBuiltIn = false;
|
|
213
|
+
validatedTemplate.filePath = filePath;
|
|
214
|
+
|
|
215
|
+
// 注册ID到路径的映射
|
|
216
|
+
this.registerIdPathMapping(uniqueId, fileName);
|
|
217
|
+
|
|
218
|
+
// 存储到内存
|
|
219
|
+
this.loadedTemplates.set(uniqueId, validatedTemplate);
|
|
220
|
+
|
|
221
|
+
logger.info(`创建模板: ${validatedTemplate.name} -> ID: ${uniqueId}`);
|
|
222
|
+
|
|
223
|
+
return validatedTemplate;
|
|
224
|
+
} catch (error) {
|
|
225
|
+
logger.error('创建模板失败:', error);
|
|
226
|
+
throw error;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* 更新模板
|
|
232
|
+
* @param {string} id - 模板ID
|
|
233
|
+
* @param {Object} templateData - 新的模板数据
|
|
234
|
+
* @returns {Object} 更新后的模板
|
|
235
|
+
*/
|
|
236
|
+
async updateTemplate(id, templateData) {
|
|
237
|
+
try {
|
|
238
|
+
const existingTemplate = this.getTemplate(id);
|
|
239
|
+
|
|
240
|
+
if (!existingTemplate) {
|
|
241
|
+
throw new Error(`模板不存在: ${id}`);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// 内置模板不能修改
|
|
245
|
+
if (existingTemplate.isBuiltIn) {
|
|
246
|
+
throw new Error('内置模板不能修改');
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// 验证模板数据
|
|
250
|
+
const validatedTemplate = TemplateSchema.parse(templateData);
|
|
251
|
+
|
|
252
|
+
// 准备保存的数据
|
|
253
|
+
const dataToSave = {
|
|
254
|
+
name: validatedTemplate.name,
|
|
255
|
+
description: validatedTemplate.description || '',
|
|
256
|
+
content: validatedTemplate.content,
|
|
257
|
+
type: validatedTemplate.type,
|
|
258
|
+
format: validatedTemplate.format
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// 更新文件
|
|
262
|
+
await fs.writeFile(existingTemplate.filePath, YAML.stringify(dataToSave), 'utf8');
|
|
263
|
+
|
|
264
|
+
// 更新内存中的数据
|
|
265
|
+
existingTemplate.name = validatedTemplate.name;
|
|
266
|
+
existingTemplate.description = validatedTemplate.description;
|
|
267
|
+
existingTemplate.content = validatedTemplate.content;
|
|
268
|
+
existingTemplate.type = validatedTemplate.type;
|
|
269
|
+
existingTemplate.format = validatedTemplate.format;
|
|
270
|
+
|
|
271
|
+
logger.info(`更新模板: ${existingTemplate.name} -> ID: ${id}`);
|
|
272
|
+
|
|
273
|
+
return existingTemplate;
|
|
274
|
+
} catch (error) {
|
|
275
|
+
logger.error('更新模板失败:', error);
|
|
276
|
+
throw error;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* 删除模板
|
|
282
|
+
* @param {string} id - 模板ID
|
|
283
|
+
*/
|
|
284
|
+
async deleteTemplate(id) {
|
|
285
|
+
try {
|
|
286
|
+
const template = this.getTemplate(id);
|
|
287
|
+
|
|
288
|
+
if (!template) {
|
|
289
|
+
throw new Error(`模板不存在: ${id}`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// 内置模板不能删除
|
|
293
|
+
if (template.isBuiltIn) {
|
|
294
|
+
throw new Error('内置模板不能删除');
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// 删除文件
|
|
298
|
+
await fs.remove(template.filePath);
|
|
299
|
+
|
|
300
|
+
// 从内存中移除
|
|
301
|
+
this.loadedTemplates.delete(id);
|
|
302
|
+
this.idToPathMap.delete(id);
|
|
303
|
+
|
|
304
|
+
logger.info(`删除模板: ${template.name} -> ID: ${id}`);
|
|
305
|
+
} catch (error) {
|
|
306
|
+
logger.error('删除模板失败:', error);
|
|
307
|
+
throw error;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* 重新加载模板
|
|
313
|
+
*/
|
|
314
|
+
async reloadTemplates() {
|
|
315
|
+
logger.info('重新加载模板');
|
|
316
|
+
return await this.loadTemplates();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* 验证模板数据结构
|
|
321
|
+
* @param {Object} templateData - 要验证的模板数据
|
|
322
|
+
* @returns {Object} 验证后的模板数据
|
|
323
|
+
*/
|
|
324
|
+
validateTemplateData(templateData) {
|
|
325
|
+
return TemplateSchema.parse(templateData);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// 创建全局TemplateManager实例
|
|
330
|
+
export const templateManager = new TemplateManager();
|
|
331
|
+
|
|
332
|
+
// 导出TemplateManager类供测试使用
|
|
333
|
+
export { TemplateManager };
|
|
@@ -240,7 +240,10 @@ export function generateToolmDescription() {
|
|
|
240
240
|
const toolComparison = generateToolComparison(tools);
|
|
241
241
|
|
|
242
242
|
// 组装完整的描述(优化结构,减少冗余)
|
|
243
|
-
const description =
|
|
243
|
+
const description = `🛠️ ToolM 是 Prompt Manager 新一代工具系统运行时,提供统一的工具管理和执行能力。
|
|
244
|
+
|
|
245
|
+
【规范名称】promptmanager_toolm
|
|
246
|
+
【调用说明】在提示词中使用 promptmanager_toolm,实际调用时自动映射到 mcp__[server]__action
|
|
244
247
|
|
|
245
248
|
## 核心特性
|
|
246
249
|
|
|
@@ -318,7 +321,7 @@ ${categorizedToolList}
|
|
|
318
321
|
* @returns {string} 默认描述
|
|
319
322
|
*/
|
|
320
323
|
function getDefaultDescription() {
|
|
321
|
-
return
|
|
324
|
+
return `🛠️ ToolM 是 Prompt Manager 新一代工具系统运行时,提供统一的工具管理和执行能力。
|
|
322
325
|
|
|
323
326
|
## 核心特性
|
|
324
327
|
|
|
@@ -27,8 +27,28 @@ const __dirname = path.dirname(__filename);
|
|
|
27
27
|
export async function syncSystemTools() {
|
|
28
28
|
logger.info('开始同步系统工具到沙箱环境...');
|
|
29
29
|
|
|
30
|
-
//
|
|
31
|
-
const
|
|
30
|
+
// 判断运行环境类型
|
|
31
|
+
const isElectronPackaged = process.resourcesPath &&
|
|
32
|
+
(process.resourcesPath.includes('app.asar') ||
|
|
33
|
+
process.resourcesPath.includes('Electron.app'));
|
|
34
|
+
|
|
35
|
+
const isNpmPackage = __dirname.includes('node_modules');
|
|
36
|
+
|
|
37
|
+
let toolsDir;
|
|
38
|
+
|
|
39
|
+
if (isElectronPackaged) {
|
|
40
|
+
// Electron 打包环境:工具位于 runtime/toolbox/
|
|
41
|
+
toolsDir = path.join(process.resourcesPath, 'runtime', 'toolbox');
|
|
42
|
+
logger.debug('Electron 打包环境,使用工具目录:', { toolsDir });
|
|
43
|
+
} else if (isNpmPackage) {
|
|
44
|
+
// NPM 包环境:工具位于 packages/resources/tools/
|
|
45
|
+
toolsDir = path.join(__dirname, '..', '..', 'resources', 'tools');
|
|
46
|
+
logger.debug('NPM 包环境,使用工具目录:', { toolsDir });
|
|
47
|
+
} else {
|
|
48
|
+
// 开发环境:工具位于 packages/resources/tools/
|
|
49
|
+
toolsDir = path.join(__dirname, '..', '..', 'resources', 'tools');
|
|
50
|
+
logger.debug('开发环境,使用工具目录:', { toolsDir });
|
|
51
|
+
}
|
|
32
52
|
|
|
33
53
|
// 目标沙箱目录
|
|
34
54
|
const toolboxDir = path.join(os.homedir(), '.prompt-manager', 'toolbox');
|
|
@@ -39,7 +59,31 @@ export async function syncSystemTools() {
|
|
|
39
59
|
// 检查源目录是否存在
|
|
40
60
|
if (!await pathExists(toolsDir)) {
|
|
41
61
|
logger.warn(`系统工具目录不存在,跳过同步: ${toolsDir}`);
|
|
42
|
-
|
|
62
|
+
|
|
63
|
+
// 在打包环境中,如果工具目录不存在,尝试从其他可能的位置查找
|
|
64
|
+
if (isPackaged) {
|
|
65
|
+
// 尝试其他可能的路径
|
|
66
|
+
const alternativePaths = [
|
|
67
|
+
path.join(process.resourcesPath, 'app.asar', 'runtime', 'toolbox'),
|
|
68
|
+
path.join(process.resourcesPath, '..', 'runtime', 'toolbox'),
|
|
69
|
+
path.join(__dirname, '..', '..', 'resources', 'tools') // 回退到开发路径
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
for (const altPath of alternativePaths) {
|
|
73
|
+
if (await pathExists(altPath)) {
|
|
74
|
+
toolsDir = altPath;
|
|
75
|
+
logger.info(`找到替代工具目录: ${toolsDir}`);
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!await pathExists(toolsDir)) {
|
|
81
|
+
logger.warn('所有可能的工具目录都不存在,跳过工具同步');
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
43
87
|
}
|
|
44
88
|
|
|
45
89
|
try {
|
|
@@ -121,7 +121,7 @@ export class Config {
|
|
|
121
121
|
|
|
122
122
|
// 其他配置
|
|
123
123
|
this.serverName = process.env.MCP_SERVER_NAME || 'prompt-manager';
|
|
124
|
-
this.serverVersion = process.env.MCP_SERVER_VERSION || '0.1.
|
|
124
|
+
this.serverVersion = process.env.MCP_SERVER_VERSION || '0.1.9';
|
|
125
125
|
this.logLevel = process.env.LOG_LEVEL || 'info';
|
|
126
126
|
this.maxPrompts = parseInt(process.env.MAX_PROMPTS) || 1000;
|
|
127
127
|
this.recursiveScan = process.env.RECURSIVE_SCAN !== 'false'; // 默认启用递归扫描
|
|
@@ -255,6 +255,13 @@ export class Config {
|
|
|
255
255
|
return this.port;
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
+
/**
|
|
259
|
+
* 设置服务器端口(用于动态端口分配)
|
|
260
|
+
*/
|
|
261
|
+
setPort(port) {
|
|
262
|
+
this.port = port;
|
|
263
|
+
}
|
|
264
|
+
|
|
258
265
|
/**
|
|
259
266
|
* 获取服务器名称
|
|
260
267
|
*/
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import net from 'net';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 检查指定端口是否可用
|
|
5
|
+
* @param {number} port - 要检查的端口号
|
|
6
|
+
* @returns {Promise<boolean>} - 端口是否可用
|
|
7
|
+
*/
|
|
8
|
+
export async function checkPortAvailable(port) {
|
|
9
|
+
return new Promise((resolve) => {
|
|
10
|
+
const server = net.createServer();
|
|
11
|
+
|
|
12
|
+
server.listen(port, () => {
|
|
13
|
+
server.once('close', () => {
|
|
14
|
+
resolve(true);
|
|
15
|
+
});
|
|
16
|
+
server.close();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
server.on('error', () => {
|
|
20
|
+
resolve(false);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 在指定范围内查找可用端口
|
|
27
|
+
* @param {number} startPort - 起始端口号
|
|
28
|
+
* @param {number} maxAttempts - 最大尝试次数
|
|
29
|
+
* @returns {Promise<number>} - 可用的端口号
|
|
30
|
+
*/
|
|
31
|
+
export async function findAvailablePort(startPort, maxAttempts = 10) {
|
|
32
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
33
|
+
const port = startPort + i;
|
|
34
|
+
if (await checkPortAvailable(port)) {
|
|
35
|
+
return port;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
throw new Error(`无法在 ${startPort} 到 ${startPort + maxAttempts - 1} 范围内找到可用端口`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 检查端口是否被特定进程占用
|
|
43
|
+
* @param {number} port - 端口号
|
|
44
|
+
* @returns {Promise<Object>} - 端口占用信息
|
|
45
|
+
*/
|
|
46
|
+
export async function getPortInfo(port) {
|
|
47
|
+
return new Promise((resolve) => {
|
|
48
|
+
const server = net.createServer();
|
|
49
|
+
|
|
50
|
+
server.listen(port, () => {
|
|
51
|
+
server.close();
|
|
52
|
+
resolve({ available: true, process: null });
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
server.on('error', (error) => {
|
|
56
|
+
if (error.code === 'EADDRINUSE') {
|
|
57
|
+
resolve({ available: false, error: '端口已被占用' });
|
|
58
|
+
} else {
|
|
59
|
+
resolve({ available: false, error: error.message });
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
@@ -371,6 +371,33 @@ export class Util {
|
|
|
371
371
|
// 返回默认路径
|
|
372
372
|
return devWebPath;
|
|
373
373
|
};
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* 获取内置配置文件目录
|
|
377
|
+
* 兼容开发环境和打包后的环境
|
|
378
|
+
* @returns {string} 配置目录路径
|
|
379
|
+
*/
|
|
380
|
+
getBuiltInConfigsDir() {
|
|
381
|
+
// 检查是否在 Electron 环境中运行
|
|
382
|
+
const isElectron = typeof process !== 'undefined' &&
|
|
383
|
+
process.versions &&
|
|
384
|
+
process.versions.electron;
|
|
385
|
+
|
|
386
|
+
// 检查是否是打包应用
|
|
387
|
+
// 在 macOS 打包应用中,process.resourcesPath 存在
|
|
388
|
+
if (process.resourcesPath) {
|
|
389
|
+
// 在打包应用中,内置配置位于 Resources/runtime/configs/
|
|
390
|
+
const packagedConfigPath = path.join(process.resourcesPath, 'runtime', 'configs');
|
|
391
|
+
if (this._pathExistsSync(packagedConfigPath)) {
|
|
392
|
+
return packagedConfigPath;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// 在开发环境中,配置位于 packages/server/configs/
|
|
397
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
398
|
+
const __dirname = path.dirname(__filename);
|
|
399
|
+
return path.resolve(__dirname, '../configs');
|
|
400
|
+
}
|
|
374
401
|
|
|
375
402
|
_pathExistsSync(filePath) {
|
|
376
403
|
try {
|