@myassis/gateway 1.0.1 → 1.0.3

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 (54) 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 +2 -2
  18. package/dist/services/NotificationService.js +4 -4
  19. package/dist/services/ServiceManager.js +12 -12
  20. package/dist/services/TaskSchedulerService.js +21 -12
  21. package/dist/services/TaskService.js +25 -22
  22. package/dist/services/WebSocketService.js +16 -6
  23. package/dist/services/agent/Agent.js +7 -7
  24. package/dist/services/agent/AgentManager.js +26 -18
  25. package/dist/services/agent/AgentStore.js +1 -1
  26. package/dist/services/dataService.js +63 -73
  27. package/dist/services/index.js +9 -9
  28. package/dist/services/llm/LLMClient.js +11 -9
  29. package/dist/services/memory/MemoryManager.js +180 -17
  30. package/dist/services/model/index.js +1 -1
  31. package/dist/services/session/MigrationManager.js +1 -1
  32. package/dist/services/session/Session.js +74 -50
  33. package/dist/services/session/SessionManager.js +14 -7
  34. package/dist/services/session/SessionStore.js +2 -28
  35. package/dist/services/session/index.js +2 -2
  36. package/dist/services/systemPrompt.js +9 -5
  37. package/dist/services/task/PushTokenStore.js +2 -20
  38. package/dist/services/task/TaskStore.js +2 -21
  39. package/dist/services/tools/edit.js +122 -148
  40. package/dist/services/tools/exec.js +1 -1
  41. package/dist/services/tools/fetch.js +1 -1
  42. package/dist/services/tools/file.js +4 -9
  43. package/dist/services/tools/index.js +18 -17
  44. package/dist/services/tools/model.js +9 -7
  45. package/dist/services/tools/sessionsSpawn.js +54 -0
  46. package/dist/services/tools/skill.js +14 -13
  47. package/dist/services/tools/task.js +3 -5
  48. package/dist/stores/authStore.js +52 -66
  49. package/dist/stores/index.js +3 -3
  50. package/dist/stores/persistStore.js +37 -3
  51. package/package.json +7 -3
  52. package/scripts/fix-dist-imports.js +95 -0
  53. package/scripts/fix-imports.js +90 -0
  54. package/scripts/postbuild.js +39 -0
@@ -3,11 +3,12 @@
3
3
  * 基于 SQLite 数据库的任务调度
4
4
  * 每分钟检查待执行任务并发送通知
5
5
  */
6
- import { taskStore } from './task/TaskStore';
7
- import { webSocketService } from './WebSocketService';
8
- import { sessionManager } from './session';
9
- import { getLogger, getUTCTimeKey, formatUTCForLog, holidayService } from '@pocketclaw/shared';
10
- import { tasksService } from './dataService';
6
+ import { taskStore } from './task/TaskStore.js';
7
+ import { webSocketService } from './WebSocketService.js';
8
+ import { getSessionManager } from './session.js';
9
+ import { getLogger, getUTCTimeKey, formatUTCForLog, holidayService } from '@myassis/shared';
10
+ import { tasksService } from './dataService.js';
11
+ import { authStore } from '@/stores';
11
12
  const logger = getLogger('TaskSchedulerService');
12
13
  const EXPIRED_THRESHOLD = 60 * 60 * 1000; // 1小时
13
14
  const CHECK_INTERVAL = 60 * 1000; // 1分钟
@@ -65,13 +66,17 @@ class TaskSchedulerService {
65
66
  try {
66
67
  const now = Date.now();
67
68
  const nowBeforeHour = now - EXPIRED_THRESHOLD;
68
- const response = await tasksService.list();
69
- if (!response.success) {
69
+ const authMap = authStore.getAll();
70
+ if (!authMap) {
71
+ return;
72
+ }
73
+ const response = await Promise.all(Array.from(authMap.values()).map(x => tasksService.list(x.user.id)));
74
+ if (response.some(x => !x.success)) {
70
75
  logger.error('获取任务失败');
71
76
  return;
72
77
  }
73
78
  // 获取过期待执行任务
74
- const tasks = response.data.filter(x => x.scheduledAt < nowBeforeHour && x.status !== 'completed' && x.status !== 'expired' && x.status !== 'error');
79
+ const tasks = response.flatMap(x => x.data).filter(x => x.scheduledAt < nowBeforeHour && x.status !== 'completed' && x.status !== 'expired' && x.status !== 'error');
75
80
  if (tasks.length > 0) {
76
81
  for (let task of tasks) {
77
82
  await tasksService.updateStatus(task.id, 'expired');
@@ -90,13 +95,17 @@ class TaskSchedulerService {
90
95
  const now = Date.now();
91
96
  const nowBeforeHour = now - EXPIRED_THRESHOLD;
92
97
  const timeKey = getUTCTimeKey(new Date());
93
- const response = await tasksService.list();
94
- if (!response.success) {
98
+ const authMap = authStore.getAll();
99
+ if (!authMap) {
100
+ return;
101
+ }
102
+ const response = await Promise.all(Array.from(authMap.values()).map(x => tasksService.list(x.user.id)));
103
+ if (response.some(x => !x.success)) {
95
104
  logger.error('获取任务失败');
96
105
  return;
97
106
  }
98
107
  // 获取待执行任务
99
- const tasks = response.data.filter(x => x.scheduledAt >= nowBeforeHour && x.scheduledAt <= now && x.status === 'pending');
108
+ const tasks = response.flatMap(x => x.data).filter(x => x.scheduledAt >= nowBeforeHour && x.scheduledAt <= now && x.status === 'pending');
100
109
  if (tasks.length > 0) {
101
110
  logger.info(`Found ${tasks.length} tasks to execute at ${timeKey}`);
102
111
  for (const task of tasks) {
@@ -152,7 +161,7 @@ class TaskSchedulerService {
152
161
  */
153
162
  async executeTaskLocally(task) {
154
163
  try {
155
- const currentSession = sessionManager.getCurrentSession();
164
+ const currentSession = getSessionManager(task.userId).getCurrentSession();
156
165
  if (!currentSession) {
157
166
  logger.warn(`No current session found, cannot execute task: ${task.title}`);
158
167
  return;
@@ -7,10 +7,11 @@
7
7
  * - platformApply === currentPlatform → 本地 SQLite 存储(localTaskService)
8
8
  * - platformApply !== currentPlatform → 服务器 API(tasksApi)
9
9
  */
10
- import { tasksApi } from '../api';
11
- import { localTaskService } from './LocalTaskService';
12
- import { getLogger } from '@pocketclaw/shared';
13
- import { getPlatform } from '@pocketclaw/shared/dist/utils/system.js';
10
+ import { authStore } from '@/stores';
11
+ import { tasksApi } from '../api.js';
12
+ import { localTaskService } from './LocalTaskService.js';
13
+ import { getLogger } from '@myassis/shared';
14
+ import { getPlatform } from '@myassis/shared/dist/utils/system.js';
14
15
  // 当前网关平台标识
15
16
  const CURRENT_PLATFORM = process.env.GATEWAY_PLATFORM || getPlatform();
16
17
  const logger = getLogger('TaskService');
@@ -29,7 +30,7 @@ class TaskService {
29
30
  /**
30
31
  * 创建任务(自动路由)
31
32
  */
32
- async createTask(data) {
33
+ async createTask(data, token) {
33
34
  const isLocal = isLocalTask(data.platformApply);
34
35
  logger.info('[TaskService] 创建任务', {
35
36
  title: data.title,
@@ -73,19 +74,19 @@ class TaskService {
73
74
  ? (typeof data.endTime === 'object' ? new Date(data.endTime).toISOString() : data.endTime)
74
75
  : undefined,
75
76
  platform_apply: data.platformApply,
76
- });
77
+ }, token);
77
78
  return { id: String(result.id || result), isLocal: false };
78
79
  }
79
80
  }
80
81
  /**
81
82
  * 获取任务(自动路由 - 先本地后服务器)
82
83
  */
83
- async getTask(taskId) {
84
+ async getTask(taskId, token) {
84
85
  const localTask = localTaskService.getTask(taskId);
85
86
  if (localTask)
86
87
  return localTask;
87
88
  try {
88
- const result = await tasksApi.get(taskId);
89
+ const result = await tasksApi.get(taskId, token);
89
90
  return this.transformServerTask(result);
90
91
  }
91
92
  catch (error) {
@@ -99,12 +100,14 @@ class TaskService {
99
100
  async listTasks(params) {
100
101
  const localResult = localTaskService.listTasks(params);
101
102
  let serverTasks = [];
103
+ const userId = params.userId;
104
+ const token = authStore.get(userId).accessToken;
102
105
  try {
103
106
  const serverResult = await tasksApi.list({
104
- status: params?.status,
105
- page: params?.page ? String(params.page) : undefined,
106
- pageSize: params?.pageSize ? String(params.pageSize) : undefined,
107
- });
107
+ status: params.status,
108
+ page: params.page ? String(params.page) : undefined,
109
+ pageSize: params.pageSize ? String(params.pageSize) : undefined,
110
+ }, token);
108
111
  if (Array.isArray(serverResult)) {
109
112
  serverTasks = serverResult.map(t => this.transformServerTask(t)).filter(Boolean);
110
113
  }
@@ -126,7 +129,7 @@ class TaskService {
126
129
  /**
127
130
  * 更新任务(自动路由)
128
131
  */
129
- async updateTask(taskId, data) {
132
+ async updateTask(taskId, data, token) {
130
133
  const localTask = localTaskService.getTask(taskId);
131
134
  if (localTask) {
132
135
  const result = localTaskService.updateTask(taskId, data);
@@ -151,7 +154,7 @@ class TaskService {
151
154
  ? (typeof data.endTime === 'object' ? new Date(data.endTime).toISOString() : data.endTime)
152
155
  : undefined,
153
156
  status: data.status,
154
- });
157
+ }, token);
155
158
  return true;
156
159
  }
157
160
  catch (error) {
@@ -163,14 +166,14 @@ class TaskService {
163
166
  /**
164
167
  * 删除任务(自动路由)
165
168
  */
166
- async deleteTask(taskId) {
169
+ async deleteTask(taskId, token) {
167
170
  const localTask = localTaskService.getTask(taskId);
168
171
  if (localTask) {
169
172
  return localTaskService.deleteTask(taskId);
170
173
  }
171
174
  else {
172
175
  try {
173
- await tasksApi.delete(taskId);
176
+ await tasksApi.delete(taskId, token);
174
177
  return true;
175
178
  }
176
179
  catch (error) {
@@ -182,7 +185,7 @@ class TaskService {
182
185
  /**
183
186
  * 更新任务状态
184
187
  */
185
- async updateTaskStatus(taskId, status) {
188
+ async updateTaskStatus(taskId, status, token) {
186
189
  const localTask = localTaskService.getTask(taskId);
187
190
  if (localTask) {
188
191
  await localTaskService.updateTaskStatus(taskId, status);
@@ -190,7 +193,7 @@ class TaskService {
190
193
  }
191
194
  else {
192
195
  try {
193
- await tasksApi.updateStatus(taskId, { status });
196
+ await tasksApi.updateStatus(taskId, { status }, token);
194
197
  return true;
195
198
  }
196
199
  catch (error) {
@@ -202,14 +205,14 @@ class TaskService {
202
205
  /**
203
206
  * 完成任务
204
207
  */
205
- async completeTask(taskId) {
206
- return this.updateTaskStatus(taskId, 'completed');
208
+ async completeTask(taskId, token) {
209
+ return this.updateTaskStatus(taskId, 'completed', token);
207
210
  }
208
211
  /**
209
212
  * 取消任务
210
213
  */
211
- async cancelTask(taskId) {
212
- return this.updateTaskStatus(taskId, 'cancelled');
214
+ async cancelTask(taskId, token) {
215
+ return this.updateTaskStatus(taskId, 'cancelled', token);
213
216
  }
214
217
  /**
215
218
  * 转换服务器任务格式
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { WebSocketServer, WebSocket } from 'ws';
6
6
  import { authStore } from '../stores/index.js';
7
- import { getLogger } from '@pocketclaw/shared';
7
+ import { getLogger } from '@myassis/shared';
8
8
  const logger = getLogger('WebSocketService');
9
9
  class WebSocketService {
10
10
  wss = null;
@@ -93,13 +93,23 @@ class WebSocketService {
93
93
  }
94
94
  /**
95
95
  * 从请求中提取用户标识
96
- * Desktop 不存储 token,Gateway 直接信任连接并使用已登录用户的 ID
96
+ * 支持两种方式:
97
+ * 1. URL 查询参数 ?token=xxx(Desktop WebSocket 连接)
98
+ * 2. 直接使用 authStore 中的用户 ID
97
99
  */
98
100
  getClientKey(req) {
99
- // 直接使用 authStore 中的用户 ID(Gateway 登录后的用户)
100
- const userId = authStore.getUserId();
101
- if (userId) {
102
- return String(userId);
101
+ // 优先从 URL 查询参数中提取 token
102
+ let token = null;
103
+ const url = req.url || '';
104
+ const match = url.match(/[?&]token=([^&]+)/);
105
+ if (match) {
106
+ token = decodeURIComponent(match[1]);
107
+ }
108
+ if (token) {
109
+ const userId = authStore.getUserId(token);
110
+ if (userId) {
111
+ return String(userId);
112
+ }
103
113
  }
104
114
  return null;
105
115
  }
@@ -1,5 +1,5 @@
1
1
  import { v4 as uuidv4 } from 'uuid';
2
- import { sessionManager } from '../session/SessionManager';
2
+ import { getSessionManager } from '../session/SessionManager.js';
3
3
  /**
4
4
  * Agent - Business logic entity
5
5
  * Represents an AI agent with independent system prompt and multiple sessions
@@ -16,7 +16,7 @@ export class Agent {
16
16
  updatedAt;
17
17
  isCurrent; // 是否是当前正在查看的 Agent
18
18
  store;
19
- constructor(data, store, agentManager) {
19
+ constructor(data, store) {
20
20
  this.id = data.id;
21
21
  this.userId = data.userId;
22
22
  this.name = data.name;
@@ -33,14 +33,14 @@ export class Agent {
33
33
  * Get all sessions under this agent
34
34
  */
35
35
  getSessions() {
36
- const allSessions = sessionManager.getUserSessions();
36
+ const allSessions = getSessionManager(this.userId).getUserSessions();
37
37
  return allSessions.filter(s => s.agentId === this.id);
38
38
  }
39
39
  /**
40
40
  * Create new session under this agent
41
41
  */
42
42
  createSession(config = {}) {
43
- const session = sessionManager.createSession({
43
+ const session = getSessionManager(this.userId).createSession({
44
44
  id: config.id || uuidv4(),
45
45
  title: config.title || '新会话',
46
46
  selectModelId: config.selectModelId,
@@ -55,7 +55,7 @@ export class Agent {
55
55
  const session = this.getSession(sessionId);
56
56
  if (!session)
57
57
  return null;
58
- return sessionManager.updateSession(sessionId, data);
58
+ return getSessionManager(this.userId).updateSession(sessionId, data);
59
59
  }
60
60
  /**
61
61
  * Delete session under this agent
@@ -64,13 +64,13 @@ export class Agent {
64
64
  const session = this.getSession(sessionId);
65
65
  if (!session)
66
66
  return false;
67
- return sessionManager.deleteSession(sessionId);
67
+ return getSessionManager(this.userId).deleteSession(sessionId);
68
68
  }
69
69
  /**
70
70
  * Get session under this agent
71
71
  */
72
72
  getSession(sessionId) {
73
- const session = sessionManager.getSession(sessionId);
73
+ const session = getSessionManager(this.userId).getSession(sessionId);
74
74
  if (session && session.agentId === this.id) {
75
75
  return session;
76
76
  }
@@ -1,8 +1,8 @@
1
1
  import { v4 as uuidv4 } from 'uuid';
2
- import { Agent } from './Agent';
3
- import { sessionManager } from '../session/SessionManager';
2
+ import { Agent } from './Agent.js';
3
+ import { getSessionManager } from '../session/SessionManager.js';
4
4
  import { authStore } from '@/stores/authStore';
5
- import { getLogger } from '@pocketclaw/shared';
5
+ import { getLogger } from '@myassis/shared';
6
6
  import appConfig from '@/config';
7
7
  const logger = getLogger('AgentManager');
8
8
  /**
@@ -13,8 +13,10 @@ export class AgentManager {
13
13
  store;
14
14
  agents = null;
15
15
  initialized = false;
16
- constructor(store) {
16
+ userId;
17
+ constructor(store, userId) {
17
18
  this.store = store;
19
+ this.userId = userId;
18
20
  }
19
21
  /**
20
22
  * Initialize - Load all agents from database on startup
@@ -34,12 +36,12 @@ export class AgentManager {
34
36
  // Load all agents from database
35
37
  const agentDataList = this.store.findByUserId(userId);
36
38
  for (const agentData of agentDataList) {
37
- const agent = new Agent(agentData, this.store, this);
39
+ const agent = new Agent(agentData, this.store);
38
40
  this.agents.set(agent.id, agent);
39
41
  }
40
- // If no agents exist, create default "MyClaw" agent
42
+ // If no agents exist, create default "我的助手" agent
41
43
  if (this.agents.size === 0) {
42
- logger.info('No agents found, creating default "MyClaw" agent');
44
+ logger.info('No agents found, creating default "我的助手" agent');
43
45
  this.createDefaultAgent(userId);
44
46
  }
45
47
  // Ensure exactly one agent has isCurrent=true
@@ -58,11 +60,11 @@ export class AgentManager {
58
60
  * Get current user ID
59
61
  */
60
62
  getCurrentUserId() {
61
- const user = authStore.getUser();
63
+ const user = authStore.getUser(this.userId);
62
64
  return user ? String(user.id) : null;
63
65
  }
64
66
  /**
65
- * Create default "MyClaw" agent
67
+ * Create default "我的助手" agent
66
68
  */
67
69
  createDefaultAgent(userId) {
68
70
  const agentData = {
@@ -78,7 +80,7 @@ export class AgentManager {
78
80
  isCurrent: true,
79
81
  };
80
82
  this.store.insert(agentData);
81
- const agent = new Agent(agentData, this.store, this);
83
+ const agent = new Agent(agentData, this.store);
82
84
  this.agents.set(agent.id, agent);
83
85
  // Create default session
84
86
  agent.createSession({ title: '新会话' });
@@ -95,7 +97,7 @@ export class AgentManager {
95
97
  return [];
96
98
  const agentDataList = this.store.findByUserId(userId);
97
99
  return agentDataList.map(data => {
98
- const sessions = sessionManager.getUserSessions();
100
+ const sessions = getSessionManager(this.userId).getUserSessions();
99
101
  const agentSessions = sessions
100
102
  .filter(s => s.agentId === data.id)
101
103
  .map(s => ({
@@ -146,7 +148,7 @@ export class AgentManager {
146
148
  updatedAt: Date.now(),
147
149
  };
148
150
  this.store.insert(agentData);
149
- const agent = new Agent(agentData, this.store, this);
151
+ const agent = new Agent(agentData, this.store);
150
152
  this.agents.set(agent.id, agent);
151
153
  // Create default session
152
154
  agent.createSession({ title: '新会话' });
@@ -186,7 +188,7 @@ export class AgentManager {
186
188
  // Delete all sessions under this agent
187
189
  const sessions = agent.getSessions();
188
190
  for (const session of sessions) {
189
- sessionManager.deleteSession(session.id);
191
+ getSessionManager(this.userId).deleteSession(session.id);
190
192
  }
191
193
  // Delete agent
192
194
  this.store.delete(agentId);
@@ -233,12 +235,15 @@ export class AgentManager {
233
235
  * Get sessions under an agent
234
236
  */
235
237
  getAgentSessions(agentId) {
236
- const sessions = sessionManager.getUserSessions();
238
+ const sessions = getSessionManager(this.userId).getUserSessions();
237
239
  return sessions
238
240
  .filter(s => s.agentId === agentId)
239
241
  .map(s => ({
240
242
  id: s.id,
241
243
  title: s.title,
244
+ selectModelId: s.selectModelId,
245
+ isCurrent: s.isCurrent,
246
+ unreadCount: s.unreadCount,
242
247
  createdAt: s.createdAt,
243
248
  updatedAt: s.updatedAt,
244
249
  }));
@@ -258,8 +263,11 @@ export class AgentManager {
258
263
  }
259
264
  }
260
265
  // Export singleton
261
- export let agentManager;
262
- export function initAgentManager(store) {
263
- agentManager = new AgentManager(store);
264
- return agentManager;
266
+ export let agentManagerMap = new Map();
267
+ export function initAgentManager(store, userId) {
268
+ if (agentManagerMap.has(userId)) {
269
+ return agentManagerMap.get(userId);
270
+ }
271
+ agentManagerMap.set(userId, new AgentManager(store, userId));
272
+ return agentManagerMap.get(userId);
265
273
  }
@@ -1,4 +1,4 @@
1
- import { getLogger } from '@pocketclaw/shared';
1
+ import { getLogger } from '@myassis/shared';
2
2
  const logger = getLogger('AgentStore');
3
3
  /**
4
4
  * AgentStore - Data access layer