@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.
- package/package.json +31 -24
- package/packages/resources/tools/agent-browser/README.md +640 -0
- package/packages/resources/tools/agent-browser/agent-browser.tool.js +1389 -0
- package/packages/resources/tools/thinking/README.md +324 -0
- package/packages/resources/tools/thinking/thinking.tool.js +911 -0
- package/packages/server/README.md +3 -4
- package/packages/server/api/admin.routes.js +668 -664
- package/packages/server/api/open.routes.js +68 -67
- package/packages/server/api/surge.routes.js +5 -6
- package/packages/server/api/tool.routes.js +70 -71
- package/packages/server/app.js +70 -73
- package/packages/server/configs/authors.json +40 -0
- package/packages/server/configs/models/built-in/bigmodel.yaml +6 -6
- package/packages/server/configs/models/providers.yaml +4 -4
- package/packages/server/configs/templates/built-in/general-iteration.yaml +1 -1
- package/packages/server/configs/templates/built-in/general-optimize.yaml +1 -1
- package/packages/server/configs/templates/built-in/output-format-optimize.yaml +1 -1
- package/packages/server/index.js +3 -9
- package/packages/server/mcp/heartbeat-patch.js +1 -3
- package/packages/server/mcp/mcp.server.js +64 -134
- package/packages/server/mcp/prompt.handler.js +101 -95
- package/packages/server/middlewares/auth.middleware.js +31 -31
- package/packages/server/server.js +60 -45
- package/packages/server/services/TerminalService.js +156 -70
- package/packages/server/services/WebSocketService.js +35 -34
- package/packages/server/services/author-config.service.js +199 -0
- package/packages/server/services/manager.js +66 -60
- package/packages/server/services/model.service.js +5 -9
- package/packages/server/services/optimization.service.js +25 -22
- package/packages/server/services/template.service.js +3 -8
- package/packages/server/toolm/author-sync.service.js +97 -0
- package/packages/server/toolm/index.js +1 -2
- package/packages/server/toolm/package-installer.service.js +47 -50
- package/packages/server/toolm/tool-context.service.js +64 -62
- package/packages/server/toolm/tool-dependency.service.js +28 -30
- package/packages/server/toolm/tool-description-generator-optimized.service.js +55 -55
- package/packages/server/toolm/tool-description-generator.service.js +20 -23
- package/packages/server/toolm/tool-environment.service.js +45 -44
- package/packages/server/toolm/tool-execution.service.js +49 -48
- package/packages/server/toolm/tool-loader.service.js +13 -18
- package/packages/server/toolm/tool-logger.service.js +33 -39
- package/packages/server/toolm/tool-manager.handler.js +17 -15
- package/packages/server/toolm/tool-manual-generator.service.js +107 -87
- package/packages/server/toolm/tool-mode-handlers.service.js +52 -59
- package/packages/server/toolm/tool-storage.service.js +11 -12
- package/packages/server/toolm/tool-sync.service.js +36 -39
- package/packages/server/toolm/tool-utils.js +0 -1
- package/packages/server/toolm/tool-yaml-parser.service.js +12 -11
- package/packages/server/toolm/validate-system.js +56 -84
- package/packages/server/utils/config.js +97 -12
- package/packages/server/utils/logger.js +1 -1
- package/packages/server/utils/port-checker.js +8 -8
- package/packages/server/utils/util.js +470 -467
- package/packages/resources/tools/cognitive-thinking/README.md +0 -284
- package/packages/resources/tools/cognitive-thinking/cognitive-thinking.tool.js +0 -837
- package/packages/server/mcp/sequential-thinking.handler.js +0 -318
- package/packages/server/mcp/think-plan.handler.js +0 -274
- package/packages/server/mcp/thinking-toolkit.handler.js +0 -380
- package/packages/web/0.d1c5a72339dfc32ad86a.js +0 -1
- package/packages/web/112.8807b976372b2b0541a8.js +0 -1
- package/packages/web/130.584c7e365da413f5d9be.js +0 -1
- package/packages/web/142.72c985bc29720f975cca.js +0 -1
- package/packages/web/165.a05fc53bf84d18db36b8.js +0 -2
- package/packages/web/165.a05fc53bf84d18db36b8.js.LICENSE.txt +0 -9
- package/packages/web/203.724ab9f717b80554c397.js +0 -1
- package/packages/web/241.bf941d4f02866795f64a.js +0 -1
- package/packages/web/249.54cfb224af63f5f5ec55.js +0 -1
- package/packages/web/291.6df35042f8f296fca7cd.js +0 -1
- package/packages/web/319.2fab900a31b29873f666.js +0 -1
- package/packages/web/32.c78d866281995ec33a7b.js +0 -1
- package/packages/web/325.9ca297d0f73f38468ce9.js +0 -1
- package/packages/web/366.2f9b48fdbf8eee039e57.js +0 -1
- package/packages/web/378.6be08c612cd5a3ef97dc.js +0 -1
- package/packages/web/393.7a2f817515c5e90623d7.js +0 -1
- package/packages/web/412.062df5f732d5ba203415.js +0 -1
- package/packages/web/426.08656fef4918b3fb19ad.js +0 -1
- package/packages/web/465.2be8018327130a3bd798.js +0 -1
- package/packages/web/48.8ca96fc93667a715e67a.js +0 -1
- package/packages/web/480.44c1f1a2927486ac3d4f.js +0 -1
- package/packages/web/489.e041a8d0db15dc96d607.js +0 -1
- package/packages/web/490.9ffb26c907de020d671b.js +0 -1
- package/packages/web/492.58781369e348d91fc06a.js +0 -1
- package/packages/web/495.ed63e99791a87167c6b3.js +0 -1
- package/packages/web/510.4cc07ab7d30d5c1cd17f.js +0 -1
- package/packages/web/543.3af155ed4fa237664308.js +0 -1
- package/packages/web/567.f04ab60f8e2c2fb0745a.js +0 -1
- package/packages/web/592.f3ad085fa9c1849daa06.js +0 -1
- package/packages/web/616.b03fb801b3433b17750f.js +0 -1
- package/packages/web/617.d88def54921d2c4dc44c.js +0 -1
- package/packages/web/641.d30787d674f548928261.js +0 -1
- package/packages/web/672.5269c8399fa42a5af95d.js +0 -1
- package/packages/web/731.97cab92b71811c502bda.js +0 -1
- package/packages/web/746.3947c6f0235407e420fb.js +0 -1
- package/packages/web/756.a53233b3f3913900d5ac.js +0 -1
- package/packages/web/77.68801af593a28a631fbf.js +0 -1
- package/packages/web/802.53b2bff3cf2a69f7b80c.js +0 -1
- package/packages/web/815.b6dfab82265f56c7e046.js +0 -1
- package/packages/web/821.f5a13e5c735aac244eb9.js +0 -1
- package/packages/web/846.b9bf97d5f559270675ce.js +0 -1
- package/packages/web/869.7c10403f500e6201407f.js +0 -1
- package/packages/web/885.135050364f99e6924fb5.js +0 -1
- package/packages/web/901.fd5aeb9df630609a2b43.js +0 -1
- package/packages/web/928.f67e590de3caa4daa3ae.js +0 -1
- package/packages/web/955.d833403521ba4dd567ee.js +0 -1
- package/packages/web/981.a45cb745cf424044c8c8.js +0 -1
- package/packages/web/992.645320b60c74c8787482.js +0 -1
- package/packages/web/996.ed9a963dc9e7439eca9a.js +0 -1
- package/packages/web/css/codemirror-theme_xq-light.css +0 -43
- package/packages/web/css/codemirror.css +0 -344
- package/packages/web/css/main.196f434e6a88cd448158.css +0 -7278
- package/packages/web/css/terminal-fix.css +0 -571
- package/packages/web/index.html +0 -3
- package/packages/web/main.dceff50c7307dda04873.js +0 -2
- package/packages/web/main.dceff50c7307dda04873.js.LICENSE.txt +0 -3
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 作者配置加载服务
|
|
3
|
+
*
|
|
4
|
+
* 职责:
|
|
5
|
+
* - 从配置文件加载作者信息
|
|
6
|
+
* - 构建别名映射
|
|
7
|
+
* - 提供作者查询接口
|
|
8
|
+
*
|
|
9
|
+
* 注意:此服务只负责读取配置,不允许在程序中设置作者信息
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import fs from 'fs-extra';
|
|
13
|
+
import { logger } from '../utils/logger.js';
|
|
14
|
+
import { util } from '../utils/util.js';
|
|
15
|
+
|
|
16
|
+
class AuthorConfigService {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.config = null;
|
|
19
|
+
this.aliasMap = new Map();
|
|
20
|
+
this.cacheTTL = 24 * 60 * 60 * 1000;
|
|
21
|
+
this.lastLoadTime = 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async loadConfig() {
|
|
25
|
+
const now = Date.now();
|
|
26
|
+
|
|
27
|
+
if (this.config && now - this.lastLoadTime < this.cacheTTL) {
|
|
28
|
+
return this.config;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const configPath = util.getDefaultUserConfigPath();
|
|
32
|
+
logger.debug('Loading author config from path', { configPath });
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const configData = await fs.readJson(configPath);
|
|
36
|
+
|
|
37
|
+
if (!configData.authors) {
|
|
38
|
+
throw new Error('配置文件格式错误:缺少 authors 字段');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.config = configData;
|
|
42
|
+
this.lastLoadTime = now;
|
|
43
|
+
|
|
44
|
+
this.buildAliasMap();
|
|
45
|
+
|
|
46
|
+
logger.info('Author config loaded successfully', {
|
|
47
|
+
authorCount: Object.keys(configData.authors).length
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return this.config;
|
|
51
|
+
} catch (error) {
|
|
52
|
+
if (error.code === 'ENOENT') {
|
|
53
|
+
logger.warn('作者配置文件不存在,将使用默认配置', { path: configPath });
|
|
54
|
+
return this.getDefaultConfig();
|
|
55
|
+
} else {
|
|
56
|
+
logger.error('加载作者配置失败:', error);
|
|
57
|
+
return this.getDefaultConfig();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
buildAliasMap() {
|
|
63
|
+
this.aliasMap.clear();
|
|
64
|
+
|
|
65
|
+
if (!this.config || !this.config.authors) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
for (const [authorId, author] of Object.entries(this.config.authors)) {
|
|
70
|
+
this.aliasMap.set(authorId.toLowerCase(), authorId);
|
|
71
|
+
|
|
72
|
+
if (author.name) {
|
|
73
|
+
this.aliasMap.set(author.name.toLowerCase(), authorId);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (author.aliases && Array.isArray(author.aliases)) {
|
|
77
|
+
for (const alias of author.aliases) {
|
|
78
|
+
this.aliasMap.set(alias.toLowerCase(), authorId);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async resolveAuthor(authorInput) {
|
|
85
|
+
await this.loadConfig();
|
|
86
|
+
|
|
87
|
+
if (!this.config || !this.config.settings) {
|
|
88
|
+
logger.debug('Using default author info (config not loaded)');
|
|
89
|
+
return this.getDefaultAuthorInfo();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!authorInput) {
|
|
93
|
+
const defaultAuthorId = this.config.settings.default_author;
|
|
94
|
+
return this.getAuthorInfo(defaultAuthorId);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const normalized = authorInput.toString().trim().toLowerCase();
|
|
98
|
+
|
|
99
|
+
if (this.aliasMap.has(normalized)) {
|
|
100
|
+
const authorId = this.aliasMap.get(normalized);
|
|
101
|
+
logger.debug('Author resolved via alias map', { alias: authorInput, authorId });
|
|
102
|
+
return this.getAuthorInfo(authorId);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
logger.warn(`未知作者 "${authorInput}",将使用默认作者`);
|
|
106
|
+
const defaultAuthorId = this.config.settings.default_author;
|
|
107
|
+
return this.getAuthorInfo(defaultAuthorId);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
getGitHubAvatarUrl(githubUrl) {
|
|
111
|
+
if (!githubUrl) return null;
|
|
112
|
+
|
|
113
|
+
/*eslint no-useless-escape: "off"*/
|
|
114
|
+
const match = githubUrl.match(/github\.com\/([^\/]+)/i);
|
|
115
|
+
if (!match) return null;
|
|
116
|
+
return `https://github.com/${match[1]}.png`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
getAuthorInfo(authorId) {
|
|
120
|
+
if (!this.config || !this.config.authors) {
|
|
121
|
+
return this.getDefaultAuthorInfo();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const author = this.config.authors[authorId];
|
|
125
|
+
|
|
126
|
+
if (!author) {
|
|
127
|
+
logger.warn(`作者 "${authorId}" 不存在于配置中`);
|
|
128
|
+
return this.getDefaultAuthorInfo();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
id: authorId,
|
|
133
|
+
name: author.name || authorId,
|
|
134
|
+
github: author.github || null,
|
|
135
|
+
homepage: author.homepage || null,
|
|
136
|
+
bio: author.bio || '开发者',
|
|
137
|
+
featured: author.featured || false,
|
|
138
|
+
sort_order: author.sort_order || 999,
|
|
139
|
+
aliases: author.aliases || [],
|
|
140
|
+
avatar_url: author.github ? this.getGitHubAvatarUrl(author.github) : null
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async getAllAuthors() {
|
|
145
|
+
await this.loadConfig();
|
|
146
|
+
|
|
147
|
+
if (!this.config || !this.config.authors) {
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const authors = Object.entries(this.config.authors)
|
|
152
|
+
.map(([id, author]) => ({
|
|
153
|
+
id,
|
|
154
|
+
name: author.name || id,
|
|
155
|
+
github: author.github || null,
|
|
156
|
+
homepage: author.homepage || null,
|
|
157
|
+
bio: author.bio || '开发者',
|
|
158
|
+
featured: author.featured || false,
|
|
159
|
+
sort_order: author.sort_order || 999,
|
|
160
|
+
aliases: author.aliases || []
|
|
161
|
+
}))
|
|
162
|
+
.sort((a, b) => a.sort_order - b.sort_order);
|
|
163
|
+
|
|
164
|
+
return authors;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
getDefaultAuthorInfo() {
|
|
168
|
+
return {
|
|
169
|
+
id: 'default',
|
|
170
|
+
name: 'Unknown',
|
|
171
|
+
github: null,
|
|
172
|
+
homepage: null,
|
|
173
|
+
bio: '开发者',
|
|
174
|
+
featured: false,
|
|
175
|
+
sort_order: 999,
|
|
176
|
+
aliases: []
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
getDefaultConfig() {
|
|
181
|
+
return {
|
|
182
|
+
version: '1.0.0',
|
|
183
|
+
last_updated: new Date().toISOString(),
|
|
184
|
+
authors: {},
|
|
185
|
+
settings: {
|
|
186
|
+
default_author: 'default',
|
|
187
|
+
cache_avatar_hours: 24
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async reload() {
|
|
193
|
+
this.lastLoadTime = 0;
|
|
194
|
+
this.aliasMap.clear();
|
|
195
|
+
return await this.loadConfig();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export const authorConfigService = new AuthorConfigService();
|
|
@@ -13,18 +13,27 @@ import { GROUP_META_FILENAME } from '../utils/util.js';
|
|
|
13
13
|
const PromptSchema = z.object({
|
|
14
14
|
name: z.string().min(1, 'Prompt名称不能为空'),
|
|
15
15
|
description: z.string().optional(),
|
|
16
|
-
messages: z
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
16
|
+
messages: z
|
|
17
|
+
.array(
|
|
18
|
+
z.object({
|
|
19
|
+
role: z.enum(['user', 'assistant', 'system']),
|
|
20
|
+
content: z.object({
|
|
21
|
+
text: z.string()
|
|
22
|
+
})
|
|
23
|
+
})
|
|
24
|
+
)
|
|
25
|
+
.optional(),
|
|
26
|
+
arguments: z
|
|
27
|
+
.array(
|
|
28
|
+
z.object({
|
|
29
|
+
name: z.string().min(1, '参数名不能为空'),
|
|
30
|
+
description: z.string().optional(),
|
|
31
|
+
type: z.enum(['string', 'number', 'boolean']).optional().default('string'),
|
|
32
|
+
required: z.boolean().optional().default(true)
|
|
33
|
+
})
|
|
34
|
+
)
|
|
35
|
+
.optional()
|
|
36
|
+
.default([]),
|
|
28
37
|
// 元数据字段(可选,用于远程服务)
|
|
29
38
|
uniqueId: z.string().optional(),
|
|
30
39
|
filePath: z.string().optional(),
|
|
@@ -53,11 +62,11 @@ class PromptManager {
|
|
|
53
62
|
const hash = crypto.createHash('sha256');
|
|
54
63
|
hash.update(relativePath);
|
|
55
64
|
const hashHex = hash.digest('hex');
|
|
56
|
-
|
|
65
|
+
|
|
57
66
|
// 取前8位作为ID,保证长度一致
|
|
58
67
|
// 8位十六进制可以表示 16^8 = 4,294,967,296 种不同的值,足够保证唯一性
|
|
59
68
|
const shortId = hashHex.substring(0, 8);
|
|
60
|
-
|
|
69
|
+
|
|
61
70
|
return shortId;
|
|
62
71
|
}
|
|
63
72
|
|
|
@@ -71,10 +80,10 @@ class PromptManager {
|
|
|
71
80
|
const hash = crypto.createHash('sha256');
|
|
72
81
|
hash.update(relativePath);
|
|
73
82
|
const hashHex = hash.digest('hex');
|
|
74
|
-
|
|
83
|
+
|
|
75
84
|
// 取指定长度的字符作为ID
|
|
76
85
|
const shortId = hashHex.substring(0, length);
|
|
77
|
-
|
|
86
|
+
|
|
78
87
|
return shortId;
|
|
79
88
|
}
|
|
80
89
|
|
|
@@ -88,7 +97,7 @@ class PromptManager {
|
|
|
88
97
|
}
|
|
89
98
|
|
|
90
99
|
/**
|
|
91
|
-
* 读取组元数据的函数
|
|
100
|
+
* 读取组元数据的函数
|
|
92
101
|
* @param {string} dir - 目录路径
|
|
93
102
|
* @returns {Object} 组元数据
|
|
94
103
|
*/
|
|
@@ -131,13 +140,13 @@ class PromptManager {
|
|
|
131
140
|
};
|
|
132
141
|
|
|
133
142
|
const response = await fetch(config.remoteUrl, { headers });
|
|
134
|
-
|
|
143
|
+
|
|
135
144
|
if (!response.ok) {
|
|
136
145
|
throw new Error(`远程服务器返回错误: ${response.status} ${response.statusText}`);
|
|
137
146
|
}
|
|
138
147
|
|
|
139
148
|
const data = await response.json();
|
|
140
|
-
|
|
149
|
+
|
|
141
150
|
// 清空之前的加载结果
|
|
142
151
|
this.loadedPrompts.clear();
|
|
143
152
|
this.loadErrors.clear();
|
|
@@ -149,20 +158,20 @@ class PromptManager {
|
|
|
149
158
|
for (const promptData of data) {
|
|
150
159
|
try {
|
|
151
160
|
const validatedPrompt = PromptSchema.parse(promptData);
|
|
152
|
-
|
|
161
|
+
|
|
153
162
|
// 检查远程服务是否提供了uniqueId
|
|
154
163
|
if (promptData.uniqueId) {
|
|
155
164
|
// 远程服务提供了uniqueId,直接使用
|
|
156
165
|
validatedPrompt.uniqueId = promptData.uniqueId;
|
|
157
|
-
|
|
166
|
+
|
|
158
167
|
// 可选:如果提供了其他元数据字段就使用,没提供就设置默认值
|
|
159
168
|
validatedPrompt.filePath = promptData.filePath || `remote://${validatedPrompt.name}`;
|
|
160
169
|
validatedPrompt.fileName = promptData.fileName || `${validatedPrompt.name}.yaml`;
|
|
161
170
|
validatedPrompt.relativePath = promptData.relativePath || `${validatedPrompt.name}.yaml`;
|
|
162
|
-
|
|
171
|
+
|
|
163
172
|
// 注册ID到路径的映射
|
|
164
173
|
this.registerIdPathMapping(promptData.uniqueId, validatedPrompt.relativePath);
|
|
165
|
-
|
|
174
|
+
|
|
166
175
|
// 使用唯一ID作为存储键
|
|
167
176
|
this.loadedPrompts.set(promptData.uniqueId, validatedPrompt);
|
|
168
177
|
logger.debug(`使用远程服务提供的uniqueId: ${validatedPrompt.name} -> ID: ${promptData.uniqueId}`);
|
|
@@ -174,28 +183,28 @@ class PromptManager {
|
|
|
174
183
|
validatedPrompt.filePath = `remote://${validatedPrompt.name}`;
|
|
175
184
|
validatedPrompt.fileName = `${validatedPrompt.name}.yaml`;
|
|
176
185
|
validatedPrompt.relativePath = virtualPath;
|
|
177
|
-
|
|
186
|
+
|
|
178
187
|
// 注册ID到路径的映射
|
|
179
188
|
this.registerIdPathMapping(uniqueId, virtualPath);
|
|
180
|
-
|
|
189
|
+
|
|
181
190
|
// 使用唯一ID作为存储键
|
|
182
191
|
this.loadedPrompts.set(uniqueId, validatedPrompt);
|
|
183
192
|
logger.debug(`使用兼容模式加载远程prompt: ${validatedPrompt.name} -> ID: ${uniqueId}`);
|
|
184
193
|
}
|
|
185
|
-
|
|
194
|
+
|
|
186
195
|
successCount++;
|
|
187
196
|
} catch (error) {
|
|
188
197
|
errorCount++;
|
|
189
198
|
this.loadErrors.set(promptData.name || 'unknown', error.message);
|
|
190
|
-
logger.error(
|
|
199
|
+
logger.error('验证远程prompt失败:', error.message);
|
|
191
200
|
}
|
|
192
201
|
}
|
|
193
202
|
|
|
194
203
|
logger.info(`远程Prompt加载完成: 成功 ${successCount} 个, 失败 ${errorCount} 个`);
|
|
195
|
-
|
|
204
|
+
|
|
196
205
|
return {
|
|
197
206
|
success: successCount,
|
|
198
|
-
errorCount
|
|
207
|
+
errorCount,
|
|
199
208
|
prompts: Array.from(this.loadedPrompts.values()),
|
|
200
209
|
loadErrors: Object.fromEntries(this.loadErrors)
|
|
201
210
|
};
|
|
@@ -207,7 +216,7 @@ class PromptManager {
|
|
|
207
216
|
|
|
208
217
|
/**
|
|
209
218
|
* 递归扫描目录,获取所有prompt文件
|
|
210
|
-
*
|
|
219
|
+
*
|
|
211
220
|
* @param {string} currentPath - 当前目录路径
|
|
212
221
|
* @param {string} relativePath - 相对于当前目录的路径
|
|
213
222
|
* @param {boolean} inheritedEnabled - 从父目录继承的启用状态
|
|
@@ -220,20 +229,20 @@ class PromptManager {
|
|
|
220
229
|
if (relativePath) {
|
|
221
230
|
// console.log('读取组元数据:', "{relativePath:", relativePath, ", currentPath:", currentPath, "}");
|
|
222
231
|
const meta = this.readGroupMeta(path.join(currentPath, relativePath));
|
|
223
|
-
currentEnabled = currentEnabled &&
|
|
232
|
+
currentEnabled = currentEnabled && meta.enabled !== false;
|
|
224
233
|
}
|
|
225
234
|
|
|
226
235
|
// 如果当前目录被禁用,直接返回空数组
|
|
227
236
|
if (!currentEnabled) {
|
|
228
237
|
return [];
|
|
229
238
|
}
|
|
230
|
-
|
|
239
|
+
|
|
231
240
|
try {
|
|
232
241
|
const items = await fs.readdir(currentPath, { withFileTypes: true });
|
|
233
242
|
// console.log('扫描目录:', "{currentPath:", currentPath, ", items:", items, "}");
|
|
234
|
-
|
|
243
|
+
|
|
235
244
|
for (const item of items) {
|
|
236
|
-
const itemPath = path.join(currentPath, item.name);
|
|
245
|
+
const itemPath = path.join(currentPath, item.name); // 文件的绝对路径,相对于当前目录,如:/home/work/prompt-manager/prompts/xxx/xxx.yaml
|
|
237
246
|
const itemRelativePath = relativePath ? path.join(relativePath, item.name) : item.name; // 文件的相对路径,相对于当前目录,如:xxx/xxx.yaml
|
|
238
247
|
|
|
239
248
|
if (item.isDirectory()) {
|
|
@@ -255,7 +264,7 @@ class PromptManager {
|
|
|
255
264
|
} catch (error) {
|
|
256
265
|
logger.warn(`扫描目录 ${currentPath} 时发生错误:`, error.message);
|
|
257
266
|
}
|
|
258
|
-
|
|
267
|
+
|
|
259
268
|
return promptFiles;
|
|
260
269
|
}
|
|
261
270
|
|
|
@@ -270,10 +279,10 @@ class PromptManager {
|
|
|
270
279
|
|
|
271
280
|
try {
|
|
272
281
|
logger.info(`开始从 ${this.promptsDir} 加载prompts`);
|
|
273
|
-
|
|
282
|
+
|
|
274
283
|
// 确保目录存在
|
|
275
284
|
await fs.ensureDir(this.promptsDir);
|
|
276
|
-
|
|
285
|
+
|
|
277
286
|
// 根据配置决定是否递归扫描
|
|
278
287
|
let promptFiles;
|
|
279
288
|
if (config.recursiveScan) {
|
|
@@ -292,7 +301,7 @@ class PromptManager {
|
|
|
292
301
|
relativePath: file
|
|
293
302
|
}));
|
|
294
303
|
}
|
|
295
|
-
|
|
304
|
+
|
|
296
305
|
logger.debug(`找到 ${promptFiles.length} 个prompt文件`);
|
|
297
306
|
|
|
298
307
|
// 清空之前的加载结果
|
|
@@ -304,18 +313,16 @@ class PromptManager {
|
|
|
304
313
|
const results = await Promise.allSettled(loadPromises);
|
|
305
314
|
|
|
306
315
|
// 统计加载结果
|
|
307
|
-
let successCount = 0;
|
|
308
316
|
let errorCount = 0;
|
|
309
317
|
|
|
310
|
-
let loadedCount
|
|
318
|
+
let loadedCount = 0;
|
|
311
319
|
let disabledCount = 0;
|
|
312
320
|
|
|
313
321
|
results.forEach((result, index) => {
|
|
314
322
|
if (result.status === 'fulfilled') {
|
|
315
|
-
successCount++;
|
|
316
323
|
if (result.value !== null) {
|
|
317
324
|
loadedCount++;
|
|
318
|
-
}else{
|
|
325
|
+
} else {
|
|
319
326
|
disabledCount++;
|
|
320
327
|
}
|
|
321
328
|
} else {
|
|
@@ -328,10 +335,10 @@ class PromptManager {
|
|
|
328
335
|
});
|
|
329
336
|
|
|
330
337
|
logger.info(`本地Prompt加载完成: 启用 ${loadedCount} 个, 禁用 ${disabledCount} 个, 失败 ${errorCount} 个\n`);
|
|
331
|
-
|
|
338
|
+
|
|
332
339
|
return {
|
|
333
340
|
success: loadedCount,
|
|
334
|
-
errorCount
|
|
341
|
+
errorCount,
|
|
335
342
|
prompts: Array.from(this.loadedPrompts.values()),
|
|
336
343
|
loadErrors: Object.fromEntries(this.loadErrors)
|
|
337
344
|
};
|
|
@@ -349,7 +356,7 @@ class PromptManager {
|
|
|
349
356
|
const fileName = typeof fileInfo === 'string' ? fileInfo : fileInfo.fileName;
|
|
350
357
|
const filePath = typeof fileInfo === 'string' ? path.join(this.promptsDir, fileInfo) : fileInfo.filePath;
|
|
351
358
|
const relativePath = typeof fileInfo === 'string' ? fileInfo : fileInfo.relativePath;
|
|
352
|
-
|
|
359
|
+
|
|
353
360
|
if (fileName === GROUP_META_FILENAME) {
|
|
354
361
|
// 跳过组元数据文件
|
|
355
362
|
return null;
|
|
@@ -358,7 +365,7 @@ class PromptManager {
|
|
|
358
365
|
try {
|
|
359
366
|
const content = await fs.readFile(filePath, 'utf8');
|
|
360
367
|
const ext = path.extname(fileName).toLowerCase();
|
|
361
|
-
|
|
368
|
+
|
|
362
369
|
let promptData;
|
|
363
370
|
if (ext === '.json') {
|
|
364
371
|
promptData = JSON.parse(content);
|
|
@@ -373,47 +380,46 @@ class PromptManager {
|
|
|
373
380
|
}
|
|
374
381
|
|
|
375
382
|
// 检查目录启用状态
|
|
376
|
-
const fileDir = path.dirname(filePath);
|
|
377
383
|
const relativeDir = path.dirname(relativePath);
|
|
378
384
|
if (relativeDir && relativeDir !== '.') {
|
|
379
385
|
// 检查从根目录到当前文件路径的所有目录的启用状态
|
|
380
386
|
const dirParts = relativeDir.split(path.sep);
|
|
381
387
|
let currentPath = this.promptsDir;
|
|
382
388
|
let inheritedEnabled = true;
|
|
383
|
-
|
|
389
|
+
|
|
384
390
|
for (const dirPart of dirParts) {
|
|
385
391
|
currentPath = path.join(currentPath, dirPart);
|
|
386
392
|
const meta = this.readGroupMeta(currentPath);
|
|
387
|
-
inheritedEnabled = inheritedEnabled &&
|
|
388
|
-
|
|
393
|
+
inheritedEnabled = inheritedEnabled && meta.enabled !== false;
|
|
394
|
+
|
|
389
395
|
if (!inheritedEnabled) {
|
|
390
396
|
logger.debug(`skipped loading prompt: ${promptData.name} -> parent directory disabled`);
|
|
391
397
|
return null;
|
|
392
398
|
}
|
|
393
399
|
}
|
|
394
400
|
}
|
|
395
|
-
|
|
401
|
+
|
|
396
402
|
logger.debug(`loaded prompt: ${promptData.name} -> ${filePath}`);
|
|
397
403
|
|
|
398
404
|
// 验证prompt数据结构
|
|
399
405
|
const validatedPrompt = PromptSchema.parse(promptData);
|
|
400
|
-
|
|
406
|
+
|
|
401
407
|
// 添加文件路径信息
|
|
402
408
|
validatedPrompt.filePath = filePath;
|
|
403
409
|
validatedPrompt.fileName = fileName;
|
|
404
410
|
validatedPrompt.relativePath = relativePath;
|
|
405
|
-
|
|
411
|
+
|
|
406
412
|
// 生成基于文件路径的唯一ID
|
|
407
413
|
const uniqueId = this.generateUniqueId(relativePath);
|
|
408
414
|
validatedPrompt.uniqueId = uniqueId;
|
|
409
|
-
|
|
415
|
+
|
|
410
416
|
// 注册ID到路径的映射
|
|
411
417
|
this.registerIdPathMapping(uniqueId, relativePath);
|
|
412
|
-
|
|
418
|
+
|
|
413
419
|
// 使用唯一ID作为存储键
|
|
414
420
|
this.loadedPrompts.set(uniqueId, validatedPrompt);
|
|
415
421
|
logger.debug(`成功加载prompt: ${validatedPrompt.name} -> ID: ${uniqueId} (${relativePath})`);
|
|
416
|
-
|
|
422
|
+
|
|
417
423
|
return validatedPrompt;
|
|
418
424
|
} catch (error) {
|
|
419
425
|
if (error instanceof z.ZodError) {
|
|
@@ -440,14 +446,14 @@ class PromptManager {
|
|
|
440
446
|
if (this.loadedPrompts.has(id)) {
|
|
441
447
|
return this.loadedPrompts.get(id);
|
|
442
448
|
}
|
|
443
|
-
|
|
449
|
+
|
|
444
450
|
// 如果直接匹配失败,尝试匹配原始名称(向后兼容)
|
|
445
|
-
for (const
|
|
451
|
+
for (const prompt of this.loadedPrompts.values()) {
|
|
446
452
|
if (prompt.name === id || prompt.relativePath === id) {
|
|
447
453
|
return prompt;
|
|
448
454
|
}
|
|
449
455
|
}
|
|
450
|
-
|
|
456
|
+
|
|
451
457
|
return null;
|
|
452
458
|
}
|
|
453
459
|
|
|
@@ -509,4 +515,4 @@ class PromptManager {
|
|
|
509
515
|
export const promptManager = new PromptManager(config.getPromptsDir());
|
|
510
516
|
|
|
511
517
|
// 导出PromptManager类供测试使用
|
|
512
|
-
export { PromptManager };
|
|
518
|
+
export { PromptManager };
|
|
@@ -1,15 +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
7
|
import { util } from '../utils/util.js';
|
|
10
|
-
|
|
11
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
-
const __dirname = path.dirname(__filename);
|
|
8
|
+
import { config } from '../utils/config.js';
|
|
13
9
|
|
|
14
10
|
/**
|
|
15
11
|
* 模型数据结构验证schema
|
|
@@ -39,7 +35,7 @@ const ALGORITHM = 'aes-256-cbc';
|
|
|
39
35
|
class ModelManager {
|
|
40
36
|
constructor() {
|
|
41
37
|
this.builtInDir = path.join(util.getBuiltInConfigsDir(), 'models/built-in');
|
|
42
|
-
this.customDir =
|
|
38
|
+
this.customDir = config.getModelsDir();
|
|
43
39
|
this.loadedModels = new Map();
|
|
44
40
|
this.idToPathMap = new Map();
|
|
45
41
|
this.providersConfig = null;
|
|
@@ -93,7 +89,7 @@ class ModelManager {
|
|
|
93
89
|
return this.providersConfig;
|
|
94
90
|
}
|
|
95
91
|
} catch (error) {
|
|
96
|
-
logger.warn(
|
|
92
|
+
logger.warn('加载提供商配置失败:', error.message);
|
|
97
93
|
}
|
|
98
94
|
|
|
99
95
|
this.providersConfig = { providers: {} };
|
|
@@ -149,7 +145,7 @@ class ModelManager {
|
|
|
149
145
|
let encrypted = cipher.update(text, 'utf8', 'hex');
|
|
150
146
|
encrypted += cipher.final('hex');
|
|
151
147
|
|
|
152
|
-
return iv.toString('hex')
|
|
148
|
+
return `${iv.toString('hex')}:${encrypted}`;
|
|
153
149
|
} catch (error) {
|
|
154
150
|
logger.error('加密失败:', error);
|
|
155
151
|
throw error;
|
|
@@ -470,4 +466,4 @@ class ModelManager {
|
|
|
470
466
|
export const modelManager = new ModelManager();
|
|
471
467
|
|
|
472
468
|
// 导出ModelManager类供测试使用
|
|
473
|
-
export { ModelManager };
|
|
469
|
+
export { ModelManager };
|