@myassis/gateway 1.0.0 → 1.0.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.
Files changed (50) hide show
  1. package/dist/api/index.js +69 -59
  2. package/dist/config/index.js +2 -2
  3. package/dist/index.js +85 -11
  4. package/dist/middleware/auth.js +9 -25
  5. package/dist/middleware/errorHandler.js +1 -1
  6. package/dist/routes/agent.js +30 -19
  7. package/dist/routes/auth.js +35 -26
  8. package/dist/routes/chat.js +1 -1
  9. package/dist/routes/models.js +11 -14
  10. package/dist/routes/service.js +3 -6
  11. package/dist/routes/settings.js +11 -9
  12. package/dist/routes/skillHub.js +10 -6
  13. package/dist/routes/skills.js +13 -10
  14. package/dist/routes/tasks.js +6 -3
  15. package/dist/routes/upload.js +5 -2
  16. package/dist/routes/version.js +4 -4
  17. package/dist/services/LocalTaskService.js +1 -1
  18. package/dist/services/NotificationService.js +1 -1
  19. package/dist/services/ServiceManager.js +12 -12
  20. package/dist/services/TaskSchedulerService.js +18 -9
  21. package/dist/services/TaskService.js +23 -20
  22. package/dist/services/WebSocketService.js +16 -6
  23. package/dist/services/agent/Agent.js +7 -7
  24. package/dist/services/agent/AgentManager.js +25 -17
  25. package/dist/services/agent/AgentStore.js +1 -1
  26. package/dist/services/dataService.js +60 -70
  27. package/dist/services/index.js +1 -1
  28. package/dist/services/llm/LLMClient.js +11 -9
  29. package/dist/services/memory/MemoryManager.js +177 -14
  30. package/dist/services/session/MigrationManager.js +1 -1
  31. package/dist/services/session/Session.js +70 -46
  32. package/dist/services/session/SessionManager.js +12 -5
  33. package/dist/services/session/SessionStore.js +1 -27
  34. package/dist/services/session/index.js +1 -1
  35. package/dist/services/systemPrompt.js +8 -4
  36. package/dist/services/task/PushTokenStore.js +1 -19
  37. package/dist/services/task/TaskStore.js +1 -20
  38. package/dist/services/tools/edit.js +122 -148
  39. package/dist/services/tools/exec.js +1 -1
  40. package/dist/services/tools/fetch.js +1 -1
  41. package/dist/services/tools/file.js +4 -9
  42. package/dist/services/tools/index.js +4 -3
  43. package/dist/services/tools/model.js +8 -6
  44. package/dist/services/tools/sessionsSpawn.js +54 -0
  45. package/dist/services/tools/skill.js +13 -12
  46. package/dist/services/tools/task.js +2 -4
  47. package/dist/stores/authStore.js +52 -66
  48. package/dist/stores/persistStore.js +37 -3
  49. package/package.json +8 -4
  50. package/scripts/postbuild.js +39 -0
package/dist/api/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Gateway 统一 API 调用模块
3
3
  * 负责与后端 Server 通信
4
- * 自动从 authStore 获取 Token 并注入到请求头
4
+ * 多用户模式:每个请求显式传 token(替代 authStore
5
5
  *
6
6
  * 服务端路由结构:
7
7
  * - /api/v1/auth/* - 认证路由
@@ -9,7 +9,12 @@
9
9
  * - /api/v1/skill-hubs/* - 技能库路由
10
10
  */
11
11
  import crypto from 'crypto';
12
- import { authStore } from '../stores';
12
+ import { AsyncLocalStorage } from 'async_hooks';
13
+ // 多用户模式请求上下文:存储当前请求的 token
14
+ const requestContext = new AsyncLocalStorage();
15
+ export function runWithToken(token, fn) {
16
+ return requestContext.run({ token }, fn);
17
+ }
13
18
  // Server 服务地址(从环境变量读取)
14
19
  const SERVER_BASE_URL = process.env.SERVER_BASE_URL || 'http://localhost:3000';
15
20
  // 签名密钥(用于请求签名)
@@ -35,16 +40,15 @@ function generateSignature(params, timestamp) {
35
40
  return crypto.createHash('sha256').update(signString).digest('hex');
36
41
  }
37
42
  // 基础请求方法工厂
38
- // prefix: 路由前缀,如 '/api/v1/auth', '/api/v1/data', '/api/v1/skill-hubs'
39
43
  function createRequest(prefix) {
40
44
  return async function (path, options = {}) {
41
- const { method = 'GET', body, headers = {}, params, skipAuth = false } = options;
45
+ const { method = 'GET', body, headers = {}, params, skipAuth = false, token, } = options;
42
46
  // 构建 URL
43
47
  let url = `${SERVER_BASE_URL}${prefix}${path}`;
44
48
  // 添加查询参数
45
49
  if (params) {
46
50
  const queryString = Object.entries(params)
47
- .filter(([_, v]) => v !== undefined && v !== null)
51
+ .filter(([, v]) => v !== undefined && v !== null)
48
52
  .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
49
53
  .join('&');
50
54
  if (queryString) {
@@ -63,10 +67,12 @@ function createRequest(prefix) {
63
67
  ...headers
64
68
  };
65
69
  // 自动注入 Token(除非明确跳过)
70
+ // 优先级:显式参数 > AsyncLocalStorage 上下文 > authStore(降级兼容)
66
71
  if (!skipAuth) {
67
- const token = authStore.getToken();
68
- if (token) {
69
- requestHeaders['Authorization'] = `Bearer ${token}`;
72
+ const ctx = requestContext.getStore();
73
+ const authToken = token || ctx?.token;
74
+ if (authToken) {
75
+ requestHeaders['Authorization'] = `Bearer ${authToken}`;
70
76
  }
71
77
  }
72
78
  try {
@@ -90,40 +96,44 @@ function createRequest(prefix) {
90
96
  };
91
97
  }
92
98
  // 创建不同前缀的请求方法
93
- const authRequest = createRequest('/api/v1/auth'); // /api/v1/auth/*
94
- const dataRequest = createRequest('/api/v1/data'); // /api/v1/data/*
95
- const skillHubRequest = createRequest('/api/v1/skill-hubs'); // /api/v1/skill-hubs/*
99
+ const _authRequest = createRequest('/api/v1/auth');
100
+ const _dataRequest = createRequest('/api/v1/data');
101
+ const _skillHubRequest = createRequest('/api/v1/skill-hubs');
102
+ function authRequest(path, options) {
103
+ return _authRequest(path, options || {});
104
+ }
105
+ function dataRequest(path, options) {
106
+ return _dataRequest(path, options || {});
107
+ }
108
+ function skillHubRequest(path, options) {
109
+ return _skillHubRequest(path, options || {});
110
+ }
96
111
  // ============ 认证 API ============
97
112
  export const authApi = {
98
113
  login: (data) => authRequest('/login', { method: 'POST', body: data, skipAuth: true }),
99
114
  register: (data) => authRequest('/register', { method: 'POST', body: data, skipAuth: true }),
100
- logout: () => authRequest('/logout', { method: 'POST', body: {} }),
115
+ logout: (token) => authRequest('/logout', { method: 'POST', body: {}, token }),
101
116
  refresh: (refreshToken) => authRequest('/refresh', { method: 'POST', body: { refreshToken }, skipAuth: true }),
102
- me: () => authRequest('/me'),
103
- updateProfile: (data) => authRequest('/profile', { method: 'PUT', body: data }),
104
- changePassword: (data) => authRequest('/password', { method: 'PUT', body: data }),
105
- deleteAccount: (data, token) => {
106
- const headers = {};
107
- if (token)
108
- headers['Authorization'] = `Bearer ${token}`;
109
- return authRequest('/account/delete', { method: 'POST', body: data, headers });
110
- },
117
+ me: (token) => authRequest('/me', { token }),
118
+ updateProfile: (data, token) => authRequest('/profile', { method: 'PUT', body: data, token }),
119
+ changePassword: (data, token) => authRequest('/password', { method: 'PUT', body: data, token }),
120
+ deleteAccount: (data, token) => authRequest('/account/delete', { method: 'POST', body: data, token }),
111
121
  };
112
122
  // ============ 技能 API (data) ============
113
123
  export const skillsApi = {
114
- list: () => dataRequest('/skills?brief=true'),
115
- get: (skillId) => dataRequest(`/skills/${skillId}`),
116
- install: (data) => dataRequest('/skills', { method: 'POST', body: data }),
117
- create: (data) => dataRequest('/skills', { method: 'POST', body: data }),
118
- update: (skillId, data) => dataRequest(`/skills/${skillId}`, { method: 'PUT', body: data }),
119
- uninstall: (skillId) => dataRequest(`/skills/${skillId}`, { method: 'DELETE' }),
120
- setApiKey: (skillId, data) => dataRequest(`/skills/${skillId}/api-key`, { method: 'POST', body: data }),
121
- rate: (skillId, data) => dataRequest(`/skills/${skillId}/rating`, { method: 'POST', body: data }),
122
- parse: (data) => dataRequest('/skills/parse', { method: 'POST', body: data }),
124
+ list: (token) => dataRequest('/skills?brief=true', { token }),
125
+ get: (skillId, token) => dataRequest(`/skills/${skillId}`, { token }),
126
+ install: (data, token) => dataRequest('/skills', { method: 'POST', body: data, token }),
127
+ create: (data, token) => dataRequest('/skills', { method: 'POST', body: data, token }),
128
+ update: (skillId, data, token) => dataRequest(`/skills/${skillId}`, { method: 'PUT', body: data, token }),
129
+ uninstall: (skillId, token) => dataRequest(`/skills/${skillId}`, { method: 'DELETE', token }),
130
+ setApiKey: (skillId, data, token) => dataRequest(`/skills/${skillId}/api-key`, { method: 'POST', body: data, token }),
131
+ rate: (skillId, data, token) => dataRequest(`/skills/${skillId}/rating`, { method: 'POST', body: data, token }),
132
+ parse: (data, token) => dataRequest('/skills/parse', { method: 'POST', body: data, token }),
123
133
  };
124
134
  // ============ 技能库 API (skill-hubs) ============
125
135
  export const skillHubApi = {
126
- list: (params) => {
136
+ list: (params, token) => {
127
137
  const queryParams = {};
128
138
  if (params?.page)
129
139
  queryParams.page = String(params.page);
@@ -133,44 +143,44 @@ export const skillHubApi = {
133
143
  queryParams.keyword = params.keyword;
134
144
  if (params?.category)
135
145
  queryParams.category = params.category;
136
- return skillHubRequest('/', { params: queryParams });
146
+ return skillHubRequest('/', { params: queryParams, token });
137
147
  },
138
- get: (hubId) => skillHubRequest(`/${hubId}`),
139
- categories: () => skillHubRequest('/categories'),
140
- preinstalled: () => skillHubRequest('/preinstalled'),
141
- user: () => skillHubRequest(`/user`),
142
- checkUsed: (hubId) => skillHubRequest(`/${hubId}/used`),
143
- rate: (hubId, rating) => skillHubRequest(`/${hubId}/rate`, { method: 'POST', body: { rating } }),
144
- userRating: (hubId) => skillHubRequest(`/${hubId}/user-rating`),
145
- install: (hubId) => skillHubRequest(`/${hubId}/install`, { method: 'POST', body: {} }),
146
- use: (hubId) => skillHubRequest(`/${hubId}/use`, { method: 'POST', body: {} }),
148
+ get: (hubId, token) => skillHubRequest(`/${hubId}`, { token }),
149
+ categories: (token) => skillHubRequest('/categories', { token }),
150
+ preinstalled: (token) => skillHubRequest('/preinstalled', { token }),
151
+ user: (token) => skillHubRequest(`/user`, { token }),
152
+ checkUsed: (hubId, token) => skillHubRequest(`/${hubId}/used`, { token }),
153
+ rate: (hubId, rating, token) => skillHubRequest(`/${hubId}/rate`, { method: 'POST', body: { rating }, token }),
154
+ userRating: (hubId, token) => skillHubRequest(`/${hubId}/user-rating`, { token }),
155
+ install: (hubId, token) => skillHubRequest(`/${hubId}/install`, { method: 'POST', body: {}, token }),
156
+ use: (hubId, token) => skillHubRequest(`/${hubId}/use`, { method: 'POST', body: {}, token }),
147
157
  };
148
158
  // ============ 模型 API (data) ============
149
159
  export const modelsApi = {
150
- list: () => dataRequest('/models'),
151
- get: (modelId) => dataRequest(`/models/${modelId}`),
152
- create: (data) => dataRequest('/models', { method: 'POST', body: data }),
153
- update: (modelId, data) => dataRequest(`/models/${modelId}`, { method: 'PUT', body: data }),
154
- delete: (modelId) => dataRequest(`/models/${modelId}`, { method: 'DELETE' }),
155
- setPrimary: (modelId) => dataRequest(`/models/${modelId}/primary`, { method: 'POST', body: {} }),
160
+ list: (token) => dataRequest('/models', { token }),
161
+ get: (modelId, token) => dataRequest(`/models/${modelId}`, { token }),
162
+ create: (data, token) => dataRequest('/models', { method: 'POST', body: data, token }),
163
+ update: (modelId, data, token) => dataRequest(`/models/${modelId}`, { method: 'PUT', body: data, token }),
164
+ delete: (modelId, token) => dataRequest(`/models/${modelId}`, { method: 'DELETE', token }),
165
+ setPrimary: (modelId, token) => dataRequest(`/models/${modelId}/primary`, { method: 'POST', body: {}, token }),
156
166
  };
157
167
  // ============ 任务 API (data) ============
158
168
  export const tasksApi = {
159
- list: (params) => dataRequest('/tasks', { params }),
160
- get: (taskId) => dataRequest(`/tasks/${taskId}`),
161
- create: (data) => dataRequest('/tasks', { method: 'POST', body: data }),
162
- update: (taskId, data) => dataRequest(`/tasks/${taskId}`, { method: 'PUT', body: data }),
163
- delete: (taskId) => dataRequest(`/tasks/${taskId}`, { method: 'DELETE' }),
164
- complete: (taskId) => dataRequest(`/tasks/${taskId}/complete`, { method: 'POST', body: {} }),
165
- cancel: (taskId) => dataRequest(`/tasks/${taskId}/cancel`, { method: 'POST', body: {} }),
166
- updateStatus: (taskId, data) => dataRequest(`/tasks/${taskId}/status`, { method: 'POST', body: data }),
167
- notifying: () => dataRequest('/tasks/notifying'),
168
- resetSession: () => dataRequest('/reset-session', { method: 'POST', body: {} }),
169
+ list: (params, token) => dataRequest('/tasks', { params, token }),
170
+ get: (taskId, token) => dataRequest(`/tasks/${taskId}`, { token }),
171
+ create: (data, token) => dataRequest('/tasks', { method: 'POST', body: data, token }),
172
+ update: (taskId, data, token) => dataRequest(`/tasks/${taskId}`, { method: 'PUT', body: data, token }),
173
+ delete: (taskId, token) => dataRequest(`/tasks/${taskId}`, { method: 'DELETE', token }),
174
+ complete: (taskId, token) => dataRequest(`/tasks/${taskId}/complete`, { method: 'POST', body: {}, token }),
175
+ cancel: (taskId, token) => dataRequest(`/tasks/${taskId}/cancel`, { method: 'POST', body: {}, token }),
176
+ updateStatus: (taskId, data, token) => dataRequest(`/tasks/${taskId}/status`, { method: 'POST', body: data, token }),
177
+ notifying: (token) => dataRequest('/tasks/notifying', { token }),
178
+ resetSession: (token) => dataRequest('/reset-session', { method: 'POST', body: {}, token }),
169
179
  };
170
180
  // ============ 设置 API (data) ============
171
181
  export const settingsApi = {
172
- get: () => dataRequest('/settings'),
173
- update: (data) => dataRequest('/settings', { method: 'PUT', body: data }),
182
+ get: (token) => dataRequest('/settings', { token }),
183
+ update: (data, token) => dataRequest('/settings', { method: 'PUT', body: data, token }),
174
184
  };
175
185
  export default {
176
186
  authApi,
@@ -1,4 +1,4 @@
1
- import { getLogger } from '@pocketclaw/shared';
1
+ import { getLogger } from '@myassis/shared';
2
2
  const logger = getLogger('GatewayConfig');
3
3
  import { config as dotenvConfig } from 'dotenv';
4
4
  import * as path from 'path';
@@ -36,6 +36,6 @@ export const appConfig = {
36
36
  clientUrl: process.env.CLIENT_URL || 'http://localhost:3000',
37
37
  nodeEnv: process.env.NODE_ENV || defaultEnv,
38
38
  messageKeep: 4,
39
- appName: 'MyClaw'
39
+ appName: '我的助手'
40
40
  };
41
41
  export default appConfig;
package/dist/index.js CHANGED
@@ -1,10 +1,11 @@
1
+ #!/usr/bin/env node
1
2
  import express from 'express';
2
3
  import cors from 'cors';
3
4
  import helmet from 'helmet';
4
5
  import compression from 'compression';
5
6
  import http from 'http';
6
7
  import { appConfig } from './config/index.js';
7
- import { getLogger } from '@pocketclaw/shared';
8
+ import { getLogger } from '@myassis/shared';
8
9
  import authRoutes from './routes/auth.js';
9
10
  import agentRoutes from './routes/agent.js';
10
11
  import modelsRoutes from './routes/models.js';
@@ -19,9 +20,10 @@ import uploadRoutes from './routes/upload.js';
19
20
  import versionRoutes from './routes/version.js';
20
21
  import { errorHandler } from './middleware/errorHandler.js';
21
22
  import { authStore } from './stores/index.js';
23
+ import { persistStore } from './stores/persistStore.js';
22
24
  import { webSocketService } from './services/WebSocketService.js';
23
25
  import { taskSchedulerService } from './services/TaskSchedulerService.js';
24
- import { getServiceInfo, installService, uninstallService, startService, stopService, updateService, } from './services/ServiceManager.js';
26
+ import { getServiceInfo, installService, uninstallService, startService, stopService, restartService, updateService, } from './services/ServiceManager.js';
25
27
  const logger = getLogger('index');
26
28
  // ─── CLI 模式 ─────────────────────────────────────────
27
29
  // gateway install | start | stop | uninstall | status | update
@@ -39,17 +41,87 @@ if (cliCommand) {
39
41
  if (!info.canManage)
40
42
  console.log(`(当前平台不支持服务管理)`);
41
43
  }
44
+ else if (cliCommand === 'addCors') {
45
+ const domain = process.argv[3];
46
+ if (!domain) {
47
+ console.log('用法: gateway addCors <域名>');
48
+ process.exit(1);
49
+ }
50
+ const added = persistStore.addCorsDomain(domain);
51
+ if (added) {
52
+ console.log(`✅ 已添加 CORS 域名: ${domain}`);
53
+ }
54
+ else {
55
+ console.log(`⚠️ 域名已存在: ${domain}`);
56
+ }
57
+ }
58
+ else if (cliCommand === 'removeCors') {
59
+ const domain = process.argv[3];
60
+ if (!domain) {
61
+ console.log('用法: gateway removeCors <域名>');
62
+ process.exit(1);
63
+ }
64
+ const removed = persistStore.removeCorsDomain(domain);
65
+ if (removed) {
66
+ console.log(`✅ 已移除 CORS 域名: ${domain}`);
67
+ }
68
+ else {
69
+ console.log(`⚠️ 域名不存在: ${domain}`);
70
+ }
71
+ }
72
+ else if (cliCommand === 'listCors') {
73
+ const domains = persistStore.getCorsDomains();
74
+ if (domains.length === 0) {
75
+ console.log('暂无自定义 CORS 域名(仅使用内置内网规则)');
76
+ }
77
+ else {
78
+ console.log(`自定义 CORS 域名 (${domains.length}):`);
79
+ domains.forEach(d => console.log(` - ${d}`));
80
+ }
81
+ }
82
+ else if (cliCommand === '--help' || cliCommand === '-h') {
83
+ console.log(`我的助手 Gateway CLI
84
+
85
+ 用法: gateway <命令>
86
+
87
+ 服务管理命令:
88
+ install 安装 Gateway 服务(后台运行)
89
+ uninstall 卸载 Gateway 服务
90
+ start 启动 Gateway 服务
91
+ stop 停止 Gateway 服务
92
+ restart 重启 Gateway 服务
93
+ update 更新 Gateway(需重新安装)
94
+ status 查看服务状态
95
+
96
+ CORS 管理命令:
97
+ addCors <域名> 添加允许的跨域域名
98
+ removeCors <域名> 移除允许的跨域域名
99
+ listCors 列出所有自定义跨域域名
100
+
101
+ 其他:
102
+ --help, -h 显示本帮助信息
103
+ `);
104
+ const domains = persistStore.getCorsDomains();
105
+ if (domains.length === 0) {
106
+ console.log('暂无自定义 CORS 域名(仅使用内置内网规则)');
107
+ }
108
+ else {
109
+ console.log(`自定义 CORS 域名 (${domains.length}):`);
110
+ domains.forEach(d => console.log(` - ${d}`));
111
+ }
112
+ }
42
113
  else {
43
114
  const fnMap = {
44
115
  install: installService,
45
116
  uninstall: uninstallService,
46
117
  start: startService,
47
118
  stop: stopService,
119
+ restart: restartService,
48
120
  update: updateService,
49
121
  };
50
122
  const handler = fnMap[cliCommand];
51
123
  if (!handler) {
52
- console.log(`未知命令: ${cliCommand}\n可用命令: install | start | stop | uninstall | status | update`);
124
+ console.log(`未知命令: ${cliCommand}。输入 "gateway --help" 查看帮助。`);
53
125
  process.exit(1);
54
126
  }
55
127
  const result = await handler();
@@ -93,6 +165,15 @@ else {
93
165
  if (lanPatterns.some(pattern => pattern.test(origin))) {
94
166
  return callback(null, true);
95
167
  }
168
+ // 检查自定义域名(支持子域名匹配)
169
+ const customDomains = persistStore.getCorsDomains();
170
+ const originLower = origin.toLowerCase();
171
+ if (customDomains.some(d => {
172
+ const dLower = d.toLowerCase();
173
+ return originLower === dLower || originLower.endsWith('.' + dLower);
174
+ })) {
175
+ return callback(null, true);
176
+ }
96
177
  callback(new Error('Not allowed by CORS'));
97
178
  },
98
179
  credentials: true,
@@ -134,14 +215,7 @@ else {
134
215
  if (port !== appConfig.port) {
135
216
  logger.info(`Port ${appConfig.port} is in use, fallback to port ${port}`);
136
217
  }
137
- logger.info(`MyClaw Gateway Service running on port ${port}`);
138
- if (authStore.isAuthenticated()) {
139
- const user = authStore.getUser();
140
- logger.info(`User logged in: ${user?.nickname || user?.email} (ID: ${user?.id})`);
141
- }
142
- else {
143
- logger.info(`No user logged in`);
144
- }
218
+ logger.info(`我的助手 Gateway Service running on port ${port}`);
145
219
  }).on('error', (err) => {
146
220
  if (err.code === 'EADDRINUSE') {
147
221
  const nextPort = port + 1;
@@ -1,4 +1,11 @@
1
- import jwt from 'jsonwebtoken';
1
+ /**
2
+ * 认证中间件
3
+ * 从请求头 Authorization: Bearer <token> 解码 JWT,提取 userId
4
+ * 同时保留原始 token 供路由转发给 Server
5
+ */
6
+ import { authStore } from '@/stores';
7
+ import { getLogger } from '@myassis/shared';
8
+ const logger = getLogger('middleware/auth');
2
9
  const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production';
3
10
  /**
4
11
  * 从请求头提取 Bearer token
@@ -10,18 +17,6 @@ function extractToken(req) {
10
17
  }
11
18
  return undefined;
12
19
  }
13
- /**
14
- * 解码 JWT,提取 userId
15
- */
16
- function decodeUserId(token) {
17
- try {
18
- const payload = jwt.verify(token, JWT_SECRET);
19
- return payload.userId;
20
- }
21
- catch {
22
- return undefined;
23
- }
24
- }
25
20
  /**
26
21
  * 需要认证:token 无效返回 401
27
22
  */
@@ -31,7 +26,7 @@ export function requireAuth(req, res, next) {
31
26
  res.status(401).json({ success: false, error: 'Unauthorized' });
32
27
  return;
33
28
  }
34
- const userId = decodeUserId(token);
29
+ const userId = authStore.getUserId(token);
35
30
  if (!userId) {
36
31
  res.status(401).json({ success: false, error: 'Unauthorized' });
37
32
  return;
@@ -40,14 +35,3 @@ export function requireAuth(req, res, next) {
40
35
  req.token = token;
41
36
  next();
42
37
  }
43
- /**
44
- * 可选认证:token 存在则解码,不存在也放行
45
- */
46
- export function optionalAuth(req, _res, next) {
47
- const token = extractToken(req);
48
- if (token) {
49
- req.userId = decodeUserId(token);
50
- req.token = token;
51
- }
52
- next();
53
- }
@@ -1,4 +1,4 @@
1
- import { getLogger } from '@pocketclaw/shared';
1
+ import { getLogger } from '@myassis/shared';
2
2
  const logger = getLogger('ErrorHandler');
3
3
  export const errorHandler = (err, req, res, next) => {
4
4
  logger.error('[Gateway Error]', err.message, err.stack);
@@ -1,26 +1,26 @@
1
1
  import { Router } from 'express';
2
2
  import { requireAuth } from '@/middleware/auth.js';
3
- import { sessionManager } from '@/services/session';
4
- import { initAgentManager, agentManager } from '@/services/agent/AgentManager';
3
+ import { runWithToken } from '@/api/index.js';
4
+ import { getSessionManager } from '@/services/session';
5
+ import { initAgentManager, agentManagerMap } from '@/services/agent/AgentManager';
5
6
  import { AgentStore } from '@/services/agent/AgentStore';
6
- import { getLogger } from '@pocketclaw/shared';
7
+ import { getLogger } from '@myassis/shared';
7
8
  const logger = getLogger('AgentRoutes');
8
9
  import { sessionStore } from '@/services/session/SessionStore';
9
10
  const router = Router();
10
11
  // 所有路由需要认证
11
- router.use(requireAuth);
12
+ router.use(requireAuth, (req, _res, next) => {
13
+ void runWithToken(req.token, async () => { next(); });
14
+ });
12
15
  // Middleware: ensure agentManager is initialized
13
16
  function ensureAgentManager(req, res, next) {
14
17
  try {
15
18
  // Get or create AgentStore using SessionStore's db instance
16
19
  const db = sessionStore.getDb();
17
20
  const agentStore = new AgentStore(db);
18
- // Initialize AgentManager if not already done
19
- if (!agentManager) {
20
- initAgentManager(agentStore);
21
- }
21
+ const agentManager = initAgentManager(agentStore, req.userId);
22
22
  agentManager.initialize();
23
- req.agentManager = agentManager;
23
+ req.agentManager = agentManagerMap;
24
24
  next();
25
25
  }
26
26
  catch (error) {
@@ -36,6 +36,7 @@ function ensureAgentManager(req, res, next) {
36
36
  router.get('/list', ensureAgentManager, async (req, res) => {
37
37
  try {
38
38
  const userId = req.userId;
39
+ const agentManager = agentManagerMap.get(userId);
39
40
  const agentList = agentManager.getAgentList();
40
41
  res.json({ success: true, data: agentList });
41
42
  }
@@ -51,6 +52,7 @@ router.get('/list', ensureAgentManager, async (req, res) => {
51
52
  router.get('/:id', ensureAgentManager, async (req, res) => {
52
53
  try {
53
54
  const userId = req.userId;
55
+ const agentManager = agentManagerMap.get(userId);
54
56
  const agent = agentManager.getAgent(req.params.id);
55
57
  if (!agent) {
56
58
  return res.status(404).json({ success: false, error: 'Agent not found' });
@@ -85,6 +87,7 @@ router.post('/create', ensureAgentManager, async (req, res) => {
85
87
  if (!name) {
86
88
  return res.status(400).json({ success: false, error: 'Name is required' });
87
89
  }
90
+ const agentManager = agentManagerMap.get(userId);
88
91
  const agent = agentManager.createAgent({
89
92
  name,
90
93
  description,
@@ -120,6 +123,7 @@ router.put('/:id', ensureAgentManager, async (req, res) => {
120
123
  const userId = req.userId;
121
124
  logger.debug('update agent', req.body);
122
125
  const { name, description, systemPrompt, avatar, config } = req.body;
126
+ const agentManager = agentManagerMap.get(userId);
123
127
  const agent = agentManager.updateAgent(req.params.id, {
124
128
  name,
125
129
  description,
@@ -155,6 +159,7 @@ router.put('/:id', ensureAgentManager, async (req, res) => {
155
159
  router.delete('/:id', ensureAgentManager, async (req, res) => {
156
160
  try {
157
161
  const userId = req.userId;
162
+ const agentManager = agentManagerMap.get(userId);
158
163
  const deleted = agentManager.deleteAgent(req.params.id);
159
164
  if (!deleted) {
160
165
  return res.status(404).json({ success: false, error: 'Agent not found' });
@@ -175,6 +180,7 @@ router.delete('/:id', ensureAgentManager, async (req, res) => {
175
180
  router.get('/current', ensureAgentManager, async (req, res) => {
176
181
  try {
177
182
  const userId = req.userId;
183
+ const agentManager = agentManagerMap.get(userId);
178
184
  const current = agentManager.getCurrentAgent();
179
185
  res.json({
180
186
  success: true, data: current ? {
@@ -203,6 +209,7 @@ router.post('/set-current', ensureAgentManager, async (req, res) => {
203
209
  try {
204
210
  const userId = req.userId;
205
211
  const { agentId } = req.body;
212
+ const agentManager = agentManagerMap.get(userId);
206
213
  agentManager.setCurrentAgent(agentId || null);
207
214
  res.json({ success: true });
208
215
  }
@@ -219,6 +226,7 @@ router.post('/set-current', ensureAgentManager, async (req, res) => {
219
226
  router.get('/:id/sessions', ensureAgentManager, async (req, res) => {
220
227
  try {
221
228
  const userId = req.userId;
229
+ const agentManager = agentManagerMap.get(userId);
222
230
  const agent = agentManager.getAgent(req.params.id);
223
231
  if (!agent) {
224
232
  return res.status(404).json({ success: false, error: 'Agent not found' });
@@ -238,6 +246,7 @@ router.get('/:id/sessions', ensureAgentManager, async (req, res) => {
238
246
  router.post('/:id/sessions', ensureAgentManager, async (req, res) => {
239
247
  try {
240
248
  const userId = req.userId;
249
+ const agentManager = agentManagerMap.get(userId);
241
250
  const agent = agentManager.getAgent(req.params.id);
242
251
  if (!agent) {
243
252
  return res.status(404).json({ success: false, error: 'Agent not found' });
@@ -270,6 +279,7 @@ router.post('/:id/sessions', ensureAgentManager, async (req, res) => {
270
279
  router.put('/:id/sessions/:sessionId', ensureAgentManager, async (req, res) => {
271
280
  try {
272
281
  const userId = req.userId;
282
+ const agentManager = agentManagerMap.get(userId);
273
283
  const agent = agentManager.getAgent(req.params.id);
274
284
  if (!agent) {
275
285
  return res.status(404).json({ success: false, error: 'Agent not found' });
@@ -299,6 +309,7 @@ router.put('/:id/sessions/:sessionId', ensureAgentManager, async (req, res) => {
299
309
  router.delete('/:id/sessions/:sessionId', ensureAgentManager, async (req, res) => {
300
310
  try {
301
311
  const userId = req.userId;
312
+ const agentManager = agentManagerMap.get(userId);
302
313
  const agent = agentManager.getAgent(req.params.id);
303
314
  if (!agent) {
304
315
  return res.status(404).json({ success: false, error: 'Agent not found' });
@@ -323,7 +334,7 @@ router.get('/sessions/:sessionId/messages', ensureAgentManager, async (req, res)
323
334
  const page = parseInt(req.query.page) || 1;
324
335
  const pageSize = parseInt(req.query.pageSize) || 20;
325
336
  // Find session directly
326
- const session = sessionManager.getSession(sessionId);
337
+ const session = getSessionManager(userId).getSession(sessionId);
327
338
  if (session) {
328
339
  session.loadMessages(); // Ensure messages are loaded
329
340
  const messages = session.getMessagesByPage(page, pageSize);
@@ -354,7 +365,7 @@ router.delete('/sessions/:sessionId/messages/:messageId', ensureAgentManager, as
354
365
  const userId = req.userId;
355
366
  const { sessionId, messageId } = req.params;
356
367
  // Find session directly
357
- const session = sessionManager.getSession(sessionId);
368
+ const session = getSessionManager(userId).getSession(sessionId);
358
369
  if (session) {
359
370
  session.deleteMessage(messageId);
360
371
  return res.json({ success: true });
@@ -378,7 +389,7 @@ router.put('/sessions/:sessionId/messages/:messageId/feedback', ensureAgentManag
378
389
  if (feedback !== null && feedback !== 'like' && feedback !== 'dislike') {
379
390
  return res.status(400).json({ success: false, error: 'Invalid feedback value. Must be "like", "dislike", or null' });
380
391
  }
381
- const session = sessionManager.getSession(sessionId);
392
+ const session = getSessionManager(userId).getSession(sessionId);
382
393
  if (session) {
383
394
  session.updateMessageFeedback(messageId, feedback);
384
395
  return res.json({ success: true, data: { messageId, feedback } });
@@ -399,7 +410,7 @@ router.delete('/sessions/:sessionId/messages', ensureAgentManager, async (req, r
399
410
  const userId = req.userId;
400
411
  const { sessionId } = req.params;
401
412
  // Find session directly
402
- const session = sessionManager.getSession(sessionId);
413
+ const session = getSessionManager(userId).getSession(sessionId);
403
414
  if (session) {
404
415
  session.clearMessages();
405
416
  return res.json({ success: true });
@@ -419,12 +430,12 @@ router.post('/sessions/:sessionId/stream', ensureAgentManager, async (req, res)
419
430
  try {
420
431
  const userId = req.userId;
421
432
  const { sessionId } = req.params;
422
- const { content, attachments, userMessageId, assistantMessageId, taskId } = req.body;
433
+ const { content, attachments, userMessageId, assistantMessageId } = req.body;
423
434
  if (!content) {
424
435
  return res.status(400).json({ success: false, error: 'Content is required' });
425
436
  }
426
437
  // Find session directly
427
- const session = sessionManager.getSession(sessionId);
438
+ const session = getSessionManager(userId).getSession(sessionId);
428
439
  if (session) {
429
440
  // Set SSE headers
430
441
  res.setHeader('Content-Type', 'text/event-stream');
@@ -464,7 +475,7 @@ router.post('/sessions/:sessionId/reset', async (req, res) => {
464
475
  try {
465
476
  const userId = req.userId;
466
477
  const { sessionId } = req.params;
467
- const stopped = sessionManager.stopSession(sessionId);
478
+ const stopped = getSessionManager(userId).stopSession(sessionId);
468
479
  res.json({ success: true, data: { stopped } });
469
480
  }
470
481
  catch (error) {
@@ -481,9 +492,9 @@ router.post('/sessions/:sessionId/set-current', async (req, res) => {
481
492
  try {
482
493
  const userId = req.userId;
483
494
  const { sessionId } = req.params;
484
- sessionManager.setCurrentSession(sessionId);
495
+ getSessionManager(userId).setCurrentSession(sessionId);
485
496
  // 同步清除 session 的未读数
486
- const session = sessionManager.getSession(sessionId);
497
+ const session = getSessionManager(userId).getSession(sessionId);
487
498
  if (session) {
488
499
  session.clearUnreadCount();
489
500
  }
@@ -502,7 +513,7 @@ router.post('/sessions/:sessionId/set-current', async (req, res) => {
502
513
  router.get('/sessions/unread-count', async (req, res) => {
503
514
  try {
504
515
  const userId = req.userId;
505
- const total = sessionManager.getTotalUnreadCount();
516
+ const total = getSessionManager(userId).getTotalUnreadCount();
506
517
  res.json({ success: true, data: { total } });
507
518
  }
508
519
  catch (error) {