@vrs-soft/wecom-aibot-mcp 3.4.9-beta.0 → 3.4.9-beta.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/channel-server.js +38 -6
- package/package.json +1 -1
package/dist/channel-server.js
CHANGED
|
@@ -436,13 +436,32 @@ function connectSSE(ccId) {
|
|
|
436
436
|
sseWatchdogActive = true; // v3.4.3
|
|
437
437
|
// v3.4.5: 每 10s 主动 ping daemon,证明客户端还活着 → 刷 lastOnline
|
|
438
438
|
// 没 ping daemon 端 30s 后视为客户端死亡,立刻 unregister(v1.3.12 daemon 端机制)
|
|
439
|
+
// v3.4.9-beta.2: ping 收到非 2xx(含 404 "ccId not registered")→ abort SSE 触发重连链
|
|
440
|
+
// 修复"SSE 假死但 daemon 已 unregister,CC 端不知情继续飘着"的漏洞
|
|
439
441
|
if (ccId) {
|
|
440
|
-
pingTimer = setInterval(() => {
|
|
442
|
+
pingTimer = setInterval(async () => {
|
|
441
443
|
const pingUrl = `${MCP_URL}/cc/${encodeURIComponent(ccId)}/ping`;
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
444
|
+
try {
|
|
445
|
+
const res = await fetch(pingUrl, {
|
|
446
|
+
method: 'POST',
|
|
447
|
+
headers: { 'Content-Type': 'application/json', ...getAuthHeaders() },
|
|
448
|
+
});
|
|
449
|
+
if (!res.ok) {
|
|
450
|
+
logger.warn?.('ping 收到非 2xx,abort SSE 触发重连', { ccId, status: res.status });
|
|
451
|
+
try {
|
|
452
|
+
sseAbortController?.abort();
|
|
453
|
+
}
|
|
454
|
+
catch { /* ignore */ }
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
catch (err) {
|
|
458
|
+
logChannel('ping daemon failed', { error: String(err) });
|
|
459
|
+
// 网络错误也 abort 触发重连
|
|
460
|
+
try {
|
|
461
|
+
sseAbortController?.abort();
|
|
462
|
+
}
|
|
463
|
+
catch { /* ignore */ }
|
|
464
|
+
}
|
|
446
465
|
}, 10000);
|
|
447
466
|
}
|
|
448
467
|
while (true) {
|
|
@@ -1097,6 +1116,11 @@ function registerChannelTools(server) {
|
|
|
1097
1116
|
project_dir: z.string().optional().describe('项目目录路径(用于更新配置文件)'),
|
|
1098
1117
|
}, async ({ cc_id, project_dir }) => {
|
|
1099
1118
|
const localProjectDir = project_dir || process.cwd();
|
|
1119
|
+
// v3.4.9-beta.2: 顺序改成"先转发 daemon exit,再 abort SSE"
|
|
1120
|
+
// 否则 mcp 端 abort SSE → daemon 端 SSE close 先触发 unregisterCcId 走 grace period(不是 instant)
|
|
1121
|
+
// → daemon 端 exit_headless_mode 走 error 分支(getConnectedClient 找不到 ccId 了)
|
|
1122
|
+
// 结果:instant 释放 robot 的分支永远进不去
|
|
1123
|
+
const result = await forwardToHttpMcp('exit_headless_mode', { cc_id, project_dir: localProjectDir });
|
|
1100
1124
|
// 断开 SSE 连接(abort 后重连逻辑不会触发)
|
|
1101
1125
|
if (sseAbortController) {
|
|
1102
1126
|
sseAbortController.abort();
|
|
@@ -1108,7 +1132,15 @@ function registerChannelTools(server) {
|
|
|
1108
1132
|
// 注销本地 active-projects 记录
|
|
1109
1133
|
unregisterActiveProject(localProjectDir);
|
|
1110
1134
|
logChannel('本地 active-projects 已注销', { projectDir: localProjectDir });
|
|
1111
|
-
|
|
1135
|
+
// v3.4.9-beta.1: 关闭本地 wecom-aibot.json 的 wechatMode 开关(治本)
|
|
1136
|
+
// 否则 stop-hook 读到 wechatMode:true 会阻塞 stop 并发 stderr 提示"恢复轮询",
|
|
1137
|
+
// LLM 看到提示又调 enter_headless_mode → CC 被"自动复活"永远退不掉
|
|
1138
|
+
updateWechatModeConfig(localProjectDir, {
|
|
1139
|
+
wechatMode: false,
|
|
1140
|
+
heartbeatJobId: undefined,
|
|
1141
|
+
});
|
|
1142
|
+
logger.info('本地 wecom-aibot.json wechatMode 已置 false', { projectDir: localProjectDir });
|
|
1143
|
+
return result;
|
|
1112
1144
|
});
|
|
1113
1145
|
// ============================================
|
|
1114
1146
|
// 工具 11: 从消息识别用户
|