@becrafter/prompt-manager 0.0.19 → 0.1.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/README.md +145 -234
- 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/tray.1.png +0 -0
- package/app/desktop/assets/tray.png +0 -0
- package/app/desktop/main.js +27 -0
- package/app/desktop/package-lock.json +201 -4
- package/app/desktop/package.json +23 -29
- package/app/desktop/src/services/module-loader.js +43 -22
- package/app/desktop/src/services/runtime-manager.js +172 -23
- package/app/desktop/src/services/update-manager.js +6 -7
- package/app/desktop/src/ui/admin-window-manager.js +757 -0
- package/app/desktop/src/ui/splash-manager.js +253 -0
- package/app/desktop/src/ui/tray-manager.js +8 -24
- package/app/desktop/src/utils/icon-manager.js +39 -47
- package/app/desktop/src/utils/resource-paths.js +0 -23
- package/app/desktop/src/utils/resource-sync.js +260 -0
- package/app/desktop/src/utils/runtime-sync.js +241 -0
- package/examples/prompts/recommend/human_3-0_growth_diagnostic_coach_prompt.yaml +105 -0
- package/package.json +16 -13
- package/packages/admin-ui/.babelrc +3 -0
- package/packages/admin-ui/admin.html +237 -4784
- package/packages/admin-ui/css/main.css +2592 -0
- package/packages/admin-ui/css/recommended-prompts.css +610 -0
- package/packages/admin-ui/package-lock.json +6973 -0
- package/packages/admin-ui/package.json +36 -0
- package/packages/admin-ui/src/codemirror.js +53 -0
- package/packages/admin-ui/src/index.js +3188 -0
- package/packages/admin-ui/webpack.config.js +76 -0
- package/packages/resources/tools/chrome-devtools/README.md +310 -0
- package/packages/resources/tools/chrome-devtools/chrome-devtools.tool.js +1703 -0
- package/packages/resources/tools/file-reader/README.md +289 -0
- package/packages/resources/tools/file-reader/file-reader.tool.js +1545 -0
- package/packages/resources/tools/filesystem/README.md +359 -0
- package/packages/resources/tools/filesystem/filesystem.tool.js +514 -160
- package/packages/resources/tools/ollama-remote/README.md +192 -0
- package/packages/resources/tools/ollama-remote/ollama-remote.tool.js +421 -0
- package/packages/resources/tools/pdf-reader/README.md +236 -0
- package/packages/resources/tools/pdf-reader/pdf-reader.tool.js +565 -0
- package/packages/resources/tools/playwright/README.md +306 -0
- package/packages/resources/tools/playwright/playwright.tool.js +1186 -0
- package/packages/resources/tools/todolist/README.md +394 -0
- package/packages/resources/tools/todolist/todolist.tool.js +1312 -0
- package/packages/server/README.md +142 -0
- package/packages/server/api/admin.routes.js +42 -11
- package/packages/server/api/surge.routes.js +43 -0
- package/packages/server/app.js +119 -14
- package/packages/server/index.js +39 -0
- package/packages/server/mcp/mcp.server.js +324 -105
- package/packages/server/mcp/sequential-thinking.handler.js +318 -0
- package/packages/server/mcp/think-plan.handler.js +274 -0
- package/packages/server/middlewares/auth.middleware.js +6 -0
- package/packages/server/package.json +51 -0
- package/packages/server/server.js +37 -1
- package/packages/server/toolm/index.js +9 -0
- package/packages/server/toolm/package-installer.service.js +267 -0
- package/packages/server/toolm/test-tools.js +264 -0
- package/packages/server/toolm/tool-context.service.js +334 -0
- package/packages/server/toolm/tool-dependency.service.js +168 -0
- package/packages/server/toolm/tool-description-generator-optimized.service.js +375 -0
- package/packages/server/toolm/tool-description-generator.service.js +312 -0
- package/packages/server/toolm/tool-environment.service.js +200 -0
- package/packages/server/toolm/tool-execution.service.js +277 -0
- package/packages/server/toolm/tool-loader.service.js +219 -0
- package/packages/server/toolm/tool-logger.service.js +223 -0
- package/packages/server/toolm/tool-manager.handler.js +65 -0
- package/packages/server/toolm/tool-manual-generator.service.js +389 -0
- package/packages/server/toolm/tool-mode-handlers.service.js +224 -0
- package/packages/server/toolm/tool-storage.service.js +111 -0
- package/packages/server/toolm/tool-sync.service.js +138 -0
- package/packages/server/toolm/tool-utils.js +20 -0
- package/packages/server/toolm/tool-yaml-parser.service.js +81 -0
- package/packages/server/toolm/validate-system.js +421 -0
- package/packages/server/utils/config.js +49 -5
- package/packages/server/utils/util.js +65 -10
- package/scripts/build-icons.js +99 -69
- package/scripts/build.sh +57 -0
- package/scripts/surge/CNAME +1 -0
- package/scripts/surge/README.md +47 -0
- package/scripts/surge/package-lock.json +34 -0
- package/scripts/surge/package.json +20 -0
- package/scripts/surge/sync-to-surge.js +151 -0
- package/app/desktop/assets/icons/icon_1024x1024.png +0 -0
- package/app/desktop/assets/icons/icon_128x128.png +0 -0
- package/app/desktop/assets/icons/icon_16x16.png +0 -0
- package/app/desktop/assets/icons/icon_24x24.png +0 -0
- package/app/desktop/assets/icons/icon_256x256.png +0 -0
- package/app/desktop/assets/icons/icon_32x32.png +0 -0
- package/app/desktop/assets/icons/icon_48x48.png +0 -0
- package/app/desktop/assets/icons/icon_512x512.png +0 -0
- package/app/desktop/assets/icons/icon_64x64.png +0 -0
- package/app/desktop/assets/icons/icon_96x96.png +0 -0
- package/packages/admin-ui/js/closebrackets.min.js +0 -8
- package/packages/admin-ui/js/codemirror.min.js +0 -8
- package/packages/admin-ui/js/js-yaml.min.js +0 -2
- package/packages/admin-ui/js/markdown.min.js +0 -8
- package/packages/resources/tools/index.js +0 -16
- package/packages/server/mcp/toolx.handler.js +0 -131
- package/scripts/icns-builder/package.json +0 -12
- /package/packages/server/mcp/{mcp.handler.js → prompt.handler.js} +0 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# @becrafter/prompt-manager-core
|
|
2
|
+
|
|
3
|
+
Core library for managing prompts with MCP (Model Context Protocol) support.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @becrafter/prompt-manager-core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Basic Usage
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
import { startServer, stopServer, config } from '@becrafter/prompt-manager-core';
|
|
17
|
+
|
|
18
|
+
// Start the server with default configuration
|
|
19
|
+
const server = await startServer();
|
|
20
|
+
|
|
21
|
+
console.log('Server is running!');
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Advanced Usage with Configuration
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
import { startServer, stopServer, config } from '@becrafter/prompt-manager-core';
|
|
28
|
+
|
|
29
|
+
// Start the server with custom configuration
|
|
30
|
+
const server = await startServer({
|
|
31
|
+
configOverrides: {
|
|
32
|
+
promptsDir: './my-prompts',
|
|
33
|
+
port: 3000,
|
|
34
|
+
adminEnable: true,
|
|
35
|
+
adminRequireAuth: false
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Stop the server when needed
|
|
40
|
+
// await stopServer();
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Using the Express App Instance
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
import { app, config } from '@becrafter/prompt-manager-core';
|
|
47
|
+
|
|
48
|
+
// You can use the app instance to add custom routes
|
|
49
|
+
app.get('/custom-endpoint', (req, res) => {
|
|
50
|
+
res.json({ message: 'Hello from custom endpoint!' });
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Then start the server
|
|
54
|
+
import { startServer } from '@becrafter/prompt-manager-core';
|
|
55
|
+
await startServer();
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Accessing Prompt Manager
|
|
59
|
+
|
|
60
|
+
```javascript
|
|
61
|
+
import { promptManager, util } from '@becrafter/prompt-manager-core';
|
|
62
|
+
|
|
63
|
+
// Load prompts
|
|
64
|
+
await promptManager.loadPrompts();
|
|
65
|
+
|
|
66
|
+
// Get all prompts
|
|
67
|
+
const prompts = promptManager.getPrompts();
|
|
68
|
+
console.log('Loaded prompts:', prompts);
|
|
69
|
+
|
|
70
|
+
// Get a specific prompt by ID
|
|
71
|
+
const prompt = promptManager.getPrompt('prompt-id');
|
|
72
|
+
console.log('Prompt:', prompt);
|
|
73
|
+
|
|
74
|
+
// Search prompts
|
|
75
|
+
const searchTerm = 'search term';
|
|
76
|
+
const searchResults = prompts.filter(prompt =>
|
|
77
|
+
prompt.name.includes(searchTerm) ||
|
|
78
|
+
(prompt.description && prompt.description.includes(searchTerm))
|
|
79
|
+
);
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## API Reference
|
|
83
|
+
|
|
84
|
+
### Functions
|
|
85
|
+
|
|
86
|
+
- `startServer(options = {})` - Starts the prompt manager server
|
|
87
|
+
- `stopServer()` - Stops the prompt manager server
|
|
88
|
+
- `getServerState()` - Gets the current server state
|
|
89
|
+
- `getServerAddress()` - Gets the server address
|
|
90
|
+
- `isServerRunning()` - Checks if the server is running
|
|
91
|
+
|
|
92
|
+
### Objects
|
|
93
|
+
|
|
94
|
+
- `app` - Express application instance
|
|
95
|
+
- `config` - Configuration object
|
|
96
|
+
- `logger` - Logger instance
|
|
97
|
+
- `util` - Utility functions
|
|
98
|
+
- `promptManager` - Prompt manager instance
|
|
99
|
+
|
|
100
|
+
### MCP Related
|
|
101
|
+
|
|
102
|
+
- `getMcpServer()` - Gets the MCP server instance
|
|
103
|
+
- `handleGetPrompt`, `handleSearchPrompts`, `handleReloadPrompts` - MCP handlers
|
|
104
|
+
|
|
105
|
+
### Routes
|
|
106
|
+
|
|
107
|
+
- `adminRouter` - Admin API routes
|
|
108
|
+
- `openRouter` - Open API routes
|
|
109
|
+
|
|
110
|
+
### Middleware
|
|
111
|
+
|
|
112
|
+
- `adminAuthMiddleware` - Admin authentication middleware
|
|
113
|
+
|
|
114
|
+
## Configuration Options
|
|
115
|
+
|
|
116
|
+
You can override default configuration using the `configOverrides` option:
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
{
|
|
120
|
+
promptsDir: './prompts', // Directory for prompt files
|
|
121
|
+
port: 5621, // Server port
|
|
122
|
+
serverName: 'prompt-manager', // Server name
|
|
123
|
+
serverVersion: '0.0.19', // Server version
|
|
124
|
+
logLevel: 'info', // Log level
|
|
125
|
+
maxPrompts: 1000, // Max prompt count
|
|
126
|
+
recursiveScan: true, // Recursive scan for prompts
|
|
127
|
+
adminEnable: true, // Enable admin functionality
|
|
128
|
+
adminPath: '/admin', // Admin path
|
|
129
|
+
adminRequireAuth: true, // Require admin authentication
|
|
130
|
+
admins: [ // Admin accounts
|
|
131
|
+
{
|
|
132
|
+
username: 'admin',
|
|
133
|
+
password: 'admin',
|
|
134
|
+
token: '...'
|
|
135
|
+
}
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
MIT
|
|
@@ -17,6 +17,20 @@ const router = express.Router();
|
|
|
17
17
|
|
|
18
18
|
// 获取prompts目录路径(在启动时可能被覆盖)
|
|
19
19
|
let promptsDir = config.getPromptsDir();
|
|
20
|
+
const PROMPT_NAME_REGEX = /^(?![.]{1,2}$)[^\\/:*?"<>|\r\n]{1,64}$/;
|
|
21
|
+
|
|
22
|
+
// 获取服务器配置端点
|
|
23
|
+
router.get('/config', (req, res) => {
|
|
24
|
+
// 检查是否启用了管理员功能
|
|
25
|
+
if (!config.adminEnable) {
|
|
26
|
+
return res.status(404).json({ error: 'Admin功能未启用' });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
res.json({
|
|
30
|
+
requireAuth: config.adminRequireAuth,
|
|
31
|
+
adminEnable: config.adminEnable
|
|
32
|
+
});
|
|
33
|
+
});
|
|
20
34
|
|
|
21
35
|
// 登录端点
|
|
22
36
|
router.post('/login', (req, res) => {
|
|
@@ -25,6 +39,11 @@ router.post('/login', (req, res) => {
|
|
|
25
39
|
return res.status(404).json({ error: 'Admin功能未启用' });
|
|
26
40
|
}
|
|
27
41
|
|
|
42
|
+
// 如果不需要认证,返回默认token
|
|
43
|
+
if (!config.adminRequireAuth) {
|
|
44
|
+
return res.json({ token: config.admins[0].token });
|
|
45
|
+
}
|
|
46
|
+
|
|
28
47
|
const { username, password } = req.body;
|
|
29
48
|
|
|
30
49
|
if (!username || !password) {
|
|
@@ -129,14 +148,15 @@ router.get('/prompts/:name', adminAuthMiddleware, (req, res) => {
|
|
|
129
148
|
router.post('/prompts', adminAuthMiddleware, (req, res) => {
|
|
130
149
|
try {
|
|
131
150
|
const { name, group, yaml: yamlContent, relativePath: originalRelativePath } = req.body;
|
|
151
|
+
const trimmedName = (name || '').trim();
|
|
132
152
|
|
|
133
|
-
if (!
|
|
153
|
+
if (!trimmedName || !yamlContent) {
|
|
134
154
|
return res.status(400).json({ error: '名称和YAML内容是必需的' });
|
|
135
155
|
}
|
|
136
156
|
|
|
137
157
|
// 验证名称格式
|
|
138
|
-
if (
|
|
139
|
-
return res.status(400).json({ error: '
|
|
158
|
+
if (!PROMPT_NAME_REGEX.test(trimmedName)) {
|
|
159
|
+
return res.status(400).json({ error: '名称格式无效,不能包含 / \\ : * ? \" < > | 或换行,长度需在1-64字符' });
|
|
140
160
|
}
|
|
141
161
|
|
|
142
162
|
// 计算目标路径
|
|
@@ -148,7 +168,7 @@ router.post('/prompts', adminAuthMiddleware, (req, res) => {
|
|
|
148
168
|
targetSegments.push(groupName);
|
|
149
169
|
}
|
|
150
170
|
|
|
151
|
-
const finalFileName = `${
|
|
171
|
+
const finalFileName = `${trimmedName}.yaml`;
|
|
152
172
|
targetSegments.push(finalFileName);
|
|
153
173
|
|
|
154
174
|
const targetRelativePath = path.posix.join(...targetSegments);
|
|
@@ -160,7 +180,7 @@ router.post('/prompts', adminAuthMiddleware, (req, res) => {
|
|
|
160
180
|
// 检查是否重名(同目录下)
|
|
161
181
|
const prompts = util.getPromptsFromFiles();
|
|
162
182
|
const existingPrompt = prompts.find(p => {
|
|
163
|
-
if (p.name !==
|
|
183
|
+
if (p.name !== trimmedName) return false;
|
|
164
184
|
const isOriginalFile = normalizedOriginalPath && p.relativePath === normalizedOriginalPath;
|
|
165
185
|
if (isOriginalFile) return false;
|
|
166
186
|
const sameRelativePath = p.relativePath === targetRelativePath;
|
|
@@ -193,7 +213,7 @@ router.post('/prompts', adminAuthMiddleware, (req, res) => {
|
|
|
193
213
|
// 创建新分组目录
|
|
194
214
|
router.post('/groups', adminAuthMiddleware, (req, res) => {
|
|
195
215
|
try {
|
|
196
|
-
const { name } = req.body;
|
|
216
|
+
const { name, parent } = req.body;
|
|
197
217
|
|
|
198
218
|
if (!name) {
|
|
199
219
|
return res.status(400).json({ error: '分组名称是必需的' });
|
|
@@ -201,18 +221,29 @@ router.post('/groups', adminAuthMiddleware, (req, res) => {
|
|
|
201
221
|
|
|
202
222
|
// 验证名称格式
|
|
203
223
|
if (!util.isValidGroupName(name)) {
|
|
204
|
-
return res.status(400).json({ error: '
|
|
224
|
+
return res.status(400).json({ error: '名称格式无效,不能包含 / \\ : * ? \" < > | 或换行,长度需在1-64字符' });
|
|
205
225
|
}
|
|
206
226
|
|
|
207
|
-
|
|
227
|
+
// 构建目标目录路径
|
|
228
|
+
let targetPath;
|
|
229
|
+
if (parent) {
|
|
230
|
+
// 验证父级目录路径
|
|
231
|
+
const resolvedParent = util.resolveGroupDir(parent);
|
|
232
|
+
if (!resolvedParent) {
|
|
233
|
+
return res.status(400).json({ error: '无效的父级目录路径' });
|
|
234
|
+
}
|
|
235
|
+
targetPath = path.join(resolvedParent.dir, name);
|
|
236
|
+
} else {
|
|
237
|
+
targetPath = path.join(promptsDir, name);
|
|
238
|
+
}
|
|
208
239
|
|
|
209
240
|
// 检查目录是否已存在
|
|
210
|
-
if (fs.existsSync(
|
|
241
|
+
if (fs.existsSync(targetPath)) {
|
|
211
242
|
return res.status(400).json({ error: '分组已存在' });
|
|
212
243
|
}
|
|
213
244
|
|
|
214
245
|
// 创建目录
|
|
215
|
-
fs.mkdirSync(
|
|
246
|
+
fs.mkdirSync(targetPath, { recursive: true });
|
|
216
247
|
|
|
217
248
|
res.json({ message: '分组创建成功' });
|
|
218
249
|
} catch (error) {
|
|
@@ -228,7 +259,7 @@ router.patch('/groups/rename', adminAuthMiddleware, (req, res) => {
|
|
|
228
259
|
return res.status(400).json({ error: '分组路径和新名称是必需的' });
|
|
229
260
|
}
|
|
230
261
|
if (!util.isValidGroupName(newName)) {
|
|
231
|
-
return res.status(400).json({ error: '
|
|
262
|
+
return res.status(400).json({ error: '名称格式无效,不能包含 / \\ : * ? \" < > | 或换行,长度需在1-64字符' });
|
|
232
263
|
}
|
|
233
264
|
if (groupPath === 'default') {
|
|
234
265
|
return res.status(400).json({ error: '默认分组不允许重命名' });
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Surge 静态资源代理接口
|
|
3
|
+
* 代理所有 /surge/* 请求到指定的 Surge 静态资源地址
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import express from 'express';
|
|
7
|
+
import { createProxyMiddleware } from 'http-proxy-middleware';
|
|
8
|
+
import { config } from '../utils/config.js';
|
|
9
|
+
|
|
10
|
+
const router = express.Router();
|
|
11
|
+
|
|
12
|
+
// 从配置中获取 Surge 目标地址,默认为 https://becrafter.surge.sh
|
|
13
|
+
const surgeTarget = config.surgeTarget || 'https://becrafter.surge.sh';
|
|
14
|
+
|
|
15
|
+
// 创建代理中间件
|
|
16
|
+
const surgeProxy = createProxyMiddleware({
|
|
17
|
+
target: surgeTarget,
|
|
18
|
+
changeOrigin: true,
|
|
19
|
+
// 当路由已经去除了 /surge 前缀后,路径变为 /test_prompts.json
|
|
20
|
+
// 我们需要将其重写为 /assets/test_prompts.json
|
|
21
|
+
pathRewrite: {
|
|
22
|
+
'^/': '/assets/', // 将 / 开头的路径重写为 /assets/ 开头
|
|
23
|
+
},
|
|
24
|
+
// 设置代理请求头
|
|
25
|
+
onProxyReq: (proxyReq, req, res) => {
|
|
26
|
+
// 可以在这里添加自定义逻辑,如设置请求头等
|
|
27
|
+
console.log(`代理请求: ${req.method} ${req.url} -> ${surgeTarget}${req.url.replace(/^\/(.*)/, '/assets/$1')}`);
|
|
28
|
+
},
|
|
29
|
+
onProxyRes: (proxyRes, req, res) => {
|
|
30
|
+
// 可以在这里添加自定义逻辑,如修改响应头等
|
|
31
|
+
proxyRes.headers['surge-proxy'] = 'enabled';
|
|
32
|
+
},
|
|
33
|
+
onError: (err, req, res) => {
|
|
34
|
+
console.error('Surge 代理错误:', err);
|
|
35
|
+
res.status(500).send('代理错误');
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// 使用代理中间件处理所有请求
|
|
40
|
+
// 由于在 app.js 中已经使用了 /surge 路径前缀,这里直接处理所有请求
|
|
41
|
+
router.use(surgeProxy);
|
|
42
|
+
|
|
43
|
+
export const surgeRouter = router;
|
package/packages/server/app.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
2
|
import cors from 'cors';
|
|
3
3
|
import path from 'path';
|
|
4
|
+
import fs from 'fs';
|
|
4
5
|
import { randomUUID } from 'node:crypto';
|
|
5
6
|
import { util } from './utils/util.js';
|
|
6
7
|
import { config } from './utils/config.js';
|
|
7
8
|
import { logger } from './utils/logger.js';
|
|
8
9
|
import { adminRouter } from './api/admin.routes.js';
|
|
9
10
|
import { openRouter } from './api/open.routes.js';
|
|
11
|
+
import { surgeRouter } from './api/surge.routes.js';
|
|
10
12
|
import { getMcpServer } from './mcp/mcp.server.js';
|
|
11
13
|
import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
|
|
12
14
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
@@ -14,7 +16,60 @@ import { InMemoryEventStore } from '@modelcontextprotocol/sdk/examples/shared/in
|
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
const app = express();
|
|
17
|
-
const adminUiRoot = util.
|
|
19
|
+
const adminUiRoot = util.getWebUiRoot();
|
|
20
|
+
|
|
21
|
+
// 自定义中间件,用于处理 ASAR 包中的静态文件
|
|
22
|
+
function serveAsarStatic(root) {
|
|
23
|
+
return async (req, res, next) => {
|
|
24
|
+
// 检查 root 是否是 ASAR 包中的路径
|
|
25
|
+
if (root.includes('.asar')) {
|
|
26
|
+
try {
|
|
27
|
+
// 动态导入 asar 模块(在 ES 模块环境中)
|
|
28
|
+
const { default: asar } = await import('@electron/asar');
|
|
29
|
+
|
|
30
|
+
// 从 root 中提取 asar 文件路径和相对路径
|
|
31
|
+
const asarPathMatch = root.match(/^(.*?\.asar)(\/.*)?$/);
|
|
32
|
+
const asarFilePath = asarPathMatch ? asarPathMatch[1] : root.replace(/\/.*$/, '.asar');
|
|
33
|
+
const relativePathInAsar = asarPathMatch && asarPathMatch[2] ? asarPathMatch[2].substring(1) : 'web';
|
|
34
|
+
|
|
35
|
+
const filePath = path.posix.join(relativePathInAsar, req.path).replace(/^\//, '');
|
|
36
|
+
const stat = asar.statFile(asarFilePath, filePath);
|
|
37
|
+
|
|
38
|
+
if (stat) {
|
|
39
|
+
const fileContent = asar.extractFile(asarFilePath, filePath);
|
|
40
|
+
|
|
41
|
+
// 设置适当的 Content-Type
|
|
42
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
43
|
+
const mimeTypes = {
|
|
44
|
+
'.html': 'text/html',
|
|
45
|
+
'.js': 'application/javascript',
|
|
46
|
+
'.css': 'text/css',
|
|
47
|
+
'.json': 'application/json',
|
|
48
|
+
'.png': 'image/png',
|
|
49
|
+
'.jpg': 'image/jpeg',
|
|
50
|
+
'.gif': 'image/gif',
|
|
51
|
+
'.svg': 'image/svg+xml',
|
|
52
|
+
'.ico': 'image/x-icon'
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
if (mimeTypes[ext]) {
|
|
56
|
+
res.setHeader('Content-Type', mimeTypes[ext]);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
res.send(fileContent);
|
|
60
|
+
} else {
|
|
61
|
+
next();
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
// 文件不存在或 asar 模块无法导入,继续到下一个中间件
|
|
65
|
+
next();
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
// 不是 ASAR 包中的路径,使用默认的静态文件服务
|
|
69
|
+
express.static(root)(req, res, next);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
18
73
|
|
|
19
74
|
// 全局中间件
|
|
20
75
|
app.use(cors());
|
|
@@ -23,17 +78,50 @@ app.use(express.urlencoded({ extended: true }));
|
|
|
23
78
|
|
|
24
79
|
|
|
25
80
|
// 为管理员界面提供静态文件服务 - 根路径
|
|
26
|
-
|
|
81
|
+
// 检查是否需要使用 ASAR 处理(需要验证 .asar 文件实际上存在)
|
|
82
|
+
const isAsarPath = adminUiRoot.includes('.asar') && fs.existsSync(adminUiRoot.replace(/\/.*$/, '.asar'));
|
|
83
|
+
if (isAsarPath) {
|
|
84
|
+
app.use(config.adminPath, serveAsarStatic(adminUiRoot));
|
|
85
|
+
} else {
|
|
86
|
+
app.use(config.adminPath, express.static(adminUiRoot));
|
|
87
|
+
}
|
|
27
88
|
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
89
|
+
// 统一处理 index.html 请求的辅助函数
|
|
90
|
+
async function sendIndexHtml(req, res) {
|
|
91
|
+
if (isAsarPath) {
|
|
92
|
+
try {
|
|
93
|
+
// 动态导入 asar 模块(在 ES 模块环境中)
|
|
94
|
+
const { default: asar } = await import('@electron/asar');
|
|
95
|
+
|
|
96
|
+
// 从 adminUiRoot 中提取 asar 文件路径和相对路径
|
|
97
|
+
const asarPathMatch = adminUiRoot.match(/^(.*?\.asar)(\/.*)?$/);
|
|
98
|
+
const asarFilePath = asarPathMatch ? asarPathMatch[1] : adminUiRoot.replace(/\/.*$/, '.asar');
|
|
99
|
+
const relativePathInAsar = asarPathMatch && asarPathMatch[2] ? asarPathMatch[2].substring(1) : 'web';
|
|
100
|
+
const indexPath = path.posix.join(relativePathInAsar, 'index.html');
|
|
101
|
+
|
|
102
|
+
// 检查文件是否存在
|
|
103
|
+
const stat = asar.statFile(asarFilePath, indexPath);
|
|
104
|
+
if (stat) {
|
|
105
|
+
// 读取 index.html 文件内容
|
|
106
|
+
const fileContent = asar.extractFile(asarFilePath, indexPath);
|
|
107
|
+
res.setHeader('Content-Type', 'text/html');
|
|
108
|
+
res.send(fileContent);
|
|
109
|
+
} else {
|
|
110
|
+
res.status(500).send('Internal Server Error');
|
|
111
|
+
}
|
|
112
|
+
} catch (error) {
|
|
113
|
+
res.status(500).send('Internal Server Error');
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
res.sendFile(path.join(adminUiRoot, 'index.html'));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
32
119
|
|
|
33
|
-
// 为管理员界面提供根路径访问(当用户访问 /admin
|
|
34
|
-
app.get(config.adminPath
|
|
35
|
-
|
|
36
|
-
|
|
120
|
+
// 为管理员界面提供根路径访问(当用户访问 /admin 时显示 index.html)
|
|
121
|
+
app.get(config.adminPath, sendIndexHtml);
|
|
122
|
+
|
|
123
|
+
// 为管理员界面提供根路径访问(当用户访问 /admin/ 时显示 index.html)
|
|
124
|
+
app.get(config.adminPath + '/', sendIndexHtml);
|
|
37
125
|
|
|
38
126
|
|
|
39
127
|
// 注册后台API
|
|
@@ -42,6 +130,9 @@ app.use('/adminapi', adminRouter);
|
|
|
42
130
|
// 注册开放API
|
|
43
131
|
app.use('/openapi', openRouter);
|
|
44
132
|
|
|
133
|
+
// 注册 Surge 静态资源代理 API
|
|
134
|
+
app.use('/surge', surgeRouter);
|
|
135
|
+
|
|
45
136
|
|
|
46
137
|
const transports = {};
|
|
47
138
|
const mcpServers = {}; // 存储每个会话的MCP服务器实例
|
|
@@ -72,17 +163,31 @@ app.all('/mcp', (req, res) => {
|
|
|
72
163
|
}
|
|
73
164
|
} else if (!sessionId && req.method === 'POST' && isInitializeRequest(req.body)) {
|
|
74
165
|
const eventStore = new InMemoryEventStore();
|
|
166
|
+
|
|
167
|
+
// 预先创建 MCP 服务器实例,避免异步时序问题
|
|
168
|
+
let mcpServerPromise = null;
|
|
169
|
+
let serverReady = false;
|
|
170
|
+
|
|
75
171
|
transport = new StreamableHTTPServerTransport({
|
|
76
172
|
sessionIdGenerator: () => randomUUID(),
|
|
77
173
|
eventStore, // Enable resumability
|
|
78
|
-
onsessioninitialized: sessionId => {
|
|
174
|
+
onsessioninitialized: async sessionId => {
|
|
79
175
|
// Store the transport by session ID when session is initialized
|
|
80
176
|
console.log(`StreamableHTTP session initialized with ID: ${sessionId}`);
|
|
81
177
|
transports[sessionId] = transport;
|
|
82
178
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
179
|
+
try {
|
|
180
|
+
// 为新会话创建MCP服务器实例(同步等待完成)
|
|
181
|
+
const server = await getMcpServer();
|
|
182
|
+
mcpServers[sessionId] = server;
|
|
183
|
+
server.connect(transport);
|
|
184
|
+
serverReady = true;
|
|
185
|
+
console.log(`MCP server connected for session ${sessionId}`);
|
|
186
|
+
} catch (error) {
|
|
187
|
+
console.error('创建MCP服务器失败:', error);
|
|
188
|
+
// 即使失败也标记为ready,避免阻塞
|
|
189
|
+
serverReady = true;
|
|
190
|
+
}
|
|
86
191
|
}
|
|
87
192
|
});
|
|
88
193
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt Manager Core Library
|
|
3
|
+
*
|
|
4
|
+
* This is the main entry point for the Prompt Manager core library.
|
|
5
|
+
* It exports the main server functionality and utility functions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Export the main server functionality
|
|
9
|
+
export { startServer, stopServer, getServerState, getServerAddress, isServerRunning } from './server.js';
|
|
10
|
+
|
|
11
|
+
// Export the application instance
|
|
12
|
+
export { default as app } from './app.js';
|
|
13
|
+
|
|
14
|
+
// Export utility functions
|
|
15
|
+
export { config } from './utils/config.js';
|
|
16
|
+
export { logger } from './utils/logger.js';
|
|
17
|
+
export { util } from './utils/util.js';
|
|
18
|
+
|
|
19
|
+
// Export services
|
|
20
|
+
export { promptManager } from './services/manager.js';
|
|
21
|
+
|
|
22
|
+
// Export MCP functionality
|
|
23
|
+
export { getMcpServer } from './mcp/mcp.server.js';
|
|
24
|
+
export {
|
|
25
|
+
handleGetPrompt,
|
|
26
|
+
handleSearchPrompts,
|
|
27
|
+
handleReloadPrompts
|
|
28
|
+
} from './mcp/prompt.handler.js';
|
|
29
|
+
export { handleSequentialThinking } from './mcp/sequential-thinking.handler.js';
|
|
30
|
+
export { handleThinkPlan } from './mcp/think-plan.handler.js';
|
|
31
|
+
export { handleToolM } from './toolm/index.js';
|
|
32
|
+
|
|
33
|
+
// Export API routes
|
|
34
|
+
export { adminRouter } from './api/admin.routes.js';
|
|
35
|
+
export { openRouter } from './api/open.routes.js';
|
|
36
|
+
export { surgeRouter } from './api/surge.routes.js';
|
|
37
|
+
|
|
38
|
+
// Export middlewares
|
|
39
|
+
export { adminAuthMiddleware } from './middlewares/auth.middleware.js';
|