@myassis/gateway 1.0.0
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 +194 -0
- package/dist/.env +6 -0
- package/dist/api/index.js +182 -0
- package/dist/config/index.js +41 -0
- package/dist/index.js +183 -0
- package/dist/middleware/auth.js +53 -0
- package/dist/middleware/errorHandler.js +20 -0
- package/dist/routes/agent.js +513 -0
- package/dist/routes/auth.js +172 -0
- package/dist/routes/chat.js +45 -0
- package/dist/routes/config.js +21 -0
- package/dist/routes/models.js +123 -0
- package/dist/routes/service.js +240 -0
- package/dist/routes/settings.js +101 -0
- package/dist/routes/skillHub.js +126 -0
- package/dist/routes/skills.js +159 -0
- package/dist/routes/tasks.js +149 -0
- package/dist/routes/upload.js +129 -0
- package/dist/routes/version.js +66 -0
- package/dist/services/HMSPushService.js +24 -0
- package/dist/services/LocalTaskService.js +223 -0
- package/dist/services/NotificationService.js +242 -0
- package/dist/services/ServiceManager.js +348 -0
- package/dist/services/TaskSchedulerService.js +195 -0
- package/dist/services/TaskService.js +240 -0
- package/dist/services/WebSocketService.js +236 -0
- package/dist/services/agent/Agent.js +120 -0
- package/dist/services/agent/AgentManager.js +265 -0
- package/dist/services/agent/AgentStore.js +73 -0
- package/dist/services/dataService.js +293 -0
- package/dist/services/index.js +15 -0
- package/dist/services/llm/LLMClient.js +724 -0
- package/dist/services/memory/MemoryManager.js +117 -0
- package/dist/services/model/ModelCapabilities.js +141 -0
- package/dist/services/model/index.js +4 -0
- package/dist/services/models.js +16 -0
- package/dist/services/session/MigrationManager.js +176 -0
- package/dist/services/session/Session.js +733 -0
- package/dist/services/session/SessionManager.js +255 -0
- package/dist/services/session/SessionStore.js +186 -0
- package/dist/services/session/index.js +3 -0
- package/dist/services/skills.js +34 -0
- package/dist/services/systemPrompt.js +150 -0
- package/dist/services/task/PushTokenStore.js +124 -0
- package/dist/services/task/TaskStore.js +143 -0
- package/dist/services/tools/calculator.js +27 -0
- package/dist/services/tools/edit.js +318 -0
- package/dist/services/tools/exec.js +119 -0
- package/dist/services/tools/fetch.js +155 -0
- package/dist/services/tools/file.js +315 -0
- package/dist/services/tools/index.js +48 -0
- package/dist/services/tools/keyboard.js +145 -0
- package/dist/services/tools/model.js +86 -0
- package/dist/services/tools/mouse.js +55 -0
- package/dist/services/tools/screenshot.js +19 -0
- package/dist/services/tools/search.js +53 -0
- package/dist/services/tools/skill.js +108 -0
- package/dist/services/tools/task.js +110 -0
- package/dist/services/tools/types.js +1 -0
- package/dist/services/tools/webFetch.js +34 -0
- package/dist/stores/authStore.js +178 -0
- package/dist/stores/index.js +6 -0
- package/dist/stores/memoryStore.js +191 -0
- package/dist/stores/persistStore.js +317 -0
- package/package.json +94 -0
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { requireAuth } from '@/middleware/auth.js';
|
|
3
|
+
import { sessionManager } from '@/services/session';
|
|
4
|
+
import { initAgentManager, agentManager } from '@/services/agent/AgentManager';
|
|
5
|
+
import { AgentStore } from '@/services/agent/AgentStore';
|
|
6
|
+
import { getLogger } from '@pocketclaw/shared';
|
|
7
|
+
const logger = getLogger('AgentRoutes');
|
|
8
|
+
import { sessionStore } from '@/services/session/SessionStore';
|
|
9
|
+
const router = Router();
|
|
10
|
+
// 所有路由需要认证
|
|
11
|
+
router.use(requireAuth);
|
|
12
|
+
// Middleware: ensure agentManager is initialized
|
|
13
|
+
function ensureAgentManager(req, res, next) {
|
|
14
|
+
try {
|
|
15
|
+
// Get or create AgentStore using SessionStore's db instance
|
|
16
|
+
const db = sessionStore.getDb();
|
|
17
|
+
const agentStore = new AgentStore(db);
|
|
18
|
+
// Initialize AgentManager if not already done
|
|
19
|
+
if (!agentManager) {
|
|
20
|
+
initAgentManager(agentStore);
|
|
21
|
+
}
|
|
22
|
+
agentManager.initialize();
|
|
23
|
+
req.agentManager = agentManager;
|
|
24
|
+
next();
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
logger.error('Failed to initialize agentManager:', error);
|
|
28
|
+
res.status(500).json({ success: false, error: 'Failed to initialize agent manager' });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// ============ Agent CRUD ============
|
|
32
|
+
/**
|
|
33
|
+
* GET /api/agent/list
|
|
34
|
+
* Get all agents for current user
|
|
35
|
+
*/
|
|
36
|
+
router.get('/list', ensureAgentManager, async (req, res) => {
|
|
37
|
+
try {
|
|
38
|
+
const userId = req.userId;
|
|
39
|
+
const agentList = agentManager.getAgentList();
|
|
40
|
+
res.json({ success: true, data: agentList });
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
logger.error(`Get agent list error: ${error}`);
|
|
44
|
+
res.status(500).json({ success: false, error: 'Failed to get agent list' });
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
/**
|
|
48
|
+
* GET /api/agent/:id
|
|
49
|
+
* Get agent details
|
|
50
|
+
*/
|
|
51
|
+
router.get('/:id', ensureAgentManager, async (req, res) => {
|
|
52
|
+
try {
|
|
53
|
+
const userId = req.userId;
|
|
54
|
+
const agent = agentManager.getAgent(req.params.id);
|
|
55
|
+
if (!agent) {
|
|
56
|
+
return res.status(404).json({ success: false, error: 'Agent not found' });
|
|
57
|
+
}
|
|
58
|
+
res.json({
|
|
59
|
+
success: true,
|
|
60
|
+
data: {
|
|
61
|
+
id: agent.id,
|
|
62
|
+
name: agent.name,
|
|
63
|
+
description: agent.description,
|
|
64
|
+
systemPrompt: agent.systemPrompt,
|
|
65
|
+
avatar: agent.avatar,
|
|
66
|
+
config: agent.config,
|
|
67
|
+
createdAt: agent.createdAt,
|
|
68
|
+
updatedAt: agent.updatedAt,
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
logger.error(`Get agent error: ${error}`);
|
|
74
|
+
res.status(500).json({ success: false, error: 'Failed to get agent' });
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
/**
|
|
78
|
+
* POST /api/agent/create
|
|
79
|
+
* Create new agent
|
|
80
|
+
*/
|
|
81
|
+
router.post('/create', ensureAgentManager, async (req, res) => {
|
|
82
|
+
try {
|
|
83
|
+
const userId = req.userId;
|
|
84
|
+
const { name, description, systemPrompt, avatar, config } = req.body;
|
|
85
|
+
if (!name) {
|
|
86
|
+
return res.status(400).json({ success: false, error: 'Name is required' });
|
|
87
|
+
}
|
|
88
|
+
const agent = agentManager.createAgent({
|
|
89
|
+
name,
|
|
90
|
+
description,
|
|
91
|
+
systemPrompt,
|
|
92
|
+
avatar,
|
|
93
|
+
config,
|
|
94
|
+
});
|
|
95
|
+
res.json({
|
|
96
|
+
success: true,
|
|
97
|
+
data: {
|
|
98
|
+
id: agent.id,
|
|
99
|
+
name: agent.name,
|
|
100
|
+
description: agent.description,
|
|
101
|
+
systemPrompt: agent.systemPrompt,
|
|
102
|
+
avatar: agent.avatar,
|
|
103
|
+
createdAt: agent.createdAt,
|
|
104
|
+
updatedAt: agent.updatedAt,
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
logger.error(`Create agent error: ${error}`);
|
|
110
|
+
const message = error instanceof Error ? error.message : 'Failed to create agent';
|
|
111
|
+
res.status(400).json({ success: false, error: message });
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
/**
|
|
115
|
+
* PUT /api/agent/:id
|
|
116
|
+
* Update agent
|
|
117
|
+
*/
|
|
118
|
+
router.put('/:id', ensureAgentManager, async (req, res) => {
|
|
119
|
+
try {
|
|
120
|
+
const userId = req.userId;
|
|
121
|
+
logger.debug('update agent', req.body);
|
|
122
|
+
const { name, description, systemPrompt, avatar, config } = req.body;
|
|
123
|
+
const agent = agentManager.updateAgent(req.params.id, {
|
|
124
|
+
name,
|
|
125
|
+
description,
|
|
126
|
+
systemPrompt,
|
|
127
|
+
avatar,
|
|
128
|
+
config,
|
|
129
|
+
});
|
|
130
|
+
if (!agent) {
|
|
131
|
+
return res.status(404).json({ success: false, error: 'Agent not found' });
|
|
132
|
+
}
|
|
133
|
+
res.json({
|
|
134
|
+
success: true,
|
|
135
|
+
data: {
|
|
136
|
+
id: agent.id,
|
|
137
|
+
name: agent.name,
|
|
138
|
+
description: agent.description,
|
|
139
|
+
systemPrompt: agent.systemPrompt,
|
|
140
|
+
avatar: agent.avatar,
|
|
141
|
+
updatedAt: agent.updatedAt,
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
logger.error(`Update agent error: ${error}`);
|
|
147
|
+
const message = error instanceof Error ? error.message : 'Failed to update agent';
|
|
148
|
+
res.status(400).json({ success: false, error: message });
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
/**
|
|
152
|
+
* DELETE /api/agent/:id
|
|
153
|
+
* Delete agent
|
|
154
|
+
*/
|
|
155
|
+
router.delete('/:id', ensureAgentManager, async (req, res) => {
|
|
156
|
+
try {
|
|
157
|
+
const userId = req.userId;
|
|
158
|
+
const deleted = agentManager.deleteAgent(req.params.id);
|
|
159
|
+
if (!deleted) {
|
|
160
|
+
return res.status(404).json({ success: false, error: 'Agent not found' });
|
|
161
|
+
}
|
|
162
|
+
res.json({ success: true });
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
logger.error(`Delete agent error: ${error}`);
|
|
166
|
+
const message = error instanceof Error ? error.message : 'Failed to delete agent';
|
|
167
|
+
res.status(400).json({ success: false, error: message });
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
// ============ Current Agent ============
|
|
171
|
+
/**
|
|
172
|
+
* GET /api/agent/current
|
|
173
|
+
* Get current agent
|
|
174
|
+
*/
|
|
175
|
+
router.get('/current', ensureAgentManager, async (req, res) => {
|
|
176
|
+
try {
|
|
177
|
+
const userId = req.userId;
|
|
178
|
+
const current = agentManager.getCurrentAgent();
|
|
179
|
+
res.json({
|
|
180
|
+
success: true, data: current ? {
|
|
181
|
+
id: current.id,
|
|
182
|
+
userId: current.userId,
|
|
183
|
+
name: current.name,
|
|
184
|
+
description: current.description,
|
|
185
|
+
systemPrompt: current.systemPrompt,
|
|
186
|
+
avatar: current.avatar,
|
|
187
|
+
config: current.config,
|
|
188
|
+
createdAt: current.createdAt,
|
|
189
|
+
updatedAt: current.updatedAt,
|
|
190
|
+
} : null
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
logger.error(`Get current agent error: ${error}`);
|
|
195
|
+
res.status(500).json({ success: false, error: 'Failed to get current agent' });
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
/**
|
|
199
|
+
* POST /api/agent/set-current
|
|
200
|
+
* Set current agent
|
|
201
|
+
*/
|
|
202
|
+
router.post('/set-current', ensureAgentManager, async (req, res) => {
|
|
203
|
+
try {
|
|
204
|
+
const userId = req.userId;
|
|
205
|
+
const { agentId } = req.body;
|
|
206
|
+
agentManager.setCurrentAgent(agentId || null);
|
|
207
|
+
res.json({ success: true });
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
logger.error(`Set current agent error: ${error}`);
|
|
211
|
+
res.status(500).json({ success: false, error: 'Failed to set current agent' });
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
// ============ Session Management under Agent ============
|
|
215
|
+
/**
|
|
216
|
+
* GET /api/agent/:id/sessions
|
|
217
|
+
* Get all sessions under an agent
|
|
218
|
+
*/
|
|
219
|
+
router.get('/:id/sessions', ensureAgentManager, async (req, res) => {
|
|
220
|
+
try {
|
|
221
|
+
const userId = req.userId;
|
|
222
|
+
const agent = agentManager.getAgent(req.params.id);
|
|
223
|
+
if (!agent) {
|
|
224
|
+
return res.status(404).json({ success: false, error: 'Agent not found' });
|
|
225
|
+
}
|
|
226
|
+
const sessions = agentManager.getAgentSessions(req.params.id);
|
|
227
|
+
res.json({ success: true, data: sessions });
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
logger.error(`Get agent sessions error: `, error);
|
|
231
|
+
res.status(500).json({ success: false, error: 'Failed to get sessions' });
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
/**
|
|
235
|
+
* POST /api/agent/:id/sessions
|
|
236
|
+
* Create new session under agent
|
|
237
|
+
*/
|
|
238
|
+
router.post('/:id/sessions', ensureAgentManager, async (req, res) => {
|
|
239
|
+
try {
|
|
240
|
+
const userId = req.userId;
|
|
241
|
+
const agent = agentManager.getAgent(req.params.id);
|
|
242
|
+
if (!agent) {
|
|
243
|
+
return res.status(404).json({ success: false, error: 'Agent not found' });
|
|
244
|
+
}
|
|
245
|
+
const { title, selectModelId } = req.body;
|
|
246
|
+
const session = agent.createSession({
|
|
247
|
+
title: title || '新会话',
|
|
248
|
+
selectModelId,
|
|
249
|
+
});
|
|
250
|
+
res.json({
|
|
251
|
+
success: true,
|
|
252
|
+
data: {
|
|
253
|
+
id: session.id,
|
|
254
|
+
title: session.title,
|
|
255
|
+
selectModelId: session.selectModelId,
|
|
256
|
+
createdAt: session.createdAt,
|
|
257
|
+
updatedAt: session.updatedAt,
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
logger.error(`Create session error: ${error}`);
|
|
263
|
+
res.status(500).json({ success: false, error: 'Failed to create session' });
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
/**
|
|
267
|
+
* PUT /api/agent/:id/sessions/:sessionId
|
|
268
|
+
* Update session under agent
|
|
269
|
+
*/
|
|
270
|
+
router.put('/:id/sessions/:sessionId', ensureAgentManager, async (req, res) => {
|
|
271
|
+
try {
|
|
272
|
+
const userId = req.userId;
|
|
273
|
+
const agent = agentManager.getAgent(req.params.id);
|
|
274
|
+
if (!agent) {
|
|
275
|
+
return res.status(404).json({ success: false, error: 'Agent not found' });
|
|
276
|
+
}
|
|
277
|
+
const { title, selectModelId, unreadCount } = req.body;
|
|
278
|
+
const session = agent.updateSession(req.params.sessionId, { title, selectModelId, unreadCount });
|
|
279
|
+
res.json({
|
|
280
|
+
success: true,
|
|
281
|
+
data: {
|
|
282
|
+
id: session.id,
|
|
283
|
+
title: session.title,
|
|
284
|
+
selectModelId: session.selectModelId,
|
|
285
|
+
createdAt: session.createdAt,
|
|
286
|
+
updatedAt: session.updatedAt,
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
logger.error(`Update session error: ${error}`);
|
|
292
|
+
res.status(500).json({ success: false, error: 'Failed to update session' });
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
/**
|
|
296
|
+
* DELETE /api/agent/:id/sessions/:sessionId
|
|
297
|
+
* Delete session under agent
|
|
298
|
+
*/
|
|
299
|
+
router.delete('/:id/sessions/:sessionId', ensureAgentManager, async (req, res) => {
|
|
300
|
+
try {
|
|
301
|
+
const userId = req.userId;
|
|
302
|
+
const agent = agentManager.getAgent(req.params.id);
|
|
303
|
+
if (!agent) {
|
|
304
|
+
return res.status(404).json({ success: false, error: 'Agent not found' });
|
|
305
|
+
}
|
|
306
|
+
const deleted = agent.deleteSession(req.params.sessionId);
|
|
307
|
+
res.json({ success: deleted });
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
logger.error(`Delete session error: ${error}`);
|
|
311
|
+
res.status(500).json({ success: false, error: 'Failed to delete session' });
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
// ============ Session Messages ============
|
|
315
|
+
/**
|
|
316
|
+
* GET /api/agent/sessions/:sessionId/messages
|
|
317
|
+
* Get messages for a session
|
|
318
|
+
*/
|
|
319
|
+
router.get('/sessions/:sessionId/messages', ensureAgentManager, async (req, res) => {
|
|
320
|
+
try {
|
|
321
|
+
const userId = req.userId;
|
|
322
|
+
const { sessionId } = req.params;
|
|
323
|
+
const page = parseInt(req.query.page) || 1;
|
|
324
|
+
const pageSize = parseInt(req.query.pageSize) || 20;
|
|
325
|
+
// Find session directly
|
|
326
|
+
const session = sessionManager.getSession(sessionId);
|
|
327
|
+
if (session) {
|
|
328
|
+
session.loadMessages(); // Ensure messages are loaded
|
|
329
|
+
const messages = session.getMessagesByPage(page, pageSize);
|
|
330
|
+
return res.json({
|
|
331
|
+
success: true,
|
|
332
|
+
data: messages.messages.reverse(), // Return in chronological order
|
|
333
|
+
pagination: {
|
|
334
|
+
page,
|
|
335
|
+
pageSize,
|
|
336
|
+
total: messages.total,
|
|
337
|
+
hasMore: messages.hasMore,
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
res.status(404).json({ success: false, error: 'Session not found' });
|
|
342
|
+
}
|
|
343
|
+
catch (error) {
|
|
344
|
+
logger.error(`Get messages error: ${error}`);
|
|
345
|
+
res.status(500).json({ success: false, error: 'Failed to get messages' });
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
/**
|
|
349
|
+
* DELETE /api/agent/sessions/:sessionId/messages/:messageId
|
|
350
|
+
* Delete a specific message
|
|
351
|
+
*/
|
|
352
|
+
router.delete('/sessions/:sessionId/messages/:messageId', ensureAgentManager, async (req, res) => {
|
|
353
|
+
try {
|
|
354
|
+
const userId = req.userId;
|
|
355
|
+
const { sessionId, messageId } = req.params;
|
|
356
|
+
// Find session directly
|
|
357
|
+
const session = sessionManager.getSession(sessionId);
|
|
358
|
+
if (session) {
|
|
359
|
+
session.deleteMessage(messageId);
|
|
360
|
+
return res.json({ success: true });
|
|
361
|
+
}
|
|
362
|
+
res.status(404).json({ success: false, error: 'Session or message not found' });
|
|
363
|
+
}
|
|
364
|
+
catch (error) {
|
|
365
|
+
logger.error(`Delete message error: ${error}`);
|
|
366
|
+
res.status(500).json({ success: false, error: 'Failed to delete message' });
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
/**
|
|
370
|
+
* PUT /api/agent/sessions/:sessionId/messages/:messageId/feedback
|
|
371
|
+
* Update message feedback (like/dislike)
|
|
372
|
+
*/
|
|
373
|
+
router.put('/sessions/:sessionId/messages/:messageId/feedback', ensureAgentManager, async (req, res) => {
|
|
374
|
+
try {
|
|
375
|
+
const userId = req.userId;
|
|
376
|
+
const { sessionId, messageId } = req.params;
|
|
377
|
+
const { feedback } = req.body; // 'like' | 'dislike' | null
|
|
378
|
+
if (feedback !== null && feedback !== 'like' && feedback !== 'dislike') {
|
|
379
|
+
return res.status(400).json({ success: false, error: 'Invalid feedback value. Must be "like", "dislike", or null' });
|
|
380
|
+
}
|
|
381
|
+
const session = sessionManager.getSession(sessionId);
|
|
382
|
+
if (session) {
|
|
383
|
+
session.updateMessageFeedback(messageId, feedback);
|
|
384
|
+
return res.json({ success: true, data: { messageId, feedback } });
|
|
385
|
+
}
|
|
386
|
+
res.status(404).json({ success: false, error: 'Session or message not found' });
|
|
387
|
+
}
|
|
388
|
+
catch (error) {
|
|
389
|
+
logger.error(`Update message feedback error: ${error}`);
|
|
390
|
+
res.status(500).json({ success: false, error: 'Failed to update message feedback' });
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
/**
|
|
394
|
+
* DELETE /api/agent/sessions/:sessionId/messages
|
|
395
|
+
* Clear all messages in a session
|
|
396
|
+
*/
|
|
397
|
+
router.delete('/sessions/:sessionId/messages', ensureAgentManager, async (req, res) => {
|
|
398
|
+
try {
|
|
399
|
+
const userId = req.userId;
|
|
400
|
+
const { sessionId } = req.params;
|
|
401
|
+
// Find session directly
|
|
402
|
+
const session = sessionManager.getSession(sessionId);
|
|
403
|
+
if (session) {
|
|
404
|
+
session.clearMessages();
|
|
405
|
+
return res.json({ success: true });
|
|
406
|
+
}
|
|
407
|
+
res.status(404).json({ success: false, error: 'Session not found' });
|
|
408
|
+
}
|
|
409
|
+
catch (error) {
|
|
410
|
+
logger.error(`Clear messages error: ${error}`);
|
|
411
|
+
res.status(500).json({ success: false, error: 'Failed to clear messages' });
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
/**
|
|
415
|
+
* POST /api/agent/sessions/:sessionId/stream
|
|
416
|
+
* Stream chat response
|
|
417
|
+
*/
|
|
418
|
+
router.post('/sessions/:sessionId/stream', ensureAgentManager, async (req, res) => {
|
|
419
|
+
try {
|
|
420
|
+
const userId = req.userId;
|
|
421
|
+
const { sessionId } = req.params;
|
|
422
|
+
const { content, attachments, userMessageId, assistantMessageId, taskId } = req.body;
|
|
423
|
+
if (!content) {
|
|
424
|
+
return res.status(400).json({ success: false, error: 'Content is required' });
|
|
425
|
+
}
|
|
426
|
+
// Find session directly
|
|
427
|
+
const session = sessionManager.getSession(sessionId);
|
|
428
|
+
if (session) {
|
|
429
|
+
// Set SSE headers
|
|
430
|
+
res.setHeader('Content-Type', 'text/event-stream');
|
|
431
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
432
|
+
res.setHeader('Connection', 'keep-alive');
|
|
433
|
+
res.flushHeaders();
|
|
434
|
+
// Stream response
|
|
435
|
+
await session.streamChat(content, attachments || [], res, userMessageId, assistantMessageId);
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
res.status(404).json({ success: false, error: 'Session not found' });
|
|
439
|
+
}
|
|
440
|
+
catch (error) {
|
|
441
|
+
logger.error(`Stream chat error: ${error}`);
|
|
442
|
+
if (error.message.includes('未提供认证 Token')) {
|
|
443
|
+
res.status(401).json({ success: false, error: 'Token失效' });
|
|
444
|
+
}
|
|
445
|
+
if (!res.headersSent) {
|
|
446
|
+
res.status(500).json({ success: false, error: 'Failed to stream chat' });
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
sendSSE(res, { type: 'error', error: error.message });
|
|
450
|
+
res.end();
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
// SSE 辅助方法:发送数据并立即 flush
|
|
455
|
+
const sendSSE = (res, data) => {
|
|
456
|
+
res.write(`data: ${JSON.stringify(data)}\n\n`);
|
|
457
|
+
res.flush?.();
|
|
458
|
+
};
|
|
459
|
+
/**
|
|
460
|
+
* POST /api/agent/sessions/:sessionId/reset
|
|
461
|
+
* Stop session generation
|
|
462
|
+
*/
|
|
463
|
+
router.post('/sessions/:sessionId/reset', async (req, res) => {
|
|
464
|
+
try {
|
|
465
|
+
const userId = req.userId;
|
|
466
|
+
const { sessionId } = req.params;
|
|
467
|
+
const stopped = sessionManager.stopSession(sessionId);
|
|
468
|
+
res.json({ success: true, data: { stopped } });
|
|
469
|
+
}
|
|
470
|
+
catch (error) {
|
|
471
|
+
logger.error(`Reset session error: ${error}`);
|
|
472
|
+
const message = error instanceof Error ? error.message : 'Failed to reset session';
|
|
473
|
+
res.status(500).json({ success: false, error: message });
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
/**
|
|
477
|
+
* POST /api/agent/sessions/:sessionId/set-current
|
|
478
|
+
* Mark session as current (clears unread count)
|
|
479
|
+
*/
|
|
480
|
+
router.post('/sessions/:sessionId/set-current', async (req, res) => {
|
|
481
|
+
try {
|
|
482
|
+
const userId = req.userId;
|
|
483
|
+
const { sessionId } = req.params;
|
|
484
|
+
sessionManager.setCurrentSession(sessionId);
|
|
485
|
+
// 同步清除 session 的未读数
|
|
486
|
+
const session = sessionManager.getSession(sessionId);
|
|
487
|
+
if (session) {
|
|
488
|
+
session.clearUnreadCount();
|
|
489
|
+
}
|
|
490
|
+
res.json({ success: true });
|
|
491
|
+
}
|
|
492
|
+
catch (error) {
|
|
493
|
+
logger.error(`Set current session error: ${error}`);
|
|
494
|
+
const message = error instanceof Error ? error.message : 'Failed to set current session';
|
|
495
|
+
res.status(500).json({ success: false, error: message });
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
/**
|
|
499
|
+
* GET /api/agent/sessions/unread-count
|
|
500
|
+
* Get total unread count across all sessions
|
|
501
|
+
*/
|
|
502
|
+
router.get('/sessions/unread-count', async (req, res) => {
|
|
503
|
+
try {
|
|
504
|
+
const userId = req.userId;
|
|
505
|
+
const total = sessionManager.getTotalUnreadCount();
|
|
506
|
+
res.json({ success: true, data: { total } });
|
|
507
|
+
}
|
|
508
|
+
catch (error) {
|
|
509
|
+
logger.error(`Get unread count error: ${error}`);
|
|
510
|
+
res.status(500).json({ success: false, error: 'Failed to get unread count' });
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
export default router;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { authApi } from '../api/index.js';
|
|
3
|
+
import { authStore } from '../stores/index.js';
|
|
4
|
+
import { optionalAuth, requireAuth } from '../middleware/auth.js';
|
|
5
|
+
import { getLogger } from '@pocketclaw/shared';
|
|
6
|
+
import { getPlatform } from '@pocketclaw/shared/dist/utils/system.js';
|
|
7
|
+
const logger = getLogger('auth');
|
|
8
|
+
const router = Router();
|
|
9
|
+
// Check auth status - 解码请求中的 token 返回认证状态
|
|
10
|
+
router.get('/isAuth', optionalAuth, async (req, res) => {
|
|
11
|
+
const userId = req.userId;
|
|
12
|
+
res.json({
|
|
13
|
+
authenticated: !!userId,
|
|
14
|
+
user: userId ? { id: userId } : null,
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
// Get publish token - 获取发布技能库所需的 token
|
|
18
|
+
router.get('/publish-token', requireAuth, async (req, res) => {
|
|
19
|
+
res.json({
|
|
20
|
+
success: true,
|
|
21
|
+
token: req.token,
|
|
22
|
+
user: { id: req.userId },
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
// Register - 使用 authApi 注册
|
|
26
|
+
router.post('/register', async (req, res) => {
|
|
27
|
+
try {
|
|
28
|
+
const { username, password } = req.body;
|
|
29
|
+
if (!username || !password) {
|
|
30
|
+
res.status(400).json({ success: false, error: 'Missing required fields' });
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
// 使用 authApi.register(服务端路由期望 account 字段)
|
|
34
|
+
const response = await authApi.register({
|
|
35
|
+
account: username,
|
|
36
|
+
password,
|
|
37
|
+
nickname: username,
|
|
38
|
+
platform: getPlatform(),
|
|
39
|
+
});
|
|
40
|
+
if (response.success && response.data) {
|
|
41
|
+
const expiresIn = response.data.expiresIn || 86400;
|
|
42
|
+
authStore.save({
|
|
43
|
+
token: response.data.accessToken,
|
|
44
|
+
refreshToken: response.data.refreshToken,
|
|
45
|
+
expiresIn,
|
|
46
|
+
expiresAt: Date.now() + expiresIn * 1000,
|
|
47
|
+
user: response.data.user,
|
|
48
|
+
});
|
|
49
|
+
res.status(201).json({
|
|
50
|
+
success: true,
|
|
51
|
+
user: response.data.user,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
res.status(400).json({ success: false, error: response.error || 'Registration failed' });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
logger.error('Register error:', error);
|
|
60
|
+
res.status(error.status || 500).json({
|
|
61
|
+
success: false,
|
|
62
|
+
error: error.message || 'Registration failed',
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
// Login - 使用 authApi 登录
|
|
67
|
+
router.post('/login', async (req, res) => {
|
|
68
|
+
try {
|
|
69
|
+
const { username, password } = req.body;
|
|
70
|
+
logger.debug('Login attempt for:', username);
|
|
71
|
+
if (!username || !password) {
|
|
72
|
+
res.status(400).json({ success: false, error: 'Missing required fields' });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
// 使用 authApi.login(服务端路由期望 email 字段)
|
|
76
|
+
const response = await authApi.login({
|
|
77
|
+
account: username,
|
|
78
|
+
password,
|
|
79
|
+
platform: getPlatform(),
|
|
80
|
+
});
|
|
81
|
+
if (response.success && response.data) {
|
|
82
|
+
const expiresIn = response.data.expiresIn || 86400;
|
|
83
|
+
authStore.save({
|
|
84
|
+
token: response.data.accessToken,
|
|
85
|
+
refreshToken: response.data.refreshToken,
|
|
86
|
+
expiresIn,
|
|
87
|
+
expiresAt: Date.now() + expiresIn * 1000,
|
|
88
|
+
user: response.data.user,
|
|
89
|
+
});
|
|
90
|
+
res.json({
|
|
91
|
+
success: true,
|
|
92
|
+
user: response.data.user,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
res.status(401).json({ success: false, error: response.error || 'Invalid credentials' });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
logger.error('Login error:', error);
|
|
101
|
+
res.status(error.status || 500).json({
|
|
102
|
+
success: false,
|
|
103
|
+
error: error.message || 'Login failed',
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
// Refresh token - 使用 authApi 刷新
|
|
108
|
+
router.post('/refresh', async (req, res) => {
|
|
109
|
+
try {
|
|
110
|
+
const { refreshToken } = req.body;
|
|
111
|
+
if (!refreshToken) {
|
|
112
|
+
res.status(400).json({ success: false, error: 'Missing refresh token' });
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const response = await authApi.refresh(refreshToken);
|
|
116
|
+
if (response.success && response.accessToken) {
|
|
117
|
+
authStore.updateToken(response.accessToken, response.refreshToken);
|
|
118
|
+
res.json({ success: true });
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
res.status(401).json({ success: false, error: response.error || 'Invalid refresh token' });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
logger.error('Refresh error:', error);
|
|
126
|
+
res.status(error.status || 500).json({
|
|
127
|
+
success: false,
|
|
128
|
+
error: error.message || 'Refresh failed',
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
// Logout - 清除本地存储
|
|
133
|
+
router.post('/logout', requireAuth, async (req, res) => {
|
|
134
|
+
try {
|
|
135
|
+
// refreshToken 在请求 body 中
|
|
136
|
+
const { refreshToken } = req.body;
|
|
137
|
+
// 尝试转发登出请求到 Server(忽略错误)
|
|
138
|
+
if (refreshToken) {
|
|
139
|
+
authApi.logout().catch(() => { });
|
|
140
|
+
}
|
|
141
|
+
authStore.clear();
|
|
142
|
+
res.json({ success: true });
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
authStore.clear();
|
|
146
|
+
res.json({ success: true });
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
// Delete account - 删除账号
|
|
150
|
+
router.post('/account/delete', requireAuth, async (req, res) => {
|
|
151
|
+
try {
|
|
152
|
+
const { password } = req.body;
|
|
153
|
+
if (!password) {
|
|
154
|
+
res.status(400).json({ success: false, error: 'Missing password' });
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
// 转发删除账号请求到 Server(带上 token)
|
|
158
|
+
const response = await authApi.deleteAccount({ password }, req.token);
|
|
159
|
+
if (response.success) {
|
|
160
|
+
authStore.clear();
|
|
161
|
+
}
|
|
162
|
+
res.json(response);
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
logger.error('Delete account error:', error);
|
|
166
|
+
res.status(error.status || 500).json({
|
|
167
|
+
success: false,
|
|
168
|
+
error: error.message || 'Delete account failed',
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
export default router;
|