openclaw-vchat-plugin 0.0.7 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands.d.ts +1 -0
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +37 -62
- package/dist/commands.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +3 -1
- package/dist/constants.js.map +1 -1
- package/dist/group-event-store.d.ts +47 -0
- package/dist/group-event-store.d.ts.map +1 -0
- package/dist/group-event-store.js +145 -0
- package/dist/group-event-store.js.map +1 -0
- package/dist/index.js +74 -8
- package/dist/index.js.map +1 -1
- package/dist/message-handler.d.ts.map +1 -1
- package/dist/message-handler.js +11 -4
- package/dist/message-handler.js.map +1 -1
- package/dist/plugin-update-runner.d.ts +2 -0
- package/dist/plugin-update-runner.d.ts.map +1 -0
- package/dist/plugin-update-runner.js +179 -0
- package/dist/plugin-update-runner.js.map +1 -0
- package/dist/relay-server.d.ts +2 -0
- package/dist/relay-server.d.ts.map +1 -1
- package/dist/relay-server.js +268 -8
- package/dist/relay-server.js.bak-self-send-filter-20260310 +1028 -0
- package/dist/relay-server.js.map +1 -1
- package/dist/routes/config.routes.d.ts.map +1 -1
- package/dist/routes/config.routes.js +53 -0
- package/dist/routes/config.routes.js.map +1 -1
- package/dist/services/config.service.d.ts +8 -0
- package/dist/services/config.service.d.ts.map +1 -1
- package/dist/services/config.service.js +200 -2
- package/dist/services/config.service.js.map +1 -1
- package/dist/services/plugin-update.service.d.ts +24 -0
- package/dist/services/plugin-update.service.d.ts.map +1 -0
- package/dist/services/plugin-update.service.js +308 -0
- package/dist/services/plugin-update.service.js.map +1 -0
- package/dist/services/skills.service.d.ts +33 -0
- package/dist/services/skills.service.d.ts.map +1 -0
- package/dist/services/skills.service.js +177 -0
- package/dist/services/skills.service.js.map +1 -0
- package/package.json +1 -1
- package/src/commands.ts +38 -61
- package/src/constants.ts +4 -1
- package/src/group-event-store.ts +204 -0
- package/src/index.ts +72 -8
- package/src/message-handler.ts +19 -5
- package/src/plugin-update-runner.ts +217 -0
- package/src/relay-server.ts +292 -8
- package/src/routes/config.routes.ts +56 -0
- package/src/services/config.service.ts +223 -3
- package/src/services/plugin-update.service.ts +335 -0
- package/src/services/skills.service.ts +175 -0
package/dist/relay-server.js
CHANGED
|
@@ -10,16 +10,20 @@ const multer_1 = __importDefault(require("multer"));
|
|
|
10
10
|
const path_1 = __importDefault(require("path"));
|
|
11
11
|
const fs_1 = __importDefault(require("fs"));
|
|
12
12
|
const http_1 = __importDefault(require("http"));
|
|
13
|
+
const child_process_1 = require("child_process");
|
|
13
14
|
const ws_1 = require("ws");
|
|
14
15
|
const crypto_1 = require("crypto");
|
|
15
16
|
const session_manager_1 = require("./session-manager");
|
|
16
17
|
const message_handler_1 = require("./message-handler");
|
|
17
18
|
const media_handler_1 = require("./media-handler");
|
|
18
19
|
const group_manager_1 = require("./group-manager");
|
|
20
|
+
const group_event_store_1 = require("./group-event-store");
|
|
19
21
|
const commands_1 = require("./commands");
|
|
20
22
|
const constants_1 = require("./constants");
|
|
21
23
|
const session_key_1 = require("./session-key");
|
|
22
24
|
const config_routes_1 = __importDefault(require("./routes/config.routes"));
|
|
25
|
+
const skills_service_1 = require("./services/skills.service");
|
|
26
|
+
const plugin_update_service_1 = require("./services/plugin-update.service");
|
|
23
27
|
function createRequestMemo(ttlMs) {
|
|
24
28
|
const inflight = new Map();
|
|
25
29
|
const cache = new Map();
|
|
@@ -55,6 +59,7 @@ function toBridgeSessionPayload(item) {
|
|
|
55
59
|
lastMessageTime: Number(item.lastMessageTime) || createdAt,
|
|
56
60
|
createdAt,
|
|
57
61
|
agentId: String(item.agentId || '').trim() || 'main',
|
|
62
|
+
model: String(item.model || '').trim(),
|
|
58
63
|
};
|
|
59
64
|
}
|
|
60
65
|
function parseTimestamp(value) {
|
|
@@ -128,15 +133,63 @@ function isRenderableHistoryRole(roleRaw) {
|
|
|
128
133
|
return true;
|
|
129
134
|
return roleRaw === 'user' || roleRaw === 'assistant' || roleRaw === 'system';
|
|
130
135
|
}
|
|
131
|
-
function
|
|
136
|
+
function getHistoryProvenance(row) {
|
|
137
|
+
if (!row || typeof row !== 'object')
|
|
138
|
+
return null;
|
|
139
|
+
return row?.provenance || row?.message?.provenance || null;
|
|
140
|
+
}
|
|
141
|
+
function shouldHideHistoryRow(row, currentSessionKey) {
|
|
142
|
+
const provenance = getHistoryProvenance(row);
|
|
143
|
+
if (!provenance || typeof provenance !== 'object')
|
|
144
|
+
return false;
|
|
145
|
+
const kind = String(provenance?.kind || '').trim().toLowerCase();
|
|
146
|
+
if (kind !== 'inter_session')
|
|
147
|
+
return false;
|
|
148
|
+
const sourceTool = String(provenance?.sourceTool || '').trim().toLowerCase();
|
|
149
|
+
const sourceSessionKey = String(provenance?.sourceSessionKey || '').trim().toLowerCase();
|
|
150
|
+
const targetSessionKey = String(currentSessionKey || '').trim().toLowerCase();
|
|
151
|
+
// Hide self-routed relay messages. They are persisted by OpenClaw with user role,
|
|
152
|
+
// but should not be rendered as human-authored bubbles in the WeChat UI.
|
|
153
|
+
return sourceTool === 'sessions_send' && Boolean(targetSessionKey) && sourceSessionKey === targetSessionKey;
|
|
154
|
+
}
|
|
155
|
+
function normalizePreviewMessageText(raw, currentSessionKey) {
|
|
132
156
|
if (!raw || typeof raw !== 'object')
|
|
133
157
|
return normalizeMessageText(raw);
|
|
134
158
|
const roleRaw = getHistoryRoleRaw(raw);
|
|
135
159
|
if (!isRenderableHistoryRole(roleRaw))
|
|
136
160
|
return '';
|
|
161
|
+
if (shouldHideHistoryRow(raw, currentSessionKey))
|
|
162
|
+
return '';
|
|
137
163
|
return normalizeMessageText(raw);
|
|
138
164
|
}
|
|
139
|
-
function
|
|
165
|
+
function isInternalSessionTitle(raw) {
|
|
166
|
+
const title = String(raw || '').trim();
|
|
167
|
+
if (!title)
|
|
168
|
+
return true;
|
|
169
|
+
const lower = title.toLowerCase();
|
|
170
|
+
if (lower === 'openclaw-wechat-plugin'
|
|
171
|
+
|| lower === 'openclaw-vchat-plugin'
|
|
172
|
+
|| lower === 'openclaw-vchat'
|
|
173
|
+
|| lower === 'openclaw-tui') {
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
return (lower.startsWith('webchat:')
|
|
177
|
+
|| lower.startsWith('agent:')
|
|
178
|
+
|| lower.includes('wechat:direct:')
|
|
179
|
+
|| lower.includes('g-agent-')
|
|
180
|
+
|| /^[a-f0-9-]{24,}$/i.test(title));
|
|
181
|
+
}
|
|
182
|
+
function pickVisibleSessionTitle(row, lastMessage) {
|
|
183
|
+
const candidates = [row?.title, row?.displayName, row?.name];
|
|
184
|
+
for (const candidate of candidates) {
|
|
185
|
+
const title = String(candidate || '').trim();
|
|
186
|
+
if (!title || isInternalSessionTitle(title))
|
|
187
|
+
continue;
|
|
188
|
+
return title;
|
|
189
|
+
}
|
|
190
|
+
return String(lastMessage || '').trim() || '新对话';
|
|
191
|
+
}
|
|
192
|
+
function normalizeHistoryMessages(rawPayload, sessionId, limit, before, currentSessionKey) {
|
|
140
193
|
const payload = rawPayload || {};
|
|
141
194
|
const rows = Array.isArray(payload.messages) ? payload.messages
|
|
142
195
|
: Array.isArray(payload.items) ? payload.items
|
|
@@ -144,6 +197,8 @@ function normalizeHistoryMessages(rawPayload, sessionId, limit, before) {
|
|
|
144
197
|
: Array.isArray(payload.entries) ? payload.entries
|
|
145
198
|
: [];
|
|
146
199
|
const mapped = rows.map((row) => {
|
|
200
|
+
if (shouldHideHistoryRow(row, currentSessionKey))
|
|
201
|
+
return null;
|
|
147
202
|
const roleRaw = getHistoryRoleRaw(row) || 'assistant';
|
|
148
203
|
if (!isRenderableHistoryRole(roleRaw))
|
|
149
204
|
return null;
|
|
@@ -170,12 +225,91 @@ function normalizeHistoryMessages(rawPayload, sessionId, limit, before) {
|
|
|
170
225
|
return filtered;
|
|
171
226
|
return filtered.slice(filtered.length - limit);
|
|
172
227
|
}
|
|
228
|
+
function readConfiguredPrimaryModel(gateway) {
|
|
229
|
+
try {
|
|
230
|
+
const raw = gateway.readConfig();
|
|
231
|
+
const agents = Array.isArray(raw?.agents?.list) ? raw.agents.list : [];
|
|
232
|
+
const mainAgent = agents.find((item) => String(item?.id || '').trim() === 'main');
|
|
233
|
+
const candidates = [
|
|
234
|
+
mainAgent?.model,
|
|
235
|
+
raw?.agents?.defaults?.model?.primary,
|
|
236
|
+
raw?.agents?.defaults?.primary,
|
|
237
|
+
raw?.defaults?.primary,
|
|
238
|
+
raw?.model,
|
|
239
|
+
];
|
|
240
|
+
for (const candidate of candidates) {
|
|
241
|
+
const value = String(candidate || '').trim();
|
|
242
|
+
if (value)
|
|
243
|
+
return value;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
// ignore
|
|
248
|
+
}
|
|
249
|
+
return '';
|
|
250
|
+
}
|
|
251
|
+
function readOpenClawVersion() {
|
|
252
|
+
try {
|
|
253
|
+
const value = (0, child_process_1.execFileSync)('openclaw', ['--version'], {
|
|
254
|
+
encoding: 'utf8',
|
|
255
|
+
timeout: 3000,
|
|
256
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
257
|
+
});
|
|
258
|
+
return String(value || '').trim();
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
return '';
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
async function getOpenClawSummary(gateway) {
|
|
265
|
+
const version = readOpenClawVersion();
|
|
266
|
+
try {
|
|
267
|
+
const status = await gateway.call('status', {});
|
|
268
|
+
const recent = status?.sessions?.recent?.[0];
|
|
269
|
+
const model = String(status?.sessions?.defaults?.model
|
|
270
|
+
|| recent?.model
|
|
271
|
+
|| readConfiguredPrimaryModel(gateway)
|
|
272
|
+
|| '').trim();
|
|
273
|
+
return {
|
|
274
|
+
online: true,
|
|
275
|
+
version,
|
|
276
|
+
model,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
catch (err) {
|
|
280
|
+
return {
|
|
281
|
+
online: false,
|
|
282
|
+
version,
|
|
283
|
+
model: readConfiguredPrimaryModel(gateway),
|
|
284
|
+
error: err?.message || 'gateway-unavailable',
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
function buildGatewayRecentModelHints(statusPayload) {
|
|
289
|
+
const hints = new Map();
|
|
290
|
+
const recent = Array.isArray(statusPayload?.sessions?.recent) ? statusPayload.sessions.recent : [];
|
|
291
|
+
for (const row of recent) {
|
|
292
|
+
const model = String(row?.model || '').trim();
|
|
293
|
+
if (!model)
|
|
294
|
+
continue;
|
|
295
|
+
const key = String(row?.key || row?.sessionKey || '').trim().toLowerCase();
|
|
296
|
+
if (key) {
|
|
297
|
+
hints.set(key, model);
|
|
298
|
+
const parsed = (0, session_key_1.parseWeChatDirectThreadSessionKey)(key);
|
|
299
|
+
if (parsed) {
|
|
300
|
+
hints.set(`${parsed.userId}:${parsed.agentId}:${parsed.sessionId}`, model);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return hints;
|
|
305
|
+
}
|
|
173
306
|
async function listGatewayWechatSessions(gateway, userId, agentId) {
|
|
174
307
|
const targetUserId = (0, session_key_1.sanitizeWeChatId)(userId, 'unknown');
|
|
175
308
|
const targetAgentId = (0, session_key_1.sanitizeWeChatId)(agentId, '');
|
|
176
309
|
const response = await gateway.call('sessions.list', {});
|
|
177
310
|
const rawSessions = Array.isArray(response?.sessions) ? response.sessions : [];
|
|
178
311
|
const now = Date.now();
|
|
312
|
+
let recentModelHints = null;
|
|
179
313
|
const rows = [];
|
|
180
314
|
for (const row of rawSessions) {
|
|
181
315
|
const key = String(row?.key || row?.sessionKey || row?.id || '').trim().toLowerCase();
|
|
@@ -188,19 +322,37 @@ async function listGatewayWechatSessions(gateway, userId, agentId) {
|
|
|
188
322
|
continue;
|
|
189
323
|
if (targetAgentId && parsed.agentId !== targetAgentId)
|
|
190
324
|
continue;
|
|
191
|
-
const lastMessage = normalizePreviewMessageText(row?.lastMessage || row?.preview || row?.summary || '');
|
|
192
|
-
const title =
|
|
325
|
+
const lastMessage = normalizePreviewMessageText(row?.lastMessage || row?.preview || row?.summary || '', key);
|
|
326
|
+
const title = pickVisibleSessionTitle(row, lastMessage);
|
|
193
327
|
const createdAt = parseTimestamp(row?.createdAt) || pickTimestamp(row, now);
|
|
194
328
|
const lastMessageTime = pickTimestamp(row, createdAt || now);
|
|
329
|
+
const modelProvider = String(row?.modelProvider || '').trim();
|
|
330
|
+
const modelId = String(row?.model || '').trim();
|
|
331
|
+
let model = modelProvider && modelId ? `${modelProvider}/${modelId}` : modelId;
|
|
332
|
+
if (!model) {
|
|
333
|
+
if (!recentModelHints) {
|
|
334
|
+
try {
|
|
335
|
+
const status = await gateway.call('status', {});
|
|
336
|
+
recentModelHints = buildGatewayRecentModelHints(status);
|
|
337
|
+
}
|
|
338
|
+
catch {
|
|
339
|
+
recentModelHints = new Map();
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
model = recentModelHints.get(key)
|
|
343
|
+
|| recentModelHints.get(`${parsed.userId}:${parsed.agentId}:${parsed.sessionId}`)
|
|
344
|
+
|| '';
|
|
345
|
+
}
|
|
195
346
|
rows.push({
|
|
196
347
|
id: parsed.sessionId,
|
|
197
348
|
userId: targetUserId,
|
|
198
349
|
agentId: parsed.agentId,
|
|
199
350
|
gatewaySessionKey: key,
|
|
200
|
-
title
|
|
351
|
+
title,
|
|
201
352
|
lastMessage,
|
|
202
353
|
lastMessageTime,
|
|
203
354
|
createdAt: createdAt || lastMessageTime || now,
|
|
355
|
+
model,
|
|
204
356
|
});
|
|
205
357
|
}
|
|
206
358
|
return rows.sort((a, b) => b.lastMessageTime - a.lastMessageTime);
|
|
@@ -233,7 +385,7 @@ async function callGatewayHistory(gateway, key, sessionId, limit, before) {
|
|
|
233
385
|
for (const params of attempts) {
|
|
234
386
|
try {
|
|
235
387
|
const result = await gateway.call('chat.history', params);
|
|
236
|
-
const messages = normalizeHistoryMessages(result, sessionId, limit, before);
|
|
388
|
+
const messages = normalizeHistoryMessages(result, sessionId, limit, before, key);
|
|
237
389
|
const hasMore = Boolean(result?.hasMore) || messages.length === limit;
|
|
238
390
|
return { messages, hasMore };
|
|
239
391
|
}
|
|
@@ -270,11 +422,13 @@ function createRelayServer(config, gateway) {
|
|
|
270
422
|
const app = (0, express_1.default)();
|
|
271
423
|
const dataDir = path_1.default.join(config.uploadDir, '..');
|
|
272
424
|
const sessionManager = new session_manager_1.SessionManager(dataDir);
|
|
425
|
+
const groupEventStore = new group_event_store_1.GroupEventStore(dataDir);
|
|
273
426
|
const messageHandler = new message_handler_1.MessageHandler(config, sessionManager, gateway);
|
|
274
427
|
const mediaHandler = new media_handler_1.MediaHandler(config);
|
|
275
428
|
const groupManager = new group_manager_1.GroupManager(dataDir);
|
|
276
429
|
const memoSessions = createRequestMemo(1000);
|
|
277
430
|
const memoGroups = createRequestMemo(1000);
|
|
431
|
+
const memoSkills = createRequestMemo(1500);
|
|
278
432
|
const memoHistory = createRequestMemo(800);
|
|
279
433
|
// ===== 中间件 =====
|
|
280
434
|
app.use((0, cors_1.default)());
|
|
@@ -325,12 +479,14 @@ function createRelayServer(config, gateway) {
|
|
|
325
479
|
// ===== 所有内部路由需要密钥验证 =====
|
|
326
480
|
const internal = express_1.default.Router();
|
|
327
481
|
internal.use(internalAuth);
|
|
328
|
-
internal.get('/bridge/capabilities', (_req, res) => {
|
|
482
|
+
internal.get('/bridge/capabilities', async (_req, res) => {
|
|
483
|
+
const openclaw = await getOpenClawSummary(gateway);
|
|
329
484
|
res.json({
|
|
330
485
|
protocolVersion: constants_1.BRIDGE_PROTOCOL_VERSION,
|
|
331
486
|
pluginVersion: constants_1.BRIDGE_PLUGIN_VERSION,
|
|
332
487
|
capabilities: Array.from(constants_1.BRIDGE_CAPABILITIES),
|
|
333
488
|
bridgeRole: 'gateway-bridge',
|
|
489
|
+
openclaw,
|
|
334
490
|
runtime: {
|
|
335
491
|
persistMessagesEnabled: sessionManager.isPersistMessagesEnabled(),
|
|
336
492
|
localFallbackEnabled: Boolean(config.allowLocalFallback),
|
|
@@ -365,6 +521,7 @@ function createRelayServer(config, gateway) {
|
|
|
365
521
|
lastMessageTime: item.lastMessageTime || item.createdAt || Date.now(),
|
|
366
522
|
createdAt: local?.createdAt || item.createdAt,
|
|
367
523
|
agentId: item.agentId,
|
|
524
|
+
model: item.model,
|
|
368
525
|
});
|
|
369
526
|
}).sort((a, b) => b.lastMessageTime - a.lastMessageTime);
|
|
370
527
|
sessionManager.pruneSessions(userId, sessions.map(item => item.id), normalizedAgentId);
|
|
@@ -579,6 +736,19 @@ function createRelayServer(config, gateway) {
|
|
|
579
736
|
.then(payload => res.json(payload))
|
|
580
737
|
.catch((err) => res.status(500).json({ error: err?.message || '群列表获取失败' }));
|
|
581
738
|
});
|
|
739
|
+
internal.get('/groups/:id/history', (req, res) => {
|
|
740
|
+
try {
|
|
741
|
+
const limit = parseInt(String(req.query.limit || ''), 10) || 100;
|
|
742
|
+
const before = req.query.before ? parseInt(String(req.query.before), 10) : undefined;
|
|
743
|
+
res.json({
|
|
744
|
+
messages: groupEventStore.list(req.params.id, limit, before),
|
|
745
|
+
source: 'local-group-events',
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
catch (err) {
|
|
749
|
+
res.status(500).json({ error: err?.message || '群历史获取失败' });
|
|
750
|
+
}
|
|
751
|
+
});
|
|
582
752
|
internal.post('/groups', (req, res) => {
|
|
583
753
|
const { name, emoji, agentIds, mode } = req.body;
|
|
584
754
|
if (!name) {
|
|
@@ -610,6 +780,96 @@ function createRelayServer(config, gateway) {
|
|
|
610
780
|
const group = groupManager.removeAgent(req.params.id, req.params.agentId);
|
|
611
781
|
group ? res.json(group) : res.status(404).json({ error: '群不存在' });
|
|
612
782
|
});
|
|
783
|
+
// ===== Skills API =====
|
|
784
|
+
internal.get('/skills', async (req, res) => {
|
|
785
|
+
try {
|
|
786
|
+
const agentId = typeof req.query.agentId === 'string' ? req.query.agentId : '';
|
|
787
|
+
const memoKey = `skills:${String(agentId || '*').trim() || '*'}`;
|
|
788
|
+
const payload = await memoSkills(memoKey, async () => {
|
|
789
|
+
const status = await (0, skills_service_1.getSkillsStatus)(gateway, agentId);
|
|
790
|
+
return {
|
|
791
|
+
...status,
|
|
792
|
+
source: 'gateway',
|
|
793
|
+
};
|
|
794
|
+
});
|
|
795
|
+
res.json(payload);
|
|
796
|
+
}
|
|
797
|
+
catch (err) {
|
|
798
|
+
res.status(500).json({ error: err?.message || '获取 Skills 列表失败' });
|
|
799
|
+
}
|
|
800
|
+
});
|
|
801
|
+
internal.post('/skills/install', async (req, res) => {
|
|
802
|
+
try {
|
|
803
|
+
const result = await (0, skills_service_1.installSkill)(gateway, {
|
|
804
|
+
name: typeof req.body?.name === 'string' ? req.body.name : '',
|
|
805
|
+
installId: typeof req.body?.installId === 'string' ? req.body.installId : '',
|
|
806
|
+
timeoutMs: req.body?.timeoutMs,
|
|
807
|
+
});
|
|
808
|
+
res.json(result);
|
|
809
|
+
}
|
|
810
|
+
catch (err) {
|
|
811
|
+
res.status(500).json({ error: err?.message || '安装 Skill 失败' });
|
|
812
|
+
}
|
|
813
|
+
});
|
|
814
|
+
internal.post('/skills/:skillKey/update', async (req, res) => {
|
|
815
|
+
try {
|
|
816
|
+
const skillKey = String(req.params.skillKey || '').trim();
|
|
817
|
+
if (!skillKey) {
|
|
818
|
+
res.status(400).json({ error: '缺少 skillKey' });
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
const payload = {
|
|
822
|
+
enabled: typeof req.body?.enabled === 'boolean' ? req.body.enabled : undefined,
|
|
823
|
+
apiKey: typeof req.body?.apiKey === 'string' ? req.body.apiKey : undefined,
|
|
824
|
+
env: req.body?.env && typeof req.body.env === 'object' ? req.body.env : undefined,
|
|
825
|
+
};
|
|
826
|
+
try {
|
|
827
|
+
const result = await (0, skills_service_1.updateSkill)(gateway, skillKey, payload);
|
|
828
|
+
res.json(result);
|
|
829
|
+
}
|
|
830
|
+
catch (err) {
|
|
831
|
+
if (typeof payload.enabled !== 'boolean')
|
|
832
|
+
throw err;
|
|
833
|
+
const fallback = (0, skills_service_1.setSkillEnabledLocally)(gateway, skillKey, payload.enabled);
|
|
834
|
+
res.json({
|
|
835
|
+
...fallback,
|
|
836
|
+
source: 'local-config',
|
|
837
|
+
warning: err?.message || 'Gateway skills.update 失败,已改为本地配置写入',
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
catch (err) {
|
|
842
|
+
res.status(500).json({ error: err?.message || '更新 Skill 状态失败' });
|
|
843
|
+
}
|
|
844
|
+
});
|
|
845
|
+
// ===== Plugin update =====
|
|
846
|
+
internal.get('/plugin/update/info', async (_req, res) => {
|
|
847
|
+
try {
|
|
848
|
+
const info = await (0, plugin_update_service_1.checkPluginUpdateInfo)();
|
|
849
|
+
res.json(info);
|
|
850
|
+
}
|
|
851
|
+
catch (err) {
|
|
852
|
+
res.status(500).json({ error: err?.message || '检查插件更新失败' });
|
|
853
|
+
}
|
|
854
|
+
});
|
|
855
|
+
internal.get('/plugin/update/status', (_req, res) => {
|
|
856
|
+
try {
|
|
857
|
+
res.json((0, plugin_update_service_1.readPluginUpdateState)());
|
|
858
|
+
}
|
|
859
|
+
catch (err) {
|
|
860
|
+
res.status(500).json({ error: err?.message || '获取插件更新状态失败' });
|
|
861
|
+
}
|
|
862
|
+
});
|
|
863
|
+
internal.post('/plugin/update/start', async (req, res) => {
|
|
864
|
+
try {
|
|
865
|
+
const targetVersion = typeof req.body?.version === 'string' ? req.body.version : '';
|
|
866
|
+
const result = await (0, plugin_update_service_1.startPluginUpdate)(targetVersion);
|
|
867
|
+
res.json(result);
|
|
868
|
+
}
|
|
869
|
+
catch (err) {
|
|
870
|
+
res.status(400).json({ error: err?.message || '启动插件更新失败' });
|
|
871
|
+
}
|
|
872
|
+
});
|
|
613
873
|
// ===== 聊天 =====
|
|
614
874
|
internal.get('/chat/history', async (req, res) => {
|
|
615
875
|
logBridgeRequest(req, 'chat.history');
|
|
@@ -853,7 +1113,7 @@ function createRelayServer(config, gateway) {
|
|
|
853
1113
|
console.error('[WS] 错误:', err.message);
|
|
854
1114
|
});
|
|
855
1115
|
});
|
|
856
|
-
return { app, server, sessionManager, wss };
|
|
1116
|
+
return { app, server, sessionManager, groupEventStore, wss };
|
|
857
1117
|
}
|
|
858
1118
|
// ===== WebSocket 处理函数 =====
|
|
859
1119
|
function wsSend(ws, data) {
|