@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
|
@@ -8,87 +8,88 @@ import { util } from '../utils/util.js';
|
|
|
8
8
|
const router = express.Router();
|
|
9
9
|
|
|
10
10
|
router.get('/prompts', (req, res) => {
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
try {
|
|
12
|
+
const prompts = util.getPromptsFromFiles();
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// 检查目录状态 - util.getPromptsFromFiles() 已经正确处理了继承的启用状态
|
|
21
|
-
// groupEnabled 已经考虑了父目录的禁用状态
|
|
22
|
-
const groupActive = prompt.groupEnabled !== false;
|
|
23
|
-
return groupActive;
|
|
24
|
-
});
|
|
25
|
-
logger.debug(`filtered prompts: ${JSON.stringify(filtered)}`);
|
|
14
|
+
// 过滤出启用的提示词
|
|
15
|
+
const filtered = prompts.filter(prompt => {
|
|
16
|
+
// 检查提示词本身是否启用
|
|
17
|
+
const promptActive = prompt.enabled === true;
|
|
18
|
+
if (!promptActive) return false;
|
|
26
19
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
// 检查目录状态 - util.getPromptsFromFiles() 已经正确处理了继承的启用状态
|
|
21
|
+
// groupEnabled 已经考虑了父目录的禁用状态
|
|
22
|
+
const groupActive = prompt.groupEnabled !== false;
|
|
23
|
+
return groupActive;
|
|
24
|
+
});
|
|
25
|
+
logger.debug(`filtered prompts: ${JSON.stringify(filtered)}`);
|
|
30
26
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
prompt: {
|
|
36
|
-
id: prompt.uniqueId,
|
|
37
|
-
name: prompt.name,
|
|
38
|
-
description: prompt.description || `Prompt: ${prompt.name}`,
|
|
39
|
-
metadata: {
|
|
40
|
-
// fileName: prompt.fileName,
|
|
41
|
-
fullPath: prompt.relativePath
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
score: score
|
|
45
|
-
};
|
|
46
|
-
})
|
|
47
|
-
.filter(result => result.score > 0) // 只返回有匹配的结果
|
|
48
|
-
.sort((a, b) => b.score - a.score); // 按相似度得分降序排列
|
|
27
|
+
// 判断是否有搜索参数,且搜索参数名为search
|
|
28
|
+
if (req.query.search) {
|
|
29
|
+
const search = req.query.search;
|
|
49
30
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
31
|
+
// 实现相似度匹配算法
|
|
32
|
+
const searchResults = filtered
|
|
33
|
+
.map(prompt => {
|
|
34
|
+
const score = util.calculateSimilarityScore(search, prompt);
|
|
35
|
+
return {
|
|
36
|
+
prompt: {
|
|
37
|
+
id: prompt.uniqueId,
|
|
38
|
+
name: prompt.name,
|
|
39
|
+
description: prompt.description || `Prompt: ${prompt.name}`,
|
|
40
|
+
metadata: {
|
|
41
|
+
// fileName: prompt.fileName,
|
|
42
|
+
fullPath: prompt.relativePath
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
score
|
|
46
|
+
};
|
|
47
|
+
})
|
|
48
|
+
.filter(result => result.score > 0) // 只返回有匹配的结果
|
|
49
|
+
.sort((a, b) => b.score - a.score); // 按相似度得分降序排列
|
|
63
50
|
|
|
64
|
-
|
|
51
|
+
// 只返回prompt字段
|
|
52
|
+
res.json(searchResults.map(result => result.prompt));
|
|
53
|
+
} else {
|
|
54
|
+
// 无搜索参数时,返回精简信息
|
|
55
|
+
const simplifiedPrompts = filtered.map(prompt => ({
|
|
56
|
+
id: prompt.uniqueId,
|
|
57
|
+
name: prompt.name,
|
|
58
|
+
description: prompt.description || `Prompt: ${prompt.name}`,
|
|
59
|
+
metadata: {
|
|
60
|
+
// fileName: prompt.fileName,
|
|
61
|
+
fullPath: prompt.relativePath
|
|
65
62
|
}
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
}));
|
|
64
|
+
|
|
65
|
+
res.json(simplifiedPrompts);
|
|
68
66
|
}
|
|
67
|
+
} catch (error) {
|
|
68
|
+
res.status(500).json({ error: error.message });
|
|
69
|
+
}
|
|
69
70
|
});
|
|
70
71
|
|
|
71
72
|
// 查看提示词内容
|
|
72
73
|
router.post('/process', async (req, res) => {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (!promptName) {
|
|
77
|
-
return res.status(400).json({ error: 'Missing promptName' });
|
|
78
|
-
}
|
|
74
|
+
try {
|
|
75
|
+
const { promptName, arguments: args } = req.body;
|
|
79
76
|
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
if (!promptName) {
|
|
78
|
+
return res.status(400).json({ error: 'Missing promptName' });
|
|
79
|
+
}
|
|
82
80
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
81
|
+
const prompts = util.getPromptsFromFiles();
|
|
82
|
+
const prompt = prompts.find(p => p.name === promptName);
|
|
86
83
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
} catch (error) {
|
|
90
|
-
res.status(500).json({ error: error.message });
|
|
84
|
+
if (!prompt) {
|
|
85
|
+
return res.status(404).json({ error: `Prompt "${promptName}" not found` });
|
|
91
86
|
}
|
|
87
|
+
|
|
88
|
+
const processedPrompt = await util.processPromptContent(prompt, args);
|
|
89
|
+
res.json({ processedText: processedPrompt });
|
|
90
|
+
} catch (error) {
|
|
91
|
+
res.status(500).json({ error: error.message });
|
|
92
|
+
}
|
|
92
93
|
});
|
|
93
94
|
|
|
94
|
-
export const openRouter = router;
|
|
95
|
+
export const openRouter = router;
|
|
@@ -19,18 +19,17 @@ const surgeProxy = createProxyMiddleware({
|
|
|
19
19
|
// 当路由已经去除了 /surge 前缀后,路径变为 /test_prompts.json
|
|
20
20
|
// 我们需要将其重写为 /assets/test_prompts.json
|
|
21
21
|
pathRewrite: {
|
|
22
|
-
'^/': '/assets/'
|
|
22
|
+
'^/': '/assets/' // 将 / 开头的路径重写为 /assets/ 开头
|
|
23
23
|
},
|
|
24
24
|
// 设置代理请求头
|
|
25
|
-
onProxyReq: (
|
|
25
|
+
onProxyReq: (_proxyReq, _req, _res) => {
|
|
26
26
|
// 可以在这里添加自定义逻辑,如设置请求头等
|
|
27
|
-
console.log(`代理请求: ${req.method} ${req.url} -> ${surgeTarget}${req.url.replace(/^\/(.*)/, '/assets/$1')}`);
|
|
28
27
|
},
|
|
29
|
-
onProxyRes: (proxyRes,
|
|
28
|
+
onProxyRes: (proxyRes, _req, _res) => {
|
|
30
29
|
// 可以在这里添加自定义逻辑,如修改响应头等
|
|
31
30
|
proxyRes.headers['surge-proxy'] = 'enabled';
|
|
32
31
|
},
|
|
33
|
-
onError: (err,
|
|
32
|
+
onError: (err, _req, res) => {
|
|
34
33
|
console.error('Surge 代理错误:', err);
|
|
35
34
|
res.status(500).send('代理错误');
|
|
36
35
|
}
|
|
@@ -40,4 +39,4 @@ const surgeProxy = createProxyMiddleware({
|
|
|
40
39
|
// 由于在 app.js 中已经使用了 /surge 路径前缀,这里直接处理所有请求
|
|
41
40
|
router.use(surgeProxy);
|
|
42
41
|
|
|
43
|
-
export const surgeRouter = router;
|
|
42
|
+
export const surgeRouter = router;
|
|
@@ -11,19 +11,20 @@ import fse from 'fs-extra';
|
|
|
11
11
|
import { logger } from '../utils/logger.js';
|
|
12
12
|
import { toolLoaderService } from '../toolm/tool-loader.service.js';
|
|
13
13
|
import { pathExists } from '../toolm/tool-utils.js';
|
|
14
|
-
import
|
|
14
|
+
import { config } from '../utils/config.js';
|
|
15
|
+
import { authorConfigService } from '../services/author-config.service.js';
|
|
15
16
|
|
|
16
17
|
// 配置 multer 用于处理文件上传
|
|
17
18
|
const upload = multer({
|
|
18
19
|
storage: multer.diskStorage({
|
|
19
20
|
destination: (req, file, cb) => {
|
|
20
|
-
const uploadDir =
|
|
21
|
+
const uploadDir = config.getTempDir();
|
|
21
22
|
fs.ensureDirSync(uploadDir);
|
|
22
23
|
cb(null, uploadDir);
|
|
23
24
|
},
|
|
24
25
|
filename: (req, file, cb) => {
|
|
25
26
|
// 生成唯一文件名,避免并发上传冲突
|
|
26
|
-
const uniqueName = `${Date.now()}-${Math.round(Math.random() *
|
|
27
|
+
const uniqueName = `${Date.now()}-${Math.round(Math.random() * 1e9)}-${file.originalname}`;
|
|
27
28
|
cb(null, uniqueName);
|
|
28
29
|
}
|
|
29
30
|
}),
|
|
@@ -53,14 +54,7 @@ router.get('/list', async (req, res) => {
|
|
|
53
54
|
await toolLoaderService.initialize();
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
const {
|
|
57
|
-
search,
|
|
58
|
-
category,
|
|
59
|
-
tags,
|
|
60
|
-
author,
|
|
61
|
-
page = 1,
|
|
62
|
-
limit = 20
|
|
63
|
-
} = req.query;
|
|
57
|
+
const { search, category, tags, author, page = 1, limit = 20 } = req.query;
|
|
64
58
|
|
|
65
59
|
// 获取所有工具
|
|
66
60
|
let tools = toolLoaderService.getAllTools();
|
|
@@ -76,9 +70,7 @@ router.get('/list', async (req, res) => {
|
|
|
76
70
|
}
|
|
77
71
|
|
|
78
72
|
if (category) {
|
|
79
|
-
tools = tools.filter(tool =>
|
|
80
|
-
tool.metadata.category === category
|
|
81
|
-
);
|
|
73
|
+
tools = tools.filter(tool => tool.metadata.category === category);
|
|
82
74
|
}
|
|
83
75
|
|
|
84
76
|
if (tags) {
|
|
@@ -91,9 +83,7 @@ router.get('/list', async (req, res) => {
|
|
|
91
83
|
|
|
92
84
|
if (author) {
|
|
93
85
|
const authorLower = author.toLowerCase();
|
|
94
|
-
tools = tools.filter(tool =>
|
|
95
|
-
tool.metadata.author?.toLowerCase() === authorLower
|
|
96
|
-
);
|
|
86
|
+
tools = tools.filter(tool => tool.metadata.author?.toLowerCase() === authorLower);
|
|
97
87
|
}
|
|
98
88
|
|
|
99
89
|
// 排序
|
|
@@ -105,21 +95,37 @@ router.get('/list', async (req, res) => {
|
|
|
105
95
|
const endIndex = startIndex + limit;
|
|
106
96
|
const paginatedTools = tools.slice(startIndex, endIndex);
|
|
107
97
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
98
|
+
const formattedTools = await Promise.all(
|
|
99
|
+
paginatedTools.map(async tool => {
|
|
100
|
+
const metadata = tool.metadata || {};
|
|
101
|
+
const authorName = metadata.author || 'BeCrafter';
|
|
102
|
+
|
|
103
|
+
const authorInfo = await authorConfigService.resolveAuthor(authorName);
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
id: metadata.id || tool.name,
|
|
107
|
+
name: metadata.name || tool.name,
|
|
108
|
+
description: metadata.description || '',
|
|
109
|
+
version: metadata.version || '1.0.0',
|
|
110
|
+
category: metadata.category || 'other',
|
|
111
|
+
author: authorInfo.name,
|
|
112
|
+
author_info: {
|
|
113
|
+
id: authorInfo.id,
|
|
114
|
+
name: authorInfo.name,
|
|
115
|
+
github: authorInfo.github,
|
|
116
|
+
homepage: authorInfo.homepage,
|
|
117
|
+
bio: authorInfo.bio,
|
|
118
|
+
featured: authorInfo.featured,
|
|
119
|
+
sort_order: authorInfo.sort_order,
|
|
120
|
+
aliases: authorInfo.aliases,
|
|
121
|
+
avatar_url: authorInfo.avatar_url
|
|
122
|
+
},
|
|
123
|
+
tags: metadata.tags || [],
|
|
124
|
+
scenarios: metadata.scenarios || [],
|
|
125
|
+
limitations: metadata.limitations || []
|
|
126
|
+
};
|
|
127
|
+
})
|
|
128
|
+
);
|
|
123
129
|
|
|
124
130
|
const response = {
|
|
125
131
|
success: true,
|
|
@@ -130,7 +136,6 @@ router.get('/list', async (req, res) => {
|
|
|
130
136
|
};
|
|
131
137
|
|
|
132
138
|
res.json(response);
|
|
133
|
-
|
|
134
139
|
} catch (error) {
|
|
135
140
|
logger.error('获取工具列表失败:', error);
|
|
136
141
|
res.status(500).json({
|
|
@@ -151,7 +156,7 @@ router.get('/detail/:toolName', async (req, res) => {
|
|
|
151
156
|
}
|
|
152
157
|
|
|
153
158
|
const { toolName } = req.params;
|
|
154
|
-
|
|
159
|
+
|
|
155
160
|
// 检查工具是否存在
|
|
156
161
|
if (!toolLoaderService.hasTool(toolName)) {
|
|
157
162
|
return res.status(404).json({
|
|
@@ -182,7 +187,6 @@ router.get('/detail/:toolName', async (req, res) => {
|
|
|
182
187
|
success: true,
|
|
183
188
|
tool: toolDetail
|
|
184
189
|
});
|
|
185
|
-
|
|
186
190
|
} catch (error) {
|
|
187
191
|
logger.error('获取工具详情失败:', error);
|
|
188
192
|
res.status(500).json({
|
|
@@ -203,7 +207,7 @@ router.get('/readme/:toolName', async (req, res) => {
|
|
|
203
207
|
}
|
|
204
208
|
|
|
205
209
|
const { toolName } = req.params;
|
|
206
|
-
|
|
210
|
+
|
|
207
211
|
// 检查工具是否存在
|
|
208
212
|
if (!toolLoaderService.hasTool(toolName)) {
|
|
209
213
|
return res.status(404).json({
|
|
@@ -212,13 +216,11 @@ router.get('/readme/:toolName', async (req, res) => {
|
|
|
212
216
|
});
|
|
213
217
|
}
|
|
214
218
|
|
|
215
|
-
const tool = toolLoaderService.getTool(toolName);
|
|
216
|
-
|
|
217
219
|
// 查找 README.md 文件
|
|
218
|
-
const toolboxDir =
|
|
220
|
+
const toolboxDir = config.getToolDir(toolName);
|
|
219
221
|
const readmePath = path.join(toolboxDir, 'README.md');
|
|
220
|
-
|
|
221
|
-
if (!await pathExists(readmePath)) {
|
|
222
|
+
|
|
223
|
+
if (!(await pathExists(readmePath))) {
|
|
222
224
|
return res.status(404).json({
|
|
223
225
|
success: false,
|
|
224
226
|
error: `工具 '${toolName}' 的 README.md 文件不存在`
|
|
@@ -227,13 +229,12 @@ router.get('/readme/:toolName', async (req, res) => {
|
|
|
227
229
|
|
|
228
230
|
const fs = await import('fs');
|
|
229
231
|
const readmeContent = await fs.promises.readFile(readmePath, 'utf-8');
|
|
230
|
-
|
|
232
|
+
|
|
231
233
|
res.json({
|
|
232
234
|
success: true,
|
|
233
235
|
toolName,
|
|
234
236
|
content: readmeContent
|
|
235
237
|
});
|
|
236
|
-
|
|
237
238
|
} catch (error) {
|
|
238
239
|
logger.error('读取工具 README 失败:', error);
|
|
239
240
|
res.status(500).json({
|
|
@@ -245,7 +246,7 @@ router.get('/readme/:toolName', async (req, res) => {
|
|
|
245
246
|
|
|
246
247
|
/**
|
|
247
248
|
* 上传工具包
|
|
248
|
-
*
|
|
249
|
+
*
|
|
249
250
|
* 上传内容必须是 .zip 文件
|
|
250
251
|
* 上传后需要做工具名的重复性检查,重复的不允许会给出提示,让用户自己判断是否需要覆盖
|
|
251
252
|
* 解压缩 .zip 文件,然后检查里面是否存在规范约定的两个文件,至少存在这两个
|
|
@@ -275,9 +276,9 @@ router.post('/upload', upload.single('file'), async (req, res) => {
|
|
|
275
276
|
}
|
|
276
277
|
|
|
277
278
|
// 创建临时解压目录
|
|
278
|
-
const tempDir =
|
|
279
|
+
const tempDir = config.getTempDir();
|
|
279
280
|
fs.ensureDirSync(tempDir);
|
|
280
|
-
extractedDir = path.join(tempDir, `extracted_${Date.now()}_${Math.round(Math.random() *
|
|
281
|
+
extractedDir = path.join(tempDir, `extracted_${Date.now()}_${Math.round(Math.random() * 1e9)}`);
|
|
281
282
|
fs.ensureDirSync(extractedDir);
|
|
282
283
|
|
|
283
284
|
// 解压ZIP文件
|
|
@@ -286,10 +287,10 @@ router.post('/upload', upload.single('file'), async (req, res) => {
|
|
|
286
287
|
|
|
287
288
|
// 检查解压后的目录结构
|
|
288
289
|
const files = fs.readdirSync(extractedDir);
|
|
289
|
-
|
|
290
|
+
|
|
290
291
|
// 查找工具文件(以 .tool.js 结尾的文件)
|
|
291
|
-
const toolFiles = files.filter(
|
|
292
|
-
file.endsWith('.tool.js') && fs.statSync(path.join(extractedDir, file)).isFile()
|
|
292
|
+
const toolFiles = files.filter(
|
|
293
|
+
file => file.endsWith('.tool.js') && fs.statSync(path.join(extractedDir, file)).isFile()
|
|
293
294
|
);
|
|
294
295
|
|
|
295
296
|
if (toolFiles.length === 0) {
|
|
@@ -304,8 +305,8 @@ router.post('/upload', upload.single('file'), async (req, res) => {
|
|
|
304
305
|
const toolName = toolFileName.replace('.tool.js', '');
|
|
305
306
|
|
|
306
307
|
// 检查是否存在 README.md
|
|
307
|
-
const hasReadme = files.some(
|
|
308
|
-
file.toLowerCase() === 'readme.md' && fs.statSync(path.join(extractedDir, file)).isFile()
|
|
308
|
+
const hasReadme = files.some(
|
|
309
|
+
file => file.toLowerCase() === 'readme.md' && fs.statSync(path.join(extractedDir, file)).isFile()
|
|
309
310
|
);
|
|
310
311
|
|
|
311
312
|
if (!hasReadme) {
|
|
@@ -316,7 +317,7 @@ router.post('/upload', upload.single('file'), async (req, res) => {
|
|
|
316
317
|
}
|
|
317
318
|
|
|
318
319
|
// 检查工具是否已存在
|
|
319
|
-
const toolboxDir =
|
|
320
|
+
const toolboxDir = config.getToolboxDir();
|
|
320
321
|
const targetToolDir = path.join(toolboxDir, toolName);
|
|
321
322
|
const toolExists = await pathExists(targetToolDir);
|
|
322
323
|
|
|
@@ -325,7 +326,7 @@ router.post('/upload', upload.single('file'), async (req, res) => {
|
|
|
325
326
|
return res.status(409).json({
|
|
326
327
|
success: false,
|
|
327
328
|
error: `工具 "${toolName}" 已存在`,
|
|
328
|
-
toolName
|
|
329
|
+
toolName,
|
|
329
330
|
canOverwrite: true
|
|
330
331
|
});
|
|
331
332
|
}
|
|
@@ -341,7 +342,7 @@ router.post('/upload', upload.single('file'), async (req, res) => {
|
|
|
341
342
|
|
|
342
343
|
// 检查工具文件是否可导入(语法验证)
|
|
343
344
|
const toolFilePath = path.join(targetToolDir, toolFileName);
|
|
344
|
-
if (!await pathExists(toolFilePath)) {
|
|
345
|
+
if (!(await pathExists(toolFilePath))) {
|
|
345
346
|
return res.status(500).json({
|
|
346
347
|
success: false,
|
|
347
348
|
error: `工具文件 ${toolFileName} 不存在`
|
|
@@ -353,19 +354,19 @@ router.post('/upload', upload.single('file'), async (req, res) => {
|
|
|
353
354
|
try {
|
|
354
355
|
const toolModule = await import(`file://${toolFilePath}`);
|
|
355
356
|
tool = toolModule.default || toolModule;
|
|
356
|
-
|
|
357
|
+
|
|
357
358
|
// 验证工具接口是否符合规范
|
|
358
359
|
if (typeof tool.execute !== 'function') {
|
|
359
360
|
return res.status(400).json({
|
|
360
361
|
success: false,
|
|
361
|
-
error:
|
|
362
|
+
error: '工具文件缺少必需的 execute 方法'
|
|
362
363
|
});
|
|
363
364
|
}
|
|
364
|
-
|
|
365
|
+
|
|
365
366
|
// 运行测试验证工具是否能正常工作
|
|
366
367
|
const { createToolContext } = await import('../toolm/tool-context.service.js');
|
|
367
|
-
|
|
368
|
-
|
|
368
|
+
await createToolContext(toolName, tool);
|
|
369
|
+
|
|
369
370
|
// 执行一个简单的测试,检查工具是否能被正确初始化
|
|
370
371
|
if (typeof tool.getMetadata === 'function') {
|
|
371
372
|
try {
|
|
@@ -377,13 +378,12 @@ router.post('/upload', upload.single('file'), async (req, res) => {
|
|
|
377
378
|
logger.warn(`工具 ${toolName} 的 getMetadata 方法调用失败:`, metaError.message);
|
|
378
379
|
}
|
|
379
380
|
}
|
|
380
|
-
|
|
381
|
+
|
|
381
382
|
// 简单测试 execute 方法是否存在
|
|
382
383
|
if (tool.execute && typeof tool.execute === 'function') {
|
|
383
384
|
// 为了安全起见,不实际执行工具,只验证其签名
|
|
384
385
|
logger.info(`工具 ${toolName} 的 execute 方法存在,签名验证通过`);
|
|
385
386
|
}
|
|
386
|
-
|
|
387
387
|
} catch (importError) {
|
|
388
388
|
// 清理失败的工具目录
|
|
389
389
|
await fse.remove(targetToolDir);
|
|
@@ -423,7 +423,7 @@ router.post('/upload', upload.single('file'), async (req, res) => {
|
|
|
423
423
|
// 重新加载工具
|
|
424
424
|
await toolLoaderService.reload();
|
|
425
425
|
}
|
|
426
|
-
|
|
426
|
+
|
|
427
427
|
// 验证工具是否可以被加载
|
|
428
428
|
if (toolLoaderService.hasTool(toolName)) {
|
|
429
429
|
logger.info(`工具 ${toolName} 验证通过并已加载`);
|
|
@@ -435,22 +435,21 @@ router.post('/upload', upload.single('file'), async (req, res) => {
|
|
|
435
435
|
res.json({
|
|
436
436
|
success: true,
|
|
437
437
|
message: `工具 ${toolName} 上传成功`,
|
|
438
|
-
toolName
|
|
438
|
+
toolName,
|
|
439
439
|
overwritten: toolExists && req.body.overwrite === 'true'
|
|
440
440
|
});
|
|
441
|
-
|
|
442
441
|
} catch (error) {
|
|
443
442
|
logger.error('工具上传失败:', error);
|
|
444
|
-
|
|
443
|
+
|
|
445
444
|
// 清理临时文件
|
|
446
445
|
try {
|
|
447
|
-
if (extractedDir && await pathExists(extractedDir)) {
|
|
446
|
+
if (extractedDir && (await pathExists(extractedDir))) {
|
|
448
447
|
await fse.remove(extractedDir);
|
|
449
448
|
}
|
|
450
449
|
} catch (cleanupError) {
|
|
451
450
|
logger.error('清理临时文件失败:', cleanupError);
|
|
452
451
|
}
|
|
453
|
-
|
|
452
|
+
|
|
454
453
|
res.status(500).json({
|
|
455
454
|
success: false,
|
|
456
455
|
error: error.message
|
|
@@ -458,16 +457,16 @@ router.post('/upload', upload.single('file'), async (req, res) => {
|
|
|
458
457
|
} finally {
|
|
459
458
|
// 确保临时上传的ZIP文件被清理
|
|
460
459
|
try {
|
|
461
|
-
if (tempZipPath && await pathExists(tempZipPath)) {
|
|
460
|
+
if (tempZipPath && (await pathExists(tempZipPath))) {
|
|
462
461
|
await fse.remove(tempZipPath);
|
|
463
462
|
}
|
|
464
463
|
} catch (cleanupError) {
|
|
465
464
|
logger.error('清理上传文件失败:', cleanupError);
|
|
466
465
|
}
|
|
467
|
-
|
|
466
|
+
|
|
468
467
|
// 确保临时解压目录被清理
|
|
469
468
|
try {
|
|
470
|
-
if (extractedDir && await pathExists(extractedDir)) {
|
|
469
|
+
if (extractedDir && (await pathExists(extractedDir))) {
|
|
471
470
|
await fse.remove(extractedDir);
|
|
472
471
|
}
|
|
473
472
|
} catch (cleanupError) {
|
|
@@ -476,4 +475,4 @@ router.post('/upload', upload.single('file'), async (req, res) => {
|
|
|
476
475
|
}
|
|
477
476
|
});
|
|
478
477
|
|
|
479
|
-
export { router as toolRouter };
|
|
478
|
+
export { router as toolRouter };
|