@becrafter/prompt-manager 0.1.1 → 0.1.8

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 (110) hide show
  1. package/README.md +304 -121
  2. package/app/cli/commands/start.js +28 -4
  3. package/app/cli/support/argv.js +6 -0
  4. package/env.example +32 -0
  5. package/package.json +36 -6
  6. package/packages/server/api/admin.routes.js +409 -1
  7. package/packages/server/api/open.routes.js +7 -2
  8. package/packages/server/api/tool.routes.js +479 -0
  9. package/packages/server/app.js +97 -25
  10. package/packages/server/configs/models/built-in/bigmodel.yaml +6 -0
  11. package/packages/server/configs/models/providers.yaml +50 -0
  12. package/packages/server/configs/templates/built-in/general-iteration.yaml +60 -0
  13. package/packages/server/configs/templates/built-in/general-optimize.yaml +63 -0
  14. package/packages/server/configs/templates/built-in/output-format-optimize.yaml +95 -0
  15. package/packages/server/mcp/heartbeat-patch.js +73 -0
  16. package/packages/server/mcp/mcp.server.js +63 -314
  17. package/packages/server/mcp/prompt.handler.js +26 -0
  18. package/packages/server/mcp/thinking-toolkit.handler.js +380 -0
  19. package/packages/server/package.json +35 -3
  20. package/packages/server/server.js +114 -12
  21. package/packages/server/services/TerminalService.js +498 -0
  22. package/packages/server/services/WebSocketService.js +484 -0
  23. package/packages/server/services/manager.js +38 -7
  24. package/packages/server/services/model.service.js +473 -0
  25. package/packages/server/services/optimization.service.js +457 -0
  26. package/packages/server/services/template.service.js +333 -0
  27. package/packages/server/toolm/tool-description-generator-optimized.service.js +5 -2
  28. package/packages/server/toolm/tool-sync.service.js +47 -3
  29. package/packages/server/utils/config.js +8 -1
  30. package/packages/server/utils/port-checker.js +63 -0
  31. package/packages/server/utils/util.js +27 -0
  32. package/IFLOW.md +0 -175
  33. package/app/desktop/assets/app.1.png +0 -0
  34. package/app/desktop/assets/app.png +0 -0
  35. package/app/desktop/assets/icons/icon.icns +0 -0
  36. package/app/desktop/assets/icons/icon.ico +0 -0
  37. package/app/desktop/assets/icons/icon.png +0 -0
  38. package/app/desktop/assets/icons/tray.png +0 -0
  39. package/app/desktop/assets/templates/about.html +0 -147
  40. package/app/desktop/assets/tray.1.png +0 -0
  41. package/app/desktop/assets/tray.png +0 -0
  42. package/app/desktop/main.js +0 -241
  43. package/app/desktop/package-lock.json +0 -4997
  44. package/app/desktop/package.json +0 -100
  45. package/app/desktop/preload.js +0 -7
  46. package/app/desktop/src/core/error-handler.js +0 -108
  47. package/app/desktop/src/core/event-emitter.js +0 -84
  48. package/app/desktop/src/core/logger.js +0 -108
  49. package/app/desktop/src/core/state-manager.js +0 -125
  50. package/app/desktop/src/services/module-loader.js +0 -214
  51. package/app/desktop/src/services/runtime-manager.js +0 -301
  52. package/app/desktop/src/services/service-manager.js +0 -169
  53. package/app/desktop/src/services/update-manager.js +0 -268
  54. package/app/desktop/src/ui/about-dialog-manager.js +0 -208
  55. package/app/desktop/src/ui/admin-window-manager.js +0 -757
  56. package/app/desktop/src/ui/splash-manager.js +0 -253
  57. package/app/desktop/src/ui/tray-manager.js +0 -186
  58. package/app/desktop/src/utils/icon-manager.js +0 -133
  59. package/app/desktop/src/utils/path-utils.js +0 -58
  60. package/app/desktop/src/utils/resource-paths.js +0 -49
  61. package/app/desktop/src/utils/resource-sync.js +0 -260
  62. package/app/desktop/src/utils/runtime-sync.js +0 -241
  63. package/app/desktop/src/utils/template-renderer.js +0 -284
  64. package/app/desktop/src/utils/version-utils.js +0 -59
  65. package/examples/prompts/developer/code-review.yaml +0 -32
  66. package/examples/prompts/developer/code_refactoring.yaml +0 -31
  67. package/examples/prompts/developer/doc-generator.yaml +0 -36
  68. package/examples/prompts/developer/error-code-fixer.yaml +0 -35
  69. package/examples/prompts/engineer/engineer-professional.yaml +0 -92
  70. package/examples/prompts/engineer/laowang-engineer.yaml +0 -132
  71. package/examples/prompts/engineer/nekomata-engineer.yaml +0 -123
  72. package/examples/prompts/engineer/ojousama-engineer.yaml +0 -124
  73. package/examples/prompts/generator/gen_3d_edu_webpage_html.yaml +0 -117
  74. package/examples/prompts/generator/gen_3d_webpage_html.yaml +0 -75
  75. package/examples/prompts/generator/gen_bento_grid_html.yaml +0 -112
  76. package/examples/prompts/generator/gen_html_web_page.yaml +0 -88
  77. package/examples/prompts/generator/gen_knowledge_card_html.yaml +0 -83
  78. package/examples/prompts/generator/gen_magazine_card_html.yaml +0 -82
  79. package/examples/prompts/generator/gen_mimeng_headline_title.yaml +0 -71
  80. package/examples/prompts/generator/gen_podcast_script.yaml +0 -69
  81. package/examples/prompts/generator/gen_prd_prototype_html.yaml +0 -175
  82. package/examples/prompts/generator/gen_summarize.yaml +0 -157
  83. package/examples/prompts/generator/gen_title.yaml +0 -119
  84. package/examples/prompts/generator/others/api_documentation.yaml +0 -32
  85. package/examples/prompts/generator/others/build_mcp_server.yaml +0 -26
  86. package/examples/prompts/generator/others/project_architecture.yaml +0 -31
  87. package/examples/prompts/generator/others/test_case_generator.yaml +0 -30
  88. package/examples/prompts/generator/others/writing_assistant.yaml +0 -72
  89. package/examples/prompts/recommend/human_3-0_growth_diagnostic_coach_prompt.yaml +0 -105
  90. package/examples/prompts/workflow/sixstep-workflow.yaml +0 -192
  91. package/packages/admin-ui/.babelrc +0 -3
  92. package/packages/admin-ui/admin.html +0 -412
  93. package/packages/admin-ui/css/codemirror-theme_xq-light.css +0 -43
  94. package/packages/admin-ui/css/codemirror.css +0 -344
  95. package/packages/admin-ui/css/main.css +0 -2592
  96. package/packages/admin-ui/css/recommended-prompts.css +0 -610
  97. package/packages/admin-ui/package-lock.json +0 -6981
  98. package/packages/admin-ui/package.json +0 -36
  99. package/packages/admin-ui/src/codemirror.js +0 -53
  100. package/packages/admin-ui/src/index.js +0 -3188
  101. package/packages/admin-ui/webpack.config.js +0 -76
  102. package/packages/server/toolm/test-tools.js +0 -264
  103. package/scripts/build-icons.js +0 -135
  104. package/scripts/build.sh +0 -57
  105. package/scripts/postinstall.js +0 -34
  106. package/scripts/surge/CNAME +0 -1
  107. package/scripts/surge/README.md +0 -47
  108. package/scripts/surge/package-lock.json +0 -34
  109. package/scripts/surge/package.json +0 -20
  110. package/scripts/surge/sync-to-surge.js +0 -151
@@ -0,0 +1,473 @@
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 { util } from '../utils/util.js';
10
+
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
13
+
14
+ /**
15
+ * 模型数据结构验证schema
16
+ */
17
+ const ModelSchema = z.object({
18
+ id: z.string().optional(),
19
+ name: z.string().min(1, '模型名称不能为空'),
20
+ provider: z.string().min(1, '提供商不能为空'),
21
+ model: z.string().min(1, '模型名称不能为空'),
22
+ apiEndpoint: z.string().url('API端点必须是有效的URL'),
23
+ apiKey: z.string().optional(),
24
+ enabled: z.boolean().optional().default(true),
25
+ isBuiltIn: z.boolean().optional().default(false),
26
+ filePath: z.string().optional()
27
+ });
28
+
29
+ /**
30
+ * 加密密钥(从环境变量获取,如果没有则使用默认密钥)
31
+ * 注意:在生产环境中应该使用强密钥并妥善保管
32
+ */
33
+ const ENCRYPTION_KEY = process.env.MODEL_ENCRYPTION_KEY || 'default-encryption-key-32-bytes!';
34
+ const ALGORITHM = 'aes-256-cbc';
35
+
36
+ /**
37
+ * 模型管理器类
38
+ */
39
+ class ModelManager {
40
+ constructor() {
41
+ this.builtInDir = path.join(util.getBuiltInConfigsDir(), 'models/built-in');
42
+ this.customDir = path.join(os.homedir(), '.prompt-manager/configs/models');
43
+ this.loadedModels = new Map();
44
+ this.idToPathMap = new Map();
45
+ this.providersConfig = null;
46
+ }
47
+
48
+ /**
49
+ * 基于文件路径生成固定长度的唯一ID
50
+ * @param {string} relativePath - 相对于模型目录的路径
51
+ * @returns {string} 固定长度的唯一ID字符串(8位)
52
+ */
53
+ generateUniqueId(relativePath) {
54
+ const hash = crypto.createHash('sha256');
55
+ hash.update(relativePath);
56
+ const hashHex = hash.digest('hex');
57
+ return hashHex.substring(0, 8);
58
+ }
59
+
60
+ /**
61
+ * 注册ID到路径的映射
62
+ * @param {string} id - 唯一ID
63
+ * @param {string} relativePath - 相对路径
64
+ */
65
+ registerIdPathMapping(id, relativePath) {
66
+ this.idToPathMap.set(id, relativePath);
67
+ }
68
+
69
+ /**
70
+ * 确保目录存在
71
+ */
72
+ async ensureDirectories() {
73
+ await fs.ensureDir(this.builtInDir);
74
+ await fs.ensureDir(this.customDir);
75
+ }
76
+
77
+ /**
78
+ * 加载提供商配置
79
+ * @returns {Object} 提供商配置
80
+ */
81
+ async loadProvidersConfig() {
82
+ if (this.providersConfig) {
83
+ return this.providersConfig;
84
+ }
85
+
86
+ const providersConfigPath = path.join(util.getBuiltInConfigsDir(), 'models/providers.yaml');
87
+
88
+ try {
89
+ if (fs.existsSync(providersConfigPath)) {
90
+ const content = await fs.readFile(providersConfigPath, 'utf-8');
91
+ this.providersConfig = YAML.parse(content);
92
+ logger.info('提供商配置加载成功:', providersConfigPath);
93
+ return this.providersConfig;
94
+ }
95
+ } catch (error) {
96
+ logger.warn(`加载提供商配置失败:`, error.message);
97
+ }
98
+
99
+ this.providersConfig = { providers: {} };
100
+ return this.providersConfig;
101
+ }
102
+
103
+ /**
104
+ * 获取提供商列表
105
+ * @returns {Array} 提供商列表
106
+ */
107
+ async getProviders() {
108
+ const config = await this.loadProvidersConfig();
109
+ return Object.entries(config.providers || {}).map(([key, value]) => ({
110
+ key,
111
+ ...value
112
+ }));
113
+ }
114
+
115
+ /**
116
+ * 获取提供商的默认配置
117
+ * @param {string} providerKey - 提供商键名
118
+ * @returns {Object} 提供商默认配置
119
+ */
120
+ async getProviderDefaults(providerKey) {
121
+ const config = await this.loadProvidersConfig();
122
+ const provider = config.providers?.[providerKey];
123
+
124
+ if (!provider) {
125
+ return null;
126
+ }
127
+
128
+ return {
129
+ provider: provider.name,
130
+ model: provider.defaultModel,
131
+ apiEndpoint: provider.defaultEndpoint,
132
+ models: provider.models || []
133
+ };
134
+ }
135
+
136
+ /**
137
+ * 加密 API Key
138
+ * @param {string} text - 要加密的文本
139
+ * @returns {string} 加密后的文本
140
+ */
141
+ encrypt(text) {
142
+ if (!text) return '';
143
+
144
+ try {
145
+ const iv = crypto.randomBytes(16);
146
+ const key = crypto.scryptSync(ENCRYPTION_KEY, 'salt', 32);
147
+ const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
148
+
149
+ let encrypted = cipher.update(text, 'utf8', 'hex');
150
+ encrypted += cipher.final('hex');
151
+
152
+ return iv.toString('hex') + ':' + encrypted;
153
+ } catch (error) {
154
+ logger.error('加密失败:', error);
155
+ throw error;
156
+ }
157
+ }
158
+
159
+ /**
160
+ * 解密 API Key
161
+ * @param {string} text - 要解密的文本
162
+ * @returns {string} 解密后的文本
163
+ */
164
+ decrypt(text) {
165
+ if (!text) return '';
166
+
167
+ try {
168
+ const parts = text.split(':');
169
+ const iv = Buffer.from(parts.shift(), 'hex');
170
+ const encrypted = parts.join(':');
171
+ const key = crypto.scryptSync(ENCRYPTION_KEY, 'salt', 32);
172
+ const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
173
+
174
+ let decrypted = decipher.update(encrypted, 'hex', 'utf8');
175
+ decrypted += decipher.final('utf8');
176
+
177
+ return decrypted;
178
+ } catch (error) {
179
+ logger.error('解密失败:', error);
180
+ throw error;
181
+ }
182
+ }
183
+
184
+ /**
185
+ * 加载所有模型
186
+ */
187
+ async loadModels() {
188
+ try {
189
+ logger.info('开始加载模型配置');
190
+
191
+ // 确保目录存在
192
+ await this.ensureDirectories();
193
+
194
+ // 清空之前的加载结果
195
+ this.loadedModels.clear();
196
+ this.idToPathMap.clear();
197
+
198
+ // 加载内置模型
199
+ await this.loadModelsFromDir(this.builtInDir, true);
200
+
201
+ // 加载用户自定义模型
202
+ await this.loadModelsFromDir(this.customDir, false);
203
+
204
+ logger.info(`模型加载完成: 共 ${this.loadedModels.size} 个模型`);
205
+
206
+ return {
207
+ success: this.loadedModels.size,
208
+ models: Array.from(this.loadedModels.values())
209
+ };
210
+ } catch (error) {
211
+ logger.error('加载模型时发生错误:', error);
212
+ throw error;
213
+ }
214
+ }
215
+
216
+ /**
217
+ * 从指定目录加载模型
218
+ * @param {string} dir - 目录路径
219
+ * @param {boolean} isBuiltIn - 是否为内置模型
220
+ */
221
+ async loadModelsFromDir(dir, isBuiltIn) {
222
+ try {
223
+ if (!fs.existsSync(dir)) {
224
+ logger.warn(`模型目录不存在: ${dir}`);
225
+ return;
226
+ }
227
+
228
+ const files = await fs.readdir(dir);
229
+ const modelFiles = files.filter(file => {
230
+ const ext = path.extname(file).toLowerCase();
231
+ return ['.yaml', '.yml', '.json'].includes(ext);
232
+ });
233
+
234
+ for (const fileName of modelFiles) {
235
+ const filePath = path.join(dir, fileName);
236
+ const relativePath = fileName;
237
+
238
+ try {
239
+ let content = await fs.readFile(filePath, 'utf8');
240
+ const ext = path.extname(fileName).toLowerCase();
241
+
242
+ // 环境变量替换 (支持 ${VAR} 格式)
243
+ content = content.replace(/\$\{([^}]+)\}/g, (match, varName) => {
244
+ return process.env[varName] || '';
245
+ });
246
+
247
+ let modelData;
248
+ if (ext === '.json') {
249
+ modelData = JSON.parse(content);
250
+ } else {
251
+ modelData = YAML.parse(content);
252
+ }
253
+
254
+ // 验证模型数据结构
255
+ const validatedModel = ModelSchema.parse(modelData);
256
+
257
+ // 添加元数据
258
+ validatedModel.isBuiltIn = isBuiltIn;
259
+ validatedModel.filePath = filePath;
260
+
261
+ // 解密 API Key(仅对自定义模型)
262
+ if (validatedModel.apiKey && !isBuiltIn) {
263
+ try {
264
+ validatedModel.apiKey = this.decrypt(validatedModel.apiKey);
265
+ } catch (error) {
266
+ logger.warn(`解密模型 ${validatedModel.name} 的 API Key 失败,将使用空值`);
267
+ validatedModel.apiKey = '';
268
+ }
269
+ }
270
+ // 内置模型的 API Key 直接使用配置文件中的值(明文)
271
+
272
+ // 生成唯一ID
273
+ const uniqueId = this.generateUniqueId(relativePath);
274
+ validatedModel.id = uniqueId;
275
+
276
+ // 注册ID到路径的映射
277
+ this.registerIdPathMapping(uniqueId, relativePath);
278
+
279
+ // 存储模型
280
+ this.loadedModels.set(uniqueId, validatedModel);
281
+
282
+ logger.debug(`加载模型: ${validatedModel.name} -> ID: ${uniqueId} (${isBuiltIn ? '内置' : '自定义'})`);
283
+ } catch (error) {
284
+ logger.error(`加载模型文件 ${fileName} 失败:`, error.message);
285
+ }
286
+ }
287
+ } catch (error) {
288
+ logger.error(`扫描模型目录 ${dir} 时发生错误:`, error.message);
289
+ }
290
+ }
291
+
292
+ /**
293
+ * 获取所有已加载的模型
294
+ */
295
+ getModels() {
296
+ return Array.from(this.loadedModels.values());
297
+ }
298
+
299
+ /**
300
+ * 根据ID获取模型
301
+ * @param {string} id - 模型ID
302
+ */
303
+ getModel(id) {
304
+ return this.loadedModels.get(id) || null;
305
+ }
306
+
307
+ /**
308
+ * 创建新模型
309
+ * @param {Object} modelData - 模型数据
310
+ * @returns {Object} 创建的模型
311
+ */
312
+ async createModel(modelData) {
313
+ try {
314
+ // 验证模型数据
315
+ const validatedModel = ModelSchema.parse(modelData);
316
+
317
+ // 生成文件名
318
+ const fileName = `${validatedModel.name.replace(/\s+/g, '_')}.yaml`;
319
+ const filePath = path.join(this.customDir, fileName);
320
+
321
+ // 检查文件是否已存在
322
+ if (fs.existsSync(filePath)) {
323
+ throw new Error(`模型 ${validatedModel.name} 已存在`);
324
+ }
325
+
326
+ // 加密 API Key
327
+ const encryptedApiKey = validatedModel.apiKey ? this.encrypt(validatedModel.apiKey) : '';
328
+
329
+ // 准备保存的数据
330
+ const dataToSave = {
331
+ name: validatedModel.name,
332
+ provider: validatedModel.provider,
333
+ model: validatedModel.model,
334
+ apiEndpoint: validatedModel.apiEndpoint,
335
+ apiKey: encryptedApiKey,
336
+ enabled: validatedModel.enabled !== false
337
+ };
338
+
339
+ // 保存为 YAML 文件
340
+ await fs.writeFile(filePath, YAML.stringify(dataToSave), 'utf8');
341
+
342
+ // 生成唯一ID
343
+ const uniqueId = this.generateUniqueId(fileName);
344
+
345
+ // 更新模型数据
346
+ validatedModel.id = uniqueId;
347
+ validatedModel.isBuiltIn = false;
348
+ validatedModel.filePath = filePath;
349
+
350
+ // 注册ID到路径的映射
351
+ this.registerIdPathMapping(uniqueId, fileName);
352
+
353
+ // 存储到内存
354
+ this.loadedModels.set(uniqueId, validatedModel);
355
+
356
+ logger.info(`创建模型: ${validatedModel.name} -> ID: ${uniqueId}`);
357
+
358
+ return validatedModel;
359
+ } catch (error) {
360
+ logger.error('创建模型失败:', error);
361
+ throw error;
362
+ }
363
+ }
364
+
365
+ /**
366
+ * 更新模型
367
+ * @param {string} id - 模型ID
368
+ * @param {Object} modelData - 新的模型数据
369
+ * @returns {Object} 更新后的模型
370
+ */
371
+ async updateModel(id, modelData) {
372
+ try {
373
+ const existingModel = this.getModel(id);
374
+
375
+ if (!existingModel) {
376
+ throw new Error(`模型不存在: ${id}`);
377
+ }
378
+
379
+ // 内置模型不能修改
380
+ if (existingModel.isBuiltIn) {
381
+ throw new Error('内置模型不能修改');
382
+ }
383
+
384
+ // 验证模型数据
385
+ const validatedModel = ModelSchema.parse(modelData);
386
+
387
+ // 加密 API Key
388
+ const encryptedApiKey = validatedModel.apiKey ? this.encrypt(validatedModel.apiKey) : '';
389
+
390
+ // 准备保存的数据
391
+ const dataToSave = {
392
+ name: validatedModel.name,
393
+ provider: validatedModel.provider,
394
+ model: validatedModel.model,
395
+ apiEndpoint: validatedModel.apiEndpoint,
396
+ apiKey: encryptedApiKey,
397
+ enabled: validatedModel.enabled !== false
398
+ };
399
+
400
+ // 更新文件
401
+ await fs.writeFile(existingModel.filePath, YAML.stringify(dataToSave), 'utf8');
402
+
403
+ // 更新内存中的数据
404
+ existingModel.name = validatedModel.name;
405
+ existingModel.provider = validatedModel.provider;
406
+ existingModel.model = validatedModel.model;
407
+ existingModel.apiEndpoint = validatedModel.apiEndpoint;
408
+ existingModel.apiKey = validatedModel.apiKey;
409
+ existingModel.enabled = validatedModel.enabled;
410
+
411
+ logger.info(`更新模型: ${existingModel.name} -> ID: ${id}`);
412
+
413
+ return existingModel;
414
+ } catch (error) {
415
+ logger.error('更新模型失败:', error);
416
+ throw error;
417
+ }
418
+ }
419
+
420
+ /**
421
+ * 删除模型
422
+ * @param {string} id - 模型ID
423
+ */
424
+ async deleteModel(id) {
425
+ try {
426
+ const model = this.getModel(id);
427
+
428
+ if (!model) {
429
+ throw new Error(`模型不存在: ${id}`);
430
+ }
431
+
432
+ // 内置模型不能删除
433
+ if (model.isBuiltIn) {
434
+ throw new Error('内置模型不能删除');
435
+ }
436
+
437
+ // 删除文件
438
+ await fs.remove(model.filePath);
439
+
440
+ // 从内存中移除
441
+ this.loadedModels.delete(id);
442
+ this.idToPathMap.delete(id);
443
+
444
+ logger.info(`删除模型: ${model.name} -> ID: ${id}`);
445
+ } catch (error) {
446
+ logger.error('删除模型失败:', error);
447
+ throw error;
448
+ }
449
+ }
450
+
451
+ /**
452
+ * 重新加载模型
453
+ */
454
+ async reloadModels() {
455
+ logger.info('重新加载模型');
456
+ return await this.loadModels();
457
+ }
458
+
459
+ /**
460
+ * 验证模型数据结构
461
+ * @param {Object} modelData - 要验证的模型数据
462
+ * @returns {Object} 验证后的模型数据
463
+ */
464
+ validateModelData(modelData) {
465
+ return ModelSchema.parse(modelData);
466
+ }
467
+ }
468
+
469
+ // 创建全局ModelManager实例
470
+ export const modelManager = new ModelManager();
471
+
472
+ // 导出ModelManager类供测试使用
473
+ export { ModelManager };