@vrs-soft/wecom-aibot-mcp 1.0.7 → 1.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/bin.d.ts +5 -0
- package/dist/bin.js +253 -76
- package/dist/client.d.ts +6 -0
- package/dist/client.js +17 -6
- package/dist/config-wizard.d.ts +10 -0
- package/dist/config-wizard.js +81 -66
- package/dist/connection-log.d.ts +36 -0
- package/dist/connection-log.js +210 -0
- package/dist/connection-manager.d.ts +62 -0
- package/dist/connection-manager.js +245 -0
- package/dist/headless-state.d.ts +40 -38
- package/dist/headless-state.js +136 -139
- package/dist/http-server.d.ts +1 -16
- package/dist/http-server.js +196 -319
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/keepalive-monitor.d.ts +19 -0
- package/dist/keepalive-monitor.js +98 -0
- package/dist/tools/index.d.ts +1 -2
- package/dist/tools/index.js +286 -292
- package/package.json +1 -1
- package/skills/headless-mode/SKILL.md +94 -129
package/dist/bin.d.ts
CHANGED
package/dist/bin.js
CHANGED
|
@@ -7,30 +7,26 @@
|
|
|
7
7
|
* 支持 HTTP Transport 模式:
|
|
8
8
|
* - 固定端口 18963
|
|
9
9
|
* - 支持多 Claude Code 同时连接
|
|
10
|
+
*
|
|
11
|
+
* 连接管理:
|
|
12
|
+
* - 启动时不建立 WebSocket 连接
|
|
13
|
+
* - enter_headless_mode 时按需建立连接
|
|
14
|
+
* - exit_headless_mode 时断开连接
|
|
10
15
|
*/
|
|
11
|
-
import {
|
|
16
|
+
import { spawn } from 'child_process';
|
|
17
|
+
import * as fs from 'fs';
|
|
18
|
+
import * as path from 'path';
|
|
19
|
+
import * as os from 'os';
|
|
20
|
+
import { runConfigWizard, loadConfig, saveConfig, deleteConfig, deleteMcpConfigInteractive, uninstall, addMcpConfig, detectUserIdFromMessage, ensureHookInstalled, listAllRobots, } from './config-wizard.js';
|
|
12
21
|
import { initClient } from './client.js';
|
|
13
22
|
import { registerTools } from './tools/index.js';
|
|
14
|
-
import { startHttpServer, HTTP_PORT } from './http-server.js';
|
|
23
|
+
import { startHttpServer, stopHttpServer, HTTP_PORT } from './http-server.js';
|
|
15
24
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
16
|
-
import { clearAllProjectHooks } from './headless-state.js';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const startTime = Date.now();
|
|
22
|
-
const checkInterval = setInterval(() => {
|
|
23
|
-
if (client.isConnected()) {
|
|
24
|
-
clearInterval(checkInterval);
|
|
25
|
-
resolve(true);
|
|
26
|
-
}
|
|
27
|
-
else if (Date.now() - startTime > timeoutMs) {
|
|
28
|
-
clearInterval(checkInterval);
|
|
29
|
-
resolve(false);
|
|
30
|
-
}
|
|
31
|
-
}, 500);
|
|
32
|
-
});
|
|
33
|
-
}
|
|
25
|
+
import { clearAllProjectHooks, getAllHeadlessStates } from './headless-state.js';
|
|
26
|
+
import { loadStats, cleanupOldLogs } from './connection-log.js';
|
|
27
|
+
import { startKeepaliveMonitor, stopKeepaliveMonitor } from './keepalive-monitor.js';
|
|
28
|
+
const VERSION = '1.0.9';
|
|
29
|
+
const PID_FILE = path.join(os.homedir(), '.wecom-aibot-mcp', 'server.pid');
|
|
34
30
|
function showHelp() {
|
|
35
31
|
console.log(`
|
|
36
32
|
企业微信智能机器人 MCP 服务 v${VERSION}
|
|
@@ -44,12 +40,27 @@ function showHelp() {
|
|
|
44
40
|
选项:
|
|
45
41
|
--help, -h 显示帮助信息
|
|
46
42
|
--version, -v 显示版本号
|
|
43
|
+
--start 启动 MCP Server(后台服务模式)
|
|
44
|
+
--stop 停止 MCP Server
|
|
45
|
+
--status 显示服务状态和机器人配置
|
|
47
46
|
--config 重新配置默认机器人(修改 Bot ID / Secret / 目标用户)
|
|
48
47
|
--add 添加新的机器人配置(多机器人场景)
|
|
48
|
+
--list 列出所有已配置的机器人及其占用状态
|
|
49
49
|
--delete [名称] 删除指定的机器人配置(无参数则显示列表选择)
|
|
50
|
-
--status 显示当前配置状态
|
|
51
50
|
--uninstall 卸载并删除所有配置(包括 MCP 配置、hook、skill)
|
|
52
51
|
|
|
52
|
+
使用流程:
|
|
53
|
+
1. 首次安装: npx @vrs-soft/wecom-aibot-mcp
|
|
54
|
+
(进入配置向导,完成后自动后台启动服务)
|
|
55
|
+
|
|
56
|
+
2. 已有配置: npx @vrs-soft/wecom-aibot-mcp
|
|
57
|
+
(显示状态,提示使用 --start 启动)
|
|
58
|
+
|
|
59
|
+
3. 启动服务: npx @vrs-soft/wecom-aibot-mcp --start
|
|
60
|
+
(后台启动 MCP HTTP Server)
|
|
61
|
+
|
|
62
|
+
4. 停止服务: npx @vrs-soft/wecom-aibot-mcp --stop
|
|
63
|
+
|
|
53
64
|
MCP 配置(HTTP Transport):
|
|
54
65
|
|
|
55
66
|
编辑 ~/.claude.json:
|
|
@@ -58,7 +69,7 @@ MCP 配置(HTTP Transport):
|
|
|
58
69
|
"mcpServers": {
|
|
59
70
|
"wecom-aibot": {
|
|
60
71
|
"type": "http",
|
|
61
|
-
"url": "http://127.0.0.1
|
|
72
|
+
"url": "http://127.0.0.1:18963/mcp"
|
|
62
73
|
}
|
|
63
74
|
}
|
|
64
75
|
}
|
|
@@ -80,16 +91,179 @@ function showVersion() {
|
|
|
80
91
|
console.log(`wecom-aibot-mcp v${VERSION}`);
|
|
81
92
|
}
|
|
82
93
|
function showStatus() {
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
94
|
+
const allRobots = listAllRobots();
|
|
95
|
+
const headlessStates = getAllHeadlessStates();
|
|
96
|
+
// 检查服务是否运行
|
|
97
|
+
const serverRunning = isServerRunning();
|
|
98
|
+
console.log(`\n服务状态: ${serverRunning ? '✅ 运行中' : '❌ 未启动'}\n`);
|
|
99
|
+
if (allRobots.length === 0) {
|
|
100
|
+
console.log('尚未配置机器人,请运行 npx @vrs-soft/wecom-aibot-mcp 启动配置向导');
|
|
101
|
+
return;
|
|
89
102
|
}
|
|
90
|
-
|
|
91
|
-
|
|
103
|
+
// 构建机器人占用信息
|
|
104
|
+
const robotUsage = new Map();
|
|
105
|
+
for (const { state } of headlessStates) {
|
|
106
|
+
if (state.robotName) {
|
|
107
|
+
robotUsage.set(state.robotName, {
|
|
108
|
+
agentName: state.agentName || '未知',
|
|
109
|
+
projectDir: state.projectDir,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
console.log(`已配置 ${allRobots.length} 个机器人:\n`);
|
|
114
|
+
for (const robot of allRobots) {
|
|
115
|
+
const defaultTag = robot.isDefault ? ' (默认)' : '';
|
|
116
|
+
const usage = robotUsage.get(robot.name);
|
|
117
|
+
const statusTag = usage ? ` [使用中]` : '';
|
|
118
|
+
console.log(` ${robot.name}${defaultTag}${statusTag}`);
|
|
119
|
+
console.log(` Bot ID: ${robot.botId}`);
|
|
120
|
+
console.log(` 目标用户: ${robot.targetUserId}`);
|
|
121
|
+
if (usage) {
|
|
122
|
+
console.log(` 使用者: ${usage.agentName} (${usage.projectDir})`);
|
|
123
|
+
}
|
|
124
|
+
console.log('');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// 检查服务是否运行
|
|
128
|
+
function isServerRunning() {
|
|
129
|
+
if (!fs.existsSync(PID_FILE)) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8').trim());
|
|
134
|
+
// 检查进程是否存在
|
|
135
|
+
process.kill(pid, 0);
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// 进程不存在,清理 PID 文件
|
|
140
|
+
fs.unlinkSync(PID_FILE);
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// 停止服务
|
|
145
|
+
function stopServer() {
|
|
146
|
+
if (!fs.existsSync(PID_FILE)) {
|
|
147
|
+
console.log('[mcp] 服务未运行');
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8').trim());
|
|
152
|
+
process.kill(pid, 'SIGTERM');
|
|
153
|
+
// 等待进程退出
|
|
154
|
+
let attempts = 0;
|
|
155
|
+
while (attempts < 10) {
|
|
156
|
+
try {
|
|
157
|
+
process.kill(pid, 0);
|
|
158
|
+
// 进程还存在,等待
|
|
159
|
+
setTimeout(() => { }, 500);
|
|
160
|
+
attempts++;
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
// 进程已退出
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
fs.unlinkSync(PID_FILE);
|
|
168
|
+
console.log('[mcp] 服务已停止');
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
console.error('[mcp] 停止服务失败:', err);
|
|
173
|
+
fs.unlinkSync(PID_FILE);
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// 等待连接验证(用于配置向导验证凭证)
|
|
178
|
+
async function waitForConnection(client, timeoutMs = 10000) {
|
|
179
|
+
return new Promise((resolve) => {
|
|
180
|
+
const startTime = Date.now();
|
|
181
|
+
const checkInterval = setInterval(() => {
|
|
182
|
+
if (client.isConnected()) {
|
|
183
|
+
clearInterval(checkInterval);
|
|
184
|
+
resolve(true);
|
|
185
|
+
}
|
|
186
|
+
else if (Date.now() - startTime > timeoutMs) {
|
|
187
|
+
clearInterval(checkInterval);
|
|
188
|
+
resolve(false);
|
|
189
|
+
}
|
|
190
|
+
}, 500);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
// 启动 MCP Server(前台运行,供 --start 使用)
|
|
194
|
+
async function startMcpServerForeground() {
|
|
195
|
+
const savedConfig = loadConfig();
|
|
196
|
+
if (!savedConfig || !savedConfig.botId || !savedConfig.secret || !savedConfig.targetUserId) {
|
|
197
|
+
console.error('[mcp] 未找到配置,请先运行: npx @vrs-soft/wecom-aibot-mcp');
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
// 写入 PID 文件
|
|
201
|
+
fs.writeFileSync(PID_FILE, String(process.pid));
|
|
202
|
+
// 确保 hook 已安装
|
|
203
|
+
ensureHookInstalled();
|
|
204
|
+
// 清理残留的 headless 状态
|
|
205
|
+
clearAllProjectHooks();
|
|
206
|
+
// 加载统计并清理旧日志
|
|
207
|
+
loadStats();
|
|
208
|
+
cleanupOldLogs(1 / 24);
|
|
209
|
+
// 创建 MCP Server
|
|
210
|
+
const server = new McpServer({
|
|
211
|
+
name: 'wecom-aibot-mcp',
|
|
212
|
+
version: VERSION,
|
|
213
|
+
});
|
|
214
|
+
registerTools(server);
|
|
215
|
+
// 启动 HTTP 服务
|
|
216
|
+
console.log('');
|
|
217
|
+
console.log(' ╔════════════════════════════════════════════════════════╗');
|
|
218
|
+
console.log(` ║ 企业微信智能机器人 MCP 服务 v${VERSION} ║`);
|
|
219
|
+
console.log(' ║ Claude Code 审批通道 ║');
|
|
220
|
+
console.log(' ╚════════════════════════════════════════════════════════╝');
|
|
221
|
+
console.log('');
|
|
222
|
+
console.log(`[mcp] 启动 MCP HTTP Server (端口: ${HTTP_PORT})...`);
|
|
223
|
+
await startHttpServer(server);
|
|
224
|
+
startKeepaliveMonitor();
|
|
225
|
+
console.log(`[mcp] MCP Server 已就绪`);
|
|
226
|
+
console.log(`[mcp] HTTP endpoint: http://127.0.0.1:${HTTP_PORT}/mcp`);
|
|
227
|
+
console.log(`[mcp] 健康检查: http://127.0.0.1:${HTTP_PORT}/health`);
|
|
228
|
+
console.log(`[mcp] 微信模式:enter_headless_mode 时建立连接`);
|
|
229
|
+
console.log(`[mcp] PID: ${process.pid}`);
|
|
230
|
+
// 退出处理
|
|
231
|
+
const gracefulShutdown = () => {
|
|
232
|
+
console.log('[mcp] 正在关闭...');
|
|
233
|
+
stopKeepaliveMonitor();
|
|
234
|
+
stopHttpServer();
|
|
235
|
+
if (fs.existsSync(PID_FILE)) {
|
|
236
|
+
fs.unlinkSync(PID_FILE);
|
|
237
|
+
}
|
|
238
|
+
process.exit(0);
|
|
239
|
+
};
|
|
240
|
+
process.on('SIGINT', gracefulShutdown);
|
|
241
|
+
process.on('SIGTERM', gracefulShutdown);
|
|
242
|
+
}
|
|
243
|
+
// 后台启动 MCP Server(使用 spawn)
|
|
244
|
+
function startMcpServerBackground() {
|
|
245
|
+
// 检查配置是否存在
|
|
246
|
+
const savedConfig = loadConfig();
|
|
247
|
+
if (!savedConfig || !savedConfig.botId || !savedConfig.secret || !savedConfig.targetUserId) {
|
|
248
|
+
console.error('[mcp] 未找到配置,请先运行: npx @vrs-soft/wecom-aibot-mcp');
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
251
|
+
// 检查是否已运行
|
|
252
|
+
if (isServerRunning()) {
|
|
253
|
+
console.log('[mcp] 服务已在运行中');
|
|
254
|
+
return;
|
|
92
255
|
}
|
|
256
|
+
const nodePath = process.execPath;
|
|
257
|
+
const scriptPath = process.argv[1];
|
|
258
|
+
const child = spawn(nodePath, [scriptPath, '--start', '--foreground'], {
|
|
259
|
+
detached: true,
|
|
260
|
+
stdio: 'ignore',
|
|
261
|
+
});
|
|
262
|
+
child.unref();
|
|
263
|
+
console.log('[mcp] MCP Server 已在后台启动');
|
|
264
|
+
console.log(`[mcp] HTTP endpoint: http://127.0.0.1:18963/mcp`);
|
|
265
|
+
console.log('[mcp] 健康检查: curl http://127.0.0.1:18963/health');
|
|
266
|
+
console.log('[mcp] 停止服务: npx @vrs-soft/wecom-aibot-mcp --stop');
|
|
93
267
|
}
|
|
94
268
|
async function main() {
|
|
95
269
|
const args = process.argv.slice(2);
|
|
@@ -102,11 +276,21 @@ async function main() {
|
|
|
102
276
|
showVersion();
|
|
103
277
|
process.exit(0);
|
|
104
278
|
}
|
|
105
|
-
if (args.includes('--status')) {
|
|
279
|
+
if (args.includes('--status') || args.includes('--list')) {
|
|
106
280
|
showStatus();
|
|
107
281
|
process.exit(0);
|
|
108
282
|
}
|
|
283
|
+
// --stop 命令:停止服务
|
|
284
|
+
if (args.includes('--stop')) {
|
|
285
|
+
stopServer();
|
|
286
|
+
process.exit(0);
|
|
287
|
+
}
|
|
288
|
+
// --uninstall 命令:先停止服务再卸载
|
|
109
289
|
if (args.includes('--uninstall')) {
|
|
290
|
+
if (isServerRunning()) {
|
|
291
|
+
console.log('[mcp] 正在停止服务...');
|
|
292
|
+
stopServer();
|
|
293
|
+
}
|
|
110
294
|
uninstall();
|
|
111
295
|
process.exit(0);
|
|
112
296
|
}
|
|
@@ -121,6 +305,16 @@ async function main() {
|
|
|
121
305
|
await deleteMcpConfigInteractive(instanceName);
|
|
122
306
|
process.exit(0);
|
|
123
307
|
}
|
|
308
|
+
// --start --foreground:前台启动(内部调用)
|
|
309
|
+
if (args.includes('--start') && args.includes('--foreground')) {
|
|
310
|
+
await startMcpServerForeground();
|
|
311
|
+
return; // 保持运行,不 exit
|
|
312
|
+
}
|
|
313
|
+
// --start:后台启动
|
|
314
|
+
if (args.includes('--start')) {
|
|
315
|
+
startMcpServerBackground();
|
|
316
|
+
process.exit(0);
|
|
317
|
+
}
|
|
124
318
|
const reconfig = args.includes('--config');
|
|
125
319
|
const isInteractive = process.stdin.isTTY; // 是否为用户交互模式
|
|
126
320
|
console.log('');
|
|
@@ -129,6 +323,9 @@ async function main() {
|
|
|
129
323
|
console.log(' ║ Claude Code 审批通道 ║');
|
|
130
324
|
console.log(' ╚════════════════════════════════════════════════════════╝');
|
|
131
325
|
console.log('');
|
|
326
|
+
// 加载统计并清理旧日志(保留 1 小时)
|
|
327
|
+
loadStats();
|
|
328
|
+
cleanupOldLogs(1 / 24);
|
|
132
329
|
// 获取或初始化配置
|
|
133
330
|
let config;
|
|
134
331
|
let ranWizard = false; // 是否运行了配置向导
|
|
@@ -165,35 +362,33 @@ async function main() {
|
|
|
165
362
|
ensureHookInstalled();
|
|
166
363
|
// 清理残留的 headless 状态和 Hook 配置
|
|
167
364
|
clearAllProjectHooks();
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (isInteractive) {
|
|
365
|
+
// 配置向导模式:验证连接并识别用户 ID
|
|
366
|
+
if (isInteractive && (ranWizard || reconfig)) {
|
|
367
|
+
console.log('[mcp] 验证机器人连接...');
|
|
368
|
+
// 临时建立连接验证凭证
|
|
369
|
+
const tempClient = initClient(config.botId, config.secret, config.targetUserId || 'placeholder');
|
|
370
|
+
const connected = await waitForConnection(tempClient, 10000);
|
|
371
|
+
if (!connected) {
|
|
372
|
+
console.log('[mcp] 连接失败,可能是配置错误或机器人未授权');
|
|
373
|
+
console.log('[mcp] 请检查上面的错误提示,修复后重新配置');
|
|
374
|
+
// 删除无效配置,让用户重新输入
|
|
375
|
+
deleteConfig();
|
|
180
376
|
console.log('\n请检查:');
|
|
181
377
|
console.log(' 1. Bot ID 和 Secret 是否正确');
|
|
182
378
|
console.log(' 2. 新建机器人需等待约 2 分钟同步');
|
|
183
379
|
console.log(' 3. 是否已完成授权(机器人详情 → 可使用权限 → 授权)');
|
|
184
380
|
console.log('\n修复后重新运行: npx @vrs-soft/wecom-aibot-mcp --config');
|
|
381
|
+
tempClient.disconnect();
|
|
382
|
+
process.exit(1);
|
|
185
383
|
}
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
// 连接成功
|
|
189
|
-
if (isInteractive && (ranWizard || reconfig)) {
|
|
190
|
-
// 需要通过消息自动识别用户 ID
|
|
384
|
+
// 连接成功
|
|
191
385
|
console.log('\n[mcp] ✅ 机器人连接成功!');
|
|
192
386
|
// 提示用户发送消息来识别用户 ID
|
|
193
|
-
const userId = await detectUserIdFromMessage(
|
|
387
|
+
const userId = await detectUserIdFromMessage(tempClient, 180);
|
|
194
388
|
if (!userId) {
|
|
195
389
|
console.log('\n[mcp] 未能在规定时间内识别用户 ID');
|
|
196
390
|
console.log('[mcp] 请重新运行配置:npx @vrs-soft/wecom-aibot-mcp --config');
|
|
391
|
+
tempClient.disconnect();
|
|
197
392
|
process.exit(1);
|
|
198
393
|
}
|
|
199
394
|
// 更新配置中的用户 ID
|
|
@@ -202,36 +397,18 @@ async function main() {
|
|
|
202
397
|
saveConfig(config, instanceName);
|
|
203
398
|
console.log('\n[mcp] ✅ 配置完成!');
|
|
204
399
|
console.log(`[mcp] 用户 ID: ${userId}`);
|
|
400
|
+
// 配置完成后断开连接
|
|
401
|
+
tempClient.disconnect();
|
|
402
|
+
// 首次安装后自动后台启动服务
|
|
403
|
+
console.log('\n[mcp] 正在后台启动 MCP Server...');
|
|
404
|
+
startMcpServerBackground();
|
|
205
405
|
console.log('[mcp] 请重启 Claude Code 以加载 MCP 服务\n');
|
|
206
406
|
process.exit(0);
|
|
207
407
|
}
|
|
208
|
-
//
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
});
|
|
213
|
-
// 注册工具
|
|
214
|
-
registerTools(server, wecomClient);
|
|
215
|
-
// 启动 HTTP 服务(包含 MCP endpoint)
|
|
216
|
-
console.log(`[mcp] 启动 MCP HTTP Server (端口: ${HTTP_PORT})...`);
|
|
217
|
-
await startHttpServer(wecomClient, server);
|
|
218
|
-
// 定期清理过期消息
|
|
219
|
-
const cleanupInterval = setInterval(() => {
|
|
220
|
-
wecomClient.cleanupMessages();
|
|
221
|
-
}, 60000);
|
|
222
|
-
console.log(`[mcp] MCP Server 已就绪`);
|
|
223
|
-
console.log(`[mcp] HTTP endpoint: http://127.0.0.1:${HTTP_PORT}/mcp`);
|
|
224
|
-
console.log(`[mcp] 健康检查: http://127.0.0.1:${HTTP_PORT}/health`);
|
|
225
|
-
// 退出处理:清理资源
|
|
226
|
-
const gracefulShutdown = () => {
|
|
227
|
-
console.log('[mcp] 正在关闭...');
|
|
228
|
-
clearInterval(cleanupInterval);
|
|
229
|
-
wecomClient.disconnect();
|
|
230
|
-
process.exit(0);
|
|
231
|
-
};
|
|
232
|
-
// 监听进程信号
|
|
233
|
-
process.on('SIGINT', gracefulShutdown);
|
|
234
|
-
process.on('SIGTERM', gracefulShutdown);
|
|
408
|
+
// 已有配置,显示状态并提示启动命令
|
|
409
|
+
showStatus();
|
|
410
|
+
console.log('\n[mcp] 使用 --start 启动服务,--stop 停止服务');
|
|
411
|
+
console.log('[mcp] 命令: npx @vrs-soft/wecom-aibot-mcp --start\n');
|
|
235
412
|
}
|
|
236
413
|
main().catch((err) => {
|
|
237
414
|
console.error('[mcp] 启动失败:', err);
|
package/dist/client.d.ts
CHANGED
|
@@ -4,6 +4,10 @@ interface ApprovalRecord {
|
|
|
4
4
|
result?: 'allow-once' | 'allow-always' | 'deny';
|
|
5
5
|
timestamp: number;
|
|
6
6
|
toolName?: string;
|
|
7
|
+
toolInput?: Record<string, unknown>;
|
|
8
|
+
projectDir?: string;
|
|
9
|
+
lastKeepaliveMinute?: number;
|
|
10
|
+
keepaliveCount?: number;
|
|
7
11
|
}
|
|
8
12
|
interface MessageRecord {
|
|
9
13
|
msgid: string;
|
|
@@ -41,6 +45,8 @@ declare class WecomClient {
|
|
|
41
45
|
sendApprovalRequest(title: string, description: string, requestId: string, targetUser?: string): Promise<string>;
|
|
42
46
|
getApprovalResult(taskId: string): 'pending' | 'allow-once' | 'allow-always' | 'deny';
|
|
43
47
|
getPendingApprovals(): string[];
|
|
48
|
+
getPendingApprovalsRecords(): ApprovalRecord[];
|
|
49
|
+
getApprovalRecord(taskId: string): ApprovalRecord | undefined;
|
|
44
50
|
getLatestMessage(afterTimestamp: number): MessageRecord | undefined;
|
|
45
51
|
getPendingMessages(clear?: boolean): MessageRecord[];
|
|
46
52
|
cleanupMessages(maxAgeMs?: number): void;
|
package/dist/client.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* 管理消息队列和审批状态。
|
|
6
6
|
*/
|
|
7
7
|
import AiBot from '@wecom/aibot-node-sdk';
|
|
8
|
+
import { logConnected, logAuthenticated, logDisconnected, logReconnecting, logError, } from './connection-log.js';
|
|
8
9
|
class WecomClient {
|
|
9
10
|
wsClient;
|
|
10
11
|
approvals = new Map();
|
|
@@ -22,7 +23,7 @@ class WecomClient {
|
|
|
22
23
|
this.wsClient = new AiBot.WSClient({
|
|
23
24
|
botId,
|
|
24
25
|
secret,
|
|
25
|
-
heartbeatInterval:
|
|
26
|
+
heartbeatInterval: 15000, // 15 秒心跳,更快检测断线
|
|
26
27
|
maxReconnectAttempts: -1, // 无限重连
|
|
27
28
|
});
|
|
28
29
|
this.setupEventHandlers();
|
|
@@ -37,14 +38,14 @@ class WecomClient {
|
|
|
37
38
|
}
|
|
38
39
|
setupEventHandlers() {
|
|
39
40
|
this.wsClient.on('connected', () => {
|
|
40
|
-
|
|
41
|
+
logConnected();
|
|
41
42
|
});
|
|
42
43
|
this.wsClient.on('authenticated', () => {
|
|
43
44
|
const wasReconnecting = this.wasReconnecting;
|
|
44
45
|
this.connected = true;
|
|
45
46
|
this.wasReconnecting = false;
|
|
46
47
|
this.reconnectAttempt = 0;
|
|
47
|
-
|
|
48
|
+
logAuthenticated();
|
|
48
49
|
// 重连成功后发送通知
|
|
49
50
|
if (wasReconnecting) {
|
|
50
51
|
this.sendText('【系统】连接已恢复').catch(err => {
|
|
@@ -58,7 +59,7 @@ class WecomClient {
|
|
|
58
59
|
this.connected = false;
|
|
59
60
|
this.wasReconnecting = true;
|
|
60
61
|
this.lastDisconnectTime = Date.now();
|
|
61
|
-
|
|
62
|
+
logDisconnected(reason);
|
|
62
63
|
// 发送断线通知
|
|
63
64
|
this.sendText('【系统】连接中断,正在重连...').catch(err => {
|
|
64
65
|
console.error('[wecom] 发送断线通知失败:', err);
|
|
@@ -66,10 +67,10 @@ class WecomClient {
|
|
|
66
67
|
});
|
|
67
68
|
this.wsClient.on('reconnecting', (attempt) => {
|
|
68
69
|
this.reconnectAttempt = attempt;
|
|
69
|
-
|
|
70
|
+
logReconnecting(attempt);
|
|
70
71
|
});
|
|
71
72
|
this.wsClient.on('error', (err) => {
|
|
72
|
-
|
|
73
|
+
logError(err.message);
|
|
73
74
|
// 检测授权相关错误(40058: invalid Request Parameter)
|
|
74
75
|
if (err.message.includes('40058') || err.message.includes('invalid Request Parameter')) {
|
|
75
76
|
console.log('');
|
|
@@ -90,6 +91,7 @@ class WecomClient {
|
|
|
90
91
|
});
|
|
91
92
|
// 监听模板卡片事件(审批结果)
|
|
92
93
|
this.wsClient.on('event.template_card_event', (frame) => {
|
|
94
|
+
console.log('[wecom] 收到 template_card_event 事件');
|
|
93
95
|
this.handleApprovalResponse(frame);
|
|
94
96
|
});
|
|
95
97
|
// 监听进入会话事件
|
|
@@ -304,6 +306,15 @@ class WecomClient {
|
|
|
304
306
|
.filter(([_, a]) => !a.resolved)
|
|
305
307
|
.map(([taskId, _]) => taskId);
|
|
306
308
|
}
|
|
309
|
+
// 获取所有待处理审批记录(供保活监控使用)
|
|
310
|
+
getPendingApprovalsRecords() {
|
|
311
|
+
return Array.from(this.approvals.values())
|
|
312
|
+
.filter(a => !a.resolved);
|
|
313
|
+
}
|
|
314
|
+
// 获取单个审批记录
|
|
315
|
+
getApprovalRecord(taskId) {
|
|
316
|
+
return this.approvals.get(taskId);
|
|
317
|
+
}
|
|
307
318
|
// 获取最新消息(用于等待回复)
|
|
308
319
|
getLatestMessage(afterTimestamp) {
|
|
309
320
|
return this.messages.find(m => m.timestamp > afterTimestamp && m.from_userid === this.targetUserId);
|
package/dist/config-wizard.d.ts
CHANGED
|
@@ -16,8 +16,18 @@ export declare function deleteMcpConfig(instanceName: string): boolean;
|
|
|
16
16
|
export declare function deleteMcpConfigInteractive(instanceName?: string): Promise<void>;
|
|
17
17
|
export declare function uninstall(): void;
|
|
18
18
|
export declare function addMcpConfig(): Promise<void>;
|
|
19
|
+
export declare function listAllRobots(): Array<{
|
|
20
|
+
name: string;
|
|
21
|
+
botId: string;
|
|
22
|
+
targetUserId: string;
|
|
23
|
+
isDefault: boolean;
|
|
24
|
+
}>;
|
|
19
25
|
export declare function ensureHookInstalled(): void;
|
|
20
26
|
export declare function saveConfig(config: WecomConfig, instanceName?: string): void;
|
|
27
|
+
/**
|
|
28
|
+
* 安装 headless-mode skill 到项目目录
|
|
29
|
+
*/
|
|
30
|
+
export declare function installSkill(projectDir: string): void;
|
|
21
31
|
/**
|
|
22
32
|
* 运行配置向导
|
|
23
33
|
*/
|