@vrs-soft/wecom-aibot-mcp 2.4.23 → 2.4.25

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.
@@ -12,9 +12,50 @@
12
12
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
13
13
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
14
14
  import { z } from 'zod';
15
+ import { execSync } from 'child_process';
15
16
  import { VERSION, installSkill } from './config-wizard.js';
16
17
  import { addPermissionHook, registerActiveProject, unregisterActiveProject, updateWechatModeConfig } from './project-config.js';
17
18
  import { logger } from './logger.js';
19
+ /**
20
+ * 沿进程树向上查找 Claude Code TUI 的 PID。
21
+ *
22
+ * 背景:本地 dev (`command: "node"`) 时 channel-server 是 Claude TUI 的直接子进程,
23
+ * process.ppid = Claude TUI ✓
24
+ * 但 npx 部署 (`command: "npx"`) 时多了一层 npx:
25
+ * Claude TUI → npx → node bin.js (channel-server)
26
+ * process.ppid = npx ❌
27
+ * permission-hook.sh 从 hook 自身向上查 active-projects.json 时只能命中 Claude TUI
28
+ * 这条祖先链。如果注册的是 npx 的 PID,hook 永远找不到 → 静默 exit 0 → 跳过审批。
29
+ *
30
+ * 此函数从 startPid 起向上遍历,找到第一个命令名为 "claude" 的进程,返回其 PID。
31
+ * 找不到时回退到 startPid(保持旧行为,至少 dev 场景不退化)。
32
+ */
33
+ function findClaudePid(startPid) {
34
+ let pid = startPid;
35
+ for (let i = 0; i < 8; i++) {
36
+ if (!pid || pid <= 1)
37
+ break;
38
+ try {
39
+ const comm = execSync(`ps -p ${pid} -o comm=`, { stdio: ['ignore', 'pipe', 'ignore'] })
40
+ .toString()
41
+ .trim();
42
+ // ps comm= 返回执行文件 basename。Claude Code TUI 安装名就是 "claude"
43
+ if (comm === 'claude' || comm.endsWith('/claude'))
44
+ return pid;
45
+ const ppidStr = execSync(`ps -p ${pid} -o ppid=`, { stdio: ['ignore', 'pipe', 'ignore'] })
46
+ .toString()
47
+ .trim();
48
+ const ppid = parseInt(ppidStr, 10);
49
+ if (!ppid || ppid === pid)
50
+ break;
51
+ pid = ppid;
52
+ }
53
+ catch {
54
+ break;
55
+ }
56
+ }
57
+ return startPid;
58
+ }
18
59
  const MCP_URL = process.env.MCP_URL || 'http://127.0.0.1:18963';
19
60
  const MCP_AUTH_TOKEN = process.env.MCP_AUTH_TOKEN;
20
61
  // 构建带 auth 的 fetch headers
@@ -437,9 +478,16 @@ function registerChannelTools(server) {
437
478
  const localProjectDir = project_dir || process.cwd();
438
479
  const hookResult = addPermissionHook(localProjectDir);
439
480
  logger.info('本地 PermissionRequest hook 已写入', { path: hookResult.path, success: hookResult.success });
440
- // 注册本地 PID projectDir(供本地 permission-hook.sh 通过进程树匹配项目)
441
- registerActiveProject(process.ppid ?? process.pid, localProjectDir);
442
- logger.info('本地 active-projects 已注册', { pid: process.ppid ?? process.pid, projectDir: localProjectDir });
481
+ // 注册 Claude TUI PID(不能用 process.ppid,npx 部署时 ppid 是 npx 不是 Claude)
482
+ const startPid = process.ppid ?? process.pid;
483
+ const claudePid = findClaudePid(startPid);
484
+ registerActiveProject(claudePid, localProjectDir);
485
+ logger.info('本地 active-projects 已注册', {
486
+ pid: claudePid,
487
+ rawPpid: startPid,
488
+ resolvedClaudePid: claudePid !== startPid,
489
+ projectDir: localProjectDir,
490
+ });
443
491
  // 写入本地 wecom-aibot.json(远程 HTTP MCP 写在远端 fs,agent 本地需要自己落地)
444
492
  updateWechatModeConfig(localProjectDir, {
445
493
  wechatMode: true,
@@ -1450,25 +1450,70 @@ export async function runConfigWizard() {
1450
1450
  docMcpUrl = currentDocUrl;
1451
1451
  console.log('[config] 保持原文档 MCP URL');
1452
1452
  }
1453
- // 第六步:目标用户(稍后通过消息自动识别)
1453
+ // 第六步:目标用户(默认联系人)
1454
+ // 修改场景:询问是否要重新识别;选 Y 则连接 bot 等待用户消息(与 --add 一致);选 N 保持原值
1455
+ let targetUserId = targetRobot?.targetUserId || '';
1456
+ if (targetRobot) {
1457
+ console.log(`\n当前默认联系人(targetUserId): ${targetUserId || '(未设置)'}`);
1458
+ const changeContact = await question(rl, '是否重新识别?(y/N): ');
1459
+ if (changeContact.toLowerCase() === 'y') {
1460
+ // 临时连接 bot 等待用户消息以识别 userid
1461
+ console.log('\n[config] 正在连接企业微信验证凭证...');
1462
+ const { initClient } = await import('./client.js');
1463
+ const tmpClient = initClient(botId, secret, 'placeholder', 'config-detect');
1464
+ // 等待连接(最多10秒)
1465
+ const connected = await new Promise((resolve) => {
1466
+ const start = Date.now();
1467
+ const iv = setInterval(() => {
1468
+ if (tmpClient.isConnected()) {
1469
+ clearInterval(iv);
1470
+ resolve(true);
1471
+ }
1472
+ else if (Date.now() - start > 10000) {
1473
+ clearInterval(iv);
1474
+ resolve(false);
1475
+ }
1476
+ }, 500);
1477
+ });
1478
+ if (!connected) {
1479
+ console.log('[config] ❌ 连接失败(Bot ID/Secret 可能有误),保持原默认联系人');
1480
+ tmpClient.disconnect();
1481
+ }
1482
+ else {
1483
+ const detectedUserId = await detectUserIdFromMessage(tmpClient, 180);
1484
+ tmpClient.disconnect();
1485
+ if (detectedUserId) {
1486
+ targetUserId = detectedUserId;
1487
+ console.log(`[config] ✅ 默认联系人已更新: ${targetUserId}`);
1488
+ }
1489
+ else {
1490
+ console.log('[config] 未识别到用户消息,保持原默认联系人');
1491
+ }
1492
+ }
1493
+ }
1494
+ else {
1495
+ console.log('[config] 保持原默认联系人');
1496
+ }
1497
+ }
1498
+ // 第七步:确认
1454
1499
  console.log('\n─────────────────────────────────────');
1455
1500
  console.log('配置确认:');
1456
- console.log(` 机器人名称: ${robotName}`);
1457
- console.log(` Bot ID: ${botId}`);
1458
- console.log(` Secret: ${secret.slice(0, 8)}...${secret.slice(-4)}`);
1459
- console.log(` 文档 MCP: ${docMcpUrl ? '✅ 已配置' : '(未配置)'}`);
1460
- console.log(` 目标用户: (将通过消息自动识别)`);
1501
+ console.log(` 机器人名称: ${robotName}`);
1502
+ console.log(` Bot ID: ${botId}`);
1503
+ console.log(` Secret: ${secret.slice(0, 8)}...${secret.slice(-4)}`);
1504
+ console.log(` 文档 MCP: ${docMcpUrl ? '✅ 已配置' : '(未配置)'}`);
1505
+ console.log(` 默认联系人: ${targetUserId || '(将通过消息自动识别)'}`);
1461
1506
  console.log('─────────────────────────────────────\n');
1462
1507
  const confirm = await question(rl, '确认配置?(Y/n): ');
1463
1508
  if (confirm.toLowerCase() === 'n') {
1464
1509
  console.log('[config] 配置已取消');
1465
1510
  process.exit(0);
1466
1511
  }
1467
- // 返回临时配置(targetUserId 稍后填充)
1512
+ // 返回最终配置
1468
1513
  const config = {
1469
1514
  botId,
1470
1515
  secret,
1471
- targetUserId: targetRobot?.targetUserId || '', // 修改时保留原值,新建时稍后识别
1516
+ targetUserId, // 修改时保留 / 已变更,新建时为空(稍后识别)
1472
1517
  nameTag: robotName,
1473
1518
  ...(docMcpUrl ? { doc_mcp_url: docMcpUrl } : {}),
1474
1519
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vrs-soft/wecom-aibot-mcp",
3
- "version": "2.4.23",
3
+ "version": "2.4.25",
4
4
  "description": "企业微信智能机器人 MCP 服务 - Claude Code 审批通道",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",