@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.
- package/dist/api/index.js +69 -59
- package/dist/config/index.js +2 -2
- package/dist/index.js +85 -11
- package/dist/middleware/auth.js +9 -25
- package/dist/middleware/errorHandler.js +1 -1
- package/dist/routes/agent.js +30 -19
- package/dist/routes/auth.js +35 -26
- package/dist/routes/chat.js +1 -1
- package/dist/routes/models.js +11 -14
- package/dist/routes/service.js +3 -6
- package/dist/routes/settings.js +11 -9
- package/dist/routes/skillHub.js +10 -6
- package/dist/routes/skills.js +13 -10
- package/dist/routes/tasks.js +6 -3
- package/dist/routes/upload.js +5 -2
- package/dist/routes/version.js +4 -4
- package/dist/services/LocalTaskService.js +1 -1
- package/dist/services/NotificationService.js +1 -1
- package/dist/services/ServiceManager.js +12 -12
- package/dist/services/TaskSchedulerService.js +18 -9
- package/dist/services/TaskService.js +23 -20
- package/dist/services/WebSocketService.js +16 -6
- package/dist/services/agent/Agent.js +7 -7
- package/dist/services/agent/AgentManager.js +25 -17
- package/dist/services/agent/AgentStore.js +1 -1
- package/dist/services/dataService.js +60 -70
- package/dist/services/index.js +1 -1
- package/dist/services/llm/LLMClient.js +11 -9
- package/dist/services/memory/MemoryManager.js +177 -14
- package/dist/services/session/MigrationManager.js +1 -1
- package/dist/services/session/Session.js +70 -46
- package/dist/services/session/SessionManager.js +12 -5
- package/dist/services/session/SessionStore.js +1 -27
- package/dist/services/session/index.js +1 -1
- package/dist/services/systemPrompt.js +8 -4
- package/dist/services/task/PushTokenStore.js +1 -19
- package/dist/services/task/TaskStore.js +1 -20
- package/dist/services/tools/edit.js +122 -148
- package/dist/services/tools/exec.js +1 -1
- package/dist/services/tools/fetch.js +1 -1
- package/dist/services/tools/file.js +4 -9
- package/dist/services/tools/index.js +4 -3
- package/dist/services/tools/model.js +8 -6
- package/dist/services/tools/sessionsSpawn.js +54 -0
- package/dist/services/tools/skill.js +13 -12
- package/dist/services/tools/task.js +2 -4
- package/dist/stores/authStore.js +52 -66
- package/dist/stores/persistStore.js +37 -3
- package/package.json +8 -4
- package/scripts/postbuild.js +39 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
+
import { getLogger } from '@myassis/shared';
|
|
3
|
+
import { getSessionManager } from '../session';
|
|
4
|
+
const logger = getLogger('sessionsSpawn');
|
|
5
|
+
export const sessionsSpawnTool = {
|
|
6
|
+
name: 'sessions_spawn',
|
|
7
|
+
description: '派发一个子 Agent 执行独立任务,阻塞等待完成后返回结果。' +
|
|
8
|
+
'适用于需要并行处理或隔离上下文的任务。' +
|
|
9
|
+
'子 Agent 拥有独立的 Session,不会污染主 Agent 的对话历史。',
|
|
10
|
+
parameters: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
prompt: {
|
|
14
|
+
type: 'string',
|
|
15
|
+
description: '发送给子 Agent 的完整任务描述',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
required: ['prompt'],
|
|
19
|
+
},
|
|
20
|
+
handler: async (args, sessionId, messageId, userId) => {
|
|
21
|
+
const { prompt } = args;
|
|
22
|
+
if (!prompt || typeof prompt !== 'string') {
|
|
23
|
+
return { success: false, errorMessage: 'prompt 参数缺失或格式错误' };
|
|
24
|
+
}
|
|
25
|
+
if (!userId) {
|
|
26
|
+
return { success: false, errorMessage: '未登录,无法创建子 Agent Session' };
|
|
27
|
+
}
|
|
28
|
+
const sessionManager = getSessionManager(userId);
|
|
29
|
+
// 创建子 Agent Session
|
|
30
|
+
const childSession = sessionManager.getSession(sessionId);
|
|
31
|
+
logger.info(`子 Agent Session 已创建: ${childSession.id}`);
|
|
32
|
+
const userMessageId = uuidv4();
|
|
33
|
+
const assistantMessageId = uuidv4();
|
|
34
|
+
try {
|
|
35
|
+
// 阻塞等待子 Agent 完成(res=null,不向客户端 SSE 推送)
|
|
36
|
+
const result = await childSession.streamChat(prompt, [], null, userMessageId, assistantMessageId, true);
|
|
37
|
+
if (result.success) {
|
|
38
|
+
logger.info(`子 Agent 完成: ${childSession.id}, 输出长度: ${result.data?.length ?? 0}`, result);
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
success: result.success,
|
|
42
|
+
output: result.data,
|
|
43
|
+
errorMessage: result.error
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
logger.error(`子 Agent 执行失败: ${childSession.id}`, error);
|
|
48
|
+
return {
|
|
49
|
+
success: false,
|
|
50
|
+
errorMessage: `子 Agent 执行失败: ${error?.message || '未知错误'}`,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { skillsService, skillHubService } from '../dataService';
|
|
2
|
-
import { persistStore } from '@/stores';
|
|
3
|
-
import { getLogger } from '@
|
|
2
|
+
import { authStore, persistStore } from '@/stores';
|
|
3
|
+
import { getLogger } from '@myassis/shared';
|
|
4
4
|
const logger = getLogger('skill');
|
|
5
5
|
export const skillTool = {
|
|
6
6
|
name: 'skill',
|
|
@@ -22,19 +22,20 @@ export const skillTool = {
|
|
|
22
22
|
},
|
|
23
23
|
required: ['action'],
|
|
24
24
|
},
|
|
25
|
-
handler: async (args) => {
|
|
25
|
+
handler: async (args, sessionId, messageId, userId) => {
|
|
26
26
|
try {
|
|
27
27
|
const { action, query, skillId, hubId, apiKey, apiBaseUrl, rating } = args;
|
|
28
|
+
const token = authStore.get(userId).accessToken;
|
|
28
29
|
switch (action) {
|
|
29
30
|
case 'listInstalled': {
|
|
30
|
-
const response = await skillsService.list();
|
|
31
|
+
const response = await skillsService.list(token);
|
|
31
32
|
if (!response.success)
|
|
32
33
|
return { success: false, errorMessage: response.error || '获取列表失败' };
|
|
33
34
|
const skills = response.data || [];
|
|
34
35
|
return { success: true, output: JSON.stringify({ total: skills.length, skills: skills.map((s) => ({ id: s.id, name: s.name, description: s.description, version: s.version, status: s.status })) }) };
|
|
35
36
|
}
|
|
36
37
|
case 'search': {
|
|
37
|
-
const response = await skillHubService.list({ keyword: query, pageSize: 10 });
|
|
38
|
+
const response = await skillHubService.list(token, { keyword: query, pageSize: 10 });
|
|
38
39
|
if (!response.success)
|
|
39
40
|
return { success: false, errorMessage: response.error || '搜索失败' };
|
|
40
41
|
const skills = response.data?.items || response.data || [];
|
|
@@ -44,7 +45,7 @@ export const skillTool = {
|
|
|
44
45
|
const installId = hubId || query;
|
|
45
46
|
if (!installId)
|
|
46
47
|
return { success: false, errorMessage: '安装需要提供 hubId 或 query' };
|
|
47
|
-
const response = await skillsService.install(installId);
|
|
48
|
+
const response = await skillsService.install(installId, token);
|
|
48
49
|
if (!response.success)
|
|
49
50
|
return { success: false, errorMessage: response.error || '安装失败' };
|
|
50
51
|
return { success: true };
|
|
@@ -52,7 +53,7 @@ export const skillTool = {
|
|
|
52
53
|
case 'uninstall': {
|
|
53
54
|
if (!skillId)
|
|
54
55
|
return { success: false, errorMessage: '卸载需要提供技能 ID' };
|
|
55
|
-
const response = await skillsService.uninstall(skillId);
|
|
56
|
+
const response = await skillsService.uninstall(skillId, token);
|
|
56
57
|
if (!response.success)
|
|
57
58
|
return { success: false, errorMessage: response.error || '卸载失败' };
|
|
58
59
|
return { success: true };
|
|
@@ -60,21 +61,21 @@ export const skillTool = {
|
|
|
60
61
|
case 'detail': {
|
|
61
62
|
if (!skillId)
|
|
62
63
|
return { success: false, errorMessage: '查看详情需要提供技能 ID' };
|
|
63
|
-
const response = await skillsService.get(skillId);
|
|
64
|
+
const response = await skillsService.get(skillId, token);
|
|
64
65
|
if (!response.success)
|
|
65
66
|
return { success: false, errorMessage: response.error || '获取详情失败' };
|
|
66
67
|
// 表明该技能已被使用
|
|
67
68
|
const detailHubId = response.data?.templateId;
|
|
68
69
|
if (detailHubId) {
|
|
69
|
-
skillHubService.use(detailHubId).catch((err) => logger.warn('技能使用记录失败:', err?.message));
|
|
70
|
+
skillHubService.use(detailHubId, token).catch((err) => logger.warn('技能使用记录失败:', err?.message));
|
|
70
71
|
}
|
|
71
|
-
const apikey = await skillsService.getApiKey(skillId);
|
|
72
|
+
const apikey = await skillsService.getApiKey(skillId, token);
|
|
72
73
|
return { success: true, output: JSON.stringify({ skill: { ...response.data, apiKey: apikey.apiKey } }) };
|
|
73
74
|
}
|
|
74
75
|
case 'setApiKey': {
|
|
75
76
|
if (!skillId || !apiKey)
|
|
76
77
|
return { success: false, errorMessage: '设置 API Key 需要提供技能 ID 和 API Key' };
|
|
77
|
-
const response = await skillsService.setApiKey(skillId, apiKey);
|
|
78
|
+
const response = await skillsService.setApiKey(skillId, apiKey, token);
|
|
78
79
|
if (!response)
|
|
79
80
|
return { success: false, errorMessage: '设置失败' };
|
|
80
81
|
return { success: true };
|
|
@@ -84,7 +85,7 @@ export const skillTool = {
|
|
|
84
85
|
return { success: false, errorMessage: '评分需要提供 hubId 和评分(1-5)' };
|
|
85
86
|
if (rating < 1 || rating > 5)
|
|
86
87
|
return { success: false, errorMessage: '评分必须是 1-5 之间的整数' };
|
|
87
|
-
const response = await skillHubService.rate(hubId, rating);
|
|
88
|
+
const response = await skillHubService.rate(hubId, rating, token);
|
|
88
89
|
if (!response.success)
|
|
89
90
|
return { success: false, errorMessage: response.error || '评分失败' };
|
|
90
91
|
return { success: true };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { tasksService } from '../dataService';
|
|
2
|
-
import {
|
|
3
|
-
import { validPlatformApplies, getPlatform } from '@pocketclaw/shared/dist/utils/system.js';
|
|
2
|
+
import { validPlatformApplies, getPlatform } from '@myassis/shared/dist/utils/system.js';
|
|
4
3
|
export const taskTool = {
|
|
5
4
|
name: 'task',
|
|
6
5
|
description: '任务管理工具',
|
|
@@ -32,7 +31,7 @@ export const taskTool = {
|
|
|
32
31
|
},
|
|
33
32
|
required: ['action'],
|
|
34
33
|
},
|
|
35
|
-
handler: async (args) => {
|
|
34
|
+
handler: async (args, sessionId, messageId, userId) => {
|
|
36
35
|
try {
|
|
37
36
|
const { action, title, description, taskType, recurrenceRule, intervalValue, intervalUnit, startTime, endTime, scheduledAt, platformApply, status, taskId, newTitle, newDescription, newStatus, newRecurrenceRule, newScheduledAt } = args;
|
|
38
37
|
switch (action) {
|
|
@@ -54,7 +53,6 @@ export const taskTool = {
|
|
|
54
53
|
createData.endTime = endTime;
|
|
55
54
|
if (scheduledAt)
|
|
56
55
|
createData.scheduledAt = scheduledAt;
|
|
57
|
-
const userId = authStore.getUserId();
|
|
58
56
|
if (!userId) {
|
|
59
57
|
return { success: false, errorMessage: '未登录' };
|
|
60
58
|
}
|
package/dist/stores/authStore.js
CHANGED
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
import fs from 'fs';
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import os from 'os';
|
|
8
|
-
import { getLogger } from '@
|
|
9
|
-
import {
|
|
8
|
+
import { getLogger } from '@myassis/shared';
|
|
9
|
+
import { getSessionManager } from '@/services/session';
|
|
10
10
|
const logger = getLogger('authStore');
|
|
11
11
|
// 认证数据存储文件路径(使用应用数据目录,支持 STORAGE_DIR 环境变量)
|
|
12
|
-
const AUTH_STORAGE_DIR = process.env.STORAGE_DIR || path.join(os.homedir(), '
|
|
12
|
+
const AUTH_STORAGE_DIR = process.env.STORAGE_DIR || path.join(os.homedir(), 'myassis-gateway-storage');
|
|
13
13
|
const AUTH_STORAGE_FILE = path.join(AUTH_STORAGE_DIR, 'auth', 'auth.json');
|
|
14
14
|
// 内存中的认证数据
|
|
15
|
-
let
|
|
15
|
+
let authMap = null;
|
|
16
16
|
// 确保存储目录存在
|
|
17
17
|
function ensureStorageDir() {
|
|
18
18
|
const dir = path.dirname(AUTH_STORAGE_FILE);
|
|
@@ -25,15 +25,9 @@ function loadFromFile() {
|
|
|
25
25
|
try {
|
|
26
26
|
if (fs.existsSync(AUTH_STORAGE_FILE)) {
|
|
27
27
|
const data = fs.readFileSync(AUTH_STORAGE_FILE, 'utf-8');
|
|
28
|
-
const
|
|
29
|
-
// 检查 token 是否过期
|
|
30
|
-
if (authData.expiresAt && authData.expiresAt < Date.now()) {
|
|
31
|
-
logger.debug('Token expired, clearing...');
|
|
32
|
-
clearAuth();
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
28
|
+
const obj = JSON.parse(data);
|
|
35
29
|
logger.debug('Auth data loaded from file');
|
|
36
|
-
return
|
|
30
|
+
return new Map(Object.entries(obj));
|
|
37
31
|
}
|
|
38
32
|
}
|
|
39
33
|
catch (error) {
|
|
@@ -42,10 +36,10 @@ function loadFromFile() {
|
|
|
42
36
|
return null;
|
|
43
37
|
}
|
|
44
38
|
// 保存认证数据到本地文件
|
|
45
|
-
function saveAuthToFile(
|
|
39
|
+
function saveAuthToFile(authMap) {
|
|
46
40
|
ensureStorageDir();
|
|
47
41
|
try {
|
|
48
|
-
fs.writeFileSync(AUTH_STORAGE_FILE, JSON.stringify(
|
|
42
|
+
fs.writeFileSync(AUTH_STORAGE_FILE, JSON.stringify(Object.fromEntries(authMap)), 'utf-8');
|
|
49
43
|
logger.debug('Auth data saved to file');
|
|
50
44
|
}
|
|
51
45
|
catch (error) {
|
|
@@ -66,7 +60,7 @@ function clearAuthFile() {
|
|
|
66
60
|
}
|
|
67
61
|
// 清除认证数据
|
|
68
62
|
function clearAuth() {
|
|
69
|
-
|
|
63
|
+
authMap = null;
|
|
70
64
|
clearAuthFile();
|
|
71
65
|
}
|
|
72
66
|
/**
|
|
@@ -78,84 +72,75 @@ export const authStore = {
|
|
|
78
72
|
* 应在 Gateway 启动时调用
|
|
79
73
|
*/
|
|
80
74
|
load() {
|
|
81
|
-
|
|
82
|
-
if (
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
logger.debug('Initialized, no valid auth data');
|
|
75
|
+
authMap = loadFromFile();
|
|
76
|
+
if (authMap === null) {
|
|
77
|
+
authMap = new Map();
|
|
87
78
|
}
|
|
88
79
|
},
|
|
89
80
|
/**
|
|
90
81
|
* 保存认证数据(内存 + 本地)
|
|
91
82
|
*/
|
|
92
83
|
save(authData) {
|
|
93
|
-
|
|
94
|
-
|
|
84
|
+
if (authMap === null) {
|
|
85
|
+
this.load();
|
|
86
|
+
}
|
|
87
|
+
authMap.set(authData.user.id, authData);
|
|
88
|
+
saveAuthToFile(authMap);
|
|
95
89
|
// 用户登录后初始化 SessionManager
|
|
96
|
-
|
|
90
|
+
getSessionManager(authData.user.id).initialize();
|
|
97
91
|
logger.debug('Auth saved for user:', authData.user.nickname);
|
|
98
92
|
},
|
|
93
|
+
//获取所有认证用户
|
|
94
|
+
getAll() {
|
|
95
|
+
if (authMap === null) {
|
|
96
|
+
this.load();
|
|
97
|
+
}
|
|
98
|
+
return authMap;
|
|
99
|
+
},
|
|
99
100
|
/**
|
|
100
101
|
* 获取当前认证数据
|
|
101
102
|
*/
|
|
102
|
-
get() {
|
|
103
|
-
return
|
|
103
|
+
get(userId) {
|
|
104
|
+
return authMap.get(userId);
|
|
104
105
|
},
|
|
105
106
|
/**
|
|
106
107
|
* 获取用户信息
|
|
107
108
|
*/
|
|
108
|
-
getUser() {
|
|
109
|
-
return
|
|
110
|
-
},
|
|
111
|
-
/**
|
|
112
|
-
* 获取 Token(自动检查过期)
|
|
113
|
-
*/
|
|
114
|
-
getToken() {
|
|
115
|
-
if (!auth) {
|
|
116
|
-
return null;
|
|
117
|
-
}
|
|
118
|
-
// 检查是否过期(预留 60 秒缓冲时间)
|
|
119
|
-
if (auth.expiresAt && auth.expiresAt < Date.now() + 60000) {
|
|
120
|
-
logger.debug('Token expired');
|
|
121
|
-
return null;
|
|
122
|
-
}
|
|
123
|
-
return auth.token;
|
|
109
|
+
getUser(userId) {
|
|
110
|
+
return authMap.get(userId).user || null;
|
|
124
111
|
},
|
|
125
112
|
/**
|
|
126
113
|
* 获取刷新 Token
|
|
127
114
|
*/
|
|
128
|
-
getRefreshToken() {
|
|
129
|
-
return
|
|
115
|
+
getRefreshToken(userId) {
|
|
116
|
+
return authMap.get(userId)?.refreshToken || null;
|
|
130
117
|
},
|
|
131
118
|
/**
|
|
132
119
|
* 更新 Token
|
|
133
120
|
*/
|
|
134
121
|
updateToken(token, expiresIn, refreshToken) {
|
|
135
|
-
if (
|
|
136
|
-
auth
|
|
137
|
-
if (
|
|
138
|
-
auth.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
122
|
+
if (authMap) {
|
|
123
|
+
const auth = Array.from(authMap.values()).find(x => x.accessToken === token);
|
|
124
|
+
if (auth) {
|
|
125
|
+
auth.accessToken = token;
|
|
126
|
+
if (refreshToken) {
|
|
127
|
+
auth.refreshToken = refreshToken;
|
|
128
|
+
}
|
|
129
|
+
saveAuthToFile(authMap);
|
|
130
|
+
logger.debug('Token updated');
|
|
143
131
|
}
|
|
144
|
-
saveAuthToFile(auth);
|
|
145
|
-
logger.debug('Token updated');
|
|
146
132
|
}
|
|
147
133
|
},
|
|
148
134
|
/**
|
|
149
135
|
* 获取用户 ID
|
|
150
136
|
*/
|
|
151
|
-
getUserId() {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
return this.getToken() !== null;
|
|
137
|
+
getUserId(token) {
|
|
138
|
+
if (authMap) {
|
|
139
|
+
return Array.from(authMap.values()).find(x => x.accessToken === token)?.user?.id || null;
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
159
144
|
},
|
|
160
145
|
/**
|
|
161
146
|
* 清除认证数据(内存 + 本地)
|
|
@@ -167,11 +152,12 @@ export const authStore = {
|
|
|
167
152
|
/**
|
|
168
153
|
* 更新用户信息
|
|
169
154
|
*/
|
|
170
|
-
updateUser(userData) {
|
|
171
|
-
if (
|
|
155
|
+
updateUser(token, userData) {
|
|
156
|
+
if (authMap) {
|
|
157
|
+
const auth = Array.from(authMap.values()).find(x => x.accessToken === token);
|
|
172
158
|
auth.user = { ...auth.user, ...userData };
|
|
173
|
-
saveAuthToFile(
|
|
174
|
-
logger.debug('User updated:', auth.user?.nickname || auth.user?.
|
|
159
|
+
saveAuthToFile(authMap);
|
|
160
|
+
logger.debug('User updated:', auth.user?.nickname || auth.user?.account);
|
|
175
161
|
}
|
|
176
162
|
},
|
|
177
163
|
};
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
import fs from 'fs';
|
|
6
6
|
import os from 'os';
|
|
7
7
|
import path from 'path';
|
|
8
|
-
import { getLogger } from '@
|
|
8
|
+
import { getLogger } from '@myassis/shared';
|
|
9
9
|
const logger = getLogger('persistStore');
|
|
10
10
|
// 存储文件路径(跨平台兼容)
|
|
11
|
-
const STORAGE_DIR = process.env.STORAGE_DIR || path.join(os.homedir(), '
|
|
11
|
+
const STORAGE_DIR = process.env.STORAGE_DIR || path.join(os.homedir(), 'myassis-gateway-storage');
|
|
12
12
|
const STORAGE_FILE = path.join(STORAGE_DIR, 'persist.json');
|
|
13
13
|
// 确保存储目录存在
|
|
14
14
|
function ensureStorageDir() {
|
|
@@ -312,6 +312,40 @@ export const persistStore = {
|
|
|
312
312
|
writeStorage(storage);
|
|
313
313
|
}
|
|
314
314
|
return cleaned;
|
|
315
|
-
}
|
|
315
|
+
},
|
|
316
|
+
// ─── CORS 自定义域名 ────────────────────────────────
|
|
317
|
+
/**
|
|
318
|
+
* 获取所有自定义 CORS 域名
|
|
319
|
+
*/
|
|
320
|
+
getCorsDomains() {
|
|
321
|
+
return this.get('corsDomains', []);
|
|
322
|
+
},
|
|
323
|
+
/**
|
|
324
|
+
* 添加自定义 CORS 域名
|
|
325
|
+
*/
|
|
326
|
+
addCorsDomain(domain) {
|
|
327
|
+
const domains = this.getCorsDomains();
|
|
328
|
+
const normalized = domain.trim().toLowerCase();
|
|
329
|
+
if (!normalized)
|
|
330
|
+
return false;
|
|
331
|
+
if (domains.some(d => d.toLowerCase() === normalized))
|
|
332
|
+
return false; // 重复
|
|
333
|
+
domains.push(domain.trim());
|
|
334
|
+
this.set('corsDomains', domains);
|
|
335
|
+
return true;
|
|
336
|
+
},
|
|
337
|
+
/**
|
|
338
|
+
* 移除自定义 CORS 域名
|
|
339
|
+
*/
|
|
340
|
+
removeCorsDomain(domain) {
|
|
341
|
+
const domains = this.getCorsDomains();
|
|
342
|
+
const normalized = domain.trim().toLowerCase();
|
|
343
|
+
const before = domains.length;
|
|
344
|
+
const filtered = domains.filter(d => d.toLowerCase() !== normalized);
|
|
345
|
+
if (filtered.length === before)
|
|
346
|
+
return false; // 不存在
|
|
347
|
+
this.set('corsDomains', filtered);
|
|
348
|
+
return true;
|
|
349
|
+
},
|
|
316
350
|
};
|
|
317
351
|
export default persistStore;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myassis/gateway",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "我的助手 Gateway Service - 本地 AI 网关服务,支持认证、WebSocket 实时通信和任务调度",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -9,7 +9,10 @@
|
|
|
9
9
|
"gateway": "dist/index.js"
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
|
-
"dist"
|
|
12
|
+
"dist",
|
|
13
|
+
"scripts",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE"
|
|
13
16
|
],
|
|
14
17
|
"exports": {
|
|
15
18
|
".": {
|
|
@@ -21,6 +24,7 @@
|
|
|
21
24
|
"dev": "tsx watch src/index.ts",
|
|
22
25
|
"start": "node dist/index.js",
|
|
23
26
|
"build": "tsc",
|
|
27
|
+
"postbuild": "node scripts/postbuild.js",
|
|
24
28
|
"pkg:win": "pkg . --targets node18-win-x64 --output myassis-gateway-win.exe",
|
|
25
29
|
"pkg:linux": "pkg . --targets node18-linux-x64 --output myassis-gateway-linux",
|
|
26
30
|
"pkg:all": "npm run pkg:win && npm run pkg:linux",
|
|
@@ -42,8 +46,8 @@
|
|
|
42
46
|
"url": "https://github.com/myassis/myassis.git"
|
|
43
47
|
},
|
|
44
48
|
"dependencies": {
|
|
45
|
-
"@nut-tree/nut-js": "^4.2.0",
|
|
46
|
-
"@myassis/shared": "
|
|
49
|
+
"@nut-tree-fork/nut-js": "^4.2.0",
|
|
50
|
+
"@myassis/shared": "^1.0.0",
|
|
47
51
|
"axios": "^1.15.2",
|
|
48
52
|
"bcryptjs": "^2.4.3",
|
|
49
53
|
"better-sqlite3": "^12.10.0",
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
const files = [
|
|
6
|
+
path.join(process.cwd(), 'dist/index.js'),
|
|
7
|
+
// 如果有其他 CLI 入口文件,也添加到这里
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
files.forEach(filePath => {
|
|
11
|
+
if (!fs.existsSync(filePath)) {
|
|
12
|
+
console.warn(`Warning: ${filePath} does not exist, skipping...`);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let content = fs.readFileSync(filePath, 'utf8');
|
|
17
|
+
|
|
18
|
+
// 检查是否已有 shebang
|
|
19
|
+
if (!content.startsWith('#!/usr/bin/env node')) {
|
|
20
|
+
// 添加 shebang
|
|
21
|
+
content = '#!/usr/bin/env node\n' + content;
|
|
22
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
23
|
+
console.log(`✓ Added shebang to ${path.relative(process.cwd(), filePath)}`);
|
|
24
|
+
} else {
|
|
25
|
+
console.log(`✓ Shebang already exists in ${path.relative(process.cwd(), filePath)}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 设置可执行权限(Unix/Linux/macOS)
|
|
29
|
+
if (process.platform !== 'win32') {
|
|
30
|
+
try {
|
|
31
|
+
fs.chmodSync(filePath, '755');
|
|
32
|
+
console.log(`✓ Set executable permission for ${path.relative(process.cwd(), filePath)}`);
|
|
33
|
+
} catch (err) {
|
|
34
|
+
console.warn(`Warning: Could not set executable permission for ${filePath}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
console.log('\n✓ Postbuild script completed!');
|