@becrafter/prompt-manager 0.1.2 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +304 -121
- package/app/cli/commands/start.js +65 -4
- package/app/cli/support/argv.js +6 -0
- package/env.example +32 -0
- package/package.json +36 -7
- package/packages/server/api/admin.routes.js +409 -1
- package/packages/server/api/open.routes.js +7 -2
- package/packages/server/api/tool.routes.js +479 -0
- package/packages/server/app.js +97 -25
- package/packages/server/configs/models/built-in/bigmodel.yaml +6 -0
- package/packages/server/configs/models/providers.yaml +50 -0
- package/packages/server/configs/templates/built-in/general-iteration.yaml +60 -0
- package/packages/server/configs/templates/built-in/general-optimize.yaml +63 -0
- package/packages/server/configs/templates/built-in/output-format-optimize.yaml +95 -0
- package/packages/server/mcp/heartbeat-patch.js +73 -0
- package/packages/server/mcp/mcp.server.js +63 -314
- package/packages/server/mcp/prompt.handler.js +26 -0
- package/packages/server/mcp/thinking-toolkit.handler.js +380 -0
- package/packages/server/package.json +35 -3
- package/packages/server/server.js +114 -12
- package/packages/server/services/TerminalService.js +498 -0
- package/packages/server/services/WebSocketService.js +484 -0
- package/packages/server/services/manager.js +38 -7
- package/packages/server/services/model.service.js +473 -0
- package/packages/server/services/optimization.service.js +457 -0
- package/packages/server/services/template.service.js +333 -0
- package/packages/server/toolm/tool-description-generator-optimized.service.js +5 -2
- package/packages/server/toolm/tool-sync.service.js +47 -3
- package/packages/server/utils/config.js +8 -1
- package/packages/server/utils/port-checker.js +63 -0
- package/packages/server/utils/util.js +27 -0
- package/IFLOW.md +0 -175
- package/app/desktop/assets/app.1.png +0 -0
- package/app/desktop/assets/app.png +0 -0
- package/app/desktop/assets/icons/icon.icns +0 -0
- package/app/desktop/assets/icons/icon.ico +0 -0
- package/app/desktop/assets/icons/icon.png +0 -0
- package/app/desktop/assets/icons/tray.png +0 -0
- package/app/desktop/assets/templates/about.html +0 -147
- package/app/desktop/assets/tray.1.png +0 -0
- package/app/desktop/assets/tray.png +0 -0
- package/app/desktop/main.js +0 -241
- package/app/desktop/package-lock.json +0 -5026
- package/app/desktop/package.json +0 -100
- package/app/desktop/preload.js +0 -7
- package/app/desktop/src/core/error-handler.js +0 -108
- package/app/desktop/src/core/event-emitter.js +0 -84
- package/app/desktop/src/core/logger.js +0 -108
- package/app/desktop/src/core/state-manager.js +0 -125
- package/app/desktop/src/services/module-loader.js +0 -214
- package/app/desktop/src/services/runtime-manager.js +0 -301
- package/app/desktop/src/services/service-manager.js +0 -169
- package/app/desktop/src/services/update-manager.js +0 -267
- package/app/desktop/src/ui/about-dialog-manager.js +0 -208
- package/app/desktop/src/ui/admin-window-manager.js +0 -757
- package/app/desktop/src/ui/splash-manager.js +0 -253
- package/app/desktop/src/ui/tray-manager.js +0 -186
- package/app/desktop/src/utils/icon-manager.js +0 -133
- package/app/desktop/src/utils/path-utils.js +0 -58
- package/app/desktop/src/utils/resource-paths.js +0 -49
- package/app/desktop/src/utils/resource-sync.js +0 -260
- package/app/desktop/src/utils/runtime-sync.js +0 -241
- package/app/desktop/src/utils/template-renderer.js +0 -284
- package/app/desktop/src/utils/version-utils.js +0 -59
- package/examples/prompts/developer/code-review.yaml +0 -32
- package/examples/prompts/developer/code_refactoring.yaml +0 -31
- package/examples/prompts/developer/doc-generator.yaml +0 -36
- package/examples/prompts/developer/error-code-fixer.yaml +0 -35
- package/examples/prompts/engineer/engineer-professional.yaml +0 -92
- package/examples/prompts/engineer/laowang-engineer.yaml +0 -132
- package/examples/prompts/engineer/nekomata-engineer.yaml +0 -123
- package/examples/prompts/engineer/ojousama-engineer.yaml +0 -124
- package/examples/prompts/generator/gen_3d_edu_webpage_html.yaml +0 -117
- package/examples/prompts/generator/gen_3d_webpage_html.yaml +0 -75
- package/examples/prompts/generator/gen_bento_grid_html.yaml +0 -112
- package/examples/prompts/generator/gen_html_web_page.yaml +0 -88
- package/examples/prompts/generator/gen_knowledge_card_html.yaml +0 -83
- package/examples/prompts/generator/gen_magazine_card_html.yaml +0 -82
- package/examples/prompts/generator/gen_mimeng_headline_title.yaml +0 -71
- package/examples/prompts/generator/gen_podcast_script.yaml +0 -69
- package/examples/prompts/generator/gen_prd_prototype_html.yaml +0 -175
- package/examples/prompts/generator/gen_summarize.yaml +0 -157
- package/examples/prompts/generator/gen_title.yaml +0 -119
- package/examples/prompts/generator/others/api_documentation.yaml +0 -32
- package/examples/prompts/generator/others/build_mcp_server.yaml +0 -26
- package/examples/prompts/generator/others/project_architecture.yaml +0 -31
- package/examples/prompts/generator/others/test_case_generator.yaml +0 -30
- package/examples/prompts/generator/others/writing_assistant.yaml +0 -72
- package/examples/prompts/recommend/human_3-0_growth_diagnostic_coach_prompt.yaml +0 -105
- package/examples/prompts/workflow/sixstep-workflow.yaml +0 -192
- package/packages/admin-ui/.babelrc +0 -3
- package/packages/admin-ui/admin.html +0 -412
- package/packages/admin-ui/css/codemirror-theme_xq-light.css +0 -43
- package/packages/admin-ui/css/codemirror.css +0 -344
- package/packages/admin-ui/css/main.css +0 -2592
- package/packages/admin-ui/css/recommended-prompts.css +0 -610
- package/packages/admin-ui/package-lock.json +0 -6973
- package/packages/admin-ui/package.json +0 -36
- package/packages/admin-ui/src/codemirror.js +0 -53
- package/packages/admin-ui/src/index.js +0 -3188
- package/packages/admin-ui/webpack.config.js +0 -76
- package/packages/server/toolm/test-tools.js +0 -264
- package/scripts/build-icons.js +0 -135
- package/scripts/build.sh +0 -57
- package/scripts/postinstall.js +0 -34
- package/scripts/surge/CNAME +0 -1
- package/scripts/surge/README.md +0 -47
- package/scripts/surge/package-lock.json +0 -34
- package/scripts/surge/package.json +0 -20
- package/scripts/surge/sync-to-surge.js +0 -151
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocketService - WebSocket服务管理类
|
|
3
|
+
*
|
|
4
|
+
* 提供实时双向通信服务,支持终端会话的实时数据传输
|
|
5
|
+
* 处理客户端连接、消息路由和会话管理
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { WebSocketServer } from 'ws';
|
|
9
|
+
import { randomUUID } from 'crypto';
|
|
10
|
+
import { logger } from '../utils/logger.js';
|
|
11
|
+
import { terminalService } from './TerminalService.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* WebSocket连接类
|
|
15
|
+
*/
|
|
16
|
+
class WebSocketConnection {
|
|
17
|
+
constructor(ws, clientId) {
|
|
18
|
+
this.ws = ws;
|
|
19
|
+
this.clientId = clientId;
|
|
20
|
+
this.connectedAt = new Date();
|
|
21
|
+
this.lastActivity = new Date();
|
|
22
|
+
this.sessionId = null;
|
|
23
|
+
this.isAuthenticated = false;
|
|
24
|
+
this.userInfo = null;
|
|
25
|
+
|
|
26
|
+
this.setupWebSocketEvents();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 设置WebSocket事件监听
|
|
31
|
+
*/
|
|
32
|
+
setupWebSocketEvents() {
|
|
33
|
+
this.ws.on('message', (data) => {
|
|
34
|
+
this.handleMessage(data);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
this.ws.on('close', (code, reason) => {
|
|
38
|
+
this.handleClose(code, reason);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
this.ws.on('error', (error) => {
|
|
42
|
+
this.handleError(error);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
this.ws.on('pong', () => {
|
|
46
|
+
this.lastActivity = new Date();
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 处理消息
|
|
52
|
+
*/
|
|
53
|
+
async handleMessage(data) {
|
|
54
|
+
try {
|
|
55
|
+
this.lastActivity = new Date();
|
|
56
|
+
const message = JSON.parse(data.toString());
|
|
57
|
+
|
|
58
|
+
logger.debug(`Received message from client ${this.clientId}:`, message.type);
|
|
59
|
+
|
|
60
|
+
// 根据消息类型处理
|
|
61
|
+
switch (message.type) {
|
|
62
|
+
case 'terminal.create':
|
|
63
|
+
await this.handleTerminalCreate(message);
|
|
64
|
+
break;
|
|
65
|
+
case 'terminal.data':
|
|
66
|
+
await this.handleTerminalData(message);
|
|
67
|
+
break;
|
|
68
|
+
case 'terminal.resize':
|
|
69
|
+
await this.handleTerminalResize(message);
|
|
70
|
+
break;
|
|
71
|
+
case 'terminal.close':
|
|
72
|
+
await this.handleTerminalClose(message);
|
|
73
|
+
break;
|
|
74
|
+
case 'ping':
|
|
75
|
+
this.handlePing(message);
|
|
76
|
+
break;
|
|
77
|
+
default:
|
|
78
|
+
this.sendError('Unknown message type', message.type);
|
|
79
|
+
}
|
|
80
|
+
} catch (error) {
|
|
81
|
+
logger.error(`Error handling message from client ${this.clientId}:`, error);
|
|
82
|
+
this.sendError('Message processing error', error.message);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 处理终端创建
|
|
88
|
+
*/
|
|
89
|
+
async handleTerminalCreate(message) {
|
|
90
|
+
try {
|
|
91
|
+
const options = {
|
|
92
|
+
id: message.sessionId,
|
|
93
|
+
workingDirectory: message.workingDirectory,
|
|
94
|
+
size: message.size || { cols: 80, rows: 24 },
|
|
95
|
+
shell: message.shell,
|
|
96
|
+
environment: message.environment
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const session = await terminalService.createSession(options);
|
|
100
|
+
this.sessionId = session.id;
|
|
101
|
+
|
|
102
|
+
// 监听会话数据
|
|
103
|
+
session.on('data', (data) => {
|
|
104
|
+
this.send('terminal.data', {
|
|
105
|
+
sessionId: session.id,
|
|
106
|
+
data: data
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
session.on('exit', (info) => {
|
|
111
|
+
this.send('terminal.exit', {
|
|
112
|
+
sessionId: session.id,
|
|
113
|
+
exitCode: info.exitCode,
|
|
114
|
+
signal: info.signal
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
this.send('terminal.created', {
|
|
119
|
+
sessionId: session.id,
|
|
120
|
+
info: session.getInfo()
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
logger.info(`Terminal session created for client ${this.clientId}: ${session.id}`);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
this.sendError('Failed to create terminal session', error.message);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 处理终端数据
|
|
131
|
+
*/
|
|
132
|
+
async handleTerminalData(message) {
|
|
133
|
+
if (!this.sessionId) {
|
|
134
|
+
this.sendError('No active terminal session', 'terminal.data');
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
const session = terminalService.getSession(this.sessionId);
|
|
140
|
+
if (session && session.isActive) {
|
|
141
|
+
session.write(message.data);
|
|
142
|
+
} else {
|
|
143
|
+
this.sendError('Terminal session not found or inactive', 'terminal.data');
|
|
144
|
+
}
|
|
145
|
+
} catch (error) {
|
|
146
|
+
this.sendError('Failed to write to terminal', error.message);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* 处理终端大小调整
|
|
152
|
+
*/
|
|
153
|
+
async handleTerminalResize(message) {
|
|
154
|
+
if (!this.sessionId) {
|
|
155
|
+
this.sendError('No active terminal session', 'terminal.resize');
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
const session = terminalService.getSession(this.sessionId);
|
|
161
|
+
if (session && session.isActive) {
|
|
162
|
+
session.resize(message.cols, message.rows);
|
|
163
|
+
this.send('terminal.resized', {
|
|
164
|
+
sessionId: this.sessionId,
|
|
165
|
+
size: { cols: message.cols, rows: message.rows }
|
|
166
|
+
});
|
|
167
|
+
} else {
|
|
168
|
+
this.sendError('Terminal session not found or inactive', 'terminal.resize');
|
|
169
|
+
}
|
|
170
|
+
} catch (error) {
|
|
171
|
+
this.sendError('Failed to resize terminal', error.message);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* 处理终端关闭
|
|
177
|
+
*/
|
|
178
|
+
async handleTerminalClose(message) {
|
|
179
|
+
if (this.sessionId) {
|
|
180
|
+
try {
|
|
181
|
+
await terminalService.removeSession(this.sessionId);
|
|
182
|
+
this.send('terminal.closed', {
|
|
183
|
+
sessionId: this.sessionId
|
|
184
|
+
});
|
|
185
|
+
this.sessionId = null;
|
|
186
|
+
} catch (error) {
|
|
187
|
+
this.sendError('Failed to close terminal session', error.message);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* 处理ping
|
|
194
|
+
*/
|
|
195
|
+
handlePing(message) {
|
|
196
|
+
this.send('pong', {
|
|
197
|
+
timestamp: Date.now(),
|
|
198
|
+
clientId: this.clientId
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* 处理连接关闭
|
|
204
|
+
*/
|
|
205
|
+
handleClose(code, reason) {
|
|
206
|
+
logger.info(`Client ${this.clientId} disconnected: ${code} - ${reason}`);
|
|
207
|
+
|
|
208
|
+
// 清理关联的终端会话
|
|
209
|
+
if (this.sessionId) {
|
|
210
|
+
terminalService.removeSession(this.sessionId);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* 处理错误
|
|
216
|
+
*/
|
|
217
|
+
handleError(error) {
|
|
218
|
+
logger.error(`WebSocket error for client ${this.clientId}:`, error);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* 发送消息
|
|
223
|
+
*/
|
|
224
|
+
send(type, data) {
|
|
225
|
+
if (this.ws.readyState === this.ws.OPEN) {
|
|
226
|
+
const message = {
|
|
227
|
+
type: type,
|
|
228
|
+
timestamp: Date.now(),
|
|
229
|
+
clientId: this.clientId,
|
|
230
|
+
...data
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
this.ws.send(JSON.stringify(message));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* 发送错误消息
|
|
239
|
+
*/
|
|
240
|
+
sendError(message, details = null) {
|
|
241
|
+
this.send('error', {
|
|
242
|
+
message: message,
|
|
243
|
+
details: details
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* 获取连接信息
|
|
249
|
+
*/
|
|
250
|
+
getInfo() {
|
|
251
|
+
return {
|
|
252
|
+
clientId: this.clientId,
|
|
253
|
+
connectedAt: this.connectedAt,
|
|
254
|
+
lastActivity: this.lastActivity,
|
|
255
|
+
sessionId: this.sessionId,
|
|
256
|
+
isAuthenticated: this.isAuthenticated,
|
|
257
|
+
readyState: this.ws.readyState
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* 关闭连接
|
|
263
|
+
*/
|
|
264
|
+
close(code = 1000, reason = 'Normal closure') {
|
|
265
|
+
if (this.ws.readyState === this.ws.OPEN) {
|
|
266
|
+
this.ws.close(code, reason);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* WebSocketService 主类
|
|
273
|
+
*/
|
|
274
|
+
export class WebSocketService {
|
|
275
|
+
constructor(options = {}) {
|
|
276
|
+
this.options = {
|
|
277
|
+
port: 5622,
|
|
278
|
+
host: '0.0.0.0',
|
|
279
|
+
maxConnections: 100,
|
|
280
|
+
heartbeatInterval: 30000, // 30秒心跳
|
|
281
|
+
connectionTimeout: 300000, // 5分钟超时
|
|
282
|
+
...options
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
this.wss = null;
|
|
286
|
+
this.connections = new Map();
|
|
287
|
+
this.isRunning = false;
|
|
288
|
+
|
|
289
|
+
logger.info('WebSocketService initialized');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* 启动WebSocket服务
|
|
294
|
+
*/
|
|
295
|
+
async start() {
|
|
296
|
+
if (this.isRunning) {
|
|
297
|
+
throw new Error('WebSocket service is already running');
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return new Promise((resolve, reject) => {
|
|
301
|
+
try {
|
|
302
|
+
this.wss = new WebSocketServer({
|
|
303
|
+
port: this.options.port,
|
|
304
|
+
host: this.options.host,
|
|
305
|
+
maxConnections: this.options.maxConnections
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
this.wss.on('connection', (ws, request) => {
|
|
309
|
+
this.handleConnection(ws, request);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
this.wss.on('error', (error) => {
|
|
313
|
+
logger.error('WebSocket server error:', error);
|
|
314
|
+
if (!this.isRunning) {
|
|
315
|
+
reject(error);
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
this.wss.on('listening', () => {
|
|
320
|
+
this.isRunning = true;
|
|
321
|
+
logger.info(`WebSocket server listening on ${this.options.host}:${this.options.port}`);
|
|
322
|
+
|
|
323
|
+
// 启动心跳检测
|
|
324
|
+
this.startHeartbeat();
|
|
325
|
+
|
|
326
|
+
resolve();
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
} catch (error) {
|
|
330
|
+
reject(error);
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* 停止WebSocket服务
|
|
337
|
+
*/
|
|
338
|
+
async stop() {
|
|
339
|
+
if (!this.isRunning) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// 关闭所有连接
|
|
344
|
+
for (const [clientId, connection] of this.connections) {
|
|
345
|
+
connection.close(1001, 'Server shutdown');
|
|
346
|
+
}
|
|
347
|
+
this.connections.clear();
|
|
348
|
+
|
|
349
|
+
// 停止心跳检测
|
|
350
|
+
if (this.heartbeatInterval) {
|
|
351
|
+
clearInterval(this.heartbeatInterval);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// 关闭服务器
|
|
355
|
+
if (this.wss) {
|
|
356
|
+
this.wss.close();
|
|
357
|
+
this.wss = null;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
this.isRunning = false;
|
|
361
|
+
logger.info('WebSocket service stopped');
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* 处理新连接
|
|
366
|
+
*/
|
|
367
|
+
handleConnection(ws, request) {
|
|
368
|
+
const clientId = this.generateClientId();
|
|
369
|
+
|
|
370
|
+
// 检查连接数限制
|
|
371
|
+
if (this.connections.size >= this.options.maxConnections) {
|
|
372
|
+
ws.close(1013, 'Server overload');
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
try {
|
|
377
|
+
const connection = new WebSocketConnection(ws, clientId);
|
|
378
|
+
this.connections.set(clientId, connection);
|
|
379
|
+
|
|
380
|
+
logger.info(`New client connected: ${clientId} from ${request.socket.remoteAddress}`);
|
|
381
|
+
|
|
382
|
+
// 发送欢迎消息
|
|
383
|
+
connection.send('welcome', {
|
|
384
|
+
clientId: clientId,
|
|
385
|
+
serverInfo: {
|
|
386
|
+
version: '1.0.0',
|
|
387
|
+
platform: process.platform,
|
|
388
|
+
nodeVersion: process.version
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// 监听连接关闭
|
|
393
|
+
ws.on('close', () => {
|
|
394
|
+
this.connections.delete(clientId);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
} catch (error) {
|
|
398
|
+
logger.error(`Failed to handle connection for client ${clientId}:`, error);
|
|
399
|
+
ws.close(1011, 'Internal error');
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* 生成客户端ID
|
|
405
|
+
*/
|
|
406
|
+
generateClientId() {
|
|
407
|
+
return randomUUID();
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* 启动心跳检测
|
|
412
|
+
*/
|
|
413
|
+
startHeartbeat() {
|
|
414
|
+
this.heartbeatInterval = setInterval(() => {
|
|
415
|
+
const now = Date.now();
|
|
416
|
+
|
|
417
|
+
for (const [clientId, connection] of this.connections) {
|
|
418
|
+
// 检查连接超时
|
|
419
|
+
if (now - connection.lastActivity > this.options.connectionTimeout) {
|
|
420
|
+
logger.info(`Client ${clientId} timeout, disconnecting`);
|
|
421
|
+
connection.close(1000, 'Connection timeout');
|
|
422
|
+
this.connections.delete(clientId);
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// 发送ping
|
|
427
|
+
if (connection.ws.readyState === connection.ws.OPEN) {
|
|
428
|
+
connection.ws.ping();
|
|
429
|
+
} else {
|
|
430
|
+
// 连接已关闭,清理
|
|
431
|
+
this.connections.delete(clientId);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}, this.options.heartbeatInterval);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* 广播消息给所有客户端
|
|
439
|
+
*/
|
|
440
|
+
broadcast(type, data) {
|
|
441
|
+
for (const connection of this.connections.values()) {
|
|
442
|
+
connection.send(type, data);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* 获取连接
|
|
448
|
+
*/
|
|
449
|
+
getConnection(clientId) {
|
|
450
|
+
return this.connections.get(clientId);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* 获取所有连接
|
|
455
|
+
*/
|
|
456
|
+
getAllConnections() {
|
|
457
|
+
return Array.from(this.connections.values());
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* 获取服务状态
|
|
462
|
+
*/
|
|
463
|
+
getStatus() {
|
|
464
|
+
const activeConnections = this.getAllConnections().filter(
|
|
465
|
+
conn => conn.ws.readyState === conn.ws.OPEN
|
|
466
|
+
);
|
|
467
|
+
|
|
468
|
+
return {
|
|
469
|
+
isRunning: this.isRunning,
|
|
470
|
+
port: this.options.port,
|
|
471
|
+
host: this.options.host,
|
|
472
|
+
totalConnections: this.connections.size,
|
|
473
|
+
activeConnections: activeConnections.length,
|
|
474
|
+
maxConnections: this.options.maxConnections,
|
|
475
|
+
uptime: process.uptime()
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// 创建单例实例
|
|
481
|
+
export const webSocketService = new WebSocketService();
|
|
482
|
+
|
|
483
|
+
// 导出类型定义
|
|
484
|
+
export { WebSocketConnection };
|
|
@@ -210,17 +210,22 @@ class PromptManager {
|
|
|
210
210
|
*
|
|
211
211
|
* @param {string} currentPath - 当前目录路径
|
|
212
212
|
* @param {string} relativePath - 相对于当前目录的路径
|
|
213
|
+
* @param {boolean} inheritedEnabled - 从父目录继承的启用状态
|
|
213
214
|
* @returns {Array} 所有prompt文件
|
|
214
215
|
*/
|
|
215
|
-
async scanPromptFiles(currentPath, relativePath = '') {
|
|
216
|
+
async scanPromptFiles(currentPath, relativePath = '', inheritedEnabled = true) {
|
|
216
217
|
const promptFiles = [];
|
|
218
|
+
let currentEnabled = inheritedEnabled;
|
|
217
219
|
|
|
218
220
|
if (relativePath) {
|
|
219
221
|
// console.log('读取组元数据:', "{relativePath:", relativePath, ", currentPath:", currentPath, "}");
|
|
220
222
|
const meta = this.readGroupMeta(path.join(currentPath, relativePath));
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
223
|
+
currentEnabled = currentEnabled && (meta.enabled !== false);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// 如果当前目录被禁用,直接返回空数组
|
|
227
|
+
if (!currentEnabled) {
|
|
228
|
+
return [];
|
|
224
229
|
}
|
|
225
230
|
|
|
226
231
|
try {
|
|
@@ -232,8 +237,8 @@ class PromptManager {
|
|
|
232
237
|
const itemRelativePath = relativePath ? path.join(relativePath, item.name) : item.name; // 文件的相对路径,相对于当前目录,如:xxx/xxx.yaml
|
|
233
238
|
|
|
234
239
|
if (item.isDirectory()) {
|
|
235
|
-
//
|
|
236
|
-
const subFiles = await this.scanPromptFiles(itemPath, itemRelativePath);
|
|
240
|
+
// 递归扫描子目录,传递当前目录的启用状态
|
|
241
|
+
const subFiles = await this.scanPromptFiles(itemPath, itemRelativePath, currentEnabled);
|
|
237
242
|
promptFiles.push(...subFiles);
|
|
238
243
|
} else if (item.isFile()) {
|
|
239
244
|
// 检查文件扩展名
|
|
@@ -361,10 +366,33 @@ class PromptManager {
|
|
|
361
366
|
promptData = YAML.parse(content);
|
|
362
367
|
}
|
|
363
368
|
|
|
369
|
+
// 检查提示词本身是否启用
|
|
364
370
|
if (promptData.enabled !== true) {
|
|
365
371
|
logger.debug(`skipped loading prompt: ${promptData.name} -> disabled`);
|
|
366
372
|
return null;
|
|
367
373
|
}
|
|
374
|
+
|
|
375
|
+
// 检查目录启用状态
|
|
376
|
+
const fileDir = path.dirname(filePath);
|
|
377
|
+
const relativeDir = path.dirname(relativePath);
|
|
378
|
+
if (relativeDir && relativeDir !== '.') {
|
|
379
|
+
// 检查从根目录到当前文件路径的所有目录的启用状态
|
|
380
|
+
const dirParts = relativeDir.split(path.sep);
|
|
381
|
+
let currentPath = this.promptsDir;
|
|
382
|
+
let inheritedEnabled = true;
|
|
383
|
+
|
|
384
|
+
for (const dirPart of dirParts) {
|
|
385
|
+
currentPath = path.join(currentPath, dirPart);
|
|
386
|
+
const meta = this.readGroupMeta(currentPath);
|
|
387
|
+
inheritedEnabled = inheritedEnabled && (meta.enabled !== false);
|
|
388
|
+
|
|
389
|
+
if (!inheritedEnabled) {
|
|
390
|
+
logger.debug(`skipped loading prompt: ${promptData.name} -> parent directory disabled`);
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
368
396
|
logger.debug(`loaded prompt: ${promptData.name} -> ${filePath}`);
|
|
369
397
|
|
|
370
398
|
// 验证prompt数据结构
|
|
@@ -478,4 +506,7 @@ class PromptManager {
|
|
|
478
506
|
}
|
|
479
507
|
|
|
480
508
|
// 创建全局PromptManager实例
|
|
481
|
-
export const promptManager = new PromptManager(config.getPromptsDir());
|
|
509
|
+
export const promptManager = new PromptManager(config.getPromptsDir());
|
|
510
|
+
|
|
511
|
+
// 导出PromptManager类供测试使用
|
|
512
|
+
export { PromptManager };
|